Linux c 时间时区配置

news/2024/5/2 11:10:57

写在前面:

本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。

目录

  • 时间与日期
  • 时间及时区查看命令
  • 时区设置命令
  • 常用 API
    • 时间结构体
    • 时间函数归类
    • 函数应用
  • localtime 和 localtime_r 区别
  • 参考


首发及后期更新将于 https://arachnid.cc/linux_c-time-configuration/ 上,此处仅作二次备份。


时间与日期

GMT 和 UTC

GMT,即格林尼治标准时间,也就是世界时。GMT 的正午是指当太阳横穿格林尼治子午线(本初子午线)时的时间。但由于地球自转不均匀不规则,导致 GMT 不精确,现在已经不再作为世界标准时间使用。

UTC,即协调世界时。UTC 是以原子时秒长为基础,在时刻上尽量接近于 GMT 的一种时间计量系统。为确保 UTC 与 GMT 相差不会超过 0.9 秒,在有需要的情况下会在 UTC 内加上正或负闰秒。UTC 现在作为世界标准时间使用。

所以,UTC 与 GMT 基本上等同,误差不超过 0.9 秒。

时区

地球自西向东旋转,东边比西边先看到太阳,东边的时间也比西边的早。为了统一世界的时间,1884 年的国际经度会议规规定将全球划分为 24 个时区(东、西各 12 个时区)。规定英国(格林尼治天文台旧址)为零时区(GMT+00),东 1-12 区,西 1-12 区,中国北京处于东 8 区(GMT+08)。

若中国当前时间为 8 点整,则英国时间为 0 点整。

UTC时间与本地时间

UTC + 时区差 = 本地时间

时区差东为正,西为负。在此,把东八区时区差记为 +08

UTC + (+08) = 本地(北京)时间

UNIX 时间戳

由 Unix 内核提供的基本时间服务是自国际标准时间公元 1970 年 1 月 1 日 00:00:00 以来的秒数。


时间及时区查看命令

  • 获取 UTC 世界时间

date -u

  • 获取当地时间

date

  • 获取当地时间及时差

date -R


时区设置命令

1、对于完整的 Linux 系统

  1. 命令跟描述对不上号的 tzselect

看起来很像一个时区选择的工具,但并非如此。事实上tzselect仅仅是一个查看时区表示方式的『向导』程序而已。通过依次询问大洲→国家→城市,最后告诉你如何TZ变量的写法,比如北京时间是:Asia/Shanghai

  1. TZ 变量

根据上面的指导,可以获知通过修改 TZ 变量,直接修改时区信息,例如:

date -RTue, 17 Jan 2017 13:57:06 +0000
export  TZ='Asia/Shanghai'
date -RTue, 17 Jan 2017 19:57:18 +0600

但如果不写在环境变量文件配置里的话,一般是会话级的操作,取消重新打开便会失效;因此对于在 shell 中实现更改 TZ 变量,只能做到临时变更时区信息。

正确做法是到 /etc/profile 里(或用户的 ~/.profile~/.bashrc 文件等),直接 export TZ='xxx' 更改时区(时区的名字可以用 tzselect 向导来确定)

  1. /etc/localtime 文件

默认情况下情况下,TZ 属性是空,这时候是靠 /etc/localtime 文件来确定的时区。而此文件通常又是一个到 /usr/share/zoneinfo/ 下各种时区文件的软连接。通过修改 /etc/localtime 指向的软连接,进而修改系统的时区。比如下面的方法,将 localtime 文件设置为了北京时间:

ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

2、对于裁剪的 Linux 系统,如:arm linux

  1. tzselect 命令

正常情况下,裁剪过的是不支持该命令的,因此相对于上面的 2、3 两点并不适用于此。

  1. TZ 变量

与上面一样可通过修改 TZ 变量,直接修改时区信息,但与此不同的是,并不能通过 TZ='Asia/Shanghai' 去修改,只能通过时区偏移量来修改,例如我们中国的北京时间相对于 UTC-0 的偏移量是 UTC+8 ;那么则修改为:

date -RTue, 17 Jan 2017 13:57:06 +0000
export  TZ='CST-8'
date -RTue, 17 Jan 2017 19:57:18 +0600

注意,在这里,UTC+、- 是相反的,UTC-8 代表的是相对于 UTC 加八个小时,反之减八个小时…;而 CST 则是对应为北京时区缩写。

时区表信息可看:https://www.timeanddate.com/time/zones/

  1. /etc/localtime 文件

由于裁剪问题,系统中是没有 /usr/share/zoneinfo/ 文件夹的,如有需要,将 PC 端的 /usr/share/zoneinfo 整个 zoneinfo 文件夹复制到 rootfs 的 /usr/share 下,这样嵌入式系统中就有了 timezone

最后同样执行:

ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

note:时区信息保存在 /etc/localtime 文件里,如果没有该文件则系统是零时区,有该文件时系统会去读取该文件。具体该文件的内容可以不同关心,在 /usr/share/zoneinfo/ 目录下有各个时区对应的文件,只需要拷贝过去就可以。比如我们常用的东八区时间就是对应 /usr/share/zoneinfo/Asia/Shanghai 文件,只需要将该文件指向或拷贝到 /etc/localtime 就将系统时间改为东八区。


常用 API

时间结构体

struct tm {int tm_sec;    /* Seconds (0-60) */int tm_min;    /* Minutes (0-59) */int tm_hour;   /* Hours (0-23) */int tm_mday;   /* Day of the month (1-31) */int tm_mon;    /* Month (0-11) */int tm_year;   /* Year - 1900 */int tm_wday;   /* Day of the week (0-6, Sunday = 0) */int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */int tm_isdst;  /* Daylight saving time */
};
struct timeval {time_t      tv_sec;     /* seconds (秒) */suseconds_t tv_usec;    /* microseconds (微秒) */
};struct timezone {int tz_minuteswest;     /* minutes west of Greenwich */int tz_dsttime;         /* type of DST correction */
};

时间函数归类

1、C99 标准库函数

#include <time.h>

  • 获取时间戳:
/* time()函数,返回一个从 1970 年 1 月 1 日 00:00:00 到现在的 time_t 类型 UTC 时间,当参数为 NULL 时直接返回秒数,当然也会将该值写入 t 指针指向的地址。 */
time_t time(time_t *t);/* mktime() 会把本地时间转换为 UTC 时间 */
time_t mktime(struct tm *tm);// note:两者区别在于传入的参数结构体不同,mktime 存在时区转换;time(t) 等价于 mktime(localtime(time(t)))。
  • 获取 struct tm 类型的时间:
/* gmtime() 是零时区,把 UTC 时间转换成北京时间的话,需要在年数上加 1900,月份上加 1,小时数加上 8。 */
struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);/* localtime() 将得到本地时间,该函数与 gmtime() 唯一区别是,在转换成北京时间的小时数不需要加上 8。 */
struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);// note:localtime 是将时区考虑在内了,转出的是当前时区的时间。但是注意,有些嵌入式设备上被裁减过的系统,时区没有被设置好,导致二者转出来的时间都是零时区的。在多线程应用里面,应该用后缀不带 `_r` 的函数,如: localtime_r 函数替代 localtime 函数,因为 localtime_r 是线程安全的,例子看第五大点。
  • 时间日期格式化:
/* 将 tm 结构中的时间信息转换为相应时间的字符串 */
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);/* 将日历时间参数 timep 转换为一个表示本地当前时间的字符串,函数已经由时区转换成当地时间 */
char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf);// note:两者区别在于传入的参数结构体不同,但转换出来的信息格式显示是一样的;asctime 是直接把时间格式化,而 ctime 是经过时区转换后再格式化输出;ctime(t) 等价于 asctime(localtime(t))。/* 常用时间格式化参数看下方描述 */
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);

在使用 strftime 时间格式化函数所涉及的相关参数的含义如下:

参数含义
%F将时间格式化为年-月-日
%T将时间格式化为显示时分秒: hh:mm:ss
%Y将时间格式化为带世纪部分的十制年份
%m将时间格式化为十进制表示的月份
%d将时间格式化为十进制的每月中的第几天
%H将时间格式化为24小时制的小时
%M将时间格式化为十进制表示的分钟数
%S将时间格式化为十进制表示的秒数

更多参数请阅:https://www.runoob.com/cprogramming/c-function-strftime.html

  • 获取时间差:
double difftime(time_t time1, time_t time0);

2、Uinx 系统函数

#include <sys/time.h>

#include <sys/time.h>
获取 struct timeval 类型的时间:
/* 相对于 time() 和 mktime() ,gettimeofday() 能获取更精准的微秒级别,及相应的时区信息,需要注意的是 tz 是依赖于系统,不同的系统可能存在获取不到的可能,因此通常设置为 NULL */
int gettimeofday(struct timeval *tv, struct timezone *tz);
// 成功则返回 0,失败返回 -1,错误代码存于 errno
// EFAULT:指针 tv 或 tz 所指的内存空间无效。设置 struct timeval 类型的时间:
int settimeofday(const struct timeval *tv,const struct timezone *tz);
// 成功则返回 0,失败返回 -1,错误代码存于 errno
// EPERM:调用进程没有足够权限调用 settimeofday(),即权限不够。
// EINVAL:时区或其它内容无效,无法正确设置时间。// note:settimeofday 的修改时间需要在 root 权限下才能配置成功。

3、总结

timegmtimeasctime 所表示的时间都是 UTC 时间,只是数据类型不一样,
mktimelocaltimectime 的时间都存在时区之间变换。

函数传参类型返回类型时区转换描述
time()time_ttime_tUTC+0用于获取 UTC 零时区的时间戳格式
mktime()struct tmtime_tUTC-t用于获取 UTC 零时区的时间戳格式, 但会经过时区转换, 把本地时间内部换成 UTC+0
gmtime()time_tstruct tmUTC+0用于获取 UTC 零时区的
localtime()time_tstruct tmUTC+t
asctime()struct tmstringUTC+0
ctime()time_tstringUTC+t

函数应用

1、获取当前时区的时间戳偏移量

time_t get_localtime_interval(void)
{time_t timep_utc, timep_local;struct tm tm_utc;time(&timep_utc);gmtime_r(&timep_utc, &tm_utc);timep_local = mktime(&tm_utc);return (timep_utc - timep_local);
}

2、获取当前时区的时间戳(带时区的本地时间)

time_t get_time_stamp(void)
{time_t timep;time(&timep);timep += get_localtime_interval(timep);return timep;
}

3、获取时区偏移量

time_t get_time_stamp(void)
{time_t timep_zone;timep_zone = get_localtime_interval(timep) / 3600;return timep_zone;
}

4、时区设置函数

时间函数除了gmttime()、asctime() 不受环境变量 TZ 的影响外,大部分函数都受到环境变量 TZ 的影响,这几个函数是: localtime、mktime、ctime 和 strftime。如果定义了 TZ,则这些函数将使用其值以代替系统默认时区。

在 Unix 环境下可以通过改变系统文件修改环境变量,也可以通过函数 setenv() 修改。

TZ 指定了当前的系统时区。这个时区会影响我们所做的时间转换。例如假设当前的系统时间是 8:00AM,如果我们把当前的时区设置成东八区,则标准时间就是(即 UTC+0)的时间就是 8-8=0:00AM,如果是看成是东六区的话,则标准时间就变成了 8-6=2:00AM。

#include <stdio.h>  
#include <time.h>  
#include <stdlib.h>    
int main(int argc, const char * argv[])  
{  setenv("TZ", "CST-8", 1);   // 北京东八区// setenv("TZ", "UTC+0", 1);   //将当前时区设置成标准区char buf[64];struct timeval tv;struct tm* tm_time;gettimeofday(&tv, NULL);tm_time = gmtime(&tv.tv_sec);strftime(buf, sizeof(buf), "%a %b %m %H:%M:%S %Z %Y", tm_time);printf("GMT time: %s\n", buf);tm_time = localtime(&tv.tv_sec);strftime(buf, sizeof(buf), "%a %b %m %H:%M:%S %Z %Y", tm_time);printf("local time: %s\n", buf);return 0;
}  

localtime 和 localtime_r 区别

示例来源(下方为原文备份记录):https://blog.csdn.net/test1280/article/details/80917962

localtimelocaltime_r 的函数功能: converts the calendar time timep to broken-time representation

在调用 localtimelocaltime_t 函数时,需特别注意:

  • localtime 是不可重入函数,非线程安全

  • localtime_r 是可重入函数,线程安全

1、使用 localtime 时不可重入示范:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>int main()
{time_t curTime = time(NULL);time_t aftTime = curTime + 3600*3;struct tm *pTm1 = localtime(&curTime);struct tm *pTm2 = localtime(&aftTime);fprintf(stdout, "%04d%02d%02d%02d%02d%02d\n",pTm1->tm_year + 1900,pTm1->tm_mon + 1,pTm1->tm_mday,pTm1->tm_hour,pTm1->tm_min,pTm1->tm_sec);fprintf(stdout, "%04d%02d%02d%02d%02d%02d\n",pTm2->tm_year + 1900,pTm2->tm_mon + 1,pTm2->tm_mday,pTm2->tm_hour,pTm2->tm_min,pTm2->tm_sec);return 0;
}

编译 & 运行:

$ gcc -o main main.c
$ ./main
20180704225205
20180704225205

调用 localtime 函数并获取其返回值(一个指向 struct tm 结构类型数据的指针)后,我们并未对返回值进行显式地释放

这并没有什么问题(不会导致内存泄漏)

因为 localtime 函数返回值是一个指针,指向一个静态变量,这个静态变量是库中的一个 static struct tm 类型数据。

man localtime:

The return value points to a statically allocated struct which might be overwritten by subsequent calls to any of the date and time functions.

这将引出新的问题,同一进程多个线程中同时调用(极短时间内连续调用) localtime 函数,返回值 tm 可能被覆盖。

举个栗子:

两个线程 A 和 B 同时调用 localtime 函数:

时刻 1:线程 A 调用 localtime 函数,得到一个指针,指向 static struct tm 类型变量;(tm 中存储的值更新为 value-a)

时刻 2:线程 B 调用 localtime 函数,得到一个指针,指向 static struct tm 类型变量;(tm 中存储的值更新为 value-b)

时刻 3:线程 A 对 localtime 返回的指针进行相关引用操作(例如 printf 输出某字段),此时 static struct tm 中的值实际是 value-b,并非预期的 value-a。

时刻 4:线程 B 对 localtime 返回的指针进行相关引用操作,此时 static struct tm 中的值实际是 value-b。

上面的示范代码虽然是在同一线程中,但是已经可以简单模拟这样的多线程执行调用流程。

如何解决?

localtime_rlocaltime 的可重入版本(线程安全版本)。

localtime 不可重入是由于 static struct tm 是库中的一个静态变量,如果我们在调用 localtime 时传入一个 struct tm 类型变量(指针)用于存放结果,岂不是实现可重入

Bingo!

struct tm *localtime(const time_t *timep);

struct tm *localtime_r(const time_t *timep, struct tm *result);

调用 localtime 只需要传入指向 time_t 的一个常量指针;

调用 localtime_t 不仅需要传入指向 time_t 的一个常量指针,还需要传入指向 struct tm 的一个指针,结果将存储在 result 指向的 struct tm 对象中;

The return value points to a statically allocated struct which might be overwritten by subsequent calls to any of the date and time functions.

The localtime_r() function does the same, but stores the data in a user-supplied struct.

2、使用 localtime_r 时可重入示范:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>int main()
{time_t curTime = time(NULL);time_t aftTime = curTime + 3600*3;struct tm tm1;struct tm tm2;localtime_r(&curTime, &tm1);localtime_r(&aftTime, &tm2);fprintf(stdout, "%04d%02d%02d%02d%02d%02d\n",tm1.tm_year + 1900,tm1.tm_mon + 1,tm1.tm_mday,tm1.tm_hour,tm1.tm_min,tm1.tm_sec);fprintf(stdout, "%04d%02d%02d%02d%02d%02d\n",tm2.tm_year + 1900,tm2.tm_mon + 1,tm2.tm_mday,tm2.tm_hour,tm2.tm_min,tm2.tm_sec);return 0;
}

编译 & 运行:

$ gcc -o main main.c
$ ./main
20180704200531
20180704230531

参考

https://www.cnblogs.com/sun-frederick/p/4772535.html
https://blog.csdn.net/test1280/article/details/80917962

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.cpky.cn/p/11319.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

【初阶数据结构】——牛客:OR36 链表的回文结构

文章目录 1. 题目介绍2. 思路分析3. 代码实现 1. 题目介绍 链接: link 这道题呢是让我们判断一个链表是否是回文结构。但是题目要求设计一个时间复杂度为O(n)&#xff0c;额外空间复杂度为O(1)的算法。 所以如果我们想把链表的值存到一个数组中再去判断就不可行了。 2. 思路…

Diffusion添加噪声noise的方式有哪些?怎么向图像中添加噪声?

添加噪声的方式大致分为两种&#xff0c;一种是每张图像在任意timestep都加入一样的均匀噪声&#xff0c;另一种是按照timestep添加不同程度的噪声 一、在任意timestep都加入一样的noise batch_size 32x_start torch.rand(batch_size,3,256,256) noise torch.randn_like(x_…

使用Jmeter进行http接口性能测试

在进行网页或应用程序后台接口开发时&#xff0c;一般要及时测试开发的接口能否正确接收和返回数据&#xff0c;对于单次测试&#xff0c;Postman插件是个不错的Http请求模拟工具。 但是Postman只能模拟单客户端的单次请求&#xff0c;而对于模拟多用户并发等性能测试&#xf…

五年前端的面试之旅

哈喽我是树酱&#xff0c;最近整理了下前端面试相关的知识题库&#xff0c;借此分享给各位小伙伴&#xff0c;帮助小伙伴早日拿到钟意的offer&#xff01; 前言 最近就业市场不景气&#xff0c;跟大环境较差也有关&#xff0c;确实给我们也会带来一定的挑战。在招聘网站投简历的…

中国象棋AI在线对弈游戏源码

源码介绍 这是一款html5小游戏&#xff0c;主要功能在于js&#xff0c;带一套皮肤、内置ai算法&#xff0c;有能力的可以自行修改。 源码截图 下载地址 链接&#xff1a;https://pan.baidu.com/s/1fYp1HWsd91nJOdX1M8RFtQ?pwdh2iz 提取码&#xff1a;h2iz

ArcGIS二次开发(一)——搭建开发环境以及第一个简单的ArcGIS Engine 程序

Arcgis10.2、Arcgis Engine10.2与Microsoft Visual Studio 2012的版本进行安装 1、推荐教程与安装包2、安装顺序3、安装成功测试VS新建项目可以创建ArcGIS项目&#xff0c;并且在VS中拖拽ArcGIS工具 4、搭建第一个简单的ArcGIS Engine 程序 ArcEngine和VS版本是有对应的&#x…