linux时区的几个代码片段 | 迟思堂工作室
A-A+

linux时区的几个代码片段

2016-01-23 15:36 GNU/Linux系统, 流媒体学习 暂无评论 阅读 1,591 次

这两天学习了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 周六 今天特冷



如果本文对阁下有帮助,不妨赞助笔者以输出更多好文章,谢谢!
donate



标签:

给我留言