A-A+
linux时区的几个代码片段
这两天学习了Linux环境下的时区方面的东西。做一些小笔记,也包括代码方面。
一、时区名称
从查阅到的资料看,如果没有使用夏令时的话,时区名称形式为“NAMEoffset”,即时区名加上时间偏移,时间偏移为正数表示西几区,负数表示东几区。如我们国家使用“CST-8”,即东八区。Linux目录/usr/share/zoneinfo/存储着不同的国家/地区的时区信息文件。一般嵌入式设备如果空间有限,可以精简掉部分信息,比如统一使用目录/usr/share/zoneinfo/Etc/下的文件。注意,这个目录的文件都是GMT**形式。东八区为GMT-8,而不是常识中认为的GMT+8,因为GMT+8一般理解为GMT时间加上8小时。但如果设置时区为GMT+8,系统时间与实际时间就会相差16个小时。
二、代码片段
1、获取时区:
一种实现方法是使用date +%z获取时区偏移值,单位为小时,有正负数之分。这个值是正常认知,即正数表示实际时间比GMT多多少个小时。使用date +%Z可获取时区名称。
代码如下:
// 获取时区 int get_timezone(int* timezone) { const char* date_cmd = "date +%z"; FILE *fp = NULL; char timezone_info[16] = {0}; int tmp_time_zone = 0; fp = popen(date_cmd, "r"); if (fp == NULL) { return -1; } fread(timezone_info, sizeof(timezone_info), 1, fp); pclose(fp); timezone_info[strlen(timezone_info) - 1] = '\0'; if (isdigit(timezone_info[1]) && isdigit(timezone_info[2])) { tmp_time_zone = (timezone_info[1] - '0') * 10 + (timezone_info[2] - '0'); if (timezone_info[0] == '-') { tmp_time_zone = - tmp_time_zone; } *timezone = tmp_time_zone; return 0; } else { *timezone = 0; return -1; } return 0; }
另一种方式是使用timezone这个全局变量,这个变量存储的时间单位为秒,正负数同上第一节分析时区名称含义相同。负数表示东几区。
实现代码如下:
int get_timezone(int* timezone) { extern long timezone; // 时区,单位为秒,如GMT-8,为-28800秒 tzset(); // 必须执行 *tz = -(int)(timezone / 3600); // 注意这里的负号 return 0; }
函数get_timezone返回的值的正负数经过转换后,方便程序计算。比如ONVIF设置的时间只有UTC而不是当地时间,所以将UTC时间加上这个函数返回的时区值即可算出当地时间。除了timezone外,还有tzname和daylight这两个全局变量。它们的使用示例代码如下:
/** * @brief 获取时区名称、时间偏移值 * * @param tz1[OUT] 时间名称0 * @param tz2[OUT] 时间名称1 * @param tz_seconds[OUT] 时间偏移值,单位为秒,如东八区,与GMT时间相差-28800秒 * @param daylight_saving[OUT] 夏令时 为0不使用,非0则使用 * * @return 0: 成功 -1:失败 * @note 使用东八区测试,daylight_saving为1,不知是否正常 测试: PST8PDT时区 rm /etc/localtime ln -s /usr/share/zoneinfo/PST8PDT /etc/localtime tzname[0]:PST tzname[1]: PDT timezone: 28800 (正数表示西几区) */ void get_tz_name(char* tz1, char* tz2, long* tz_seconds, int* daylight_saving) { extern char *tzname[2]; extern long timezone; // 时区,单位为秒,如-8区,为-28800秒 extern int daylight; tzset(); // 必须执行 strcpy(tz1, tzname[0]); strcpy(tz2, tzname[1]); *tz_seconds = timezone; *daylight_saving = daylight; }2、时区名称和时区值转换 ONVIF使用的时区名称是posix标准,但要从中知道是哪一个时区,以便对应于GMT**的形式。转换代码如下:/** * @brief 时区名称转换为时区数值 * * @param tzname[IN] 时区名称,posix标准,如东八区时区为GMT-8,西八区为GMT+8,西八区也可以用PST8PDT表示 * * @return -12~12: 成功 1024:失败 * @note 这里返回的时区数值是为了程序方便计算,比如返回8表示是东八区时间(与上述的时区名称不同), * 这样在GMT时间加上8小时就是当地时间,其它同理 */ int convert_tzname2tz(const char* tzname) { #define IS_TZ(foo) (!strncmp(tzname, foo, strlen(foo))) // "GMTxxx" first: GMT0/GMT+0/GMT-0 GMT-8 GMT-08 // CST if (IS_TZ("GMT") || IS_TZ("CST")) { char *endptr; long val = strtol(&tzname[3], &endptr, 10); if (val >= -12 && val <= 12) return -val; else return 1024; } else if (IS_TZ("IDLW")) { return -12; } else if (IS_TZ("NT")||IS_TZ("NUT")||IS_TZ("SST")) { return -11; } else if (IS_TZ("AHST") || IS_TZ("CAT")||IS_TZ("HST")||IS_TZ("HDT")||IS_TZ("TAHT")) { return -10; } else if (IS_TZ("YST")||IS_TZ("YDT")||IS_TZ("GAMT")) { return -9; } else if (IS_TZ("PST")||IS_TZ("PDT")) { return -8; } else if (IS_TZ("MST")||IS_TZ("MDT")) { return -7; } else if (IS_TZ("CDT")) { return -6; } else if (IS_TZ("EST")||IS_TZ("EDT")||IS_TZ("EAST")) { return -5; } else if (IS_TZ("AST")||IS_TZ("ADT")||IS_TZ("GYT")||IS_TZ("WART")) { return -4; } else if (IS_TZ("CLT")||IS_TZ("BRT")||IS_TZ("PMST")||IS_TZ("WGT")) { return -3; } else if (IS_TZ("AT")||IS_TZ("GST")) { return -2; } else if (IS_TZ("WAT")) { return -1; } else if (IS_TZ("Zulu")||IS_TZ("UT")||IS_TZ("UTC")||IS_TZ("UCT")|| IS_TZ("Greenwich")||IS_TZ("BST")) { return 0; } else if (IS_TZ("CET")||IS_TZ("FWT")||IS_TZ("MET")||IS_TZ("MEWT") ||IS_TZ("SWT")||IS_TZ("MEST")||IS_TZ("MESZ")||IS_TZ("SST") ||IS_TZ("FST")) { return 1; } else if (IS_TZ("EET")||IS_TZ("IST")) { return 2; } else if (IS_TZ("BT")||IS_TZ("MSK")) { return 3; } else if (IS_TZ("ZP4")) { return 4; } else if (IS_TZ("ZP5")) { return 5; } else if (IS_TZ("ZP6")) { return 6; } else if (IS_TZ("ZP7")) { return 7; } else if (IS_TZ("WAST")||IS_TZ("HKT")||IS_TZ("SGT")) { return 8; } else if (IS_TZ("JST")) { return 9; } else if (IS_TZ("ACT")) { return 10; } else if (IS_TZ("EAST")) { return 11; } else if (IS_TZ("IDLE")) { return 12; } else { return 1024; } return 1024; }从时区数值转换为时区名称代码示例:
/* g_tz_name是时区名称,请到 /usr/share/zoneinfo/Etc/目录查看文件。 数值前面正数表示时区位于本初子午线之西,负数表示时区位于本初子午线之东。 如不明白,请在linux环境执行命令“man tzset”阅读文档。 */ const char* g_tz_name[25] = { "GMT+12", "GMT+11", "GMT+10", "GMT+9", "GMT+8", "GMT+7", "GMT+6", "GMT+5", "GMT+4", "GMT+3", "GMT+2", "GMT+1", "GMT", "GMT-1", "GMT-2", "GMT-3", "GMT-4", "GMT-5", "GMT-6", "GMT-7", "GMT-8", "GMT-9", "GMT-10", "GMT-11", "GMT-12", }; /** * @brief 返回时区名称 * * @param timezone[IN] 时区数值,如东八区为+8,西八区为-8 * * @return 时区名称 */ const char* get_timezone_info(int timezone) { return g_tz_name[timezone + 12]; }李迟 2016.01.23 周六 今天特冷