我需要根据某个时区从字符串(%Y-%M-%d %H:%m:%s
)解析时间。
我的第一个想法是尝试boost::date_time
,但看起来它的数据库已经过时,而且时区检测算法一般都是错误的。所以我决定尝试boost::locale
。它有ICU backend,因此时区支持应该是好的。我使用以下代码:
namespace as = boost::locale::as;
void foo(std::string time, std::string timezone) {
auto glob = boost::locale::localization_backend_manager::global();
glob.select("icu"); // select icu backend
boost::locale::generator gen{glob};
auto loc = gen.generate(""); // generate locale with boost facets
auto cal = boost::locale::calendar{loc, timezone};
boost::locale::date_time dt{cal};
std::stringstream ss{time};
ss.imbue(loc);
std::cout.imbue(loc);
ss >> as::ftime("%Y-%m-%d %T") >> as::time_zone(timezone) >> dt;
std::cout << as::time_zone("UTC") << dt << std::endl;
std::cout << as::time_zone(timezone) << dt << std::endl;
}
这很好用,但是如果我传递了一些无效的时区名称(&#34; foo&#34;),则库接受它,不会抛出任何异常,时间将被解析,就好像它是UTC时间一样。这对我不利,我想以某种方式检测到这种情况,以便我可以通知用户结果不是他/她所期望的。
我的第一个想法是检查cal.get_time_zone()
,但它总是返回传递给构造函数的字符串(&#34; foo&#34;在我的情况下),无论它是否有效或不
接下来,我尝试从生成的语言环境中提取calendar_facet
,如下所示:
const auto &icu_cal = std::use_facet<boost::locale::calendar_facet>(loc);
这样我就可以访问内部abstract_calendar
类了。不幸的是,这条线并没有编译。原因是boost/locale/generator.hpp
在同一calendar_facet
命名空间中具有相同名称(boost::locale
)的静态常量。编译器报告它无法实例化std::use_facet
。也许我可以将它移动到一个单独的编译单元并避免在那里包含generator.hpp
标题,但它对我来说看起来像是一个黑客。这是一个错误还是我在这里遗漏了什么?
如何使用boost::locale
验证时区名称是否直截了当?你一般推荐它吗?谢谢你的帮助。
编辑:这是一个不为我编译的代码的最小例子
#include <boost/locale.hpp>
int main() {
auto my = boost::locale::localization_backend_manager::global();
my.select("icu");
boost::locale::generator gen{my};
std::use_facet<boost::locale::calendar_facet>(gen.generate(""));
return 0;
}
我像这样编译它(在ubuntu 16.04,gcc 5.4上):
g++ -std=c++14 -L/usr/lib/x86_64-linux-gnu/ test.cpp -lboost_locale -lboost_date_time
编辑2:借助Sehe的帮助,我设法从区域设置获取日历方面,现在我可以像这样检查时区:
int main(int argc, char **argv) {
auto my = boost::locale::localization_backend_manager::global();
my.select("icu");
boost::locale::generator gen{my};
auto ptr = std::unique_ptr<boost::locale::abstract_calendar>(std::use_facet<class boost::locale::calendar_facet>(gen.generate("")).create_calendar());
ptr->set_timezone(argv[1]);
// if ICU backend does not recognize timezone, it sets it to Etc/Unknown
if (ptr->get_timezone() != argv[1]) {
std::cout << "bad timezone " << ptr->get_timezone() << std::endl;
} else {
std::cout << "good timezone " << ptr->get_timezone() << std::endl;
}
return 0;
}
更新:虽然我设法在Linux上做了我想要的提升语言环境,但后来当我将我的代码移植到OS X时,我遇到了一些奇怪的错误(它看起来像mac不行。默认情况下有ICU后端...)。所以,我决定转而使用Howard Hinnant's date library。这个库质量很高,在linux和mac上运行良好,作者很有帮助,反应灵敏,所以非常值得推荐。
答案 0 :(得分:2)
非编译样本的修复:
<强> Live On Coliru 强>
#include <boost/locale.hpp>
int main() {
auto my = boost::locale::localization_backend_manager::global();
my.select("icu");
boost::locale::generator gen{my};
std::use_facet<class boost::locale::calendar_facet>(gen.generate(""));
}
答案 1 :(得分:1)
以下是alternative timezone library,可能更容易使用:
#include "tz.h"
#include <iostream>
#include <sstream>
int
main(int argc, char **argv)
{
try
{
auto tz = date::locate_zone(argv[1]);
std::cout << "good timezone " << tz->name() << std::endl;
date::local_seconds tp;
std::istringstream in{"2017-09-08 11:30:15"};
in >> date::parse("%Y-%m-%d %H:%M:%S", tp);
auto zt = date::make_zoned(tz, tp);
std::cout << date::format("%Y-%m-%d %T %Z which is ", zt);
std::cout << date::format("%Y-%m-%d %T %Z\n", zt.get_sys_time());
}
catch (std::exception const& e)
{
std::cout << "bad timezone " << e.what() << std::endl;
}
}
示例输出1:
good timezone America/New_York
2017-09-08 11:30:15 EDT which is 2017-09-08 15:30:15 UTC
示例输出2:
bad timezone America/New_Yor not found in timezone database