我正在尝试了解二进制文件(.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
答案 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
中此字段的文件。要使用double
和unsigned 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