时区简单理解

https://zh.wikipedia.org/wiki/%E6%97%B6%E5%8C%BA

上面的链接是时区的wiki说明,下面说说我记住的部分:

GMT时区是格林威治标准时间,我把它理解为 “真实时间”

UTC时区是根据GMT得来的“世界标准时间”,它的时间和GMT是相同的

CST可以指下列的时区:

澳洲中部时间,Central Standard Time (Australia)
中部标准时区(北美洲),Central Standard Time (North America)
北京时间,China Standard Time
古巴标准时间,Cuba Standard Time,参见北美东部时区

其中我们所在的时区背景时间 CST=UTC+8小时,也就是说,真实时间是0点的时候,背景时间是8点

ISO_8601日期格式标准

https://zh.wikipedia.org/wiki/ISO_8601

上面是日期格式标准的wiki

当前的UTC时间是2016-01-07T01:58Z,其中Z是4位数字格式的时间偏移量,不写的时候默认不偏移。

其中,字母T代表使用UTC时间,字母Z代表时间偏移量,实际写法中字母Z应该被偏移量替换,例如 “2017-1-7T10:21+0800”或者”2017-1-7T10:21-0800”,字母Z被+0800和-0800替换了。

在浏览器中直接new一个date对象,因为我们处于UTC+0800的时区,所以控制台给我们打印出来的时间是GMT+0800的时间

2016-01-07T00:00 代表UTC时区1月7日0时0分(在控制台中显示UTC+0800时区为8时0分)

2016-01-07T00:00 0800 代表UTC+0800时区1月7日0时0分,2016-01-07T00:00 -0800 代表UTC-0800时区1月7日0时0分,在控制台中显示分别如下

通过日期字符串new一个Date对象,输入的时间字符串是2016-1-7 10:21,没有带ISO标准的“T”字母,因此浏览器认为我们想输入的是当地时间

HTML5 input标签datetime属性

下面问题来了,我要在页面上输入一个时间,存入数据库,上面说了那么多时区,那么用户在页面上输入的时间应该是哪个时区的呢,传到server,存入db的应该又是哪个时区的呢?

经测试

1
<input type="datetime"  />  chrome上是不支持的
<input type="datetime-local" /> chrome支持

Google了一下据说是因为datetime输入框输入的是本地时区,考虑到时区的问题chrome没有支持此输入类型,会降级为text类型

datetime-local输入类型chrome是支持的,获取的value格式是:

“2016-1-7T10:21”

如上面所述,这个时间是标准UTC时间,这种时间从前端到后台到存入db,都是不会发生错误的。

但是对用户来说,他填写表单的时候想的时间肯定是他所在位置的本地时间,比如我输入”2016-1-7 10:21”,我真实想输入的时间是”2016-1-7T10:21+0800”,而不是”2016-1-7T10:21”。

为了支持这种情况,我需要把”2016-1-7T10:21”转换为用户真实想要的当地时间”2016-1-7 10:21”,于是”2016-1-7 10:21”.replace(“T”,” “),它实际代表的真实时间(UTC时间)是”2016-1-7T10:21+0800”

这样在前端是没问题了,但是传到后端之后,这样的非ISO标准时间是没有携带时区信息的,服务器收到之后会将这个时间安装服务器所在时区来处理,处理之后所代表的真实时间就和用户输入的真实时间不同了。所以我们在前端得把时间转换为ISO标准时间格式再传给服务器,这样服务器就能明白用户输入的真正的时间了,另一种方法,也可以把时间用毫秒数表示,传到后端,不过这种方式可读性不太好。

1
// 1.将字面时间转化为本地时间  2.将本地时间转换为真实GMT时间传入后台
    function getRealGMT(datetime){
        datetime=datetime.replace("T"," ");
        var temp=new Date(datetime);
        var realGMT=temp.getTime()+temp.getTimezoneOffset()*60000;
        return new Date(realGMT).format("yyyy-MM-ddThh:mm");
    }

转换效果如下:

mysql时区

mysql可通过 show variables like ‘%time_zone%’;命令来查看数据库设置的时区。我们的CST时区代表的是中国区的时区,即UTC+0800
所以服务器把从前端收到的ISO日期 “2016-1-7T02:21”收到之后,写入mysql的datetime字段,mysql的datetime字段会根据它的CST时区把日期转换过来,于是显示的日期就是”2016-1-7 10:21”,代表的真实时间是”2016-1-7T10:21+0800”


update @ 2020.01.29

没想到相隔刚刚好整4年后的今天,我会来更正这篇文章的理解。

4年前理了一遍时区的概念,是因为在浏览器中遇到了些时区的问题,而最近在看后端的,和mysql相关性比较大的一个时区问题,查阅文档的时候发现4年前的理解有些偏差, 详见mysql官网文档对时区的描述 5.1.12 MySQL Server Time Zone Support

这里简单的整理一下结论:

  • mysql的Date、Time、Datetime字段,是不包含时区信息的,时区设置在读写的时候都不影响这些字段的值,时区信息全靠应用层把控。
  • 仅timestamp类型字段,或者NOW()、CURTIME()函数,受mysql时区设置影响。timestamp类型字段,在写入的时候会根据时区设置,将值转换为UTC时间存储,检索的时候又根据时区设置将其转换为对应时区的时间输出。

基于上面的结论,在一个使用mysql的系统中,如果没有使用timestamp字段,那么我们要记住,应用程序所在机器的时区,就是整个系统中日期时间的时区,mysql中的数据存储是会丢掉时区的;如果使用了timestamp字段,那么我们在连接mysql的时候,需要将时区指定为写入时间的应用程序所在机器的时区,这样mysql中存储的timestamp字段值才会是正确的UTC时间, 假如有其它用户用别的时区连接到mysql,他们也能得到正确的转换时区后的时间,而不会产生偏差(当应用程序时区与mysql默认时区不一致时,如果不在连接时指定好应用程序时区,就会产生偏差了)。

另外记录一部分细节在这里备忘吧。

mysql默认使用系统时区,不过使用系统时区可能会频繁调用系统函数,造成性能问题。

可以分别设置全局时区和连接级别的时区

查询:SELECT @@GLOBAL.time_zone, @@SESSION.time_zone;

设置:SET GLOBAL time_zone = timezone;SET time_zone = timezone;

时区设置的范围是'-12:59' to '+13:00'

还有更多场景下的细节,这里不赘述了,详见文档。

最近武汉冠状病毒肺炎,天灾人祸啊,网上感染者家庭的经历,看的真让人揪心。天佑武汉,愿早日平息!

☞ 参与评论