C++ 用于时区规则的Linux/跨平台API?(更换锁定本地时间\u r)

C++ 用于时区规则的Linux/跨平台API?(更换锁定本地时间\u r),c++,linux,multithreading,timezone,C++,Linux,Multithreading,Timezone,我们有一些代码希望经常从多个线程调用localtime。(相关背景:它是一个服务器,您可以向它请求一个字符串形式的本地时间,它希望能够每秒处理100K请求。) 我们发现在Ubuntu Linux 12.04上,glibc函数localtime\u r(“可重入localtime”)调用\uuuuuz\u convert,它 (另外,它看起来像是在每次调用时调用localtime\rcalltzset,因为他们偏执地认为,从现在到上次调用localtime\r之间,程序可能已经执行了setenv(

我们有一些代码希望经常从多个线程调用
localtime
。(相关背景:它是一个服务器,您可以向它请求一个字符串形式的本地时间,它希望能够每秒处理100K请求。)

我们发现在Ubuntu Linux 12.04上,glibc函数
localtime\u r
(“可重入localtime”)调用
\uuuuuz\u convert
,它

(另外,它看起来像是在每次调用时调用
localtime\r
call
tzset
,因为他们偏执地认为,从现在到上次调用
localtime\r
之间,程序可能已经执行了
setenv(“TZ”)
和/或用户下载了一个新版本的
/etc/localtime
。(这与所描述的情况相反;glibc似乎调用了
tzset
,只是让人感到困惑。)

显然,这对性能来说很糟糕。出于我们的目的,我们希望在服务器开始运行时基本上“快照”当前时区的规则,然后永远使用该快照。因此,我们将继续遵守夏令时规则(因为何时切换到DST的规则将是快照的一部分),但我们永远不会返回磁盘、获取互斥锁或执行任何其他会导致线程阻塞的操作。(我们可以不考虑下载的tzinfo更新,也不考虑对
/etc/localtime
的更改;我们不希望服务器在运行时实际更改时区。)

然而,我在网上找不到任何关于如何处理时区规则的信息——是否有用户空间API来处理它们,或者我们是否将被迫重新实现几百行glibc代码来读取时区数据

我们是否必须重新实现
\uuuuTz\uConvert
下游的所有功能,包括
tzfile\u read
,因为它似乎不向用户公开?或者是否有一些POSIX接口和/或第三方库可用于处理时区规则

(我见过,但我不确定它是否有用。)

使用

它的速度很快,应该可以用一个非常简单的API做任何你想做的事情

特别是,对于相当于
localtime
的cctz,请使用
cctz::BreakTime()
函数。例如,

这可能会满足需要

它有一个名为
LAZY_INIT
的配置标志,该标志有完整的文档记录。默认情况下,它处于启用状态,并且在第一次访问每个时区时,将导致调用
std::call_一次。但是,您可以使用:

-DLAZY_INIT=0
然后对
std::call_once
的调用就会消失。每个时区都从磁盘读取,并在第一次访问时完全初始化(通过函数local static)。从那时起,事情就稳定且无锁,没有磁盘访问。这自然会提前增加初始化时间,但会减少“首次访问”每个时区的时间

此库需要C++11/14,因此可能不适合此原因。它基于(并大量使用)C++11
库。以下是打印当前本地时间的示例代码:

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

int
main()
{
    using namespace date;
    auto local = make_zoned(current_zone(), std::chrono::system_clock::now());
    std::cout << local << '\n';
}
该库是一种现代、高性能的线程安全设计。它也非常灵活,并有完整的文档记录。它的功能远远超出了C的
localtime
的简单替代品。与C API不同,您可以指定任何需要的功能,例如:

    auto local = make_zoned("Europe/London", std::chrono::system_clock::now());
这给出了伦敦的当前时间:

2016-04-12 15:19:59.035533 BST
请注意,默认情况下,时间戳的精度为
std::chrono::system\u clock
的精度。如果您更喜欢另一种精度,这很容易实现:

using namespace date;
using namespace std::chrono;
auto local = make_zoned("Europe/London", std::chrono::system_clock::now());
std::cout << format("%F %H:%M %Z", local) << '\n';

2016-04-12 15:22 BST
使用名称空间日期;
使用名称空间std::chrono;
auto-local=make_Zone(“欧洲/伦敦”,std::chrono::system_clock::now());

std::难道在
tzset
中的全局锁“对性能很糟糕”一点也不明显吗-你真的对它进行了基准测试吗?除非你对它进行基准测试,否则它似乎是一种众所周知的过早优化。据我所知,
tzset
有一条快速路径,如果时区没有实际改变,它基本上不会起任何作用。在现实生活中的应用程序中,我希望锁永远不会被争用。锁是争用的d任何时候两个线程同时调用
\uuuuuTz\uConvert
;检查代码。是的,我们知道这个锁存在的唯一原因是我们认为它是性能测试(服务大量本地时间请求的特定测试)中的瓶颈。你看过或吗?它看起来可以做我们想要的,但它在依赖性方面看起来相当重要。(我们目前不是一个“升级商店”。)但是,如果没有更简单的解决方案…Boost肯定是一种可能。我不是一个C++大师,但是我知道Boost和ICU都有IANA TZDB实现。我也知道ICU被其他几个人使用,所以它可能比Boost轻很多,但是我不太确定。也许其他更熟悉的人可以提供一个BE。tter答案。一般来说,指向工具或库的链接,或者如果可能的话,指向以上所有内容的链接。谢谢Greg。该库看起来很棒。您的库中也有一个全局锁,请参阅。Cloudera在评论中找到。请参见。提交
using namespace date;
using namespace std::chrono;
auto local = make_zoned("Europe/London", std::chrono::system_clock::now());
std::cout << format("%F %H:%M %Z", local) << '\n';

2016-04-12 15:22 BST