时区问题引发的Bug探讨

不要小看任何Bug,很多Bug的出现有可能引发安全漏洞。当然大部分的Bug影响功能性,并不涉及安全性,也就不构成漏洞;大部分的漏洞来源于Bug,但并不是全部,它们之间只是有一个很大的交集。

最近处理反馈时,发现一个有趣的bug,即时区不一致导致数据错误。

bug是这样的:

产品的接口流量报表中,查看当前一周的周报表时,发现未来几小时的报表数据已生成,这显然是不科学的。那么,到底是什么原因导致的呢?

1. 首先,查看流量报表是如何获取数据的

流量报表是一张时间轴折线图(如下图所示),x轴为时间轴,从周一到周日,每天以小时为单位取24个点,y轴是流量值,每个小时时段内取最大值,无流量用0填充。sql语句如下:

select strftime('%H',time,'unixepoch') as hour,max(ibps) as ibps ,max(obps) as obps from traffic where time>? and time<?  group by hour order by hour;
接口流量统计

接口流量统计

2. 其次,逐步排查问题

以周一的数据为例,进行排查。

第一步:确认传入的查询时间参数是正确的,即从每天的0时0分0秒开始,截至到次日的0时0分0秒,用时间戳表示,确认是正确的。
第二步:确认时间段内的数据是否正确,下图分别为sql查询结果和php中时间的输出结果片段。

输出片段

输出片段

由上面两图可确认,sql语句中通过strftime转化后的小时比php strftime后的时间整整慢了八个小时,从而导致数据错误。
可是相差的八个小时到底是怎么回事呢?
敏感的八小时,难道是时区问题?

第三步:确认时区

查看php环境中时区 data_default_timezone_get();//输出 Etc/GMT-8 ,为东八区,正确!
那问题很可能出在数据库sql语句中,由于产品中采用的是sqlite无配置数据库,没有单独的时区配置,那会不会是系统的时区问题呢?

时区问题

时区问题

由上可见,系统中的时间和时区都是正确的,为GMT-8东八区。
可为什么数据库中解析出来的时间会慢八个小时呢???
我们再回来看下sql语句:

select strftime('%H',time,'unixepoch') as hour,max(ibps) as ibps ,max(obps) as obps from traffic where time>? and time<?  group by hour order by hour; 

根据函数文档确认,strftime(%H,time,’unixepoch’)执行默认是显示为UTC时间的,与系统时区无关,所以执行后与本地时间(东八区)相差八个小时。
绕了一大圈终于找到了原因,解决方案就很简单了,只要把时间转化为本地时间即可,如下所示:

 select strftime('%H',time,'unixepoch',’localtime’) as hour,max(ibps) as ibps ,max(obps) as obps from traffic where time>? and time<?  group by hour order by hour;

3. 涉及到的相关知识

(1) sqlite数据库时间函数

包含下面五个,其中可以通过strftime表示其他四种函数。
详细使用方法可参考:http://www.sqlite.org/lang_datefunc.html

date(timestring, modifier, modifier, ...)
time(timestring, modifier, modifier, ...)
datetime(timestring, modifier, modifier, ...)
julianday(timestring, modifier, modifier, ...)
strftime(format, timestring, modifier, modifier, ...)

(2) 与时区相关的概念

a. GMT(Greenwich Mean Time)/GST(Greenwich Sidereal Time)格林威治标准时间,以地球自转为基础的时间计量系统,但是地球每天的自转是不规则的,而且正在缓慢减速,所以存在误差。
b. UTC(Coordinated Universal Time)协调世界时,基于原子钟,更精确
c. NTP(Network Time Protocol)网络时间协议,用来同步网络中各个计算机时间的协议,将计算机时间同步到UTC,时间源可以是原子钟,天文台,卫星,也可以从Internet上获取
d. RTC(Real Time Clock) Linux实时的时钟驱动,通常被嵌在计算机的芯片中,也有一些是在主板上使用Motorola MC146818(或clone)实现的。该硬件设备可映射到/dev/rtc,供root编程访问。
e. Epoch指的是一个特定的时间:1970-01-01 00:00:00 UTC。UNIX时间戳(Unix time, POSIX time 或 Unix timestamp)是从Epoch(1970年1月1日00:00:00 UTC)开始所经过的秒数,不考虑闰秒。一个小时表示为UNIX时间戳格式为:3600秒;一天表示为UNIX时间戳为86400秒,闰秒不计算。
f. Linux中关于时间的知识
Linux机器里有两个时钟,一个是硬件时钟,一个是内核时钟。硬件时钟是电池驱动的,通过专门的芯片工作,可以通过BIOS设置屏或一些系统命令(如hwclock)进行设置。内核时钟是由内核维护的,启动时从硬件读取时间,之后独立运行。

hwclock -r 查看硬件时间
/usr/share/zoneinfo 支持的时区文件
/etc/localtime 系统本地时区,可通过tzset或者ln –s /etc/localtime /usr/share/zoneinfo/Asia/Shanghai设置
/etc/sysconfig/clock 控制如何解读硬体时钟的时间,用来设置用户选择何种方式显示时间,如果硬件时钟为本地时间,则UTC设为0,并且不用设置环境变量TZ。

如果硬件时钟为UTC时间,则要设置UTC为1,并设置环境变量TZ为时区信息,如“Asia/Shanghai”。

如果您需要了解更多内容,可以
加入QQ群:486207500
直接询问:010-68438880-8669

Spread the word. Share this post!

Meet The Author

Leave Comment