文章目录
- 一、序
- 1.1 时间和时区
- 1.11 时间
- 1.12 时区
- 1.2 查看时间时区的命令
- 1.21 Windows
- 1.22 Linux
- 二、C语言函数
- 2.1 通用
- 2.11 函数简介
- 2.12 数据类型简介
- 2.2 windows 和 Linux特有函数
- 2.3 C语言示例
一、序
1.1 时间和时区
1.11 时间
时间是一种用来描述物体运动变化的量,它可以用光的运动路程与常数c的比值来定义。不同的物体或观察者可能感受到不同的时间流逝速度,这就是相对论中的时间膨胀效应…
时间中存在许多特殊的概念,其中一些是与日历、日期和时间测量相关的。以下是一些常见的特殊时间概念:
闰年(Leap Year):为了与地球的自转周期相匹配,每四年有一个闰年。闰年有366天,而不是普通年份的365天。2月份会多出一天,变成29天。
闰秒(Leap Second):为了保持协调世界时(UTC)与国际原子时(TAI)之间的同步,不定期地会插入闰秒。这意味着某一天会有额外的一秒,通常在UTC的最后一分钟插入。
夏令时(Daylight Saving Time):一些国家和地区在夏季将时钟向前调整一小时,通常在春季开始,秋季结束。这旨在节约能源,延长白天的活动时间。
格林尼治标准时间(Greenwich Mean Time,GMT):以英国伦敦的格林尼治皇家天文台为基准的时间标准。UTC现在通常被视为国际时间标准,但GMT仍然用于某些上下文中。
纪元(Epoch):纪元是一个特定日期或时间点,通常用作计算机系统中时间的起点。例如,UNIX操作系统使用1970年1月1日午夜(格林尼治时间)作为纪元。
星期:星期是一周的周期性单位,通常有七天。星期中的每一天都有自己的名称(如星期一、星期二)和缩写(如周一、周二)。
季节:季节是一年中的四个时期,分别是春季、夏季、秋季和冬季。季节的开始和结束时间因地理位置而异。
日落和日出:日落是太阳在地平线以下消失的时间点,日出是太阳在地平线上升起的时间点。这些时间在不同季节和地点有所变化。
时间戳(Timestamp):时间戳是一个特定时间点的表示,通常以秒数或毫秒数等形式存在。时间戳在计算机系统中用于记录事件和操作的时间信息。
这些特殊的时间概念在日常生活、科学、计算机编程和全球协调等方面都有重要作用,它们帮助我们精确测量和记录时间,以满足各种需求和要求。
时间标准:
时间标准 | 定义 | 特点 |
---|---|---|
TAI | 国际原子时,是一种使用约400个高精度原子钟的组合输出来测量的时间尺度 | 提供了我们的时钟应该走的准确速度 |
UTC | 协调世界时,是一种用来确定世界各地本地时间的时间尺度。它由两个部分组成:TAI和UT1 | 由于地球自转速度的变化,导致UTC和TAI之间会有差异。为了保持UTC和UT1之间的差异不超过0.9秒,会在UTC中加入或减去闰秒 |
UT1 | 世界时,也称为天文时间,指的是地球的自转。它用来比较TAI提供的速度和地球上一天的实际长度 | 受到地球自转速度变化、地震、潮汐等因素的影响 |
GMT | 格林威治标准时间,指的是位于伦敦郊区的皇家格林威治天文台的标准时间,因为本初子午线被定义在通过那里的经线 | UTC和GMT可以视为无差别,GMT是以原子时计时,更加精准,一般使用不需要精确到秒时,视为等同 |
1.12 时区
地球被分为24个时区,每个时区代表地球上一个特定的经度范围。时区的目的是在地球上的不同地方保持相对一致的时间,以便协调跨越多个地理区域的活动。
这很好理解,地球是球体,太阳无法同时照亮,各地太阳照射角度也不同
时区的概念起源于铁路和电报的发展。19世纪中期,铁路和电报使信息和人员快速传输变得可能,但不同城市使用不同的本地时间,这导致了混乱。因此,国际时间会议于1884年召开,决定将地球划分为24个时区,每个时区相差15度经度。
全球大部分地区都采用了这个时区系统,但还有一些例外,如印度和新尼泊尔时区,它们使用不同的偏移量。此外,一些国家也采用夏令时制度,以调整时钟以节省能源。
除了日常生活以外。当你购买一个国外其他时区的服务器,在上面部署某些服务时,就就有可能会遇到时区未正确设置而无法运行的情况;或者一种常见的情况是:电脑时间(时区)不正确时,浏览器和应用程序可能会无法联网。
时区24个,相邻时区时间差一个小时。但是很多国家国土会跨越多个时区(比如上海已经晚上了,而新疆太阳还没下山),为了避免混乱,国家会设置一个单一的时间标准。比如中国标准时间(China Standard Time,CST
),也称为北京时间(Beijing Time,BT
)。
如上图,东半球时间通常早于西半球。东半球和西半球之间的时间分界线是称为国际日期变更线(International Date Line,IDL)。国际日期变更线是一条虚拟的线,它沿着地球东西走向,大致沿着经度180度附近的线路。这个线上的一侧被认为是东半球,另一侧被认为是西半球。
1.2 查看时间时区的命令
1.21 Windows
图形界面不说了。
tzutil
是 Windows 系统中用于查看和设置时区的命令行工具:tzutil /g
:查看本地计算机当前的时区。tzutil /l
:查看所有有效的时区 ID 及其名称。tzutil /s <timezoneid>
:设置本地计算机的时区为指定的 ID。
date
和time
是 Windows 系统中用于查看和修改日期和时间的命令行工具:date
:查看或修改本地计算机当前的日期。time
:查看或修改本地计算机当前的时间。date /t
:只查看本地计算机当前的日期,不进行修改。time /t
:只查看本地计算机当前的时间,不进行修改。
1.22 Linux
自己查看帮助手册,获取更多参数。
-
查看当前时间:
- 使用
date
命令来查看当前系统的时间。
date
- 使用
-
查看当前日期:
- 使用
date
命令并指定格式选项-d
来查看当前系统的日期。
date -d
- 使用
-
查看当前时区:
- 使用
timedatectl
命令来查看当前的时区设置。
timedatectl
- 使用
-
查看可用的时区列表:
- 使用
timedatectl
命令来列出系统支持的所有时区。
timedatectl list-timezones
- 使用
-
更改时区:
- 使用
timedatectl
命令来更改系统的时区设置。例如,要将时区更改为“America/New_York”:
sudo timedatectl set-timezone America/New_York
- 使用
-
启用/禁用夏令时:
- 使用
timedatectl
命令来启用或禁用夏令时。例如:
sudo timedatectl set-timezone America/New_York sudo timedatectl set-local-rtc 1
- 使用
-
同步系统时间:
- 使用
ntpdate
命令来手动同步系统时间与网络时间服务器。例如:
sudo ntpdate time.nist.gov
- 使用
在 Linux 系统中,time
命令用于测量执行命令或程序所花费的时间。它可以帮助评估程序的性能以及执行时间。要使用 time
命令,请在终端中键入 time
,然后紧跟要执行的命令。以下是示例:
time lscpu
上述命令将测量执行 lscpu
命令所需的时间,并显示三个关键时间信息:
- real:实际经过的时间,从命令开始到完成的总时间。
- user:CPU 用户模式时间,即命令在用户空间中花费的时间。
- sys:CPU 内核模式时间,即命令在内核空间中花费的时间。
例如,如果执行 time lscpu
命令,会看到如下输出:
...
real 0m0.028s
user 0m0.004s
sys 0m0.024s
这表示 ls
命令实际花费了0.028秒的时间,其中0.004秒在用户模式中使用CPU,0.024秒在内核模式中使用CPU。
time
命令对于评估命令或程序的性能非常有用,特别是在优化代码或比较不同实现的执行时间时。请注意,time
命令不是用来设置系统时间或时区的命令,而是用来测量命令执行时间的工具。
二、C语言函数
2.1 通用
2.11 函数简介
位于time.h
中:
函数 | 函数原型 | 输入 | 输出 | 作用 | 注意点 |
---|---|---|---|---|---|
time() | time_t time(time_t *time_ptr); | time_ptr - 一个 time_t 指针 | time_t - 时间值 | 返回自1970年1月1日以来的秒数 | 用本地时间计算的 |
ctime() | char *ctime(const time_t *time_ptr); | time_ptr - 一个 time_t 指针 | char * - 时间字符串 | 将时间值转换为可读的日期和时间字符串 | 字符串可能以换行符结尾,需要适当处理 |
strftime() | size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr); | str - 字符数组,maxsize - 最大字符数,format - 格式化字符串,timeptr - struct tm 结构 | size_t - 格式化后的字符数 | 将时间值格式化为自定义字符串格式 | 需要使用适当的格式字符串,结构体 tm 需要正确设置 |
gmtime() | struct tm *gmtime(const time_t *time_ptr); | time_ptr - 一个 time_t 指针 | struct tm * - 时间结构体 | 将时间值转换为世界标准时间(UTC)的 struct tm 结构 | 需要注意时区和夏令时的差异,返回的结构体表示UTC时间 |
localtime() | struct tm *localtime(const time_t *time_ptr); | time_ptr - 一个 time_t 指针 | struct tm * - 时间结构体 | 将时间值转换为本地时间的 struct tm 结构 | 时区和夏令时规则会影响结果,返回的结构体表示本地时间 |
difftime() | double difftime(time_t time1, time_t time2); | time1 和 time2 - 两个 time_t 值 | double - 时间差(秒) | 计算两个时间值之间的时间差 | 返回的是一个浮点数,可用于比较不同时间之间的时间差 |
mktime() | time_t mktime(struct tm *timeptr); | timeptr - struct tm 结构体 | time_t - 时间值 | 将 struct tm 结构体表示的时间转换为 time_t 类型的时间值 | 结构体字段需要正确设置,不支持负数年份 |
asctime() | char *asctime(const struct tm *timeptr); | timeptr - struct tm 结构体 | char * - 时间字符串 | 将 struct tm 结构体表示的时间转换为可读的日期和时间字符串 | 返回的字符串可能以换行符结尾,需要适当处理 |
2.12 数据类型简介
time_t定义:
typedef long time_t;
这个定义意味着 time_t
是一个长整型(long)数据类型。在许多系统中,time_t
被定义为从1970年1月1日至今的秒数,用于表示时间戳。在不同的系统和编译环境中,time_t
的具体类型可能会有所不同,但通常它被定义为整数类型。
对于不同的操作系统和编译器,time_t
的实现可能会有所不同,但它的目的是为了提供一个可移植的方式来表示时间。程序员在使用 time_t
时,应该遵循标准库中相关函数的文档和规范,以确保在不同平台上的兼容性。
1970年1月1日被选为UNIX时间起始点是因为这是UNIX操作系统的创建者之一、美国计算机科学家肯·汤普森(Ken Thompson)在当时的UNIX实现中选择的日期。
这个日期的选择实际上是一种约定,因为计算机科学家需要一种标准方式来表示时间。UNIX时间是一种以秒为单位的计时方式,从1970年1月1日0时0分0秒(UTC)开始,表示自那时以来的秒数。这个日期被选为UNIX时间的起点,主要是因为它相对容易计算,而且在当时的UNIX系统中也很方便。
此外,1970年1月1日0时0分0秒(UTC)也被选为UNIX时间起始点,因为它位于协调世界时(Coordinated Universal Time,UTC)中,并且在计算时间时避免了涉及夏令时(DST)等时区变化的复杂性。这使得UNIX时间在不同地区和不同计算机系统上都可以保持一致。
从1970年1月1日开始的UNIX时间在计算机科学和软件工程中广泛使用,尤其是在操作系统、数据库、文件系统和网络协议中。因此,这个日期成为了一个重要的时间基准,用于表示和计算时间间隔,以及在计算机系统之间进行时间戳的交换。
结构体原型:(这里 把2.2节的顺便写了)
稍微看一下注释,比如月份从0开始,年数以1900开始计算。(1890就写-10)
-
struct tm
结构体(在<time.h>
中定义):struct tm {int tm_sec; // 秒(0-59)int tm_min; // 分钟(0-59)int tm_hour; // 小时(0-23)int tm_mday; // 月中的天数(1-31)int tm_mon; // 月(0-11,0 表示 1 月)int tm_year; // 年份(自 1900 年起的年数)int tm_wday; // 星期几(0-6,0 表示星期日)int tm_yday; // 年中的天数(0-365)int tm_isdst; // 夏令时标志(正数表示 DST,0 表示不是 DST,负数表示不确定) };
-
SYSTEMTIME
结构体(在<windows.h>
中定义):typedef struct _SYSTEMTIME {WORD wYear; // 年份WORD wMonth; // 月份WORD wDayOfWeek; // 星期几(0-6,0 表示星期日)WORD wDay; // 月中的天数(1-31)WORD wHour; // 小时(0-23)WORD wMinute; // 分钟(0-59)WORD wSecond; // 秒(0-59)WORD wMilliseconds; // 毫秒 } SYSTEMTIME;
-
struct timeval
结构体(在<sys/time.h>
中定义,通常用于 Linux 和 Unix 系统):struct timeval {long tv_sec; // 秒long tv_usec; // 微秒 };
-
struct timespec
结构体(在<time.h>
中定义,通常用于 Linux 和 Unix 系统):struct timespec {time_t tv_sec; // 秒long tv_nsec; // 纳秒 };
这些结构体用于表示时间的各个方面,包括年、月、日、时、分、秒等,并且在与时间相关的C函数中经常用于输入和输出。请注意,SYSTEMTIME
结构体是Windows特有的,而 struct timeval
和 struct timespec
结构体通常用于Linux和Unix系统。👇
2.2 windows 和 Linux特有函数
在Windows下:
函数 | 函数原型 | 头文件 | 输入 | 输出 | 作用 | 注意点 |
---|---|---|---|---|---|---|
GetSystemTime() | void GetSystemTime(SYSTEMTIME *lpSystemTime); | windows.h | lpSystemTime - 一个 SYSTEMTIME 结构指针 | 无 | 获取系统时间(UTC+),以年、月、日、时、分、秒等形式返回 | 无 |
GetLocalTime() | void GetLocalTime(SYSTEMTIME *lpSystemTime); | windows.h | lpSystemTime - 一个 SYSTEMTIME 结构指针 | 无 | 获取本地时间,考虑了时区和夏令时规则,以年、月、日、时、分、秒等形式返回 | 无 |
GetTickCount() | DWORD GetTickCount(void); | windows.h | 无 | 返回自系统启动以来的毫秒数 | 无 |
在Linux下:
函数 | 函数原型 | 头文件 | 输入 | 输出 | 作用 | 注意点 |
---|---|---|---|---|---|---|
gettimeofday() | int gettimeofday(struct timeval *tv, struct timezone *tz); | sys/time.h | tv - 一个 struct timeval 结构指针,tz - 一个 struct timezone 结构指针(可传入NULL ) | 0(成功)或-1(失败) | 获取当前时间,包括秒和微秒,可用于高精度计时 | 需要检查返回值以处理错误,struct timezone 在许多系统中被忽略 |
clock_gettime() | int clock_gettime(clockid_t clk_id, struct timespec *tp); | time.h | clk_id - 时钟标识,tp - 一个 struct timespec 结构指针 | 0(成功)或-1(失败) | 获取高分辨率的时间信息,可选择不同的时钟源 | 需要检查返回值以处理错误,时钟标识和支持的时钟源因系统而异 |
2.3 C语言示例
简单示例吧,自己结合前面的结构体定义、函数原型和功能介绍。应用还是很广泛的。
需要注意一些换算,尤其是gm这个结构体,time_t 变量是从1970-1-1开始计算的,tm结构体变量的成员是从1900年开始的,月数、星期几、天数从0开始。
struct tm {int tm_sec; // 秒(0-59)int tm_min; // 分钟(0-59)int tm_hour; // 小时(0-23)int tm_mday; // 月中的天数(1-31)int tm_mon; // 月(0-11,0 表示 1 月)int tm_year; // 年份(自 1900 年起的年数)int tm_wday; // 星期几(0-6,0 表示星期日)int tm_yday; // 年中的天数(0-365)int tm_isdst; // 夏令时标志(正数表示 DST,0 表示不是 DST,负数表示不确定)
};
Linux下:
输出:
代码示例:
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>int main() {struct timeval current_time;struct tm set_time ={ 0, 0, 0, 1, 0, 120, },*diff_readable;char time_str[30], *p;if (gettimeofday(¤t_time, NULL) == 0) {p = ctime(¤t_time.tv_sec);printf("Current time is %s", p); // 注意这里的字符串本来就是以换行符结尾的printf("It has passed %ld seconds %ld microseconds since 1970-1-1\n", current_time.tv_sec, current_time.tv_usec);} else {perror("gettimeofday");}time_t set_t = mktime(&set_time);printf("I set a time: %s", asctime(&set_time));double diff = difftime(current_time.tv_sec, set_t);printf("Time diff between current time and set time is %ld seconds\n",(long)(diff));time_t diff_time = (time_t)diff;diff_readable = localtime(&diff_time);printf("Transform to be readable :%d yesrs %d months %d days %d hours\n",diff_readable->tm_year-70, diff_readable->tm_mon+1,diff_readable->tm_mday, diff_readable->tm_hour);return 0;
}
Windows下:
使用Windows API :
输出:
Current time:2023-10-6-14:47
Current time:2023-10-6-22:47
It has passed 249 hours since PC started
源码:
#include <stdio.h>
#include <string.h>
#include <windows.h>int main() {SYSTEMTIME system_time = {0}, local_time = {0};GetSystemTime(&system_time);printf("Current time:%u-%u-%u-%u:%u\n",system_time.wYear, system_time.wMonth, system_time.wDay,system_time.wHour, system_time.wMinute);GetLocalTime(&local_time);printf("Current time:%u-%u-%u-%u:%u\n", local_time.wYear, local_time.wMonth, local_time.wDay,local_time.wHour, local_time.wMinute);printf("It has passed %lu hours since PC started\n",GetTickCount()/(1000*60*60));return 0;
}
使用time.h:
输出:
Current time(local): Fri Oct 6 23:00:39 2023Current time(UTC): Fri Oct 6 15:00:39 2023
源码:
#include <stdio.h>
#include <string.h>
#include <time.h>int main() {char current_time[30] = {0}, *p;time_t time_second;struct tm* utc_time;time(&time_second);p = current_time;p = ctime(&time_second);printf("Current time(local): %s\n",p);utc_time = gmtime(&time_second);time_t UTC = mktime(utc_time);p = ctime(&UTC);printf("Current time(UTC): %s\n",p);return 0;
}
总结:
一般使用time.h
的函数就能完成所有工作,不过在特定系统下使用对应的API获取时间会更快,更精确。