Rails与数据库查询到关于datetime的记录不一致引发的讨论

起因

老大发现,一个sql在rails console里执行的结果是1,在数据库(PostgreSQL)的console里执行结果是23 :

SELECT COUNT(*) FROM “users” WHERE “users”.”active” = ‘t’ AND (expire < NOW())

然后老大很快就发现这是时区的问题。。

概念

首先了解一下概念:

UTC - Coordinated Universal Time (不知道缩写为啥不是CUT。。。)是世界上主要的时间标准。

GMT - Greenwich Mean Time 初中学过!格林威治时间,是一些欧洲和非洲官方使用的时区。它的UTC offset就是”+0000”。

而中国使用的北京时间就是”UTC+0800”,UTC加8个小时的意思。

真相

那偏差又是怎么来的呢?

原来,数据库的每个连接,都是可以设置时区的,而rails用的是UTC(也就是说,rails在从数据库读活着写时间的时候,都会转换成UTC):

irb(main):015:0> User.connection.execute("SELECT  current_setting('TIMEZONE');")[0]
   (0.2ms)  SELECT current_setting('TIMEZONE');
=> {"current_setting"=>"UTC"}

而每次读出来后,rails都会根据TIMEZONE环境变量来自动做offset,让人看到的时间是正确的。

而服务器上的时间设置的+0800,所以通过数据库终端进去的时区和rails console里进去是不一样的,这就是误差的来源。在把数据库终端的连接也设置成UTC之后,查询的结果就一致了。

另外:
如果不想使用默认的UTC,想用本地的timezone,可以这样设置:

config.active_record.default_timezone = :local

默认情况,ActiveRecord::Base 保持所有的datetime的列意识到timezone的存在(UTC吧):

config.active_record.time_zone_aware_attributes = true

设置为false即可关掉这个功能。

而如果这个功能打开了,而又不想在读某些属性的时候让它转换成你本地的Time.zone,可以设置skip_time_zone_conversion_for_attributes
比如:

class Topic < ActiveRecord::Base
  self.skip_time_zone_conversion_for_attributes = [:written_on]
end

延伸

Ruby中有三个类来处理日期和时间:使用了date库的Date 和DateTime ,以及使用了自己time库的Time。
DateTime和Time都可以用来处理年月日时分秒等,但是区别在于,在后端(backend side),Time存储的是整型数,表示从Epoch到现在有多少秒。

参考

1.Timestamp

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值