写国际化的嵌入式代码,时间问题如何处理?

358次阅读  |  发布于2年以前

写国际化的程序比较难处理的两个问题,可能就是时间问题编码问题 了。今天,我们就来聊一聊时间问题。

最近设备到了国外,时间不不准了~

我一直在东八区写代码,处理时间问题时,习惯性的把时区写死为东八区,即设备的小时数总是基于GMT的小时数加上8个小时作为设备的小时数。

如果设备到了国外,设备的时间就不准了,设备的小时数对不上当地的小时数。我们的设备时间,是使用设备上GPS的授时时间给设备进行时间校准的。

设备从GPS拿到的时间数据只是UTC时间。所以,不同地区的时间,需要基于UTC时间+/-时区,向西减小,向东增加。当地的时区,可以根据当地的经度进行计算,这个后面再说。

下面,我们先来了解一些概念。

一、GMT与UTC时间

GMT时间(Greenwich Mean Time,格林威治时间),之前作为全球时间的基准参考时间。

UTC时间(Universal Time Coordinated, 世界标准时间或世界协调时间),以原子时秒长为基础,在时刻上尽量接近于世界时的一种时间计量系统。UTC是基于标准的GMT提供的准确时间。

UTC时间和GMT时间其实是同一个时间,只不过UTC时间用秒来表示。

1、获取UTC时间

获取UTC时间的接口:

#include <time.h>
time_t time(time_t *tloc);

该接口返回1970-01-01 00:00:00 +0000至今的秒数(UTC)。

使用例子:

#include <stdio.h>
#include <time.h>

time_t get_utc_time(void)
{
    return time(NULL);
}

int main(int argc, char **argv)
{
    time_t utc_time = get_utc_time();
    printf("utc_time = %ld s\n", utc_time);

    return 0;
}

运行结果:

2、获取GMT时间

获取GMT时间的接口:

#include <time.h>
struct tm *gmtime(const time_t *timep);

该接口返回tm结构的GMT时间(UTC时间)。

tm结构:

struct tm 
{
    int tm_sec;    /* Seconds (0-60) */
    int tm_min;    /* Minutes (0-59) */
    int tm_hour;   /* Hours (0-23) */
    int tm_mday;   /* Day of the month (1-31) */
    int tm_mon;    /* Month (0-11) */
    int tm_year;   /* Year - 1900 */
    int tm_wday;   /* Day of the week (0-6, Sunday = 0) */
    int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */
    int tm_isdst;  /* Daylight saving time */
};

使用例子:

#include <stdio.h>
#include <time.h>

time_t get_utc_time(void)
{
    return time(NULL);
}

int main(int argc, char **argv)
{
    time_t utc_time = get_utc_time();
    printf("utc_time = %ld s\n", utc_time);

    struct tm *gmt_tm = gmtime(&utc_time); 
    printf("gmt time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", gmt_tm->tm_year + 1900,
                                                         gmt_tm->tm_mon + 1,
                                                         gmt_tm->tm_mday,
                                                         gmt_tm->tm_hour,
                                                         gmt_tm->tm_min,
                                                         gmt_tm->tm_sec);

    return 0;
}

运行结果:

二、时区

由于世界各国家与地区经度不同,地方时区也有所不同,因此会划分为不同的时区。

正式的时区划分包括24个时区,每一时区由一个英文字母表示。每隔经度15°划分一个时区,有一个例外,每个时区有一条中央子午线。

1、时区划分方法

现今全球共分为24个时区。英国(格林尼治天文台旧址)为中时区(零时区)、东1—12区,西1—12区。每个时区横跨经度15度,时间正好是1小时。最后的东、西第12区各跨经度7.5度,以东、西经180度为界。每个时区的中央经线上的时间就是这个时区内统一采用的时间,称为区时,相邻两个时区的时间相差1小时。

2、经度范围

3、当地时区计算

需要用到的接口:

#include <time.h>
struct tm *localtime(const time_t *timep);

计算当地时区:

#include <stdio.h>
#include <time.h>

time_t get_utc_time(void)
{
    return time(NULL);
}

int main(int argc, char **argv)
{
    time_t utc_time = get_utc_time();
    printf("utc_time = %ld s\n", utc_time);

    struct tm *gmt_tm = gmtime(&utc_time); 
    printf("gmt time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", gmt_tm->tm_year + 1900,
                                                         gmt_tm->tm_mon + 1,
                                                         gmt_tm->tm_mday,
                                                         gmt_tm->tm_hour,
                                                         gmt_tm->tm_min,
                                                         gmt_tm->tm_sec);
    int gmt_hour = gmt_tm->tm_hour;

    struct tm *local_tm = localtime(&utc_time); 
    printf("local time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", local_tm->tm_year + 1900,
                                                           local_tm->tm_mon + 1,
                                                           local_tm->tm_mday,
                                                           local_tm->tm_hour,
                                                           local_tm->tm_min,
                                                           local_tm->tm_sec);
    int local_hour = local_tm->tm_hour;


    int local_time_zone = local_hour - gmt_hour;
    if (local_time_zone < -12) 
    {
        local_time_zone += 24; 
    } 
    else if (local_time_zone > 12) 
    {
        local_time_zone -= 24;
    }else{}

    printf("local_time_zone = %d\n", local_time_zone);

    return 0;
}

我们当前为北京时间:

我们把Ubuntu时间日期里的地点改到其它国家:

三、CST 时间

CST (China Standard Time,中国标准时间)。

中国标准时间一般指北京时间。北京时间是中国采用国际时区东八时区的区时作为标准时间。而中国幅员辽阔,东西相跨5个时区(即东五区、东六区、东七区、东八区、东九区5个时区)

“北京时间”适用于中国(大陆、港澳、台湾)境内,但在大陆的新疆、西藏等地,政府机关、企事业单位作息时间和邮政通讯费用优惠分界点虽然用北京时间来表示,但也比其他各省延晚2小时,使用UTC+6的情况更为普遍。

四、根据经度计算时区

时区范围是中央经线的度数向左右分别减、加7.5度。用该地的经度除以15度,当余数小于7.5度时,商数即为该地所在的时区数,当余数大于7.5度时,商数加1即为该地所在的时区数。

#include <stdio.h>
#include <time.h>

int calc_timezone(double longitude)
{
    int timezone = 0;
    int quotient = (int)(longitude / 15);
    double remainder = (longitude - quotient * 15);
    printf("quotient = %d, remainder = %lf\n", quotient, remainder);

    if (remainder <= 7.5)
    {
        timezone = quotient;
    }
    else
    {
        timezone = quotient + (quotient >= 0 ? + 1 : -1);
    }

    return timezone;
}

int main(int argc, char **argv)
{
    while (1)
    {
        double longitude = 0.0;
        printf("please input longitude:");
        scanf("%lf", &longitude);
        printf("longitude = %lf\n", longitude);
        int timezone = calc_timezone(longitude);
        printf("timezone = %d\n", timezone);
    }

    return 0;
}

这其实也是百度百科上提供的思路:

这种方式至少可以计算得到时区中心线的时区数,但是一些临界情况可能会差1小时。在网络上也没有找到其它更好的解决方案。

设备的时间,如果只是作为一个显示功能,影响可能不是很大,但是如果设备的时间来做其它事情,比如定时功能,定时多少点多少分做什么事情,影响就很大了。

对于我们的设备,定时功能使用手机APP来操作的,这时候能想到的比较好的方法就是每当使用手机APP的时候,把手机APP的时间给设备时间进行一次校准。

大家是否还有其它更好的解决方案?欢迎留言讨论!

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8