在 windows 将时间字符串转换为纪元时间
Converting time string into epoch time on windows
我有一个获取文件的界面,文件名是内部数据有效时的时间戳。我正在按顺序处理文件并将它们提供给另一个 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 个。在之前的函数中,我已经验证了字符串格式正确、文件存在以及所有爵士乐。我正在 运行 windows 8.1 使用 VS 2010 编译 64 位。
我看过 Convert Epoch Time string to Time,它建议 ctime(),但它似乎不处理构造函数中的毫秒数,甚至不处理任何 get方法,它不接受通用格式的字符串输入。我假设我必须调用一些时间 类 CTOR,它将接受文件名字符串,然后在其上调用一些访问器以获取自 1970 年以来的毫秒时间,包括闰秒等。
我没有使用 boost,也没有 access/permission 来使用它。
您可以使用这段代码(您不必担心闰年和所有相关问题)。
@Edit1:修改代码以考虑闰秒;还将其重组为 class:
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;
}
备注:
无效dates/timesSystemTimeToFileTime
会失败
常量CFoo::msecsBetweenEpochs
我觉得可以在Google上找到;我从 Python(2.7.10) 的 posixmodule.c 拿来的(实际上有秒数;我只必须乘以 1000)
您的实施产生的结果不是很准确(我使用 http://www.epochconverter.com 作为参考)。
根据SystemTimeToFileTime,时间戳为UTC。
这是一个适用于任何支持 C++11 或 C++14 的平台的答案。它建立在 C++11 中引入的 std::chrono
库之上。它还使用免费、开源、跨平台的库来简化算法(通常被认为是 lawyer-friendly)的 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};
}
从文件名中解析出数字后,创建一个 type-safe std::chrono::time_point
非常简单,它仅保存自 1970-01-01 以来的整数毫秒数(作为 int64_t
).
如果要考虑闰秒,需要this higher-level library which is a complete parser of the IANA timezone database. You will also need to keep an updated copy of the IANA timezone database downloaded for 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"
header 现在包含一个 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));
}
我有一个获取文件的界面,文件名是内部数据有效时的时间戳。我正在按顺序处理文件并将它们提供给另一个 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 个。在之前的函数中,我已经验证了字符串格式正确、文件存在以及所有爵士乐。我正在 运行 windows 8.1 使用 VS 2010 编译 64 位。
我看过 Convert Epoch Time string to Time,它建议 ctime(),但它似乎不处理构造函数中的毫秒数,甚至不处理任何 get方法,它不接受通用格式的字符串输入。我假设我必须调用一些时间 类 CTOR,它将接受文件名字符串,然后在其上调用一些访问器以获取自 1970 年以来的毫秒时间,包括闰秒等。
我没有使用 boost,也没有 access/permission 来使用它。
您可以使用这段代码(您不必担心闰年和所有相关问题)。 @Edit1:修改代码以考虑闰秒;还将其重组为 class:
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;
}
备注:
无效dates/times
SystemTimeToFileTime
会失败常量
CFoo::msecsBetweenEpochs
我觉得可以在Google上找到;我从 Python(2.7.10) 的 posixmodule.c 拿来的(实际上有秒数;我只必须乘以 1000)您的实施产生的结果不是很准确(我使用 http://www.epochconverter.com 作为参考)。
根据SystemTimeToFileTime,时间戳为UTC。
这是一个适用于任何支持 C++11 或 C++14 的平台的答案。它建立在 C++11 中引入的 std::chrono
库之上。它还使用免费、开源、跨平台的库来简化算法(通常被认为是 lawyer-friendly)的 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};
}
从文件名中解析出数字后,创建一个 type-safe std::chrono::time_point
非常简单,它仅保存自 1970-01-01 以来的整数毫秒数(作为 int64_t
).
如果要考虑闰秒,需要this higher-level library which is a complete parser of the IANA timezone database. You will also need to keep an updated copy of the IANA timezone database downloaded for 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"
header 现在包含一个 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));
}