新手问题 Rails 中的时区及时间问题

boyishwei · 2013年12月13日 · 最后由 wwwicbd 回复于 2017年10月30日 · 19006 次阅读

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

嗯,怎么说呢。这样吧,看看 37singals 的 local_time solution https://github.com/37signals/local_time

应用服务器 跟 数据库 分别放在不同机器,并且这两台机器的时区不是同一个的情况下,created_at 的时间到底是哪个时区的呢。。。

最后的解决方案也可能有问题 当你的数据库里面 created_at 由于种种原因丢失了数据 那么你的程序就 500 了

#1 楼 @xds2000 嗯,好东西:) #2 楼 @ywjno 应用服务器的时间吧,这个还真没测试过... #3 楼 @zj0713001 created_at 丢失数据?没有理解... :(

#4 楼 @boyishwei 就是 created_at 变成 nil 了呗 我遇到过一次 表里所有旧数据时间都木有了 至今没查清原因

#2 楼 @ywjno 应用服务器的时间

个人还是倾向于数据库存 UTC,其他的失去按照 timezone 转化后显示就可以了

for mongoid:

# config/application.rb
config.time_zone = 'Beijing'
#config/mongoid.yml
use_utc: false
use_activesupport_time_zone: true

我这边的测试结果似乎这样呢

我这边做的试验。

服务器是 UTC(+0) 时间。

Test One、Test Two、Test Three 三种配置下,数据库里存的都是 UTC 时间。

Test Three:

DB 时间 2015-11-07 03:02:56 created_at 2015-11-07 11:02:56 created_at.localtime 2015-11-07 03:02:56 created_at.utc 2015-11-07 03:02:56

#10 楼 @feng88724 那是因为你的服务器时间就是 UTC(+0) 时间,你把你服务器时区调成其他时区 (比如 +8 ) 试试就知道区别了。

13 楼 已删除
wfwdex [求助] 关于 Rails 中奇怪的时区问题 提及了此话题。 05月11日 08:20

楼主最后一段有笔误,应该是 created_at.localtime.to_s(:db) .

我的做法是在数据库统一使用 UTC 时间,在 ApplicationController 的时候根据 cookie 里的值给 I18n 和 Time.zone 赋值。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号