工作笔记 工作笔记
gmt和utc都是标准时间。 GMT是比较古老的时间较量标准,根据地球公转自转计算时间。UTC则是根据原子钟来计算时间,现在基本都用UTC时间。
1970 最开始UNIX的时间是1/60s加1,因此一个int大概两年多就会溢出,第一版UNIX程序员手册,将UNIX时间定义为1971.1.1 00:00:00,后面这个时间被更新了几次。当UNIX时间变为1s加1时,UNIX开始时间被定义为1970.1.1 00:00:00 参考https://en.wikipedia.org/wiki/Unix_time#History
1900 1900的资料很少,据说时因为1900.1.1为星期一的缘故,方便计算后面某一天是星期几,距1900.1.1的天数再对7取余所得即为星期数。
UNIX系统时间函数都是基于1970年的,但是有一点值得注意: localtime()函数返回的struct tm结构体成员的tm_year为当前年份-1900。
夏令时计算有几个坑,需注意:
时间服务器返回的时间为1900距今的秒数,而我们需要借助unix时间函数转为可读的时间 ,因此需要先把这个时间减去70年(2208988800s)。
夏令时的开始结束时间使用的是时区转化后的当地时间,因此时间服务器获取到的UTC时间需要转为本地时间,才能进行时间是否在夏令时区间的判断。
判断闰年的程序,网上第一次搜出来不对,导致2017年到1970年时间间隔算出来少了1天。
如下为测试夏令时程序:
1./*2. 判断一个时间是否应该开启夏令时3.*/4.5.#include <time.h>6.#include <stdio.h>7.#include <string.h>8.#include <stdlib.h>9.10.struct dst_conf11.{12. int time_zone; //该夏令时的时区13. int start_month; //开始月份14. int start_week_cnt; //第几周15. int start_weekday; //0为星期天,1为星期一16. int start_hour; //这个时间为带了时区计算的当地时间17. int end_month; 18. int end_week_cnt; 19. int end_weekday; 20. int end_hour; 21.};22.23./* 美国东部时区(-5)夏令时,3月的第二个星期天2点开始,11月的第一个星期天2点结束 */24.struct dst_conf east_dst = {-5, 3, 2, 0, 2, 11, 1, 0, 2};25.26.#define is_leap_year(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)27.28./* 计算某个日期是星期几的计算公式,基姆拉尔森公式, 返回0-6, 0为周一,6为周日 */29.int calc_weekday(int d,int m, int y)30.{31. if(m == 1 || m == 2)32. {33. m += 12;34. y--;35. }36.37. int iweek = (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400)%7;38. return iweek;39.} 40.41./* 计算某个日期距1970年1月1日0时0分0秒的秒数 */42.time_t calc_sec1970(int Y, int M, int D, int h, int m, int s)43.{44. int i = 0;45. int sec = 0;46. int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};47.48. /* 年计算 */49. for(i = 1970; i < Y; i++)50. {51. if(is_leap_year(i))52. sec += 366 * 24 * 60 * 60;53. else54. sec += 365 * 24 * 60 * 60;55. }56.57. /* 月计算 */58. for(i = 1; i < M; i++)59. {60. sec += days[i] * 24 * 60 * 60;61. if(i == 2 && is_leap_year(Y))62. {63. sec += 24 * 60 * 60;64. }65. }66.67. /* 天计算 */68. sec += (D - 1) * 24 * 60 * 60;69.70. /* 时分秒计算 */71. sec += h * 60 * 60 + m * 60 + s;72.73. return sec;74.}75.76./* 输入的时间为当地时间,非UTC+0时间 */77.time_t calc_sec1970_zone(int Y, int M, int D, int h, int m, int s, int timezone)78.{79. return calc_sec1970(Y, M, D, h, m, s) - timezone * 3600;80.}81.82./* 83. time: UTC时间,距离1970年的秒数84. conf: 夏令时的配置85. 返回值:1表示夏令时应该开启,0表示夏令时应该关闭 86.*/87.int daylight_saving_time(time_t time, struct dst_conf *conf)88.{89. struct tm* local_time;90. int start_day = 0;91. int end_day = 0;92. int weekday;93. time_t start_sec, end_sec;94.95. /* 当地时间计算,后续才能进行时间比较 */96. time += 3600 * conf->time_zone; 97.98. /* 把秒数转为可读的年月日时间 */99. local_time = localtime(&time);100. local_time->tm_mon++;101. local_time->tm_year += 1900;102.103. /* 计算开始月份1号是星期几,然后推算夏令时开始时间 */104. weekday = (calc_weekday(1, conf->start_month, local_time->tm_year) + 1)%7;105. start_day = 7 * (conf->start_week_cnt - 1) + (7 + conf->start_weekday - weekday)%7 + 1;106.107. /* 计算结束时间 */108. weekday = (calc_weekday(1, conf->end_month, local_time->tm_year) + 1)%7;109. end_day = 7 * (conf->end_week_cnt - 1) + (7 + conf->end_weekday - weekday)%7 + 1;110.111. printf("start_day: %d, end_day: %d\n", start_day, end_day);112.113. /* 把年月日时间转为1970距今的秒数, 方便时间是否在区间内的比较 */114. start_sec = calc_sec1970(local_time->tm_year, conf->start_month, start_day, 115. conf->start_hour, 0, 0);116. end_sec = calc_sec1970(local_time->tm_year, conf->end_month, end_day, 117. conf->end_hour, 0, 0);118.119. if(time >= start_sec && time <= end_sec)120. {121. printf("DST on\n");122. return 1;123. }124.125. printf("DST off\n");126. return 0;127.}128.129./* 时间同步服务器返回的是1900年1月1日0时0分0秒至今的秒数 */130.131./* 输入示例: ./a.out "2017-3-12 12:20" -5 */132.int main(int argc, char *argv[])133.{134. if(argc < 3)135. return -1;136.137. int Y, M, D, h, m;138. int time_zone = 0;139. time_t sec;140.141. if(5 != sscanf(argv[1], "%d-%d-%d %d:%d", &Y, &M, &D, &h, &m))142. {143. printf("usage: %s \"2017-3-12 12:20\" -5 \n", argv[0]);144. return -1;145. }146.147. time_zone = atoi(argv[2]);148.149. printf("input date: %d-%d-%d %d:%d, timezone = %d\n", Y, M, D, h, m, time_zone);150.151. /* 时间差计算,计算出输入时间距1970-1-1 0时0分0秒的秒数 */152. sec = calc_sec1970_zone(Y, M, D, h, m, 0, time_zone); 153.154. //2208988800后面常数为1900到1970的秒数155.156. // 这个函数传入的时间是1970年距今的秒数, 时间服务器获取到的是1900年距今的秒数157. daylight_saving_time(sec, &east_dst);158.159. return 0;160.}161.