代码之家  ›  专栏  ›  技术社区  ›  ravin.wang

将“午夜1904-1-1”后的秒数转换为日期时间字符串

  •  1
  • ravin.wang  · 技术社区  · 8 年前

    在某些多媒体元数据中,可能会有以秒为单位的日期时间,因为 UTC时间1904年1月1日午夜。

    我知道,日期时间函数通常基于C/C++标准库中的1970-1-1午夜,至少在Visual C++中是这样。C/C++/Win32 API中是否有函数将“1904-1-1午夜”后的秒数转换为日期时间字符串,如“hh:mm:ss mm.dd,yyyy”或其他格式字符串或类似“struct tm”的结构?

    struct tm
    {
        int tm_sec;   // seconds after the minute - [0, 60] including leap second
        int tm_min;   // minutes after the hour - [0, 59]
        int tm_hour;  // hours since midnight - [0, 23]
        int tm_mday;  // day of the month - [1, 31]
        int tm_mon;   // months since January - [0, 11]
        int tm_year;  // years since 1900
        int tm_wday;  // days since Sunday - [0, 6]
        int tm_yday;  // days since January 1 - [0, 365]
        int tm_isdst; // daylight savings time flag
    };
    

    解决方案#1:

    int main()
    {
        SYSTEMTIME systm;
        memset(&systm, 0, sizeof(systm));
    
        systm.wYear = 1904;
        systm.wMonth = 1;
        systm.wDay = 1;
    
        FILETIME filetm;
        if (SystemTimeToFileTime(&systm, &filetm) == FALSE){
            printf("Failed to convert system time to file-time.\n");
            return 0;
        }
    
        ULARGE_INTEGER nanoSeconds;
        nanoSeconds.HighPart = filetm.dwHighDateTime;
        nanoSeconds.LowPart = filetm.dwLowDateTime;
    
        nanoSeconds.QuadPart += 3600ULL * 10000000; // add 1hour based on 1904/1/1 midnight
    
        filetm.dwHighDateTime = nanoSeconds.HighPart;
        filetm.dwLowDateTime = nanoSeconds.LowPart;
    
        if (FileTimeToSystemTime(&filetm, &systm) == FALSE){
            printf("Failed to convert file-time to system time.\n");
            return 0;
        }
    
        printf("New system time by adding 1 hour: %d-%02d-%02d %02d:%02d:%02d.%03d\n", 
            systm.wYear, systm.wMonth, systm.wDay, 
            systm.wHour, systm.wMinute, systm.wSecond, systm.wMilliseconds);
    
        return 0;
    }
    

    输出为

    New system time by adding 1 hour: 1904-01-01 01:00:00.000
    

    解决方案#2:

    霍华德·希南特的约会。h、 它也可以解决这个问题,请参阅他提供的示例代码 https://stackoverflow.com/a/49733937/3968307

    3 回复  |  直到 8 年前
        1
  •  3
  •   Howard Hinnant    5 年前

    现在是使用的好时机 Howard Hinnant's free, open-source date/time library :

    #include "date/date.h"
    #include <cstdint>
    #include <iostream>
    #include <string>
    
    std::string
    convert(std::int64_t seconds_since_1904)
    {
        using namespace date;
        using namespace std::chrono;
        constexpr auto offset = sys_days{January/1/1970} - sys_days{January/1/1904};
        return format("%T %m.%d, %Y", sys_seconds{seconds{seconds_since_1904}} - offset);
    }
    
    int
    main()
    {
        std::cout << convert(3'606'124'378) << '\n';
    }
    

    输出:

    13:12:58 04.09, 2018
    

    使现代化

    上述代码将通过以下方式移植到C++20(当它发货时):

    • 改变 #include "date/date.h" #include <chrono>
    • 改变 using namespace date; using namespace std;
    • 改变 "%T %m.%d, %Y" "{:%T %m.%d, %Y}"
        2
  •  1
  •   vacuumhead    8 年前

    这里的时间问题实际上要求您为它编写自己的代码。1900年是一个例外,因为它可以被4整除,但仍然不是闰年,所以从1904年开始,你可以避免这个特殊的例外,并利用从1904年开始的每四年有1461天这一事实。

        3
  •  1
  •   Community Mohan Dere    4 年前

    正如您可以使用系统中的任何可用电子表格应用程序轻松计算的那样,这两个时间戳之间的秒差(假设两者都是UTC时间)与 1/1/1904 1/1/1970 2,082,844,800 sec. 所以转换函数 unix时间戳 为您的时间,包括添加 2082844800 到从任何时间函数接收的unix时间戳。如果要将时间中的时间戳传递回unix时间戳,请从时间刻度中减去该固定值。请注意,该数字与 signed int 因此,您必须使用64位数字来正确管理所有这些时间戳。如果要使用纳秒分辨率,则更糟。

    我猜不出使用那个奇怪的历元时间戳的原因,但为了说明这种差异的实际应用,互联网上有一个时间戳,它使用的历元与之接近,即NTP(网络时间协议)时间戳,它基于1900年1月1日的历元,分辨率为 1/2**32 sec. 就在附近 232 ps. 有关此协议的规范,请参阅 RFC-5905