未分类 · 2016年5月23日 0

Rails 中的时区及时间问题

Rails中的时区及时间问题,做项目的过程中发现页面中显示时间不大对,然后折腾了半天把这个算是弄清楚了,记录下来,以免其他人也掉进这个坑。

论坛里之前有过相关的讨论贴,但我觉得没说彻底:
http://ruby-china.org/topics/5940

首先: 时区时间换算关系
UTC时间 + 时差 = 当地时间

其次:确认服务器时间正确
保证你服务上时间及时区正确无误,特别是时区.

最后了解Rails中的时间设定机制
Rails3、4中,默认采用UTC时间进行存储,不管你服务器上设定的是那个时区,当前什么时间,默认情况下,Rails都会将其转换为UTC时间,然后再存在数据库中,在页面中显示也是如此。

比如
Local Time: 2013-12-13 18:00 (北京时间 +800 )
那么
UTC Time:2013-12-13 10:00

如果在这个时间点,往DB中存了一条数据,Rails默认情况下,存在DB中的时间是
2013-12-13 10:00, UTC时间

通过created_at获得的时间并且在页面显示,也将是 2013-12-13 10:00 UTC时间。

以上是Rails默认对时间的处理方式,也是鼓励这么做。

这对我们开发者或者是很清楚时区换算关系的人来说,不是什么大问题,但对一般用户来说,这绝对不能接受。

解决方式:
在application.rb中添加以下两条配置, (以北京时间为例)

config.time_zone = 'Beijing'
config.active_record.default_timezone = :local

通过添加这两条配置之后,你DB中的时间将以本地时间方式存储,在页面中显示的也将是本地时间。问题解决。

延伸
config.time_zone = ‘Beijing’
config.active_record.default_timezone = :local
这两条配置到底分别起到什么作用,我上周做过一组测试:

Test One:
默认配置,即:

config.time_zone
config.active_record.default_timezone

均不做配置

存储数据到DB时间:2013-12-07 10:54:57 (北京时间)

DB时间                        2013-12-07 02:54:57
created_at                    2013-12-07 02:54:57 UTC
created_at.localtime          2013-12-07 10:54:57 CST
created_at.utc                2013-12-07 02:54:57 UTC

可以看出数据库时间是UTC时间,created_at也是UTC时间,created_at.localtime是北京时间

Test Two:
配置:

config.time_zone = 'Beijing'

config.active_record.default_timezone不做配置

存储数据到DB时间:2013-12-07 10:57:55 (北京时间)

DB时间                        2013-12-07 02:57:55
created_at                    2013-12-07 10:57:55 +0800 CST
created_at.localtime          2013-12-07 10:57:55 CST
created_at.utc                2013-12-07 02:57:55 UTC

可见config.time_zone = ‘Beijing’配置的作用,是在ActiveRecord中取时间的时候,将UTC时间转换成Local时间,也就是通过created_at等方法获取到的将直接是Local时间。而存储在DB中的时间仍然是UTC时间。

Test Three:
配置:

config.time_zone = 'Beijing'
config.active_record.default_timezone = :local

存储数据到DB时间:2013-12-07 11:02:56 (北京时间)

DB时间                        2013-12-07 11:02:56
created_at                    2013-12-07 11:02:56 +0800
created_at.localtime          2013-12-07 11:02:56 CST
created_at.utc                2013-12-07 03:02:56 UTC

可见config.active_record.default_timezone = :local配置的作用,是在ActiveRecord中往数据库存放数据时,将按Local时间进行存储,通过添加这两项配置,就可以实现数据库存放时间以及通过created_at等方法取到的时间均为Local时间。

另外可以看出,只要你的服务器时区及时间设置正确,任何配置情况下,created_at.localtime输出的时间均是正确的Local时间。

created_at.localtime(:db).to_s
如果不想在页面中显示本地时区及时差的话,可以通过created_at.localtime(:db).to_s去掉,

2013-12-07 11:02:56 CST
通过调用created_at.localtime(:db).to_s得到的将是:
2013-12-07 11:02:56