【Linux】Linux中的时间

33 篇文章 167 订阅
本文详细介绍了时间相关概念,包括UTC(协调世界时间)、RTC(实时时钟)和日历时间,并探讨了它们之间的区别。讲解了C++中处理时间的函数,如time()、gmtime()、localtime()等,以及如何进行时区转换。同时,提到了JavaScript中处理时间的函数,并给出了示例代码。文章还讨论了Linux系统下的时区设置和RTC时间与CMOS的关系。
摘要由CSDN通过智能技术生成

几种时间的说明

UTC:全称是Coordinated Universal Time,协调世界时间,它是一个与时区相关的时间,目前将世界时区分为24个,如下所示:

时区

时区范围

时区中心线

UTC

7.5°W~7.5°E

UTC+1

7.5°E~22.5°E

15°E

UTC+2

22.5°E~37.5°E

30°E

UTC+3

37.5°E~52.5°E

45°E

UTC+4

52.5°E~67.5°E

60°E

UTC+5

67.5°E~82.5°E

75°E

UTC+6

82.5°E~97.5°E

90°E

UTC+7

97.5°E~112.5°E

105°E

UTC+8

112.5°E~127.5°E

120°E

UTC+9

127.5°E~142.5°E

135°E

UTC+10

142.5°E~157.5°E

150°E

UTC+11

157.5°E~172.5°E

165°E

东西十二区

172.5°E~172.5°W

180°

UTC-11

172.5°W~157.5°W

165°W

UTC-10

157.5°W~142.5°W

150°W

UTC-9

142.5°W~127.5°W

135°W

UTC-8

127.5°W~112.5°W

120°W

UTC-7

112.5°W~97.5°W

105°W

UTC-6

97.5°W~82.5°W

90°W

UTC-5

82.5°W~67.5°W

75°W

UTC-4

67.5°W~52.5°W

60°W

UTC-3

52.5°W~37.5°W

45°W

UTC-2

37.5°W~22.5°W

30°W

UTC-1

22.5°W~7.5°W

15°W

UTC跟GMT(Greenwich Mean Time,格林威治时间)一致,因为格林威治位于0时区,UTC时间即是0时区的时间。中国位于东八区,所以对应的时间是UTC时间加8小时,即UTC+8,也称为CST(China Standard Time)。UTC倾向于跟标准相关的时间。UTC的查看可以通过date命令(注意需要加上-u,否则看到的是本地时间,即UTC+8的时间):

RTC:Real Time Clock,实时时钟。RTC和UTC不是一个概念上的时间,RTC通常指的是系统上一个计时的芯片,它将时间保存在CMOS中,而CMOS通过电池可以保持数据不丢失,因此在系统没有上电的情况下也还会正常运行。操作系统启动时会查看这个时间,也可以同步这个时间。RTC倾向于跟硬件相关的时间。查看RTC的命令如下(需要在root权限下查看,因为它涉及硬件,需要的权限较高):

Calendar Time:日历时间,指的是从当前时间到某个标准时间经历的秒数。这里的某个时间通常是指1970年1月1日0时0分0秒,这个时间点也被称为Epoch。日历时间是一个相对的时间值,倾向于跟软件相关。

时间相关的c函数和结构体

首先需要关注的是一个结构体(来自struct_tm.h),该结构体在之后介绍的时间函数中会使用到:

/* ISO C `broken-down time' structure.  */
struct tm
{
    int tm_sec;     /* Seconds. [0-60] (1 leap second) */
    int tm_min;     /* Minutes. [0-59] */
    int tm_hour;      /* Hours. [0-23] */
    int tm_mday;      /* Day.   [1-31] */
    int tm_mon;     /* Month. [0-11] */
    int tm_year;      /* Year - 1900.  */
    int tm_wday;      /* Day of week. [0-6] */
    int tm_yday;      /* Days in year.[0-365] */
    int tm_isdst;     /* DST.   [-1/0/1]*/

# ifdef __USE_MISC
    long int tm_gmtoff;   /* Seconds east of UTC.  */
    const char *tm_zone;    /* Timezone abbreviation.  */
# else
    long int __tm_gmtoff;   /* Seconds east of UTC.  */
    const char *__tm_zone;  /* Timezone abbreviation.  */
# endif
};

之后是几个重要的函数,下面将一一介绍。

首先是time()函数,其原型如下:

/* Return the current time and put it in *TIMER if TIMER is not NULL.  */
extern time_t time (time_t *__timer) __THROW;

可以看到它的入参和返回值都是time_t,它实际上就是一个长整型,表示的就是从Epoch到函数调用之时经历的秒数,也就是前面提到的日历时间。为了得到这个时间,通常传入一个NULL作为参数,返回值就是我们需要的。下面是一个例子:

#include <stdio.h>
#include <time.h>

int main(int argc, char *argv[])
{
    time_t t = time(NULL);
    printf("time_t: %ld\n", t);
    return 0;
}

运行程序得到的是:

time_t: 1606627854

用计算器简单地算下就可以得到它就是从1970年1月1日0时0分0秒开始到现在的一个值。当然用计算器算没必要,下面会介绍函数来处理这个值。

gmtime()函数会利用time_t的值,并将其转换为一个真正的时间,从函数名称就可以看出来是格林威治时间,也就是UTC的时间。与gmtime对应的就是转化为本地时间的函数localtime()。它们的函数原型如下:

/* Return the `struct tm' representation of *TIMER
in Universal Coordinated Time (aka Greenwich Mean Time).  */
extern struct tm *gmtime (const time_t *__timer) __THROW;

/* Return the `struct tm' representation
of *TIMER in the local timezone.  */
extern struct tm *localtime (const time_t *__timer) __THROW;

这两个函数的返回值就是前面提到的结构体strum tm,接收的参数就是time()函数的返回值time_t。下面是使用这两个函数的例子:

int main(int argc, char *argv[])
{
    struct tm *tm;
    time_t t = time(NULL);
    printf("time_t: %ld\n", t);
    tm = gmtime(&t);
    printf("%d-%d-%d %d:%d:%d %s\n", tm->tm_year + 1900, tm->tm_mon + 1,
    tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_zone);
    tm = localtime(&t);
    printf("%d-%d-%d %d:%d:%d %s\n", tm->tm_year + 1900, tm->tm_mon + 1,
    tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_zone);
    return 0;
}

得到的结果如下:

time_t: 1606629854
2020-11-29 6:4:14 GMT
2020-11-29 14:4:14 CST

这里需要注意的是struct tm里面年和月两个值的参考值,所以需要增加特定的偏移之后才是真实值。从打印的结果也可以看到本地时间和UTC时间之间的差异刚好是八小时。

上面的代码中我们自己构造了显示时间的字符串,实际上这可以通过ctime()和asctime()来完成,两者功能相同,但接收的参数不用:

/* Return a string of the form "Day Mon dd hh:mm:ss yyyy\n"
that is the representation of TP in this format.  */
extern char *asctime (const struct tm *__tp) __THROW;

/* Equivalent to `asctime (localtime (timer))'.  */
extern char *ctime (const time_t *__timer) __THROW;

有了这两个函数就不需要在自己构造字符串,下面是代码示例:

#include <stdio.h>
#include <time.h>

int main(int argc, char *argv[])
{
    struct tm *tm;
    time_t t = time(NULL);
    printf("time_t: %ld\ntime: %s", t, ctime(&t));
    tm = gmtime(&t);
    printf("%s", asctime(tm));
    tm = localtime(&t);
    printf("%s", asctime(tm));
    return 0;
}

得到的结果如下:

time_t: 1606630276
time: Sun Nov 29 14:11:16 2020
Sun Nov 29 06:11:16 2020
Sun Nov 29 14:11:16 2020

这里可以看出ctime()显示的是本地时间。两个函数返回的时间字符串还自带换行,所以不需要我们添加。

最后还要介绍的函数是mktime()。它实际上跟gmtime()和localtime()刚好相反,是将struct tm转换为time_t,其原型如下:

/* Return the `time_t' representation of TP and normalize TP.  */
extern time_t mktime (struct tm *__tp) __THROW;

RTC与时间函数

前面介绍的这些函数,相关的时间只是UTC和日历时间,似乎与RTC时间没有直接的关系。要了解RTC与时间函数的关系,首先需要了解RTC时间本身。RTC时间其实是存放在CMOS中的时间,CMOS可以看成是一段非易失的介质,里面的内容是固定的,如下表所示:

而这里面的值会通过一个芯片按固定的时间修改,以保持时间的同步。time()函数的一个实现方式就是通过读取里面的值,然后与Epoch时间做减法,得到的时间间隔再转化为秒数,就可以作为返回值了。

时区

前面的内容中已经有介绍到时区相关的概念,比如UTC表示的是0时区的时间,也称为格林威治时间,UTC+8是东八区的时间,也就是我们这边的本地时间。

关于时区,在Linux下可以有很多种方式来查看,前面的说明中已经在显示时间的命令中看到,另外还可以通过几个文件来查看时区。如下所示:

这个文件比较好理解,就是一个放置了当前时区描述字符串的文件。另外还有一个:

这是一个软连接,最终指向的是一个二进制文件,而这样的二进制文件还有很多,想要改变时区可以通过修改链接来完成,当然直接覆盖并不是一个好主意,而应该通过相关的命令来实现,比如dpkg-reconfigure tzdata(Ubuntu可用),它会打开一个图形界面供设置:

修改之后相应的/etc/timezone和/etc/localtime都会改变。Linux下还提供了命令tzselect来修改时区:

但是这里只是告诉你怎么改,并没有什么实际效果,为了改变时区,还是需要依照它的说法来改,如下所示:

设置之后时间才会改变,由此也可以看出TZ的时区优先级是高于/etc/timezone和/etc/localtime的,如果要改回时区,就需要去掉TZ这个环境变量(光设置为空是没有用的):

最后需要关注的一点是这个/etc/localtime对应的二进制从哪里来,包含那些内容。这实际上是一个时区的数据库,它们由http://www.iana.org/time-zones维护,该组织定义了时区相关的信息,并提供相关的代码来操作时区相关的内容。

比如zdump.c就可以解析这里的二进制,如下所示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值