C++中日期处理总结

190811更新

我以为搞了这么一篇就一劳永逸了,结果写了个日期处理的题还是有很个小坑,当初没搞清楚。

①修正行文错误,正确版本【mktime与localtime互为反函数】。②补充mktime过程中yday的运算逻辑问题。

 

①头文件

#include<ctime>  //或<time.h>

 

②两个类型

需要理解的只有2个类型,time_t和tm;

 

time_t 为长整数。 

表示从19xx年开始经过的秒。


在vs2017中测得time_t为typedef定义的_time64_t类型     //corecrt.h

而_time64_t又是typedef定义的__int64类型             //corecrt.h

如果是某些32位系统,则time_t ===  _time32_t  === _int32。



如果没有定义,还可以这样
#ifndef _TIME_T_DEFINED
typedef long time_t;         /* 时间值 */
#define _TIME_T_DEFINED      /* 避免重复定义 time_t */
#endif

 

tm是个结构体,包含年月日时分秒。

//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Types
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

struct tm
{
    int tm_sec;   // seconds after the minute - [0, 60] including leap second
    int tm_min;   // minutes after the hour - [0, 59]
    int tm_hour;  // hours since midnight - [0, 23]
    int tm_mday;  // day of the month - [1, 31]
    int tm_mon;   // months since January - [0, 11]
    int tm_year;  // years since 1900
    int tm_wday;  // days since Sunday - [0, 6]
    int tm_yday;  // days since January 1 - [0, 365]
    int tm_isdst; // daylight savings time flag
};


//来自correct_wtime.h
//最后一个_isdat表示夏令时,国内不用理

 

time_t 和 strcut tm

我们要做的就是在这两个东西之间根据需要转来转去。

 

 

③tm转time_t --- mktime介绍

 

通常我们取得的日期都是字符串,网上有现成的轮子。

把日期字符串里的值一个个赋予struct tm的成员变量

//一般我们遇到的时间字符串格式各不相同
//有时候是"2001/01/01",有时候是"2001-01-01",按需修改即可
//核心思路是构造m结构体之后,拿它去mktime,算出size_t的秒
//如果不存在时分秒,就令它们为0

time_t StringToDatetime(const string &str)
{
	const char *cha = str.c_str();				// 将string转换成char*。
	tm tm_;										// 定义tm结构体。
	int year, month, day, hour, minute, second;	// 定义时间的各个int临时变量。
	sscanf(cha, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);// 将string存储的日期时间,转换为int临时变量。
	tm_.tm_year = year - 1900;					// 年,由于tm结构体存储的是从1900年开始的时间,所以tm_year为int临时变量减去1900。
	tm_.tm_mon = month - 1;					    // 月,由于tm结构体的月份存储范围为0-11,所以tm_mon为int临时变量减去1。
	tm_.tm_mday = day;						    // 日。
	tm_.tm_hour = hour;						    // 时。
	tm_.tm_min = minute;						// 分。
	tm_.tm_sec = second;						// 秒。
	tm_.tm_isdst = 0;							// 非夏令时。
	time_t t_ = mktime(&tm_);					// 将tm结构体转换成time_t格式。
	return t_;									// 返回值。 
}

 

该算法的核心是struct tm ------->  size_t

通过mktime()函数完成。

time_t t_=mktime(&tm_); //获得秒数

 

注意事项

3.1

mktime获得的秒数是从1970年开始的!

而struct tm存储的年份是从1900年开始的!

 

 

所以一切在1970年之前的日期进入该函数,都会报错!

time_t mktime (struct tm * timeptr){};

然后返回-1!

 

3.2

mktime计算秒数仅采用年月日+时分秒,struct tm中的wday与yday不会纳入计虑范围。

更方便的是,计算mktime秒数的过程中,还会顺便帮你填好wday与yday。

A call to this function automatically adjusts the values of the members of timeptr if they are off-range or -in the case of tm_wday and tm_yday- if they have values that do not match the date described by the other members.

 

wday表示从sunday开始的星期几[0~6];

yday表示这是一年中的第几天[0~365];

//http://www.cplusplus.com/reference/ctime/mktime/

 

190811更新。再次加重声明,【mktime计算秒数用且仅用 "年月日+时分秒" 】。

这意味着,如果你的输入格式是【2019-01-03】,没有时分秒信息。

你需要自己手动把tm_.tm_hour,tm_.tm_min,tm_.tm_sec,都设为0。

不然的话,声明一个struct tm时,时分秒一般默认被置为负数。

我不知道为什么负数是合法的,明明这个数据的合法范围应该是[0,24),[0,60),[0,60),但VS能看到确实是负数-858993460。

这样一来,算出来的秒数是不对的,一般情况下返回的秒数为-1。

连带着yday和wday也是不对的。

 

④日期大小的比较

 我研究这个问题一段时间之后,发现貌似用字符串的直接比较是最快的(存疑)。

string str1="2001-01-01";
string str2="2001-01-02";
string str3="2001-02-01";

if(str1<str2) cout<<"str2 is bigger"<<endl;
if(str2<str3) cout<<"str3 is bigger"<<endl;

 

另一种思路是比较秒数(仅限1970年之后)

string str1="2001-01-01";
string str2="2001-01-02";


time_t t1=StringToDatetime(str1);
time_t t2=StringToDatetime(str2);


//在vs2017中测得time_t不支持<运算符
//我们有2种方法

//方法1
//借助-运算符

if(t1-t2 <0) cout<<"t1 is former"<<endl;


//方法2
//借助difftime函数
//time_t difftime(time_t t1,time_t t2) 返回两个秒数之间的差值
//大部分情况下等价于-运算符
 

time_t t3 = difftime(t1,t2);  //一般等价于t1-t2
cout<<t3<<endl;
if(t3<0) cout<<"t1 is former"<<endl;

 

特殊情况在哪呢?

前文提过,我们获取time_t一般是用mktime(&tm);

而时间早于1970年会得到-1;

显然difftime无法处理负数

time_t t1 = mktime(&tm_);  
time_t t2 = -1;

time_t t3 = difftime(t1, t2);
time_t t4 = t1 - t2;

cout << (t3 == t4)<<endl;  //输出0,说明不相等


-------------
time_t t1 = mktime(&tm_); 
time_t t2 = 1;

time_t t3 = difftime(t1, t2);
time_t t4 = t1 - t2;

cout << (t3 == t4)<<endl;  //输出1,说明相等

 

综上所述,要比较日期

最快是字符串直接比较字典序大小(前提是格式相同)

最稳是转成秒数之后用 - 减法运算符来比(1970年之后)

 

⑤time_t转tm  -  gmtime与localtime介绍

 

gmtime与localtime

两者都将time_t表示的秒数还原成一个struct tm结构体。

两者的区别在于是否修正时区。

//转为格林尼治标准时间
struct tm * gmtime (const time_t * timer);
//Convert time_t to tm as UTC time


//转为本地时区的时间
struct tm * localtime (const time_t * timer);
//Convert time_t to tm as local time

 

 

cplusplus.com上写道,mktime是localtime的反过程。【感谢评论区指出】

This function performs the reverse translation that localtime does.

一般仅用localtime与mktime,可以达成无损的循环。

struct tm *tm_ptr;
time_t t_;

t_= mktime(tm_ptr); 
tm_ptr = localtime(&t_);  //传入的参数为time_t *类型,返回值是struct tm *

//如此可以无损循环

 

 

 

⑥time_t 转 string 

 

一般方法就不用介绍了吧,转成struct tm之后,

分别取出tm_.year .month ......按要求拼接字符串即可。

 

这里介绍一个函数ctime(time_t*),和文件头同名

char* ctime (const time_t * timer);

//Convert time_t value to string

 

ctime()的输出遵从如下格式:
Www Mmm dd hh:mm:ss yyyy

Wed Feb 13 16:06:10 2013

 

等价函数为asctime(struct tm *timerptr)

不过用内置函数,就只有这个输出格式。

一般没什么用,我们日常还是习惯看【yyyy-mm-dd hh:mm:ss】。

所以还是自己拼接字符串比较好。

 

 

参考:

部分代码不能编译的有删改。

//https://www.cplusplus.com/reference/ctime/

//https://www.cnblogs.com/renjiashuo/p/6913668.html

//https://www.cnblogs.com/maphc/p/3462952.html

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值