用于时区规则的Linux / crossplatform API? (替换lock localtime_r)

时间:2013-10-11 17:52:25

标签: c++ linux multithreading timezone

我们有一些代码想要经常从多个线程调用localtime。 (相关背景:它是一个服务器,你可以要求它的一个东西是作为字符串的本地时间,它希望能够每秒提供100K的请求。)

我们发现在Ubuntu Linux 12.04上,glibc函数localtime_r(“reentrant localtime”)调用__tz_convertstill takes a global lock!

(另外,FreeBSD看起来localtime_r会在每次调用时调用tzset,因为他们是偏执的,程序可能已经完成了setenv("TZ")和/或者用户在现在和最后一次调用/etc/localtime之间下载了localtime_r的新版本。(这与所描述的情况相反here;似乎glibc调用了{{1} }} on every invocation of localtime but not localtime_r,只是让人感到困惑。)

显然,这对表现来说太糟糕了。出于我们的目的,我们希望在服务器开始运行时基本上“快照”当前时区的规则,然后永远使用该快照。因此,我们将继续遵守夏令时规则(因为何时切换到DST的规则将成为快照的一部分),但我们永远不会回到磁盘,接受互斥,或做任何会导致线程的其他事情阻止。 (我们没有尊重tzinfo的下载更新,也没有尊重对tzset的更改;我们不希望服务器在运行时实际更改时区。)

但是,我无法在网上找到有关如何处理时区规则的任何信息 - 是否存在用于处理时区规则的用户空间API,或者我们是否将被迫重新实现几百行glibc代码来阅读时区数据本身。

我们是否必须重新实现/etc/localtime下游的所有内容 - 包括__tz_convert,因为它似乎没有向用户公开?或者我们可以使用一些POSIX接口和/或第三方库来处理时区规则吗?

(我见过http://www.iana.org/time-zones/repository/tz-link.html,但我不确定它是否有用。)

2 个答案:

答案 0 :(得分:3)

使用https://github.com/google/cctz

速度快,应该使用非常简单的API做你想做的一切。

特别是,对于等同于localtime的cctz,请使用cctz::BreakTime()函数。例如,https://github.com/google/cctz/blob/master/examples/example3.cc

答案 1 :(得分:1)

也许这个free, open-source timezone library符合需要。

它有一个名为LAZY_INIT的配置标志,完整记录here。默认情况下它处于打开状态,并且在第一次访问每个单独的时区时将调用std::call_once。但是你可以用:

进行编译
-DLAZY_INIT=0

然后对std::call_once的调用消失了。每个时区都从磁盘读取并在首次访问时完全初始化(通过本地静态函数)。从那时起,事情是稳定且无锁的,没有磁盘访问。当然,这会增加前期的初始化时间,但会减少每个时区的“首次访问”时间。

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

#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';
}

只为我输出:

2016-04-12 10:13:14.585945 EDT

该库是一种现代化的高性能线程安全设计。它也非常灵活并且完整记录。它的功能远远超出了C localtime的简单替代品。与C API不同,您可以指定所需的任何IANA timezone,例如:

    auto local = make_zoned("Europe/London", std::chrono::system_clock::now());

这给出了伦敦的当前时间:

2016-04-12 15:19:59.035533 BST

请注意,默认情况下,时间戳的精度是std::chrono::system_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

有关详细信息,请参阅docs