1、问题

在开发云平台程序的时候,经常会碰到时间区域转换的问题。比如,任何网络存储的文档的metadata都自己记录了编辑时间。但是,云平台记录时需要把这个时间转成标准时间,便于管理。但是用户使用的时候却是根据他自己的时间来的。比如,

  • 某人需要在北京时间12/31:11:59把新年短信发给女朋友。太早发,太晚发都会惹人不高兴。因此,系统搜索或安排任务的时候,需要根据某个时区,或是新年短信内部时间设定。
  • 再比如,在搜索电邮或短信的时候,根据RFC的定义,时间的搜索需要按照收到时间的字面数值。比如一个电邮的收到时间为Mon, 14 May 2018 23:23:27 -0700,那么搜索1 SEARCH BEFORE 14-May-2018必须返回这个电邮。但是,如果我们在标准时区,当地时间已经是5月15了。

2、概念与API

在最早的Unix中,一台机器只能处理UTC以外的一个时区。时区偏移量和名称字符串(实际上是一对名称字符串,一个用于夏季,一个用于冬季)已配置到内核中,并可供C程序使用。后来(1982)System III通过修改名为TZ的环境变量并调用函数tzset(3),可以设置session时区。从此,解释TZ值区域规范的规则成为POSIX标准的一部分。

V7和较早的BSD Unix有其他各种配置本地时区的方法,这些方法不涉及使用或解释TZ。他们与POSIX TZ的解释有一个相同的致命缺陷,那就是它们不是为了应对时区系统的历史不稳定而设计的。他们无法解释一整套历史位移/ DST的规则,从而正确表达过去本地时间和现在时刻。

在现代Unix系统上,TZ变量可能根本就没有设置,但在任何进程中可以通过明确设置TZ来覆盖系统默认时区。在启动或通过重写值TZ时,可以根据地理位置配置时区指示符(通常但不总是地区/主要城市对),例如“America / New_York”或“Europe / Vienna”或“Asia /台北”。如果标识符是通过TZ设置的,为了与POSIX标准向后兼容,可能需要以冒号开头,以区别于旧式的时区规范。

基于位置的区域命名方案[IANA-ZONES]由互联网号码分配机构IANA管理。

2.1、Unix中时间日期的格式

作为现在大多数操作系统的鼻祖,Unix中时间日期的format是相当乱的。这也反映了系统发展的历程和不同贡献者的设计偏好:

时间日期

解释

1526924458

Unix UTC seconds

Mon May 21 11:20:32 PDT 2018

date(1) output

2018-05-21 11:23:27-07:00

$ date --rfc-3339=seconds

Mon, 21 May 2018 11:23:27 -0700

e-mail RFC-822/RFC-2822 format

Fri, 24 Oct 2014 19:32:27 GMT

HTTP (RFC-2616/RFC-7231) format

20141024192327.000000Z

LDAP (RFC-2252/X.680/X.208) format

2014-10-24 15:32:27

Modified ISO-8601 local time

2014-10-24T15:32:27

Strict ISO-8601 local time

2014-10-24T19:32:27Z

RFC-3339 time, always UTC and marked Z

在用unix程序时,两个使用最多的数据结构是time_t和struct tm,time_t其实就是int32_t,而struct tm的定义如下,我们发现这个结构有个问题,没有时区信息:

struct tm
{
int tm_sec; /* seconds [0,60] (60 for + leap second) */
int tm_min; /* minutes [0,59] */
int tm_hour; /* hour [0,23] */
int tm_mday; /* day of month [1,31] */
int tm_mon ; /* month of year [0,11] */
int tm_year; /* years since 1900 */
int tm_wday; /* day of week [0,6] (Sunday = 0) */
int tm_yday; /* day of year [0,365] */
int tm_isdst; /* daylight saving flag */
};

时区是通过环境变量"TZ"来定义的,使之生效需要调用tzset函数,查看时区需要使用tzname:

setenv("TZ", "Australia/Currie", );
tzset();
printf ("tz: [%s:%s]\n", tzname[], tzname[]);

特别需要注意的是 char * tzname [2]

数组tzname包含两个字符串,它们是用户选择的时区(标准和夏令时)的标准名称。 tzname [0]是标准时区的名称(例如“EST”),tzname [1]是使用夏令时的时区名称(例如“EDT”)。这些对应于来自TZ环境变量的std和dst字符串(分别)。如果从不使用夏令时,则tzname [1]是空字符串。

tzname数组在tzset,ctime,strftime,mktime或localtime被调用时从TZ环境变量初始化。如果使用了多个缩写(例如美国东部时间和东部夏令时的“EWT”和“EDT”),则该数组包含最近的缩写。

tzname数组对于POSIX.1兼容性是必需的,但在GNU程序中,最好使用分解时间结构的tm_zone成员,因为即使它不是最新的缩写,tm_zone也会报告正确的缩写。

2.2、API

  • Unix时间:
#include <time.h>

/* Obtain current time. */
time_t current_time = time(NULL);

C标准定义time()返回的time_t值不是特定时区的,自1970年1月1日00:00 UTC时刻以来流逝的秒数。使用时不能假设其内部实现,需要使用C标准库中适当的函数(如gmtime和localtime)将time_t转换为struct tm并获得对时间戳细节的访问。  

  • 设置时区:tzset
#include <time.h>
void tzset (void);
extern char *tzname[]; /* time zone name */
extern long timezone; /* seconds west of UTC, *not* DST-corrected */
extern int daylight; /* nonzero if DST is ever in effect here */
  • 获取标准或本地时间

    • gmtime/gmtime_r:获取标准时间,不会修改时区变量
    • localtime/localtime_r:获取本地时间,如果本地时间未设置,会根据地理位置修改时区变量
#include <time.h>
struct tm *gmtime(const time_t *);
struct tm *gmtime_r(const time_t *, struct tm *);
struct tm *localtime(const time_t *);
struct tm *localtime_r(const time_t *, struct tm *);
  • 从本地时间转为Unix时间(秒):mktime
#include <time.h>

time_t mktime(struct tm *tm);

mktime函数是一个标准函数,是localtime的反函数,将输入本地时间struct tm转换为Unix时间time_t。

  • 其它函数

其它还有辅助函数帮助时间转换为字符串或从字符串转换成时间,因为和时区的关系不大,我们不详细解释。

    • asctime将struct tm对象转换为文本表示(不建议使用)
    • ctime将time_t值转换为文本表示
    • strftime将struct tm对象转换为自定义文本表示
    • wcsftime将struct tm对象转换为自定义宽字符串文本表示

3、用例

我们有一个已知时间和时区位移,需要输出在该时区的时间,返回值为该天的年月日(yyyyMMdd)。

int timestamp_to_timezonedate(time_t date_timestamp, int32_t tz_offset, char *buf, size_t bufsize) {
struct tm dat = {0};

date_timestamp += tz_offset * 60;
gmtime_r(&date_timestamp, &dat);
if (buf)
strftime(buf, bufsize, "%Y-%m-%dT%H:%M:%S", &dat);
return ((dat.tm_year+) * ) + ((dat.tm_mon+) * ) + (dat.tm_mday);
}

再看看如何获取当前时区和标准时间的差别(分钟):

//return local timezone offset in minutes
int store_local_tz_offset(void) {
time_t gmt, rawtime = time(NULL);
struct tm gbuf; gmtime_r(&rawtime, &gbuf); // Request that mktime() looks up dst in timezone database
gbuf.tm_isdst = -; //gmt = rawtime - offset
//gbuf is used as local tm, therefore gmt will be shifted back with offset
gmt = mktime(&gbuf); //rawtime - gmt
return (int)difftime(rawtime, gmt)/;
}

4、One more thing

一般的时区位移是分钟。比如Mon, 21 May 2018 11:23:27 -0700,最后几位是时区位移:[+|-]HHMM。

有些地方的时区位移是半小时,所以为了支持这些地区,在小时后面会有分钟的参数,比如印度 Tuesday 2018-05-22|04:56:39 +0630

参考文献

[1]https://www.gnu.org/software/libc/manual/html_node/Time-Zone-Functions.html

[2]http://www.catb.org/esr/time-programming/

Linux系统中时间区域和API的更多相关文章

  1. (转)浅谈 Linux 系统中的 SNMP Trap

    原文:https://www.ibm.com/developerworks/cn/linux/l-cn-snmp/index.html 简介 本文讲解 SNMP Trap,在介绍 Trap 概念之前, ...

  2. 认识Linux系统中的inode,硬链接和软链接

    在学习和创建软链接遇到了一点问题,总结一下: 在当前文件夹下面建立了两个临时文件夹tempdir1和tempdir2,然后在tempdir2里面创建了一个hello文件,然后用指令ln -s temp ...

  3. Jmeter(五十三) - 从入门到精通高级篇 - 懒人教你在Linux系统中安装Jmeter(详解教程)

    1.简介 我们绝大多数使用的都是Windows操作系统,因此在Windows系统上安装JMeter已经成了家常便饭,而且安装也相对简单,但是服务器为了安全.灵活小巧,特别是前几年的勒索病毒,现在绝大多 ...

  4. Linux 系统中僵尸进程

    Linux 系统中僵尸进程和现实中僵尸(虽然我也没见过)类似,虽然已经死了,但是由于没人给它们收尸,还能四处走动.僵尸进程指的是那些虽然已经终止的进程,但仍然保留一些信息,等待其父进程为其收尸.配图源 ...

  5. [转]理解Linux系统中的load average

    转自:http://heipark.iteye.com/blog/1340384 谢谢,写的非常好的文章. 一.什么是load average linux系统中的Load对当前CPU工作量的度量 (W ...

  6. Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf 动态库的后缀为*.so 静态库的后缀为 libxxx.a ldconfig 目录名

    Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf  动态库的后缀为*.so  静态库的后缀为 libxxx.a   ldconfig   目录名 转载自:http://b ...

  7. linux系统的时间调整

    以centos为例,其它系统应该是一样或者类似的. 需要用到两个命令: date 和 hwclock 其中 date 命令由 coreutils 这个包提供, hwclock 命令由 util-lin ...

  8. Linux系统中“动态库”和“静态库”那点事儿【转】

    转自:http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻. ...

  9. Linux系统中的load average

    1. load average 定义 linux系统中的Load对当前CPU工作量的度量.简单的说是进程队列的长度. Load Average 就是一段时间 (1 分钟.5分钟.15分钟) 内平均 L ...

随机推荐

  1. ubuntu配置默认python版本并安装pip

    ubuntu 16.04本身是自带python的,他本身是自带2.X和3.X,两个版本,默认的是2.X.这里记录一下如果在版本间切换以及如何把python版本切换到3.X下的方法. 1.查看Ubunt ...

  2. 学习Android之SimpleAdapter显示网络图片

    效果图: 此程序基本的知识点是:SimpleAdapter本身是不支持网络图片的, 假设在Map.put(a,b)中 b为一个Bitmap,程序不会报红色字体,而是在控制台输出绿色的字体,例如以下 0 ...

  3. mongodb的学习-5-概念解析

    http://www.runoob.com/mongodb/mongodb-databases-documents-collections.html mongodb中基本的概念是文档.集合.数据库 S ...

  4. Android利用fidller进行网络抓包

    http://www.oschina.net/question/221817_129716?fromerr=z7ZX9oZR http://www.trinea.cn/android/android- ...

  5. centos6.5添加阿里docker加速器

    1. 配置阿里docker加速器 vi /etc/sysconfig/docker 在文件末尾追加下面两行 other_args="--registry-mirror=https://pl8 ...

  6. 论文笔记 Robust face landmark estimation under occlusion

    1. Abstract 现实世界中的人脸很多时候都存在遮挡以及大的形状变化,而目前的人脸关键点检测方法在这种情况下表现欠佳, 因为它们未能提供一种系统的方法来处理异常.因而authors提出一种新的方 ...

  7. PAT乙级1032

    1032 挖掘机技术哪家强 (20 分)   为了用事实说明挖掘机技术到底哪家强,PAT 组织了一场挖掘机技能大赛.现请你根据比赛结果统计出技术最强的那个学校. 输入格式: 输入在第 1 行给出不超过 ...

  8. beta冲刺后续讨论

    目录 组员:胡绪佩 组员:何家伟 组员:黄鸿杰 组员: 翟丹丹 组员:周政演 组员:胡青元 组员:庄卉 组员:刘恺琳 组员:何宇恒 组员:刘一好 组员:葛家灿 组员:胡绪佩 总结 通过这次的Beta版 ...

  9. linux文件系统初始化过程(2)---挂载rootfs文件系统

    一.目的 本文主要讲述linux3.10文件系统初始化过程的第一阶段:挂载rootfs文件系统. rootfs是基于内存的文件系统,所有操作都在内存中完成:也没有实际的存储设备,所以不需要设备驱动程序 ...

  10. BFC (Block formatting context)

     一:BFC 是什么      MDN解释: A block formatting context is a part of a visual CSS rendering of a Web page. ...