前言
协调世界时(UTC)、格林威治时间(GMT)、儒略日(Julian Day)、时区(Timezone)、1970-01-01、1601-01-01......
大事记
| 时间 | 大事件 | 发明人 | 说明 | 诞生的新名词 |
|---|---|---|---|---|
| 公元前4713年1月1日12:00:00 | Julian day 的起点 | 法国学者 Joseph Justus Scliger 在1583年所创 | 为了纪念他的父亲,意大利学者 Julius Caesar Scaliger | JulianDay |
| 公元前45年1月1日 | Julian calendar(儒略历) 颁布 | Julius Caesar(时任罗马共和国独裁官) | JulianCalendar | |
| 1582年10月15日00:00:00 | 废除 Julian calendar,改用 Gregorian calendar(格里历),也就是我们说的公历。 | Pope Gregory XIII(时任罗马教皇格里高利十三世) | 1582年的10月份少了10天,从10月4日直接跳到15日,因旧历算法有偏差强行加上10天 | 公历 |
| 1601年1月1日00:00:00 | FILETIME 起点 | 微软程序员 | 以400年为一周期,1601年至2000年为一周期,Windows NT诞生于该周期内,它也是17世纪的第一天。 | FILETIME |
| 1884年 | 以英国格林威治天文台的经线为本初子午线,即0度经线 | 大英帝国 | 日不落帝国为了海上霸权的扩张计划,早在十七世纪就开始进行天体观测 | GMT |
| 1970年1月1日00:00:00 | Unix Epoch Time(UNIX 纪元时间、UNIX 时间戳) | UNIX 程序员 | UNIX 年诞生于1970年前后 | UnixTimestamp、EpochTime |
| 1972年 | UTC正式成为国际标准时间(又称协调世界时、世界标准时间) | 国际无线电咨询委员会 | 原子钟 | UTC、ZuluTime |
-
GMT和UTC区别:GMT是前世界标准时,UTC是现世界标准时。
- GMT: Greenwich Mean Time(格林威治标准时间),它规定太阳每天经过位于英国伦敦郊区的皇家格林威治天文台的时间为中午12点。
- UTC: 采用原子钟,在精度方面碾压以天文观测为主的GMT。
- 实际编程中,这两个时间值是一样的
时间函数
C语言
// 返回从 1970-01-01 00:00:00 +00:00 开始至今的秒数(以下简称UNIX时间戳)
time_t time(time_t *seconds);
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
int tm_year; // 自 1900 起的年数
int tm_wday; // 一周中的第几天,范围从 0 到 6
int tm_yday; // 一年中的第几天,范围从 0 到 365
int tm_isdst; // 夏令时
};
// UNIX时间戳 -> 本地时间结构体
struct tm* localtime(time_t const* const sourceTime);
// 本地时间结构体 -> UNIX时间戳
time_t mktime(struct tm *timeptr);
// UNIX时间戳 -> 格林威治标准时间(UTC+00:00)
struct tm *gmtime(const time_t *sourceTime);
// 时间结构体 -> ASC时间字符串
// 示例:Mon Jun 3 06:08:39 2024
char *asctime(const struct tm *timeptr);
// 时间戳 -> 本地时区下的ASC时间字符串
// 示例:Mon Jun 3 14:08:39 2024
char *ctime(const time_t *timer);
-
为什么tm_year是从1900开始而不是1970
- 计算机早期使用2位数存储年份,1980年存储为80,因此,相对于1900,将tm_year直接打印成两位数就十分便利,显然在当时是合理的。
- 后来到了2000年,很多只能处理2位数年限的计算机就出现了千年虫问题(Y2K)因为年限又从00年重新开始了。
Windows API
typedef struct _SYSTEMTIME {
WORD wYear; // 年,范围从 1601 到 30827
WORD wMonth; // 月,范围从 1 到 12
WORD wDayOfWeek; // 一周中的第几天,范围从 0 到 6
WORD wDay; // 日,范围从 1 到 31
WORD wHour; // 时,范围从 0 到 23
WORD wMinute; // 分,范围从 0 到 59
WORD wSecond; // 秒,范围从 0 到 59
WORD wMilliseconds; // 毫秒,范围从 0 到 999
} SYSTEMTIME, *PSYSTEMTIME;
// 自 1601年1月1日UTC+00:00 以来的 100 纳秒间隔数
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME, *PFILETIME;
// SYSTEMTIME
void GetSystemTime(LPSYSTEMTIME lpSystemTime); // UTC+00:00
void GetLocalTime(LPSYSTEMTIME lpSystemTime); // 本地时间结构体
// FILETIME
void GetSystemTimeAsFileTime(LPFILETIME); // UTC+00:00时间
BOOL FileTimeToLocalFileTime(const FILETIME*, LPFILETIME); // UTC+00:00时间 -> 本地时间
BOOL LocalFileTimeToFileTime(const FILETIME*, LPFILETIME); // 本地时间 -> UTC+00:00时间
// SYSTEMTIME <--> FILETIME
BOOL SystemTimeToFileTime(const SYSTEMTIME*, LPFILETIME); // 时间结构体 -> FILETIME
BOOL FileTimeToSystemTime(const FILETIME*, LPSYSTEMTIME); // FILETIME -> 时间结构体
class CFileTime : public FILETIME {
public:
static CFileTime GetCurrentTime(); // UTC+00:00时间
};
class CTime {
public:
CTime(const FILETIME&); // FILETIME -> UNIX时间戳(精度:秒)
static CTime GetCurrentTime(); // UNIX时间戳(精度:秒)
struct tm* GetGmtTm(struct tm* ptm) const; // UNIX时间戳 -> 格林威治标准时间(UTC+00:00)
struct tm* GetLocalTm(struct tm* ptm) const; // UNIX时间戳 -> 本地时间结构体
bool GetAsSystemTime(SYSTEMTIME& st) const; // UNIX时间戳 -> 本地时间结构体
// 本地时间:年月月时分秒
int GetYear() const throw();
int GetMonth() const throw();
int GetDay() const throw();
int GetHour() const throw();
int GetMinute() const throw();
int GetSecond() const throw();
private:
__time64_t m_time; // UNIX时间戳(精度:秒)
};
POCO
class Poco::Timestamp {
public:
std::time_t epochTime() const(); // UNIX时间戳(精度:秒)
INT64 utcTime() const; // 1582年10月15日00:00:00至今的100纳秒个数(即公历第一天至今)
INT64 epochMicroseconds() const; // UNIX时间戳(精度:微秒)
// FILETIME <--> Poco::Timestamp
static Timestamp fromFileTimeNP(UInt32 fileTimeLow, UInt32 fileTimeHigh);
void toFileTimeNP(UInt32& fileTimeLow, UInt32& fileTimeHigh) const;
private:
INT64 _ts; // UNIX时间戳(精度:微秒)
};
class Poco::DateTime {
public:
// 想要表示1582-10-15 00:00:00之前的时间,可以用Julian day表示,正常年月日表示的话,计算结果都是错的
double julianDay() const; // 当前时间距离Julian day相差的总天数(公元前4713年1月1日12:00:00)
private:
INT64 _utcTime; // 1582-10-15 00:00:00至当前时间所经历的100纳秒个数
short _year;
short _month;
short _day;
short _hour; // UTC+00:00
short _minute;
short _second;
short _millisecond;
short _microsecond;
};
class Poco::LocalDateTime {
private:
DateTime _dateTime; // 本地时间
int _tzd; // 与UTC+00:00相差的秒数(如中国是28800=8*3600)
};
常量
116444736000000000 的由来:
// 1601-01-01 00:00:00 到 1970-01-01 00:00:00,共经历了多少个100纳秒
// diff.QuadPart = 116444736000000000
ULARGE_INTEGER diff;
diff.LowPart = 0xD53E8000;
diff.HighPart = 0x019DB1DE;
122192928000000000 的由来:
// 1582-10-15 00:00:00 到 1970-01-01 00:00:00,共经历了多少个100纳秒
// diff.QuadPart = 122192928000000000
ULARGE_INTEGER diff;
diff.LowPart = 0x01B21DD2;
diff.HighPart = 0x13814000;
2299160.5 的由来
// 公元前4713-01-01 12:00:00 到 1582-10-15 00:00:00,共经历了多少天
// 即 Julian day 第一天,到公历第一天,共经历了多少天
// 计算公式:总天数 = int(365.25 * (y + 4712)) + int(30.61 * (m + 1)) + d - 63.5
double calculate(int y, int m, int d)
{
double ret = int(365.25 * (y + 4712)) + int(30.61 * (m + 1)) + d - 63.5;
return ret;
}
int main() {
auto days = calculate(1582, 10, 5); // days = 2299160.5
}
86400000 的由来
// 一天24小时的总毫秒数:
assert(86400, 24 * 3600 * 1000);
时间格式化
ISO 8601
格式:YYYY-MM-DDTHH:MM:SS±HH:MM
示例:
- 2023-06-04T14:23:45Z (UTC时间)
- 2023-06-04T16:23:45+02:00 (UTC+2)
RFC 822
已被 RFC 1123 取代
格式:Day, D Mon YY HH:MM:SS GMT
- Day取值:Mon/Tue/Wed/Thu/Fri/Sat/Sun
- Mon取值:Jan/Feb/Mar/Apr/May/Jun/Jul/Aug/Sep/Oct/Nov/Dec
示例:
- Sat, 1 Jan 05 12:00:00 +0100
- Sat, 1 Jan 05 11:00:00 GMT
RFC 1123
取代 RFC 822,年份改用4位数字
示例:
- Sat, 1 Jan 2005 12:00:00 +0100
- Sat, 1 Jan 2005 11:00:00 GMT
RFC 2616
与 RFC 1123 相近,天数不足2位补0,广泛应用于 HTTP 协议中
示例:
- Sat, 01 Jan 2005 12:00:00 +0100
- Sat, 01 Jan 2005 11:00:00 GMT
RFC 850
已被 RFC 1036 取代
示例:
- Saturday, 1-Jan-05 12:00:00 +0100
- Saturday, 1-Jan-05 11:00:00 GMT
RFC 1036
示例:
- Saturday, 1 Jan 05 12:00:00 +0100
- Saturday, 1 Jan 05 11:00:00 GMT
asctime
示例:
- Sat Jan 1 12:00:00 2005