我需要使用 C++ 将双精度值 Pascal TDateTime 对象转换为 Unix 纪元。
提出了一个可能的解决方案():
unsigned int UnixStartDate = 25569;
unsigned int DateTimeToUnix(double ConvDate)
{
return((unsigned int)((ConvDate - UnixStartDate) * 86400.0));
}
但是,此转换代码会产生如下错误:
TDateTime 时间值 = 37838.001388888886 (05.08.2003 02:00)
它转换为 Unix 纪元 1060041719 (05.08.2003 00:01:59),这显然是不正确的。
如何准确转换这个 TDateTime 值?
Delphi/C++Builder RTL 有一个DateTimeToUnix()函数就是为了这个目的。
In a TDateTime
,整数部分是从December 30 1899
,小数部分是一天中的时间00:00:00.000
。使用涉及不止一整天的原始数学可能有点棘手,因为浮点数学不准确.
例如,0.001388888886
不完全是00:02:00
,它更接近于00:01:59.999
。所以你遇到了舍入问题,这正是你必须注意的。TDateTime
具有毫秒精度,并且有86400000
一天中有几毫秒,所以.001388888886
is 119999.9997504
过去几毫秒00:00:00.000
,即00:01:59
如果这些毫秒被截断为119999
, or 00:02:00
如果它们被四舍五入到120000
.
RTL 停止使用浮点运算TDateTime
几年前由于微妙的精度损失。现代的TDateTime
操作通过进行往返TTimeStamp现在要避免这种情况。
由于您尝试从 RTL 外部执行此操作,因此您需要在代码中实现相关算法。您所展示的算法是 RTL 如何used转换一个TDateTime
到许多年前的 Unix 时间戳,但现在不再这样做了。当前的算法现在看起来更像这样(从原始 Pascal 转换为 C++):
#include <cmath>
#define HoursPerDay 24
#define MinsPerHour 60
#define SecsPerMin 60
#define MSecsPerSec 1000
#define MinsPerDay (HoursPerDay * MinsPerHour)
#define SecsPerDay (MinsPerDay * SecsPerMin)
#define SecsPerHour (SecsPerMin * MinsPerHour)
#define MSecsPerDay (SecsPerDay * MSecsPerSec)
#define UnixDateDelta 25569 // Days between TDateTime basis (12/31/1899) and Unix time_t basis (1/1/1970)
#define DateDelta 693594 // Days between 1/1/0001 and 12/31/1899
const float FMSecsPerDay = MSecsPerDay;
const int IMSecsPerDay = MSecsPerDay;
struct TTimeStamp
{
int Time; // Number of milliseconds since midnight
int Date; // One plus number of days since 1/1/0001
};
typedef double TDateTime;
TTimeStamp DateTimeToTimeStamp(TDateTime DateTime)
{
__int64 LTemp = std::round(DateTime * FMSecsPerDay); // <-- this might require tweaking!
__int64 LTemp2 = LTemp / IMSecsPerDay;
TTimeStamp Result;
Result.Date = DateDelta + LTemp2;
Result.Time = std::abs(LTemp) % IMSecsPerDay;
return Result;
}
__int64 DateTimeToMilliseconds(const TDateTime ADateTime)
{
TTimeStamp LTimeStamp = DateTimeToTimeStamp(ADateTime);
return (__int64(LTimeStamp.Date) * MSecsPerDay) + LTimeStamp.Time;
}
__int64 SecondsBetween(const TDateTime ANow, const TDateTime AThen)
{
return std::abs(DateTimeToMilliseconds(ANow) - DateTimeToMilliseconds(AThen)) / MSecsPerSec;
}
__int64 DateTimeToUnix(const TDateTime AValue)
{
__int64 Result = SecondsBetween(UnixDateDelta, AValue);
if (AValue < UnixDateDelta)
Result = -Result;
return Result;
}
请注意我的评论DateTimeToTimeStamp()
。我不确定是否std::round()产生exactly与德尔福的结果相同System::Round()对于所有值。你必须尝试一下。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)