前言
在应用中用到了 UTC 时间戳与北京时间进行转换的需求,这里做一个记录,方便后面有需求时直接拿来用。
一、几个时间相关的概念
- GMT 时间:Greenwich Mean Time,格林尼治平时,又称格林尼治平均时间或格林尼治标准时间。是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间。
- GMT存在较大误差,因此现在已不再被作为标准时间使用。现在的标准时间,是由原子钟报时的协调世界时(UTC)
- UTC 时间:Universal Time Coordinated,中文名称:世界标准时间或世界协调时。
- UTC时间可以理解为全世界都公用的一个时间。它实际上反映了一种约定,即为全世界所认可的一个统一时间,而不是某特定地区的时间。
- 中国人常用的北京时间比 UTC 时间快8个小时。也即 UTC 时间凌晨 0 点时,北京时间已经是早上 8 点,这就是为啥全世界人往往不直接用 UTC 时间计时原因。
- CST 时间:China Standard Time,即中国标准时间。在时区划分上,属东八区,比协调世界时早8小时,记为UTC+8。
- UNIX 时间戳(timestamp):计算机中的 UNIX 时间戳,是以 GMT/UTC 时间 1970-01-01 00:00:00 为起点,到当前具体时间的秒数(不考虑闰秒)。这样做的目的,主要是通过“整数计算”来简化计算机对时间操作的复杂度。
二、场景
1、场景 1
要求实现 UTC 时间戳和北京时间的互相转换,其中月份取值范围为 0~11 代表 1~12月,其他时间参数正常(年、日、时、分、秒)
2、场景 2
要求实现 UTC 时间戳和北京时间的互相转换,时间日期参数正常(年、月、日、时、分、秒),例如月份取值范围为 1~12 代表 1~12 月,日取值范围为 1~31 代表 1~31 日
三、验证方法
可以使用 时间戳转换在线工具 来验证转换后的时间是否正确。
这里以 2023-10-30 18:42:00 这个时间为例,转换后的 UTC 秒数为 1698662520。
四、源码
1、场景 1
其中月份取值范围为 0~11 代表 1~12月
#include <stdio.h> typedef struct { int year; // 年份 int month; // 月份范围:0-11代表1-12月 int day; // 日 int hour; // 小时 int minute; // 分钟 int second; // 秒 } DateTime; int IsLeapYear(int year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } int DaysInMonth(int year, int month) { int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; if (month == 1 && IsLeapYear(year)) return 29; else return days[month]; } int UTCSecondsToBeijing(DateTime* dt, int seconds) { int offset = 8 * 3600; // 北京为东八区,相对于UTC的偏移量为8小时 int totalSeconds = seconds + offset; dt->year = 1970; dt->month = 0; dt->day = 1; dt->hour = 0; dt->minute = 0; dt->second = 0; while (totalSeconds >= 86400) { int daysInYear = IsLeapYear(dt->year) ? 366 : 365; if (totalSeconds >= daysInYear * 86400) { totalSeconds -= daysInYear * 86400; dt->year++; } else { int month = 0; while (totalSeconds >= DaysInMonth(dt->year, month) * 86400) { totalSeconds -= DaysInMonth(dt->year, month) * 86400; month++; } dt->month = month; dt->day = totalSeconds / 86400 + 1; totalSeconds %= 86400; dt->hour = totalSeconds / 3600; dt->minute = (totalSeconds % 3600) / 60; dt->second = totalSeconds % 60; break; } } return totalSeconds; } int BeijingTimeToUTCSeconds(const DateTime* dt) { int totalSeconds = 0; for (int year = 1970; year < dt->year; year++) { totalSeconds += IsLeapYear(year) ? 366 * 86400 : 365 * 86400; } for (int month = 0; month < dt->month; month++) { totalSeconds += DaysInMonth(dt->year, month) * 86400; } totalSeconds += (dt->day - 1) * 86400; totalSeconds += dt->hour * 3600; totalSeconds += dt->minute * 60; totalSeconds += dt->second; int offset = 8 * 3600; // 北京为东八区,相对于UTC的偏移量为8小时 totalSeconds -= offset; return totalSeconds; } int main() { // 示例:UTC秒数转换成北京时间 int utcSeconds = 1698662520; // 假设给定的UTC秒数 DateTime beijingTime; int remainingSeconds = UTCSecondsToBeijing(&beijingTime, utcSeconds); printf("UTC Seconds: %d\n", utcSeconds); printf("Beijing Time: %04d-%02d-%02d %02d:%02d:%02d\n", beijingTime.year, beijingTime.month + 1, beijingTime.day, beijingTime.hour, beijingTime.minute, beijingTime.second); printf("Remaining Seconds: %d\n\n", remainingSeconds); // 示例:北京时间转换成UTC秒数 DateTime inputTime; inputTime.year = 2023; inputTime.month = 12 - 1; // 月份范围:0-11代表1-12月 inputTime.day = 30; inputTime.hour = 18; inputTime.minute = 42; inputTime.second = 0; int utcSecondsResult = BeijingTimeToUTCSeconds(&inputTime); printf("Beijing Time: %04d-%02d-%02d %02d:%02d:%02d\n", inputTime.year, inputTime.month + 1, inputTime.day, inputTime.hour, inputTime.minute, inputTime.second); printf("UTC Seconds: %d\n", utcSecondsResult); return 0; }
上述代码分为两个部分,一个是将 UTC 秒数转换为北京时间,另一个是将北京时间转换为 UTC 秒数。你可以根据需要使用其中的任一部分。示例中的北京时间转换成 UTC 秒数部分使用的北京时间为 2023年10月30日18时42分,
2、场景 2
时间日期均为正常
#include <stdio.h> typedef struct { int year; // 年份 int month; // 月 int day; // 日 int hour; // 小时 int minute; // 分钟 int second; // 秒 } DateTime; int IsLeapYear(int year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } int DaysInMonth(int year, int month) { int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; if (month == 2 && IsLeapYear(year)) return 29; else return days[month - 1]; } int UTCSecondsToBeijing(DateTime* dt, int seconds) { int offset = 8 * 3600; // 北京为东八区,相对于UTC的偏移量为8小时 int totalSeconds = seconds + offset; dt->year = 1970; dt->month = 1; dt->day = 1; dt->hour = 0; dt->minute = 0; dt->second = 0; while (totalSeconds >= 86400) { int daysInYear = IsLeapYear(dt->year) ? 366 : 365; if (totalSeconds >= daysInYear * 86400) { totalSeconds -= daysInYear * 86400; dt->year++; } else { int month = 1; while (totalSeconds >= DaysInMonth(dt->year, month) * 86400) { totalSeconds -= DaysInMonth(dt->year, month) * 86400; month++; } dt->month = month; dt->day = totalSeconds / 86400 + 1; totalSeconds %= 86400; dt->hour = totalSeconds / 3600; dt->minute = (totalSeconds % 3600) / 60; dt->second = totalSeconds % 60; break; } } return totalSeconds; } int BeijingTimeToUTCSeconds(const DateTime* dt) { int totalSeconds = 0; for (int year = 1970; year < dt->year; year++) { totalSeconds += IsLeapYear(year) ? 366 * 86400 : 365 * 86400; } for (int month = 1; month < dt->month; month++) { totalSeconds += DaysInMonth(dt->year, month) * 86400; } totalSeconds += (dt->day - 1) * 86400; totalSeconds += dt->hour * 3600; totalSeconds += dt->minute * 60; totalSeconds += dt->second; int offset = 8 * 3600; // 北京为东八区,相对于UTC的偏移量为8小时 totalSeconds -= offset; return totalSeconds; } int main() { // 示例:UTC秒数转换成北京时间 int utcSeconds = 1677566700; // 假设给定的UTC秒数 DateTime beijingTime; int remainingSeconds = UTCSecondsToBeijing(&beijingTime, utcSeconds); printf("UTC Seconds: %d\n", utcSeconds); printf("Beijing Time: %04d-%02d-%02d %02d:%02d:%02d\n", beijingTime.year, beijingTime.month, beijingTime.day, beijingTime.hour, beijingTime.minute, beijingTime.second); printf("Remaining Seconds: %d\n\n", remainingSeconds); // 示例:北京时间转换成UTC秒数 DateTime inputTime; inputTime.year = 2023; inputTime.month = 2; inputTime.day = 28; inputTime.hour = 14; inputTime.minute = 45; inputTime.second = 0; int utcSecondsResult = BeijingTimeToUTCSeconds(&inputTime); printf("Beijing Time: %04d-%02d-%02d %02d:%02d:%02d\n", inputTime.year, inputTime.month, inputTime.day, inputTime.hour, inputTime.minute, inputTime.second); printf("UTC Seconds: %d\n", utcSecondsResult); return 0; }
上述代码分为两个部分,一个是将 UTC 秒数转换为北京时间,另一个是将北京时间转换为 UTC 秒数。你可以根据需要使用其中的任一部分。示例中的北京时间转换成 UTC 秒数部分使用的北京时间为 2023年2月28日14时45分,
五、运行结果
1、场景 1
从结果可以看到将 UTC 秒数转换成北京时间,以及从北京时间转换成 UTC 秒数无误。
2、场景 2
从结果可以看到将 UTC 秒数转换成北京时间,以及从北京时间转换成 UTC 秒数无误。
六、资源自取