在Windows上将时间字符串转换为纪元时间

时间:2015-08-25 18:04:35

标签: c++ windows time

我有一个界面,我获取文件,文件名是内部数据有效时的时间戳。我按顺序处理文件并将它们提供给另一个API,自1970年1月1日起,该时间为毫秒。

因此,我将文件名作为字符串以YYYYMMDD_HHmmSS.sss的格式获取,并且必须将其转换为时间。我假设处理和收集发生在同一时区,只需将字符转换为数字。

uint64_t foo::convertFileNameToTimestamp(const std::string& filename) const
{
    const uint64_t yyyy = atoi(filename.substr(0,4).c_str());
    const uint64_t MM = atoi(filename.substr(4,2).c_str());
    const uint64_t DD = atoi(filename.substr(6,2).c_str());
    const uint64_t HH = atoi(filename.substr(9,2).c_str());
    const uint64_t MI = atoi(filename.substr(11,2).c_str());
    const uint64_t SS = atoi(filename.substr(13,2).c_str());
    const uint64_t sss = atoi(filename.substr(16,3).c_str());

    // number of milliseconds in a day
    const uint64_t DAY = 24 * 60 * 60 * 1000;
    int MD = 0;
    if (MM == 2) MD = 31; // currently feb, so add all of january's days
    else if (MM == 3) MD = 31+28; // ...
    else if (MM == 4) MD = 31+28+31;
    else if (MM == 5) MD = 31+28+31+30;
    else if (MM == 6) MD = 31+28+31+30+31;
    else if (MM == 7) MD = 31+28+31+30+31+30;
    else if (MM == 8) MD = 31+28+31+30+31+30+31;
    else if (MM == 9) MD = 31+28+31+30+31+30+31+31;
    else if (MM == 10) MD = 31+28+31+30+31+30+31+31+30;
    else if (MM == 11) MD = 31+28+31+30+31+30+31+31+30+31;
    else if (MM == 12) MD = 31+28+31+30+31+30+31+31+30+31+30;

    // year 2000 wasn't a leap year
    uint64_t YLD = ((yyyy-1970) / 4);
    if (yyyy > 2000) --YLD;

    uint64_t temp = sss;
    temp += SS * 1000;
    temp += MI * 60 * 1000;
    temp += HH * 60 * 60 * 1000;
    temp += (DD-1) * DAY;
    temp += (MD) * DAY;
    temp += (yyyy-1970) * 365 * DAY + YLD*DAY;

    return temp;
}

显然,在这里重新发明轮子。似乎应该有某种功能。另外..我如何计算闰秒?处理闰日很烦人。时间戳都来自2015年及以后,而且总是如此,但我不认为我可以盲目地增加26秒。最终我们将有27或最多25个。在以前的函数中,我已经验证了字符串是正确的格式和存在的文件以及所有爵士乐。我使用VS 2010运行Windows 8.1编译64位。

我查看了提出 ctime()Convert Epoch Time string to Time,但它似乎没有处理构造函数中的毫秒,甚至任何get方法并且它不接受通用格式的字符串输入。我假设我必须调用一些时间类CTOR,它将接受文件名字符串,然后在其上调用一些访问器以获得自1970年以来的毫秒时间,包括闰秒等。

我没有使用提升功能,也没有权限使用它。

2 个答案:

答案 0 :(得分:0)

你可以使用这段代码(你不必担心闰年和所有相关的东西)。 @ Edit1:修改代码以考虑闰秒;还将其重组为一个类:

<强> Foo.h

#ifndef __FOO__H__
#define __FOO__H__

#include <string>
#include <Windows.h>
#include <stdint.h>

class CFoo {
private:
    const static int kLeapSecsDim = 26;
    static uint64_t msecsBetweenEpochs;
    static SYSTEMTIME leapSecs[kLeapSecsDim];

    ULARGE_INTEGER leapSecsUi[kLeapSecsDim];

    int CFoo::getLeapSeconds(ULARGE_INTEGER ui) const;
public:
    CFoo();
    ~CFoo() {};

    uint64_t toEpoch(const std::string& filename) const;
};

#endif  //__FOO__H__

<强> Foo.cpp

#include "Foo.h"

uint64_t CFoo::msecsBetweenEpochs = 11644473600000; /* Milliseconds between 1.1.1601 and 1.1.1970 */
SYSTEMTIME CFoo::leapSecs[CFoo::kLeapSecsDim] = 
                            {{1972, 06, 0, 30, 23, 59, 59, 999},
                             {1972, 12, 0, 31, 23, 59, 59, 999},
                             {1973, 12, 0, 31, 23, 59, 59, 999},
                             {1974, 12, 0, 31, 23, 59, 59, 999},
                             {1975, 12, 0, 31, 23, 59, 59, 999},
                             {1976, 12, 0, 31, 23, 59, 59, 999},
                             {1977, 12, 0, 31, 23, 59, 59, 999},
                             {1978, 12, 0, 31, 23, 59, 59, 999},
                             {1979, 12, 0, 31, 23, 59, 59, 999},
                             {1981, 06, 0, 30, 23, 59, 59, 999},
                             {1982, 06, 0, 30, 23, 59, 59, 999},
                             {1983, 06, 0, 30, 23, 59, 59, 999},
                             {1985, 06, 0, 30, 23, 59, 59, 999},
                             {1987, 12, 0, 31, 23, 59, 59, 999},
                             {1989, 12, 0, 31, 23, 59, 59, 999},
                             {1990, 12, 0, 31, 23, 59, 59, 999},
                             {1992, 06, 0, 30, 23, 59, 59, 999},
                             {1993, 06, 0, 30, 23, 59, 59, 999},
                             {1994, 06, 0, 30, 23, 59, 59, 999},
                             {1995, 12, 0, 31, 23, 59, 59, 999},
                             {1997, 06, 0, 30, 23, 59, 59, 999},
                             {1998, 12, 0, 31, 23, 59, 59, 999},
                             {2005, 12, 0, 31, 23, 59, 59, 999},
                             {2008, 12, 0, 31, 23, 59, 59, 999},
                             {2012, 06, 0, 30, 23, 59, 59, 999},
                             {2015, 06, 0, 30, 23, 59, 59, 999},
                            };


int CFoo::getLeapSeconds(ULARGE_INTEGER ui) const {
    int ret = 0;
    for (int i = 0; i < kLeapSecsDim; i++) {
        if (ui.QuadPart <= this->leapSecsUi[i].QuadPart)
            break;
        ret++;
    }
    return ret;
}

CFoo::CFoo() {
    FILETIME ft;
    BOOL res;
    for (int i = 0; i < this->kLeapSecsDim; i++) {
        res = SystemTimeToFileTime(&(this->leapSecs[i]), &ft);
        if (res == FALSE)
            throw std::exception("SystemTimeToFileTime error", GetLastError());
        this->leapSecsUi[i].LowPart = ft.dwLowDateTime;
        this->leapSecsUi[i].HighPart = ft.dwHighDateTime;
    }
}

uint64_t CFoo::toEpoch(const std::string& filename) const {
    SYSTEMTIME st;
    FILETIME ft;
    ULARGE_INTEGER ui;
    st.wYear = atoi(filename.substr(0, 4).c_str());
    st.wMonth = atoi(filename.substr(4, 2).c_str());
    st.wDay = atoi(filename.substr(6, 2).c_str());
    st.wHour = atoi(filename.substr(9, 2).c_str());
    st.wMinute = atoi(filename.substr(11, 2).c_str());
    st.wSecond = atoi(filename.substr(13, 2).c_str());
    st.wMilliseconds = atoi(filename.substr(16, 3).c_str());
    BOOL result = SystemTimeToFileTime(&st, &ft);
    if (result == FALSE)
        throw std::exception("SystemTimeToFileTime error", GetLastError());
    ui.HighPart = ft.dwHighDateTime;
    ui.LowPart = ft.dwLowDateTime;
    //printf("%016I64X - %I64u\n", ui.QuadPart, ui.QuadPart);
    //printf("%016I64X - %I64u\n", ui.QuadPart/10000, ui.QuadPart/10000);
    return (ui.QuadPart / 10000) - this->msecsBetweenEpochs + this->getLeapSeconds(ui) * 1000;
}

备注

  • 对于无效的日期/时间SystemTimeToFileTime将失败

  • 常数CFoo::msecsBetweenEpochs我认为它可以在Google上找到;我从 Python(2.7.10)&#39; s posixmodule.c 中取出它(实际上有秒数;我只需将它乘以1000)

  • 您的实施会产生不太准确的结果(我使用http://www.epochconverter.com作为参考)。

  • 根据SystemTimeToFileTime,时间戳位于 UTC

答案 1 :(得分:0)

这个答案适用于任何支持C ++ 11或C ++ 14的平台。它建立在C ++ 11中引入的std::chrono库之上。它还使用免费的开源跨平台库来简化算术(MIT许可证通常被认为是律师友好型)。

如果您不需要考虑闰秒,可以使用this date library,它将如下所示:

#include <string>
#include "date.h"

using time_stamp = std::chrono::time_point<std::chrono::system_clock,
                                           std::chrono::milliseconds>;

time_stamp
convertFileNameToTimestamp(const std::string& filename)
{
    using namespace std::chrono;
    using namespace date;
    const uint64_t yyyy = atoi(filename.substr(0,4).c_str());
    const uint64_t MM = atoi(filename.substr(4,2).c_str());
    const uint64_t DD = atoi(filename.substr(6,2).c_str());
    const uint64_t HH = atoi(filename.substr(9,2).c_str());
    const uint64_t MI = atoi(filename.substr(11,2).c_str());
    const uint64_t SS = atoi(filename.substr(13,2).c_str());
    const uint64_t sss = atoi(filename.substr(16,3).c_str());
    return sys_days{year(yyyy)/MM/DD}
         + hours{HH} + minutes{MI} + seconds{SS} + milliseconds{sss};
}

解析文件名后的数字后,创建一个类型安全std::chrono::time_point非常简单,它自1970-01-01以来只保留一个整数毫秒(作为int64_t

如果您想考虑闰秒,则需要this higher-level library,这是IANA timezone database的完整解析器。您还需要保留为IANA timezone database下载的my timezone/leap-second library的更新副本以进行解析。但是一旦设置好,转换器的源代码与上面的代码非常相似,几乎一样简单:

#include <string>
#include "tz.h"

using time_stamp_ls = std::chrono::time_point<date::utc_clock,
                                              std::chrono::milliseconds>;

time_stamp_ls
convertFileNameToTimestamp_ls(const std::string& filename)
{
    using namespace std::chrono;
    using namespace date;
    const uint64_t yyyy = atoi(filename.substr(0,4).c_str());
    const uint64_t MM = atoi(filename.substr(4,2).c_str());
    const uint64_t DD = atoi(filename.substr(6,2).c_str());
    const uint64_t HH = atoi(filename.substr(9,2).c_str());
    const uint64_t MI = atoi(filename.substr(11,2).c_str());
    const uint64_t SS = atoi(filename.substr(13,2).c_str());
    const uint64_t sss = atoi(filename.substr(16,3).c_str());
    return utc_clock::sys_to_utc(sys_days{year(yyyy)/MM/DD}
         + hours{HH} + minutes{MI} + seconds{SS} + milliseconds{sss});
}

这两个函数都可以使用以下HelloWorld来执行:

#include <iostream>

int
main()
{
    std::string filename = "20150830_002120.123";
    std::cout << convertFileNameToTimestamp   (filename).time_since_epoch().count() << '\n';
    std::cout << convertFileNameToTimestamp_ls(filename).time_since_epoch().count() << '\n';
}

输出:

1440894080123
1440894106123

请注意,这些时间戳恰好相隔26,000毫秒。

<强>更新

"tz.h"标题现在包含parse函数,这使得编写这些函数更多更容易:

date::sys_time<std::chrono::milliseconds>
convertFileNameToTimestamp(const std::string& filename)
{
    using namespace std::chrono;
    using namespace date;
    std::istringstream in{filename};
    sys_time<milliseconds> tp;
    parse(in, "%Y%m%d_%H%M%S", tp);
    return tp;
}

date::utc_time<std::chrono::milliseconds>
convertFileNameToTimestamp_ls(const std::string& filename)
{
    return date::to_utc_time(convertFileNameToTimestamp(filename));
}