简体   繁体   English

将十六进制时间戳转换为可读的日期和时间

[英]convert hex Timestamp to readable date and time

I'm trying to understand how a binary file (.olk Outlook office 2016 for MAC) works. 我试图了解二进制文件(MAC的.olk Outlook Office 2016)如何工作。 I'm using a hex editor do this. 我正在使用十六进制编辑器执行此操作。 I understand most of the file, however I am stuck with this timestamp problem. 我了解大多数文件,但是仍然遇到此时间戳问题。 I am 100% sure that a part of this hex is the actual timestamp: 我100%确定此十六进制的一部分是实际时间戳记:

'00 00 00 5F BF 35 BE 41' '00 00 00 5F BF 35 BE 41'

On screen, it outputs: 23 Jan 2017 10:04:39 在屏幕上,其输出为:23 Jan 2017 10:04:39

'00 00 00 5F BF 35 BE 41' value is 4737282954089201664 according to little endian 根据小尾数法,``00 00 00 5F BF 35 BE 41''值为4737282954089201664

'00 00 00 5F BF 35 BE 41' value is 411229863489 according to big endian 根据大字节序,'00 00 00 5F BF 35 BE 41'值为411229863489

If i edit(modify) hex value 00 00 00 5F BF 35 BE 41 than i get Date and time which is 1 Jan 2001 05:30:00 如果我编辑(修改)十六进制值00 00 00 5F BF 35 BE 41比我得到的日期和时间是2001年1月1日05:30:00

Your time stamp looks like a 64-bit floating point value stored little endian. 您的时间戳看起来像一个存储了一点字节序的64位浮点值。

The number of seconds since 00:00:00 UTC on 1 January 2001 as I write this is 511696145.577641, and the hex representation of that 64-bit double is 41be7fdd1193e048. 自我编写此代码以来,自2001年1月1日00:00:00 UTC以来的秒数是511696145.577641,该64位双精度数的十六进制表示形式是41be7fdd1193e048。 Given your sample date was in Jan that seems about right. 鉴于您的样品日期是在一月份,这似乎是正确的。

Addendum after Comments 评论后的附录

Reading your question the main issue appeared to be determining how the time stamp was being stored. 阅读您的问题,主要问题似乎是确定时间戳的存储方式。 My hypothesis was that it was a standard Mac/Cocoa floating point time interval. 我的假设是这是标准的Mac / Cocoa浮点时间间隔。 I tested that hypothesis using the following code: 我使用以下代码测试了该假设:

union { double d; int64_t i; } di;
di.d = [[NSDate new] timeIntervalSinceReferenceDate];
NSLog(@"%f | %llx", di.d, di.i);

The union is an easy way to access the bits of a floating point value, and timeIntervalSinceReferenceDate gives the time in seconds since the Mac OS X/Cocoa epoch. union是访问浮点值的位的简单方法, timeIntervalSinceReferenceDatetimeIntervalSinceReferenceDate Mac OS X / Cocoa时代以来的时间(以秒为单位)。 This produced (when initially run) the values shown above. 这将产生(最初运行时)上面显示的值。 I'd proved my hypothesis. 我证明了我的假设。

If you wish to go the other way using the same technique you can use: 如果您希望使用相同的方法进行另一种选择,则可以使用:

union { double d; int64_t i; } di;
di.i = 0x41BE35BF5F000000;
NSDate *stamp = [NSDate dateWithTimeIntervalSinceReferenceDate:di.d];
NSLog(@"%llx -> %@", di.i, stamp);

and this produces: 这会产生:

41be35bf5f000000 -> 2017-01-23 04:34:39 +0000

(This differs from the time you have, it looks like you are displaying the times as local in India while the above is displaying in UTC.) (这与您所拥有的时间有所不同,似乎上面的时间以UTC显示时,您正在印度当地时间显示该时间。)

If you wish to manipulate the time stamp in C(++), rather than Objective-C(++) or Swift and Cocoa, you can use the standard C library functions as long as you take into account the different epochs. 如果您希望使用C(++)而不是Objective-C(++)或Swift和Cocoa来操作时间戳,则只要考虑到不同的时期,就可以使用标准的C库函数。 Your time stamp is based on an epoch 1 Jan 2001, "Unix" time starts at 1 Jan 1970. To interpret your time stamp as a Unix time offset you must add the Unix time offset for 1 Jan 2001 00:00 UTC to it first, you can generate that value using the C routines. 您的时间戳基于一个纪元2001年1月1日,“ Unix”时间始于1970年1月1日。要将您的时间戳解释为Unix时间偏移,您必须首先为其添加2001年1月1日00:00 UTC的Unix时间偏移。 ,您可以使用C例程生成该值。

If you are reading the bytes for your value out of a buffer you can just cast the buffer address to a double * and indirect, as the Intel cpu supports misaligned access, eg something like: 如果您要从缓冲区中读取值的字节,则可以将缓冲区地址强制转换为double *和间接地址,因为Intel cpu支持未对齐的访问,例如:

double timeOffset = *(double *)&buf[Time_STAMP_OFFSET];

However for robust code you should use something like memcpy() to copy the bytes into the double variable, which will survive changing to a CPU which doesn't support misaligned access. 但是,对于健壮的代码,您应该使用memcpy()类的东西将字节复制到double变量中,这样可以在更改为不支持未对齐访问的CPU时幸免。 You might also wish to add a call to a library function to convert little-endian to host byte order, to be robust against CPU endianness. 您可能还希望添加对库函数的调用,以将little-endian转换为主机字节顺序,以抵抗CPU字节序。

HTH 高温超导

Building on CRD's good (and upvoted) answer, and your comment concerning 1 Jan 2001 05:30:00, and assuming that you meant when you modify the hex value, it is to all zeroes, and using this free, open-source date library : 基于CRD的好答案(和赞成的),以及您对2001年1月1日05:30:00的评论,并假设您的意思是当您修改十六进制值时,它是全零,并使用此免费的开源日期图书馆

#include "date.h"
#include <cstdint>
#include <iostream>
#include <sstream>
#include <string>

int
main()
{
    std::string s = {'\x0', '\x0', '\x0', '\x5f', '\xbf', '\x35', '\xbe', '\x41'};
    std::istringstream file{s};
    union
    {
        double d;
        unsigned char b[8];
    };
    for (auto& c : b)
        file >> c;
    using namespace std::chrono;
    using namespace date;
    constexpr auto epoch = sys_days{jan/1/2001} + 5h + 30min;
    sys_seconds datetime = epoch + seconds{static_cast<std::int64_t>(d)};
    std::cout << datetime << '\n';
}

Output: 输出:

2017-01-23 10:04:39

Explanation: 说明:

This field is the binary representation of a double on macOS (and Windows). 此字段是macOS(和Windows)上的double的二进制表示形式。 I have faked a file containing this field in the variable file above. 我在上面的变量file伪造了一个包含此字段的file To read it into a double I'm using a not-quite standard trick of the union of a double and array of unsigned char , reading into the array, and then using the double . 要将其读为double,我使用了一个不很标准的技巧,将doubleunsigned char数组的并unsigned char ,读入数组,然后使用double

This double is the number of seconds since 2001-01-01 05:30:00. 这是从2001-01-01 05:30:00开始的秒数。 The date library is first used to form a std::chrono::time_point epoch with this value. 首先使用日期库形成具有此值的std::chrono::time_point纪元。 Then the double is cast to a 64bit signed integral type, and that is further converted to std::chrono::seconds , and then added to the epoch to form a new std::chrono::time_point which has seconds precision and corresponds to the value you report. 然后将double转换为64位有符号整数类型,然后将其进一步转换为std::chrono::seconds ,然后将其添加到epoch以形成新的std::chrono::time_point ,其精度为秒,对应于您报告的价值。 This date library has a streaming operator for this std::chrono::time_point for easy viewing. 此日期库具有用于此std::chrono::time_point的流运算符,以方便查看。

If you would rather write your own calendrical computations, this date library is based on the algorithms found here . 如果您想编写自己的日历计算,则此日期库基于此处找到的算法。

If you would like to break datetime into integral field types representing year, month, day, etc.: 如果您想将datetime分为代表年,月,日等的整数字段类型:

sys_days sd = floor<days>(datetime);
year_month_day ymd = sd;
auto tod = make_time(datetime - sd);
int y = int{ymd.year()};        // 2017
int m = unsigned{ymd.month()};  // 1
int d = unsigned{ymd.day()};    // 23
int h = tod.hours().count();    // 10
int M = tod.minutes().count();  // 4
int s = tod.seconds().count();  // 39

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM