将boost ptime从本地时间转换为UTC时间



我有一个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);
}

我知道,有一个缺陷,因为我从当地时间得到当前时区,所以在夏令时改变的一小时内会有一个偏移。但就我的目的而言,这不是问题。

相关内容

  • 没有找到相关文章