ActiveRecord添加到负日期年份

时间:2015-06-26 16:14:28

标签: ruby-on-rails ruby date activerecord

我需要能够保存否定日期,但一直遇到问题:从数据库中检索日期时,ActiveRecord正在添加1年。

迁移使用t.date

这是一个简单的例子:

我创建并保存日期值为Date.new(-44, 3, 15)的模型。

当插入数据库和数据库内部时,年份是正确的。手动psql检查证实了这一点。

但是,使用ActiveRecord从数据库中检索日期时,年份为-43。

我用Google搜索并搜索了没有结果的SO。知道是什么导致了这个吗?

1 个答案:

答案 0 :(得分:2)

http://ruby-doc.org/stdlib-1.9.3/libdoc/date/rdoc/Date.html#method-c-new

...BCE years are counted astronomically.
...the year before the year 1 is the year zero, 
and the year preceding the year zero is the year -1.

研究这个我认为你在Date#parse发现了一个缺陷,然后我认为它正在按照设计的方式工作,现在我不再确定了:(

Date.parse('2015-06-15 CE') == Date.new(2015, 6, 15)
# => true

Date.parse('0000-01-01 CE') == Date.new(0, 1, 1)
# => true

Date.parse('0002-01-01 BCE') == Date.new(-1, 1, 1)
# => true

Date.parse('0000-01-01 CE') == Date.parse('0001-01-01 BCE')
# => true

Date.parse('0001-01-01 BCE') == Date.new(0, 1, 1)
# => true

https://en.wikipedia.org/wiki/Astronomical_year_numbering

  

公元前1年/公元前2年编号为0,公元前2年编号为-1

所以它似乎按照设计的方式工作,但可能不如预期的那样

# The year 1 BC/BCE is numbered 0
Date.parse('0001-01-01 BCE') == Date.new(0, 1, 1)

# the year 2 BC is numbered −1
Date.parse('0002-01-01 BCE') == Date.new(-1, 1, 1)

这对您和您的rails应用程序意味着什么?

用户输入最有可能以“#00; 0044-03-15 BC'作为字符串(params),分配给模型属性。

x = Widget.create(date_column: '0044-03-15 BC')

x.date_column
 => Wed, 15 Mar -0043

x.attributes_before_type_cast['date_column']
 => "0044-03-15 BC"

x.attributes_before_type_cast['date_column'].to_date
 => Wed, 15 Mar -0043

猜测to_date也在使用Date.parse

我真的不确定如何在postgres中存储正确的值,但是rails会在该值上使用Date.parse并在显示用户输入日期时关闭一年(仅限BCE)

一些可能的hacky解决方法

1)将值存储为整数

class Whatever < ActiveRecord::Base
  # 'date' column -> the_date stored as an integer

  # override the columns 'getter' method
  def the_date
    the_time = Time.at(self[:the_date])
    Date.new(the_time.year, the_time.month, the_time.day)
  end
end

# Time#to_i - epoch time
x = Whatever.create(the_date: Time.new(-44, 3, 15, 0, 0, 0).to_i)

2)将年,月,日存储为单独的数据库字段?

class Whatever < ActiveRecord::Base
  # 'date' stored as 3 integer columns -> the_date_year, the_date_month, the_date_day

  # use this in views, etc...
  def the_date
    Date.new(the_date_year, the_date_month, the_date_day)
  end
end  

希望其他人有更好的解决方案......