delphi 如何在C++中将Pascal TDateTime(double)时间转换为Unix纪元

dvtswwa3  于 2022-11-04  发布在  Unix
关注(0)|答案(2)|浏览(250)

我需要从一个Pascal TDateTime对象,这是一个双精度值转换为Unix纪元使用c++。
提出了一种可能的解决方案(https://forextester.com/forum/viewtopic.php?f=8&t=1000):

unsigned int UnixStartDate = 25569;

unsigned int DateTimeToUnix(double ConvDate)
{
  return((unsigned int)((ConvDate - UnixStartDate) * 86400.0));
}

但是,此转换代码会产生如下错误:
T日期时间时间值= 37838.001388888886(2003年8月5日02:00)
这将转换为Unix纪元1060041719(05.08.2003 00:01:59),这显然是不正确的。
如何准确地转换此TDateTime值?

jogvjijk

jogvjijk1#

Delphi /CBuilder RTL中有一个DateTimeToUnix()函数就是用于此目的的。
TDateTime中,整数部分是December 30 1899中的天数,小数部分是00:00:00.000中的时间。由于floating-point math is inaccurate,使用包含不止一整天的原始数学可能会有点棘手。
例如,0.001388888886并不完全等于00:02:00,它更接近于00:01:59.999。因此,您遇到了舍入问题,这正是您必须注意的问题。TDateTime具有毫秒精度,一天中有86400000毫秒,因此.001388888886等于00:00:00.000之后的119999.9997504毫秒。如果这些毫秒被舍位为119999,则该值为00:01:59,或者如果它们被上舍入为120000,则该值为00:02:00
RTL在TDateTime上停止使用浮点运算是因为几年前会有细微的精度损失。现在的TDateTime操作会在TTimeStamp上来回运行以避免这种情况。
由于您尝试在RTL之外执行此操作,因此您需要在代码中实现相关算法。您所展示的算法是许多年前RTL * 如何使用 * 将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()是否对所有值都产生 * 完全 * 相同的结果。您必须用它进行实验。

相关问题