C++ 获取系统时间

C++ 获取系统时间

有的帖子真心写的很好,你都想帮作者打广告。有的烂到即使你从里面学到了知识,你也不想再见到它,你都不想在自己的笔记里放它的链接,不然说不准哪天又点进去浪费自己的时间。

说一下思路:

  1. 使用开发语言自身的标准库。对于 C++ 来说,使用 C 的或者 C++ 的
  2. 使用第三方库,如果有的话
  3. 使用系统相关 API。缺点在于不能跨平台

C 标准库

优点就是仅使用 C 标准库,能够跨平台使用;缺点 是只能精确到秒级。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include < time.h>   
#include < stdio.h>
int main()
{
time_t tt = time(NULL);
struct tm * t = localtime(&tt);
printf("%d-%02d-%02d %02d:%02d:%02d\n",
t->tm_year + 1900,
t->tm_mon + 1,
t->tm_mday,
t->tm_hour,
t->tm_min,
t->tm_sec);
return 0;
}

struct tm 结构体定义:

1
2
3
4
5
6
7
8
9
10
11
12
struct tm {
int tm_sec; /* 秒,取值范围(0~59),但当遇到闰秒时则会有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年开始至今的年数,即(Year - 1900)的值 */
int tm_wday; /* 当天在本周是第几天,取值范围(0-6, Sunday = 0) */
int tm_yday; /* 当天在今年是第几天,取值范围(0-365, 1 Jan = 0) */
int tm_isdst; /* 夏令时标记,值大于0表示夏令时生效;等于0表示夏令时失效;小于0表示数据不可用。 */
char *tm_zone; /* 时区名称,根据系统不同可能不被声明或不同全名。 */
};

帖子很棒!日常时间的获取、格式化等操作汇总

  • 计算时间差值:double difftime(time_t time1, time_t time0);

  • 利用 mktime() 函数,获取某一天是本周的第几天(tm_wday),是当年的第几天(tm_yday

    现在注意了,有了 mktime() 函数,是不是我们可以操作现在之前的任何时间呢?你可以通过这种办法算出 1945 年 8 月 15 号是星期几吗?答案是否定的。因为这个时间在 1970 年 1 月 1 日之前,所以在大多数编译器中,这样的程序虽然可以编译通过,但运行时会异常终止。

  • 使用限制:time_t 类型的限制

    既然 time_t 实际上是长整型,到未来的某一天,从一个时间点(一般是 1970 年 1 月 1 日 0 时 0 分 0 秒)到那时的秒数(即日历时间)超出了长整形所能表示的数的范围怎么办?对 time_t 数据类型的值来说,它所表示的时间不能晚于 2038 年 1 月 18 日 19 时 14 分 07 秒。

另外,要强调的是获取时间后推荐 使用本地结构保存小心 C 语言时间函数陷阱

第三方库

没有接触过

Windows API

优点:能精确到毫秒级;缺点:使用了 windows API

1
2
3
4
5
6
7
8
9
10
11
#include < windows.h>   
#include < stdio.h>
int main( void )
{
SYSTEMTIME sys;
GetLocalTime( &sys );
printf( "%4d/%02d/%02d %02d:%02d:%02d.%03d 星期%1d\n",
sys.wYear,sys.wMonth,sys.wDay,sys.wHour,sys.wMinute,
sys.wSecond,sys.wMilliseconds,sys.wDayOfWeek);
return 0;
}

SYSTEMTIME 结构体定义:

1
2
3
4
5
6
7
8
9
10
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;

更详细的信息,请查阅 MSDN-SYSTEMTIME structure条目

Linux库

参考时间精确到微秒、纳秒

  • 区分 <sys/time.h><time.h> 头文件,前者是*inux系统的系统库,后者可能也带有*inux色彩,但侧重 ANSI C 标准。

  • 区分 GNU C和 ANSI C。

    • <time.h> 中不纯粹是 ANSI C 标准的定义,还包括一些 Linux 下的扩展,比如 ANSI(C99) 标准是没有要求微秒、纳秒类型的,而在 Linux 系统中的 <time.h> 包含 struct timespec 纳秒类型。如果代码中使用了这些扩展类型、扩展函数,就会造成平台移植时的不确定性。

日期与时间函数在 <time.h> 中,主要表示处理器时钟的 clock_t 类型、表示时间的 time_t 类型、时钟每秒滴答数 CLOCKS_PER_SEC、描述日历时间的 struct tm 结构、函数 clock、timeasctimectimegmtimelocaltimemktimedifftimestrftimewcsftime(宽字符版本),其他的都是非标准扩展。

获取时间间隔

C标准库

前文提到我们可以使用 difftime() 函数,但它只能精确到秒。如更精确一些呢?

参考 详细介绍C/C++时间函数使用方法,推荐☆☆☆☆

C/C++ 中的计时函数是 clock(),而与其相关的数据类型是 clock_t。这个函数返回从“开启这个程序进程”到“程序中调用 clock() 函数”时之间的 CPU 时钟计时单元(clock tick)数。在 <time.h> 文件中,还定义了一个常量 CLOCKS_PER_SEC,它用来表示一秒钟会有多少个时钟计时单元

可以用 clock 函数来计算你的机器运行一个循环或者处理其它事件到底花了多少时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include “stdio.h”  
#include “stdlib.h”
#include “time.h”
int main( void )
{
long i = 10000000L;
clock_t start, finish;
double duration;
/* 测量一个事件持续的时间*/
printf( "Time to do %ld empty loops is ", i );
start = clock();
while( i-- )
;
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf( "%f seconds\n", duration );
}

扩展阅读

在 SUSE11 系统下,查找 CLOCKS_PER_SEC 的定义,在 time.h 中 指向 <bits/time.h>,其目录:

1
2
3
cts@SuSe-CTS2:/usr/include/bits> pwd
/usr/include/bits
cts@SuSe-CTS2:/usr/include/bits>

然后找到看到以下宏:

1
#  define CLOCKS_PER_SEC  1000000l

在 Debian8.1 系统下,同上。<bits/time.h> 目录:

1
2
3
niel@debian8light:/usr/include/x86_64-linux-gnu/bits$ pwd
/usr/include/x86_64-linux-gnu/bits
niel@debian8light:/usr/include/x86_64-linux-gnu/bits$

关于闰秒

闰秒,英文名称 leap second,即 23:59:60

time_t

尽管 C 标准没有定义,它几乎总是一个保有从 UTC 1970 年 1 月 1 日 00:00 开始秒数的整数值(不计闰秒),对应 POSIX 时间

Unix time

Unix time (also known as POSIX time or UNIX Epoch time) is a system for describing a point in time. It is the number of seconds that have elapsed since 00:00:00 Thursday, 1 January 1970, Coordinated Universal Time (UTC), minus(减去) leap seconds. Every day is treated as if it contains exactly 86400 seconds, so leap seconds are to be subtracted(扣除) since the epoch.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<iostream>
#include <ctime>
#include <cassert>

using namespace std;

int main()
{
time_t t = time(NULL);
tm tmp;
localtime_s(&tmp, &t);
auto count = (tmp.tm_hour * 60 + tmp.tm_min) * 60 + tmp.tm_sec;
assert(count == (t % 86400 + 8 * 60 * 60)); // 东八区

cout << "============2015年6月30日23:59:60" << endl;
tmp.tm_year = 2015 - 1900; // years since 1900
tmp.tm_mon = 5;
tmp.tm_mday = 30;
tmp.tm_hour = 23;
tmp.tm_min = 59;
tmp.tm_sec = 60;
auto t60 = mktime(&tmp);
char cstr[100];
asctime_s(cstr, &tmp);
cout << cstr << endl; // Wed Jul 1 00:00:00 2015
cout << "结论:在 ctime 体系中不计闰秒。\
所以即便使用 tm 构造出闰秒,在 ctime 体系中和下一日的 00:00:00 等同。\
而 localtime() 永远得不到闰秒。" << endl;
}