时间基础概念及Linux中的时间函数

时间基础概念及Linux中的时间函数

    • 时间相关概念
      • GMT 时间
      • UTC 时间
      • 时区 `Time Zone`
      • 夏令时 `DST`
      • 本地时间 `localtime`
    • Linux 系统中的时间
      • 时钟基础概念
      • 系统节拍数 `jiffies`
      • Linux系统查看时间及配置时区
      • 获取时间函数
        • 获取 ==当前时间== `time()`
        • 获取 ==当前时间(微秒)== `gettimeofday()`
          • `struct timeval` 结构体
      • 时间转换函数
        • ==time_t时间== 转换为 ==时间字符串== `ctime()/ctime_r()`
        • ==time_t时间== 转换为 ==本地时间== `localtime()` / `localtime_r()`
          • `struct tm` 结构体
        • ==time_t时间== 转换为 ==UTC时间== `gmtime()` / `gmtime_r()`
        • 转换回 ==time_t时间== `mktime()`
        • ==分解时间== 转换为 ==固定时间字符串== `asctime()/asctime_r()`
        • ==分解时间== 转换为 ==自选时间字符串== `strftime()`
        • ==自选时间字符串== 转换为 ==分解时间== `strptime()`
      • 设置时间函数
        • 设置 ==系统本地时间== `settimeofday()`
      • 例程
      • 总结
    • 参考文献

时间相关概念

GMT 时间

GMT(Greenwich Mean Time)中文全称是 格林威治标准时间 ,它规定太阳每天经过位于英国伦敦郊区的皇家格林威治天文台的时间为中午12点。

格林威治皇家天文台为了海上霸权的扩张计划,在十七世纪就开始进行天体观测。为了天文观测,选择了穿过英国伦敦格林威治天文台子午仪中心的一条经线作为零度参考线,这条线,简称格林威治子午线。

1884年10月在美国华盛顿召开了一个国际子午线会议,该会议将格林威治子午线设定为本初子午线,并将格林威治平时 (GMT, Greenwich Mean Time) 作为世界时间标准(UT, Universal Time)。由此也确定了全球24小时自然时区的划分,所有时区都以和 GMT 之间的偏移量做为参考。

1972年之前,格林威治时间(GMT)一直是世界时间的标准。1972年之后,GMT 不再是一个时间标准了。

UTC 时间

UTC(Coodinated Universal Time), 协调世界时 ,又称世界统一时间、世界标准时间、国际协调时间。由于英文(CUT)和法文(TUC)的缩写不同,作为妥协,简称UTC。

UTC是基于原子钟的国际标准时间。它的定义是通过原子钟测量出的时间来提供一个准确且稳定的时间参考。UTC起源于1970年代,旨在替代格林尼治平均时间(GMT)作为全球标准时间。

以下是对UTC时间的详细介绍:

  1. 原子钟基准:UTC是基于国际原子时(International Atomic Time,缩写为TAI)的基础上进行微调得出的。国际原子时是由全球各地的原子钟测量得出的最准确的时间标准,它以国际计量单位制中的秒作为基本单位。
  2. 闰秒:为了确保UTC与地球自转的持续时间保持一致,每隔一段时间会插入一个闰秒。闰秒的插入是由国际地球自转与参考系统服务(International Earth Rotation and Reference Systems Service,缩写为IERS)根据地球自转速度的变化情况进行决定的。这样做是为了防止地球自转与UTC时间之间的差异过大。
  3. 时间标准化:UTC时间是全球通用的时间标准,不受时区的影响。它以24小时制表示,使用从0到23的数字表示小时,从0到59的数字表示分钟,以及从0到59的数字表示秒。例如,12:00:00表示中午12点,而23:59:59表示晚上11点59分59秒。
  4. 与其他时间标准的关系:UTC与GMT非常接近,UTC 比 GMT更精准,以原子时计时,但 在不需要精确到秒的情况下,两者几乎完全一致 。在实际使用中,GMT和UTC通常可以互换使用,尽管严格来说它们在定义和计算方法上有一些微小的差异。
  5. 时间表示格式:UTC时间可以使用多种表示格式来呈现。最常见的格式是"YYYY-MM-DD HH:MM:SS",其中"YYYY"表示年份,"MM"表示月份,"DD"表示日期,"HH"表示小时,"MM"表示分钟,“SS"表示秒。例如,2023年5月31日下午3点30分15秒的UTC时间可以表示为"2023-05-31 15:30:15”。

时区 Time Zone

从格林威治本初子午线起,经度每向东或者向西间隔15°,就划分一个时区,在这个区域内,大家使用同样的标准时间。

但实际上,为了照顾到行政上的方便,常将1个国家或1个省份划在一起。所以时区并不严格按南北直线来划分,而是按自然条件来划分。另外:由于目前,国际上并没有一个批准各国更改时区的机构。一些国家会由于特定原因改变自己的时区。

全球共分为24个标准时区,相邻时区的时间相差一个小时。

在不同地区,同一个时区往往会有很多个不同的时区名称,因为名称中通常会包含该国该地区的地理信息。

在夏令时期间,当地的时区名称及字母缩写会有所变化(通常会包含“daylight”或“summer”字样)。例如美国东部标准时间叫:EST,Estern Standard Time;而东部夏令时间叫:EDT,Estern Daylight Time。

想查看世界所有时区的名字可以访问这个网站:Time Zone Abbreviations - Worldwide List

image-20230531172745008

全球被划分为24个时区,每一个时区横跨经度15度,以英国格林威治的本初子午线作为零度经线,将全球划分为东西两半球,分为东一区、东二区、东三区……东十二区以及西一区、西二区、西三区……西十二区,而本初子午线所在时区被称为中时区(或者叫零时区),划分图如下所示:

image-20230531185317254

东十二区和西十二区其实是一个时区,就是十二区东十二区与西十二区各横跨经度7.5度,以180度经线作为分界线 。每个时区的中央经线上的时间就是这个时区内统一采用的时间,称为区时。相邻两个时区的时间相差1小时。例如,我国东8区的时间总比泰国东7区的时间早1小时,而比日本东9区的时间晚1小时。因此,出国旅行的人,必须随时调整自己的手表,才能和当地时间相一致。凡向西走,每过一个时区,就要把表向前拨1小时(比如2点拨到1点);凡向东走,每过一个时区,就要把表向后拨1小时(比如1点拨到2点)。

实际上,世界上不少国家和地区都不严格按时区来计算时间。为了在全国范围内采用统一的时间,一般都把某一个时区的时间作为全国统一采用的时间。例如,我国把首都北京所在的东8区的时间作为全国统一的时间,称为北京时间,北京时间就作为我国使用的本地时间,譬如我们电脑上显示的时间就是北京时间,我国国土面积广大,由东到西横跨了5个时区,也就意味着我国最东边的地区与最西边的地区实际上相差了4、5个小时。又例如,英国、法国、荷兰和比利时等国,虽地处中时区,但为了和欧洲大多数国家时间相一致,则采用东1区的时间。

夏令时 DST

夏时制 (daylight saving time,DST) ,又称夏令时、日光节约时间(英国与其他地区称为 (Summer Time) ),是一种在夏季月份牺牲正常的日出时间,而将时间调快的做法。使用夏时制的地区,会在接近春季开始的时候,通常将时间调快60分钟,并在秋季调回正常时间。夏时制会造成在春季转换当日的睡眠时间减少一个小时,而在秋季转换当日则会多出一小时的睡眠时间。不过,澳洲豪勋爵岛的时间调整只有30分钟,而不是一般的60分钟。

全球约40%的国家在夏季使用夏令时,其他国家则全年只使用标准时间。标准时间在有的国家也因此被相应地称为冬季时间。

image-20230531172445399

1895年,乔治·哈德逊提出夏时制的概念。1916年4月30日,德意志帝国与奥匈帝国首次在全国实施夏时制。后来许多国家在不同时期都曾实行过夏时制,其中1970年代能源危机开始尤为盛行。邻近赤道的地区通常不实施夏时制,因为这些地区的日出变化极小,无法成为实施夏时制的正当理由。部分国家只在其中部分地区实施夏时制;例如澳大利亚在东南部的州分采用,其他地区则不实施。香港、澳门及台湾在二战后曾经连续多年采用夏时制,至1980年停用,中国大陆也曾经在1986年采用,至1992年停用,现时亚洲与非洲地区一般不实施夏时制。

有时,夏时制的转换会让报时工作变得更加复杂,并且会扰乱旅行、计费、纪录保存、医疗设备、重机设备与睡眠模式的运作。电脑软件通常可以自动转换时间,但各地司法管辖区的夏时制政策变更仍有可能会造成困扰。

本地时间 localtime

在日常生活中所使用的时间我们通常称之为本地时间。这个时间等于我们所在(或者所使用)时区内的当地时间,它由与世界标准时间(UTC)之间的偏移量来定义。这个偏移量可以表示为 UTC-UTC+ ,后面接上偏移的小时和分钟数。例如北京时间为 UTC+8

Linux 系统中的时间

时钟基础概念

操作系统中一般会有两个时钟,一个系统时钟(system clock),一个实时时钟RTC(Real time clock):

  • 系统时钟由系统启动之后由内核来维护,譬如使用 date 命令查看到的就是系统时钟,所以在 系统关机情况下是不存在的
  • 实时时钟一般由RTC时钟芯片提供,RTC芯片有相应的电池为其供电,以保证 系统在关机情况下RTC能够继续工作、继续计时

Linux系统 在开机启动之后首先会读取RTC硬件获取实时时钟作为系统时钟的初始值,之后内核便开始维护自己的系统时钟 。所以由此可知, RTC硬件只有在系统开机启动时会读取一次 ,目的是用于对系统时钟进行初始化操作,之后的运行过程中便不会再对其进行读取操作了。

而在系统关机时,内核会将系统时钟写入到RTC硬件、进行同步操作。

系统节拍数 jiffies

jiffies是内核中定义的一个全局变量, 内核使用 jiffies 来记录系统从 启动以来的系统节拍数 ,所以这个变量用来记录以系统节拍时间为单位的时间长度,Linux内核在编译配置时定义了一个节拍时间,使用节拍率(一秒钟多少个节拍数)来表示,譬如常用的节拍率为:

  • 100Hz(一秒钟100个节拍数,节拍时间为1s / 100)

  • 200Hz(一秒钟200个节拍,节拍时间为1s / 200)

  • 250Hz(一秒钟250个节拍,节拍时间为1s / 250)

  • 300Hz(一秒钟300个节拍,节拍时间为1s / 300)

  • 500Hz(一秒钟500个节拍,节拍时间为1s / 500)

  • …………

由此可以发现配置的节拍率越低,每一个系统节拍的时间就越短,也就意味着jiffies记录的时间精度越高,当然, 高节拍率会导致系统中断的产生更加频繁 ,频繁的中断会加剧系统的负担, 一般默认情况下都是采用 100Hz 作为系统节拍率

内核其实通过jiffies来维护系统时钟,全局变量 jiffies 在系统开机启动时会设置一个初始值 ,上面也给大家提到过,RTC实时时钟会在系统开机启动时读取一次,目的是用于对系统时钟进行初始化,这里说的初始化其实指的就是对内核的jiffies变量进行初始化操作,具体如何将读取到的实时时钟换算成jiffies数值,这里便不再给大家介绍了。

所以由此可知,操作系统使用jiffies这个全局变量来记录当前时间,当我们需要获取到系统当前时间点时,就可以使用jiffies变量去计算,当然并不需要我们手动去计算,Linux系统提供了相应的系统调用或C库函数用于获取当前时间,譬如系统调用 time()gettimeofday() ,其实质上就是通过jiffies变量换算得到。

Linux系统查看时间及配置时区

我们可以使用 date 命令查看系统当前的本地时间,如下所示:

image-20230531185625754

可以看到显示出来的字符串后面有一个 CST 字样, CST 在这里其实指的是 China Standard Time (中国标准时间)的缩写 ,表示当前查看到的时间是中国标准时间,也就是我国所使用的标准时间——北京时间,一般在安装Ubuntu系统的时候会提示用户设置所在城市,那么系统便会根据你所设置的城市来确定系统的本地时间对应的时区,譬如设置的城市为上海,那么系统的本地时间就是北京时间,因为我国统一使用北京时间作为本国的标准时间。

在Ubuntu系统下,时区信息通常以标准格式保存在一些文件当中,这些文件通常位于 /usr/share/zoneinfo 目录下, 该目录下的每一个文件(包括子目录下的文件)都包含了一个特定国家或地区内时区制度的相关信息 ,且往往根据其所描述的城市或地区缩写来加以命名,譬如 EST(美国东部标准时间)CET(欧洲中部时间)UTC(世界标准时间)HongkongIranJapan(日本标准时间) 等,也把这些文件称为时区配置文件,如下图所示:

image-20230531185805374

本地时间由时区配置文件 /etc/localtime 定义,通常链接到 /usr/share/zoneinfo 目录下的某一个文件(或其子目录下的某一个文件),如上图所示。

如果我们要 修改Ubuntu系统本地时间的时区信息 ,可以 直接将 /etc/localtime 链接到 /usr/share/zoneinfo 目录下的任意一个时区配置文件 ,譬如EST(美国东部标准时间),首先进入到 /etc 目录下,执行下面的命令:

sudo rm -rf localtime #删除原有链接文件 
sudo ln -s /usr/share/zoneinfo/EST localtime #重新建立链接文件

image-20230531190057264

可以发现后面的标识变成了EST,也就意味着当前系统的本地时间变成了EST时间(美国东部标准时间)。

获取时间函数

获取 当前时间 time()

系统调用time()用于获取当前时间,以秒为单位,返回得到的值是自 1970-01-01 00:00:00 +0000 (UTC) 以来的秒数,其函数原型如下所示(可通过"man 2 time"命令查看):

#include <time.h> // tloc: 如果tloc参数不是NULL,则返回值也存储在tloc指向的内存中
time_t time(time_t *tloc);

返回值: 成功则返回自 1970-01-01 00:00:00 +0000 (UTC) 以来的时间值(以秒为单位);失败则返回-1,并会设置errno。

获取 当前时间(微秒) gettimeofday()

time() 获取到的时间只能精确到秒,如果想要获取更加精确的时间可以使用系统调用 gettimeofday 来实现, gettimeofday() 函数提供微秒级时间精度,函数原型如下所示:

#include <sys/time.h> // tv: struct timeval结构体指针变量
// tz:是个历史产物,早期实现用其来获取系统的时区信息,目前已遭废弃,调用时应设置为NULL。
int gettimeofday(struct timeval *tv, struct timezone *tz);

返回值: 成功返回0;失败将返回-1,并设置errno。

struct timeval 结构体

获取得到的时间值存储在参数tv所指向的 struct timeval 结构体变量中,该结构体包含了两个成员变量 tv_sectv_usec ,分别用于表示秒和微秒:

struct timeval { long tv_sec; /* 秒 */long tv_usec; /* 微秒 */
}

时间转换函数

time_t时间 转换为 时间字符串 ctime()/ctime_r()

ctime() 是一个C库函数,可以将日历时间转换为可打印输出的字符串形式, ctime() 函数原型如下所示:

#include <time.h> // time_t: 时间变量指针
// buf: 是一个指向字符数组的指针,用于存储转换后的时间字符串
char *ctime(const time_t *timep); 
char *ctime_r(const time_t *timep, char *buf);

返回值: 成功将返回一个char *类型指针,指向转换后得到的字符串;失败将返回NULL。

ctime_r()ctime() 的可重入版本,一般推荐大家使用可重入函数 ctime_r() ,可重入函数 ctime_r() 多了一个参数 buf ,也就是缓冲区首地址,所以 ctime_r() 函数需要调用者提供用于存放字符串的缓冲区。

为了保证足够的缓冲区空间来存储时间字符串,buf 的长度应该至少为 26(包括终止符 \0)。这是因为时间字符串的最大长度为 25,再加上一个终止符。

time_t时间 转换为 本地时间 localtime() / localtime_r()

localtime() 函数可以把 time()gettimeofday() 得到的秒数(time_t时间或日历时间)变成一个 struct tm 结构体所表示的时间,该时间对应的是本地时间。localtime函数原型如下:

#include <time.h> // timep:需要进行转换的time_t时间变量对应的指针,可通过time()或gettimeofday()获取得到
// result: struct tm结构体类型指针,存储转换结果
struct tm *localtime(const time_t *timep); 
struct tm *localtime_r(const time_t *timep, struct tm *result);

返回值:

  • localtime() :不可重入,成功则直接返回一个有效的 struct tm 结构体指针。
  • localtime_r() :可重入,成功则返回一个有效的 struct tm 结构体指针,返回值将会等于参数 result ;失败则返回NULL。

可重入版本 localtime_r() 的区别是调用者需要自己定义 struct tm 结构体变量、并将该变量指针赋值给参数 result ,在函数内部会对该结构体变量进行赋值操作。

struct tm 结构体

struct tm 结构体如下所示,该结构体中包含了年月日时分秒星期等信息:

struct tm { int tm_sec; 	/* 秒(0-60) */ int tm_min; 	/* 分(0-59) */ int tm_hour; 	/* 时(0-23) */ int tm_mday; 	/* 日(1-31) */ int tm_mon; 	/* 月(0-11) */int tm_year; 	/* 年(这个值表示的是自1900年到现在经过的年数) */ int tm_wday; 	/* 星期(0-6, 星期日Sunday = 0、星期一=1…) */ int tm_yday; 	/* 一年里的第几天(0-365, 1 Jan = 0) */ int tm_isdst; 	/* 夏令时 (-1 表示无夏令时信息,0 表示不使用夏令时,1 表示使用夏令时) */ 
};

time_t时间 转换为 UTC时间 gmtime() / gmtime_r()

gmtime() 函数也可以把time_t时间变成一个 struct tm 结构体所表示的时间,与 localtime() 所不同的是, gmtime() 函数所得到的是UTC国际标准时间,并不是计算机的本地时间,这是它们之间的唯一区别。 gmtime() 函数原型如下所示:

#include <time.h> struct tm *gmtime(const time_t *timep); 
struct tm *gmtime_r(const time_t *timep, struct tm *result);

转换回 time_t时间 mktime()

mktime() 函数与 localtime() 函数相反, mktime() 可以将使用 struct tm 结构体表示的分解时间转换为 time_t 时间(日历时间),同样这也是一个C库函数,其函数原型如下所示:

#include <time.h> // tm:需要进行转换的struct tm结构体变量对应的指针
time_t mktime(struct tm *tm);

返回值: 成功返回转换得到time_t时间值;失败返回-1。

分解时间 转换为 固定时间字符串 asctime()/asctime_r()

asctime() 函数与 ctime() 函数的作用一样,也可将时间转换为可打印输出的字符串形式,与 ctime() 函数的区别在于, ctime() 是将 time_t 时间转换为固定格式字符串、而 asctime() 则是将 struct tm 表示的分解时间转换为固定格式的字符串。 asctime() 函数原型如下所示:

#include <time.h> // tm:需要进行转换的struct tm表示的时间
// buf:可重入版本函数asctime_r需要额外提供的参数buf,指向一个缓冲区,用于存放转换得到的字符串。
char *asctime(const struct tm *tm); 
char *asctime_r(const struct tm *tm, char *buf);

返回值: 转换失败将返回NULL;成功将返回一个char *类型指针,指向转换后得到的时间字符串,对于asctime_r函数来说,返回值就等于参数buf。

在使用 struct tm 结构体时,需要注意以下几点:

  • tm_montm_year 的取值范围与实际的月份和年份不同。tm_mon 的取值范围是 0 到 11,其中 0 表示 1 月,1 表示 2 月,依此类推。tm_year 表示从 1900 年开始的年数,例如,tm_year 为 121 表示的是 2021 年。
  • tm_wday 的取值范围是 0 到 6,其中 0 表示周日,1 表示周一,依此类推。
  • tm_yday 表示一年中的天数,从 0 开始,例如,1 月 1 日的 tm_yday 值为 0,12 月 31 日的 tm_yday 值为 365。
  • tm_isdst 是夏令时标识符,用于指示当前时间是否处于夏令时期间。如果系统无法确定夏令时信息,则 tm_isdst 的值为 -1;如果不使用夏令时,则值为 0;如果使用夏令时,则值为 1。

分解时间 转换为 自选时间字符串 strftime()

除了 asctime() 函数之外, strftime() 也可以将一个 struct tm 变量表示的分解时间转换为为格式化字符串,并且在功能上比 asctime()ctime() 更加强大,它可以根据自己的喜好自定义时间的显示格式,而 asctime()ctime() 转换得到的字符串时间格式的固定的。

strftime() 函数原型如下所示:

#include <time.h> // s:指向一个缓存区的指针,该缓冲区用于存放生成的字符串
// max:字符串的最大字节数。
// format:这是一个用字符串表示的字段,包含了普通字符和特殊格式说明符,可以是这两种字符的任意组合。
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);

返回值: 如果转换得到的目标字符串不超过最大字节数(也就是max),则返回放置到s数组中的字节数;如果超过了最大字节数,则返回0。

format 特殊格式说明符将会被替换为 struct tm 结构体对象所指时间的相应值,这些特殊格式说明符如下:

说明符表示含义实例
% a \% \mathrm{a} %a星期的缩写 S u n \mathrm{Sun} Sun
% A \% \mathrm{~A} % A星期的完整名称 S u n d a y \mathrm{Sunday} Sunday
% b \% \mathrm{~b} % b月份的缩写 M a r \mathrm{Mar} Mar
% B \% \mathrm{~B} % B月份的完整名称 M a r c h \mathrm{March} March
% c \% \mathrm{c} %c系统当前语言环境对应的首选日期和时间表示形式
% C \% \mathrm{C} %C世纪(年/100) 20 20 20
% d \% \mathrm{~d} % d十进制数表示一个月中的第几天(01-31) 15 、 05 15 、 05 1505
% D \% \mathrm{D} %D相当于 % m / % d / % y \mathrm{m} / \% \mathrm{~d} / \% \mathrm{y} m/% d/%y 01 / 14 / 21 01 / 14 / 21 01/14/21
% e \% \mathrm{e} %e % d \%\mathrm{d} %d 相同,但是单个数字时, 前导 0 会被去掉 15 、 5 15 、 5 155
% F \% \mathrm{~F} % F相当于 % Y − % m − % d \%\mathrm{Y}-\%\mathrm{m}-\%\mathrm{d} %Y%m%d 2021 − 01 − 14 2021\mathrm{-}01\mathrm{-}14 20210114
% h \% \mathrm{~h} % h相当于 % b \% \mathrm{~b} % b J a n \mathrm{Jan} Jan
% H \% \mathrm{H} %H十进制数表示的 24 小时制的小时(范围 00-23) 01 、 22 01 、 22 0122
%I十进制数表示的 12 小时制的小时(范围 01-12) 01 、 11 01 、 11 0111
% j \% \mathrm{j} %j十进制数表示的一年中的某天(范围 001-366) 050 、 285 050 、 285 050285
% k \% \mathrm{k} %k与% % \% % 相同, 但是单个数字时, 前导 0 会被去掉(范围 0-23) 1 、 22 1 、 22 122
% 1 \% 1 %1与%I 相同, 但是单个数字时, 前导 0 会被去掉(范围 1-12) 1 、 11 1 、 11 111
% m \% \mathrm{~m} % m十进制数表示的月份(范围 01-12) 01 、 10 01 、 10 0110
% M \% \mathrm{M} %M十进制数表示的分钟(范围 00-59) 01 、 55 01 、 55 0155
% n \%\mathrm{n} %n换行符
% p \% \mathrm{p} %p根据给定的时间值, 添加 “AM” 或 “PM” P M \mathrm{PM} PM
% P \% \mathrm{P} %P与 %p 相同, 但会使用小写字母表示 p m \mathrm{pm} pm
% r \% \mathrm{r} %r相当于 % I : % M : % S % p \% \mathrm{I}: \% \mathrm{M}: \% \mathrm{~S} \% \mathrm{p} %I:%M:% S%p 12 : 15 : 31 P M 12: 15: 31 \mathrm{PM} 12:15:31PM
% R \% \mathrm{R} %R相当于 % H : % M \% \mathrm{H}: \% \mathrm{M} %H:%M 12 : 16 12: 16 12:16
% S \% \mathrm{~S} % S十进制数表示的秒数(范围 00 − 60 00-60 0060 05 、 30 05 、 30 0530
% T \% \mathrm{~T} % T相当于 % H : % M : % S \% \mathrm{H}: \% \mathrm{M}: \% \mathrm{~S} %H:%M:% S 12 : 20 : 03 12: 20: 03 12:20:03
% u \% \mathrm{u} %u十进制数表示的星期(范围 1-7, 星期一为 1) 1 , 5 1,5 1,5
% U \% \mathrm{U} %U十进制数表示, 当前年份的第几个星期(范围 00 − 53 00-53 0053), 从第一个星期日作为 01 01 01 周的第一天开始
% W \% \mathrm{~W} % W十进制数表示, 当前年份的第几个星期(范围 00 − 53 00-53 0053), 从第一个星期一作为 01 01 01 周的第一天开始
% W \% \mathrm{~W} % W十进制数表示的星期, 范围为 0 − 6 0-6 06, 星期日为 0 0 0
% x \% \mathrm{x} %x系统当前语言环境的首选日期表示形式, 没有时间 01 / 14 / 21 01 / 14 / 21 01/14/21
% X \% \mathrm{X} %X系统当前语言环境的首选时间表示形式, 没有日期 12 : 30 : 16 12: 30: 16 12:30:16
% y \% \mathrm{y} %y十进制数表示的年份(后两字数字) 21 21 21
% Y \% \mathrm{Y} %Y十进制数表示的年份 (4 个数字) 2021 2021 2021
% % \% \% %%输出%符号 % \% %

通过上表可知,譬如我要想输出 2021-01-14 16:30:25<PM> January Thursday 这样一种形式表示的时间日期,那么就可以这样来设置 format 参数:

"%Y-%m-%d %H:%M:%S<%p> %B %A"

自选时间字符串 转换为 分解时间 strptime()

strptime 用于将字符串解析为时间和日期的结构体 struct tm。它在 <time.h> 头文件中定义,函数原型如下:

#include <time.h> // str: 要解析的字符串
// format: 字符串的格式
// timeptr: 指向 struct tm 结构体的指针,用于存储解析后的时间和日期信息。
char *strptime(const char *str, const char *format, struct tm *timeptr);

返回值: 指向字符串中解析后剩余部分的指针,如果解析成功,则返回的指针指向字符串的结束位置,如果解析失败,则返回 NULL

strptime 函数根据指定的格式解析字符串,并将解析结果存储在 timeptr 指向的结构体中。解析过程中,函数会根据 format 字符串中的格式指示符,逐个匹配 str 中的字符,从而将时间和日期的各个部分提取出来,并存储在 timeptr 中的相应成员中。

设置时间函数

设置 系统本地时间 settimeofday()

使用 settimeofday() 函数可以设置时间,也就是设置系统的本地时间,函数原型如下所示:

#include <sys/time.h> // tv:需要设置的时间便通过参数tv指向的struct timeval结构体变量传递进去
// tz:是个历史产物,早期实现用其来获取系统的时区信息,目前已遭废弃,调用时应设置为NULL。
int settimeofday(const struct timeval *tv, const struct timezone *tz);

返回值: 成功返回0;失败将返回-1,并设置errno。

使用 settimeofday 设置系统时间时内核会进行权限检查, 只有超级用户(root)才可以设置系统时间,普通用户将无操作权限

例程

我们通过一个程序进行测试,例程包含 timectimelocaltimegmtimemktimeasctimestrftimegettimeofdaysettimeofday 函数:

#include <stdio.h>
#include <time.h>
#include <sys/time.h>int main() {time_t rawtime;struct tm *timeinfo;struct timeval tv;struct timezone tz;char buffer[80];// 获取当前时间time(&rawtime);printf("Current time: %ld\n", rawtime);// 使用ctime将时间转换为字符串printf("Current time (ctime): %s", ctime(&rawtime));// 使用localtime将时间转换为本地时间timeinfo = localtime(&rawtime);printf("Local time: %s", asctime(timeinfo));// 使用gmtime将时间转换为GMT时间timeinfo = gmtime(&rawtime);printf("GMT time: %s", asctime(timeinfo));// 使用mktime将结构体时间转换为时间值timeinfo->tm_year = 122;  // 2022年timeinfo->tm_mon = 0;    // 1月timeinfo->tm_mday = 1;   // 1号rawtime = mktime(timeinfo);printf("Custom time (mktime): %ld\n", rawtime);// 使用asctime将时间值转换为字符串printf("Custom time (asctime): %s", asctime(localtime(&rawtime)));// 使用strftime将时间值格式化为字符串strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", localtime(&rawtime));printf("Custom time (strftime): %s\n", buffer);// 使用gettimeofday获取当前时间和时区信息gettimeofday(&tv, &tz);printf("Current time (gettimeofday): %ld seconds, %ld microseconds\n", tv.tv_sec, tv.tv_usec);printf("Timezone offset: %d seconds\n", tz.tz_minuteswest * 60);// 使用settimeofday设置系统时间tv.tv_sec += 3600;  // 延时1小时if (settimeofday(&tv, NULL) == 0) {printf("System time set successfully.\n");} else {printf("Failed to set system time.\n");}return 0;
}

在这个例程中,我们先获取当前时间,并使用 ctime 函数将其转换为字符串并打印。然后,使用 localtime 函数将时间转换为本地时间,并使用 asctime 函数将其转换为字符串并打印。接着,使用 gmtime 函数将时间转换为 GMT 时间,并再次使用 asctime 函数将其转换为字符串并打印。

然后,我们使用 mktime 函数将自定义的 struct tm 时间结构体转换为时间值,并打印转换后的时间值。使用 asctime 函数将时间值转换为字符串并打印。

接下来,我们使用 strftime 函数将时间值格式化为自定义格式的字符串,并打印。

然后,使用 gettimeofday 函数获取当前时间和时区信息,并打印时间值和时区偏移量。

最后,我们使用 settimeofday 函数将系统时间向后延时1小时,并打印设置结果。

运行这个例程,将输出包含以上所有函数的相关信息:

image-20230531192349215

因为settimeofday 函数需要管理员权限才能设置系统时间,所以运行失败。

总结

上面介绍了时间相关的基本概念,譬如GMT时间、UTC时间以及全球24个时区的划分等,并且给大家介绍了Linux系统下常用的时间相关的系统调用和库函数,主要有9个:

image-20230531185110591

参考文献

1:彻底弄懂GMT、UTC、时区和夏令时 - 知乎

2:彻底弄懂GMT、UTC、时区和夏令时 | ChampYin’s Blog

3:Time Zone Abbreviations - Worldwide List

4:Daylight saving time - Wikipedia

5:【正点原子】STM32MP1嵌入式Linux C应用编程指南V1.4 - 第七章

6:ChatGPT



部分图片来源网络,如有侵权请联系我删除。
如有疑问或错误,欢迎和我私信交流指正。
版权所有,未经授权,请勿转载!
Copyright © 2023.05 by Mr.Idleman. All rights reserved.


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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Studio||gif/glide/jre包导入失败/动态效果(内含源代码

step by step. 目录 效果&#xff1a; 1.加入依赖 如果jre包导入后没有标红但是还是无法运行&#xff1a; 2.添加ImageVIew 3.效果图&#xff1a; 终于成功了qwq 效果&#xff1a; 1.加入依赖 Android studio 图片加载框架Glide介绍及使用_秀川冈坂的博客-CSDN博客在泰…

腾讯智能识别终端设备获授权;微软将推动必应商业变现;Meta减少数字藏品;高通推出首款5G物联网处理器丨每日大事件...

‍ ‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 企业动态 腾讯智能识别终端设备获授权 3月14日&#xff0c; 腾讯科技&#xff08;深圳&#xff09;有限公司申请的“智能识别终端”外观专利获授权。摘要显示&#xff0c;本外观设计产品用于身份核验、交易支付等&#x…

基于反馈的Query改写:你说过的,我才最懂

​一、前言 本文对之前做过一段时间的Query改写&#xff08;纠错&#xff0c;本文不严格区分这两种叫法&#xff09;做一些总结&#xff0c;算法原理可以参考亚马逊的这篇论文&#xff1a;Feedback-Based Self-Learning in Large-Scale Conversational AI Agents。 二、方法 …

朋友的身份证被骗子注册了支付宝开通了花呗,消费了三千被催债才知道花呗被盗如何处理?

自己花了就自己花了呗&#xff0c;啥叫被盗了&#xff0c;我有充分的理由怀疑你撒谎。 先来看下花呗是怎么开通的 步骤1&#xff1a;首先要在支付宝进行实名认证&#xff0c;在支付宝手机版个人信息中进行实名认证后&#xff0c;才具备基本的开通花呗的基本资格。 步骤2&…

起底51信用卡:年赚21亿涉暴力催收,子公司给714高炮导流

独角兽51信用卡和他的创始人孙海涛正陷入一场巨大的风波中。这家此前知名度局限在业内和杭州本地的互联网金融公司&#xff0c;正被推向大众视野。 今年9月以来&#xff0c;杭州警方接上级部门线索传递&#xff0c;结合日常工作发现&#xff0c;51信用卡涉及大量各地异常投诉信…

西门子PLC 延时催款程序程序

西门子PLC 延时催款程序程序。 Plc程序代写服务。 西门子1200 PlC编程实例 西门子PlC延时催款程序。 非标行业是一个特殊的行业&#xff0c;面对设备发货到现场后迟迟不肯付款的和找各种理由拒绝搪塞验收的客户&#xff0c;必须的采取非常的手段&#xff0c;其中给设备加密定…

一部手机背后的小镇青年:吃着蜜糖、喝着毒药

吴宵愁这次终于在大年三十前赶回了家。父母见到回家过年的儿子既欢喜又愁闷——1990年出生的吴宵愁目前还没有女朋友。 在吴宵愁的山东农村老家&#xff0c;这个年纪没结婚就是老大不小的困难户&#xff0c;父母出门都觉得脸上无光。更何况&#xff0c;吴宵愁2018年还惹了大麻烦…

这6点解释了罗永浩为什么要卖艺

01 是的&#xff0c;我们的‘老赖又上热搜了。 &#xff08;ps:还是传统的语法&#xff0c;换了个人而已&#xff0c;味道有点改变&#xff09; 11 月 3 日下午&#xff0c;罗永浩因锤子科技的 375 万欠款被江苏丹阳法院限制高消费&#xff0c;他不得乘坐飞机头等舱、软卧、高铁…

防骗指南-披着交友恋爱的外衣,诱骗受害者赌博转钱

转自https://zhuanlan.zhihu.com/p/58012607 说来巧&#xff0c;昨天刚看了一篇关于“东南亚杀猪盘”的报道&#xff0c;晚上就有朋友在群里发了几张截图&#xff0c;如下 打开此人的朋友圈&#xff0c;各种家庭和睦&#xff0c;生活小康&#xff0c;还时不时秀秀自己的锥子…

硅谷银行破产!真相更可怕:美国疯狂薅全世界的羊毛

观点| Mr.K 主笔| Wendy.L 编辑| Emma 来源| 技术领导力(ID&#xff1a;jishulingdaoli) 今天的故事先从一名高富帅开始说起。 这位高富帅出生于1983年&#xff0c;在美国电子工业和计算机业的王国硅谷。 家大业大的他&#xff0c;喜欢为创新科技企业提供金融服务&#xff…

辟谣、催债、倒闭.....2018年后,将再无创业黄金期!

导读 上下游没钱了&#xff0c;信贷机构没钱了&#xff0c;风险投资人也没钱了。中小企业成本和资金压力大&#xff0c;企业违约、到期没法还债&#xff0c;非银行金融机构爆雷、大量出问题&#xff0c;股市非理性下滑&#xff0c;所有人进入新一轮升级性迷茫。由于各项成本的抬…

扬州大学回应「清华博士入职5年,月薪才372元」

本文来源&#xff1a;封面新闻、澎湃新闻、知乎麦格纳&#xff0c;编辑&#xff1a;募格学术 7月22日&#xff0c;一位自称从清华大学博士毕业&#xff0c;现就职于扬州大学的教师在知乎上以“麦格纳”账号发文&#xff0c;称自己入职扬州大学5年&#xff0c;月薪仅为372元。 7…

Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day22】—— 并发编程2

大家好&#xff0c;我是陈哈哈&#xff0c;北漂五年。相信大家和我一样&#xff0c;都有一个大厂梦&#xff0c;作为一名资深Java选手&#xff0c;深知面试重要性&#xff0c;接下来我准备用100天时间&#xff0c;基于Java岗面试中的高频面试题&#xff0c;以每日3题的形式&…

南京“211”大学毕业生跳楼悲剧 家人在催债声中送别

斯人已逝,余波未了!南京一所211大学的23岁毕业生许阳(化名),8月31日跳下28楼,去世前3个月有34笔网贷申请。 一名从小到大都很优秀的学生,家中相对富足的他,为何会如此频繁申请贷款,又为何走向绝路?《每日经济新闻》记者前往其江苏苏中地区的家乡深入调查。 这是一次…

chatgpt赋能python:Python分离函数介绍

Python分离函数介绍 在Python编程中&#xff0c;函数是一个非常重要的概念。函数的分离是保持代码组织结构清晰的一种方式。一个好的函数设计可以让代码变得易于维护、扩展和重用。本文将介绍Python中如何分离函数。 什么是函数 在Python中&#xff0c;函数是一段可重用的代…

自己动手写chatGPT:神经网络的神经元和损失函数

chatGPT基于所谓的大模型&#xff0c;这里有两个关键词一个是“大”&#xff0c;一个是“模型”&#xff0c;我们先看什么叫“模型”。所谓模型其实就是深度学习中的神经网络&#xff0c;后者由很多个称之为“神经元”基本单元组成。神经元是一种基础计算单元&#xff0c;它执行…

YYC松鼠聚合直播系统源码 融和电商商城、网红热点、娱乐竞技直播等

正文&#xff1a; 松鼠聚合直播系统是一套团队自主研发、源码开源&#xff0c;可自由二次开发的直播系统。系统融和电商商城、网红热点、娱乐竞技直播等&#xff0c;能够快速实现吸粉引流&#xff0c;集合在线直播、互动分享、社交传播等一体化功能&#xff0c;实现“直播”的…

ChatGPT人工智能商业高效实操指南

帮助看见未来的人更快成功 现在,ChatGPT 的时代来了,越来越多的案例佐证,它正在取代人力,并且投入产出 比高到让人无法想象!轻松为企业主省出几十万! 那么人工智能在在商业里面到底扮演了多少个角色呢? 接下来我会告诉你们 1、承担客服工作,客户满意度大大提高 客服无疑是…

ChatGPT已能模仿任何写作风格,让你的自媒体快速起号

我认识的一两个技术大佬目前失业在家&#xff0c;压力不小。对于现在的就业市场来说&#xff0c;再找工作&#xff0c;高不成低不就。他们的薪资&#xff0c;一般企业无法承受&#xff0c;大厂岗位又在缩减。今年真正感受到了寒冬。 对于我们还有饭吃的程序员&#xff0c;现在不…

或许能用 ChatGPT 插件实现财富自由

文章目录 或许能用 ChatGPT 插件实现财富自由1. 认识一下1.1 是什么1.2 怎么用 2. 举个例2.1 Wolfram2.2 Browsing 3. 怎么做到的4. 财富自由4.1 生活类插件4.2 品牌推广类 5. 限制 或许能用 ChatGPT 插件实现财富自由 我们知道&#xff0c;当前 ChatGPT 最大的局限性就是模型…