Rails 凌晨 1 点 50 分跑测试代码,发现 Date.tomorrow 返回了错误的日期,于是乎思路有点混乱,求帮助。

howiehu · 2013年11月22日 · 最后由 davidqhr 回复于 2013年11月22日 · 3012 次阅读

现在是北京时间 2013 年 11 月 22 日凌晨 1 点 50 分,我在跑测试的时候发现 Date.tomorrow 返回的时间居然也是 2013 年 11 月 22 日……

这个测试的目的是测试用户输入的生日不能晚于今天,测试代码和实现代码如下:

实现:

def birthday_cannot_be_later_than_today
  if birthday.present? && birthday > Date.today
    errors.add :birthday, '不能晚于今天'
  end
end

测试:

it 'should not later than today' do
  @user.birthday = Date.tomorrow
  expect(@user).not_to be_valid
end

以下是 console 的输出信息。

rails console --sandbox
Loading development environment in sandbox (Rails 4.0.1)
Any modifications you make will be rolled back on exit

Frame number: 0/3
[1] pry(main)> Date.current
=> Thu, 21 Nov 2013
[2] pry(main)> Date.today
=> Fri, 22 Nov 2013
[3] pry(main)> Date.tomorrow
=> Fri, 22 Nov 2013
from (pry):4:in `<main>'
[4] pry(main)> Time.now
=> 2013-11-22 01:50:33 +0800
[5] pry(main)> Time.current
=> Thu, 21 Nov 2013 17:50:45 UTC +00:00

这有点奇怪,于是我查看了 rails 的源码:

...

    # Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
    def yesterday
      ::Date.current.yesterday
    end

    # Returns a new Date representing the date 1 day after today (i.e. tomorrow's date).
    def tomorrow
      ::Date.current.tomorrow
    end

    # Returns Time.zone.today when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns Date.today.
    def current
      ::Time.zone ? ::Time.zone.today : ::Date.today
    end

...

发现 tomorrow 和 yesterday 是调用的 Date.current,这就有意思了,Date.current 是有时区概念的,所以我个人分析应该是和我没在 application 中设置 timezone 有关的。(参考了这篇文章 - http://ruby-china.org/topics/5940

所以,我在将 config.time_zone 设置成 Beijing 之后这个测试跑通了。

但是,这又引发了一个问题:

我在判断生日是否合法的时候,是在后端写的birthday > Date.tomorrow这种方式,那也就是说,不同时区的人在输入生日的时候会出现:虽然输入的是 Date.today,但是服务器还是认为会出错的情况(好吧,我知道一般没人狗血到写今天作为生日,但是为了严谨……)

那么有什么办法能够解决这个问题呢?求帮助!

试试 Time.zone.now.tomorrow

我觉得测试里面的时间也要 Mock 起来:timecop

先取得用户的时区,然后把用户的输入的时间转化为用户的时区,之后再进行操作吧 要输出也是把系统的标准时间再转化为用户的时区,这样用户的输入,和输出给用户的显示都没问题了

至于取用户的时区,完全可以通过 JS 取得用户的本地时区,http 请求头是否有我就不清楚了

首先是这样的 数据库里存的应该是 UTC 时间 然后 rails 层面是靠 rails 自己显示出对应的时间 这样的话所有时间就能统一了

Time.new 有时区 DateTime.new 没有时区

Date.today.to_time 有时区 Date.today.to_datetime 没有时区

Date.today.end_of_day 有时区 Date.today.beginning_of_day 有时区

Date 本身是一定没有时区的,所以,如果你需要判断时间,那么请用 DateTime.now.to_date 这样产生的 date 是根据你当前时区计算过的日期

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