写国际化的程序比较难处理的两个问题可能是:时间问题编码问题
本篇文章来聊一聊时间问题。
最近设备到了国外,时间不不准了~
杂烩君一直在东八区写代码,处理时间问题时,习惯性的把时区写死为东八区,即设备的小时数总是基于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_ttime(time_t *tloc)
;

该接口返回1970-01-01 00:00:00 +0000至今的秒数(UTC)。
使用例子:
#include<stdio.h>
#include<time.h>

time_tget_utc_time(void)
{

return
 time(
NULL
);

}


intmain(int argc, char **argv)
{

time_t
 utc_time = get_utc_time();

printf
(
"utc_time = %ld s\n"
, utc_time);


return0
;

}

运行结果:

2、获取GMT时间

获取GMT时间的接口:
#include<time.h>
struct tm *gmtime(consttime_t *timep)
;

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

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_tget_utc_time(void)
{

return
 time(
NULL
);

}


intmain(int argc, char **argv)
{

time_t
 utc_time = get_utc_time();

printf
(
"utc_time = %ld s\n"
, utc_time);


structtm *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);


return0
;

}

运行结果:

时区

由于世界各国家与地区经度不同,地方时区也有所不同,因此会划分为不同的时区。
正式的时区划分包括24个时区,每一时区由一个英文字母表示。每隔经度15°划分一个时区,有一个例外,每个时区有一条中央子午线。

1、时区划分方法:

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

2、经度范围:

3、当地时区计算

需要用到的接口:
#include<time.h>
struct tm *localtime(consttime_t *timep)
;

计算当地时区:
#include<stdio.h>
#include<time.h>

time_tget_utc_time(void)
{

return
 time(
NULL
);

}


intmain(int argc, char **argv)
{

time_t
 utc_time = get_utc_time();

printf
(
"utc_time = %ld s\n"
, utc_time);


structtm *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;


structtm *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

    } 

elseif
 (local_time_zone > 
12

    {

        local_time_zone -= 
24
;

    }
else
{}


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


return0
;

}

我们当前为北京时间:
我们把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>

intcalc_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;

}


intmain(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);

    }


return0
;

}

这其实也是百度百科上提供的思路:
这种方式至少可以计算得到时区中心线的时区数,但是一些临界情况可能会差1小时。在网络上也没有找到其它更好的解决方案。
设备的时间,如果只是作为一个显示功能,影响可能不是很大,但是如果设备的时间来做其它事情,比如定时功能,定时多少点多少分做什么事情,影响就很大了。
对于我们的设备,定时功能使用手机APP来操作的,这时候能想到的比较好的方法就是每当使用手机APP的时候,把手机APP的时间给设备时间进行一次校准。
大家是否有其它更好的解决方案,欢迎留言讨论
如果觉得文章有帮助,麻烦帮忙转发,谢谢!
-END-

往期推荐:点击图片即可跳转阅读
用Arduino做一面LED墙,是一种什么体验?
“寒冬”之下,二本学生是如何秋招上岸的?
什么是GPU?GPU和显卡的关系?GPU国产化布局?
蓝桥杯,进决赛了
继续阅读
阅读原文