我有一个boost::posix_time::ptime
对象(Boost v1.60),它具有系统时区中的日期和时间信息。我需要将其转换为UTC的unix时间戳。
time_t convertLocalPtimeToTimestamp(const boost::posix_time::ptime& pt)
{
using namespace boost::local_time;
static const time_t t_null = 0;
static struct tm* tm_local = localtime(&t_null);
static time_zone_ptr zone(new posix_time_zone(tm_local->tm_zone));
LOG(debug) << "Zone " << zone->to_posix_string();
local_date_time az(pt.date(), pt.time_of_day(), zone, local_date_time::EXCEPTION_ON_ERROR);
LOG(debug) << "local_date_time: " << az;
LOG(debug) << "local_time: " << az.local_time();
LOG(debug) << "utc_time: " << az.utc_time();
struct tm t = to_tm(az);
time_t ts = mktime(&t);
return ts;
}
以我为例(欧洲/马德里)的结果是:
Zone CET+00
local_date_time: 2016-Oct-05 17:36:27.701162 CET
local_time: 2016-Oct-05 17:36:27.701162
utc_time: 2016-Oct-05 17:36:27.701162
1475685387
这个结果有各种错误:
- 时区应该被检测为夏令时:CEST(+0200)而不是CET (+0100)
- 即使没有DST检测,utc_time也应该和local_time不一样。
- 最后的时间戳应该代表UTC时间,而不是本地时间。
这是我要解决的问题的Boost实现。
我不想使用其他第三方库,并且我在Windows中遇到了jgaida的答案问题(tm_zone和tm_gmtoff不是tm的成员)。
我通过在我的Windows 10电脑上多次更改时区来测试这段代码。
还测试了"(UTC-12:00)国际日期变更线西"one_answers"(UTC+14:00)基里蒂玛蒂岛"的边缘情况,结果是成功的。
time_t convertLocalPtimeToTimestamp (const boost::posix_time::ptime& pt)
{
using namespace boost::posix_time;
using namespace boost::local_time;
_tzset (); // This is needed if the time zone changes after the program starts.
// Grab copies of the current time in local and UTC form.
auto p_time = microsec_clock::universal_time (); // UTC.
auto lp_time = boost::date_time::c_local_adjustor<ptime>::utc_to_local (p_time);
// Calculate the difference between the UTC and local time and put it in a string.
// This will yield "-07:00:00" for PST and "-08:00:00" for PDT.
auto time_diff = to_simple_string (lp_time - p_time);
// Convert the ptime to a local_date_time object using the local time zone.
// We will create the time zone with the time difference string generated above.
local_date_time local_time (p_time, time_zone_ptr (new posix_time_zone (time_diff)));
// Calculate the difference in milliseconds between the UTC and local time.
auto time_t_diff = to_time_t (p_time) - to_time_t (local_time.local_time ());
// Return the given time in ms plus the time_t difference (to get the UTC ms).
return to_time_t (pt) + time_t_diff;
}
从这个免费的开源库:
#include "chrono_io.h"
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
auto az = make_zoned("Europe/Madrid",
local_days{2016_y/oct/5} + 17h + 36min + 27s + 701162us);
std::cout << "Zone " << az.get_time_zone()->name() << 'n';
std::cout << "local_date_time: " << az << 'n';
std::cout << "local_time: " << az.get_local_time() << 'n';
std::cout << "utc_time: " << az.get_sys_time() << 'n';
std::cout << floor<seconds>(az.get_sys_time()).time_since_epoch() << 'n';
}
输出为:
Zone Europe/Madrid
local_date_time: 2016-10-05 17:36:27.701162 CEST
local_time: 2016-10-05 17:36:27.701162
utc_time: 2016-10-05 15:36:27.701162
1475681787s
也可以用current_zone()
来构造zoned_time
:
auto az = make_zoned(current_zone(),
local_days{2016_y/oct/5} + 17h + 36min + 27s + 701162us);
或:
auto az = make_zoned(current_zone(), system_clock::now());
同时,我发现了一种简单的方法来检测Linux中的UTC偏移量。似乎localtime
期望一个非空时间戳来计算当前时区和偏移量。它是这样工作的:
time_t convertLocalPtimeToTimestamp(const boost::posix_time::ptime& pt)
{
time_t tt = to_time_t(pt);
struct tm* local = localtime(&tt);
LOG(debug) << "Timezone: " << local->tm_zone << " Offset: " << local->tm_gmtoff;
return (tt - local->tm_gmtoff);
}
我知道,有一个缺陷,因为我从当地时间得到当前时区,所以在夏令时改变的一小时内会有一个偏移。但就我的目的而言,这不是问题。