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

时间:2017-03-20 08:38:00

标签: c++ macos outlook

我正在尝试了解二进制文件(.olk Outlook office 2016 for MAC)的工作原理。 我正在使用十六进制编辑器这样做。我理解大部分文件,但是我遇到了这个时间戳问题。我100%确定这个十六进制的一部分是实际的时间戳:

'00 00 00 5F BF 35 BE 41'

在屏幕上,输出:2017年1月23日10:04:39

根据小端,

'00 00 00 5F BF 35 BE 41'的值为4737282954089201664

根据big endian,<00> '00 00 00 5F BF 35 BE 41'的价值是411229863489

如果我编辑(修改)十六进制值00 00 00 5F BF 35 BE 41比我得到的日期和时间是2001年1月1日05:30:00

2 个答案:

答案 0 :(得分:1)

您的时间戳看起来像存储为小端的64位浮点值。

我写这篇文章的时间是2001年1月1日00:00:00 UTC以来的秒数是511696145.577641,该64位双精度数的十六进制表示是41be7fdd1193e048。鉴于您的样本日期是在Jan,这似乎是正确的。

评论后的附录

阅读您的问题主要问题似乎是确定如何存储时间戳。我的假设是它是标准的Mac / Cocoa浮点时间间隔。我使用以下代码测试了该假设:

union

timeIntervalSinceReferenceDate是一种访问浮点值的简单方法,union { double d; int64_t i; } di; di.i = 0x41BE35BF5F000000; NSDate *stamp = [NSDate dateWithTimeIntervalSinceReferenceDate:di.d]; NSLog(@"%llx -> %@", di.i, stamp); 给出了自Mac OS X / Cocoa时代以来的秒数。这产生(当最初运行时)上面显示的值。我证明了我的假设。

如果您希望使用相同的技术,可以使用相同的方法:

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

这会产生:

double *

(这与您的时间不同,看起来您在印度显示的时间是本地时间,而上面显示的是UTC。)

如果你想用C(++)而不是Objective-C(++)或Swift和Cocoa来操作时间戳,你可以使用标准的C库函数,只要你考虑到不同的时期。你的时间戳是基于2001年1月1日的时代,&#34; Unix&#34;时间从1970年1月1日开始。要将您的时间戳解释为Unix时间偏移,您必须首先将2001年1月1日00:00 UTC的Unix时间偏移添加到它,您可以使用C例程生成该值。

如果从缓冲区读取值的字节,可以将缓冲区地址强制转换为double timeOffset = *(double *)&buf[Time_STAMP_OFFSET]; 并且间接,因为Intel cpu支持错位访问,例如类似的东西:

memcpy()

然而对于健壮的代码,您应该使用类似double的内容将字节复制到public class Base { public virtual void foo() { Debug.WriteLine("Base foo()"); } } public class A : Base { public override void foo() { base.foo(); Debug.WriteLine("A foo()"); } } public class B : Base { public override void foo() { base.foo(); Debug.WriteLine("B foo()"); } } class Program { static void Main(string[] args) { List<Base> list = new List<Base>(); list.Add(new A()); list.Add(new B()); list.Add(new A()); foreach (Base item in list) { item.foo(); } } } 变量中,这将在更换为不具备此功能的CPU后继续存在支持错位访问。您可能还希望添加对库函数的调用以将little-endian转换为主机字节顺序,以便对CPU字节顺序具有鲁棒性。

HTH

答案 1 :(得分:1)

基于CRD的良好(和赞成)答案,以及您对2001年1月1日05:30:00的评论,并假设您在修改十六进制值时的意思是全部为零,并使用这个free, open-source date library

#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';
}

输出:

2017-01-23 10:04:39

说明:

此字段是macOS(和Windows)上double的二进制表示形式。我伪造了一个包含上述变量file中此字段的文件。要使用doubleunsigned char数组的联合的非常标准技巧将其读入双精度数,读入数组,然后使用{{1} }。

此双精度是自2001-01-01 05:30:00以来的秒数。日期库首先用于形成具有此值的double纪元。然后std::chrono::time_point被转换为64位有符号整数类型,并进一步转换为double,然后添加到std::chrono::seconds以形成一个新的epoch精度并对应于您报告的值。此日期库为此std::chrono::time_point提供了一个流媒体运算符,以便于查看。

如果您希望编写自己的日历计算,则此日期库基于算法found here

如果您想将std::chrono::time_point分成代表年,月,日等的整数字段类型:

datetime