解析日期字符串时,Ruby返回无效日期(错误的DoW)

时间:2016-12-05 23:35:46

标签: ruby date time

我正在使用api,它返回引用2016年第53周的日期字符串。

我发现解析它时唯一有效的日历是JULIAN

但是,Date.parse("2016-W53", true, Date::JULIAN)会返回Mon, 27 Dec 2016,这是错误的日期,因为它应该是Mon, 26 Dec 2016

为什么会发生这种情况? 如何让它返回正确的日期?

3 个答案:

答案 0 :(得分:1)

朱利安日历是一个完全不同的日历;在Julian日历上,12/27 星期一,因为在Julian日历上,2016年1月1日是星期四,而在公历上是星期五。

通常情况下,我会说使用Date.strptime('2016-W53','%G-W%V') ...但是这似乎不起作用。根据文档,在计算一年中的几周时,Ruby使用基于ISO 8601周的年份和周数:

  

ISO 8601以工作周为基础的年份和周数:
  YYYY的第1周从星期一开始,包括YYYY-01-04   第一周之前一年的日子是在最后一周   去年。
   %G - 以周为基础的年份
  %V - 基于周的年份的周数(01..53)
  %g - 基于周的年份(00..99)的最后2位数

如上所述,本系统中一年的第一周是包含1月4日的一周;在朱利安历法中,星期日,第4天包含在1月1日的同一周;然而,在公历中,第4个星期一,因此标志着2016年的第一周“重要” - 前一周是2015年的第53周。

原因是(大概)人们不应该计算一周是2016年的第一周,而也是 2015年的最后一周;因为如果你想要一段时间,比如10周,那么你最终会计算两周。选择1月4日作为标记的原因可能是因为如果1月1日这一周包括1月4日,那么7天的大部分时间都会在新的一年中落下,所以本周将成为新年的一部分。

鉴于上述情况, 2016年没有第53周,至少按照ISO规则计算周数。您可以实现自己的系统来确定日期的含义,但是您必须向API提供商询问他们如何生成这些数字,否则您将不知道您是否正确地解释了它

答案 1 :(得分:0)

你不应该用Julian calendar解释日期,因为它不能纠正我们现在在西方使用的最新Gregorian calendar中所考虑的时间周期性“漂移”。国家。

如果输入有效,

Date.parse应该有效,看起来你的输入不是:

Date.parse("2016-W53")
# ArgumentError: invalid date
Date.parse("2020-W53")
# <Date: 2020-12-28 ((2459212j,0s,0n),+0s,2299161j)>

我想说如果标准库中的默认parse方法无法理解该值,那么您应该编写自己的解析器。

def custom_parse(str)
  regex = /\A(?<year>\d{4})-W(?<week>\d{2})\z/
  if data = regex.match(str)
    year = data[:year].to_i
    week = data[:week].to_i
    Date.commercial(year, week - 1) + 7
  end
end

custom_parse("2016-W53")
# <Date: 2017-01-02 ((2457756j,0s,0n),+0s,2299161j)>

答案 2 :(得分:0)

关于上周Wikipedia

  

如果12月31日是星期一,星期二或星期三,则是在第二年的第01周。如果是在星期四,则是在刚刚结束的第53周;如果在星期五它是在第52周(如果结束的那一年是闰年,则为53);如果是在周六或周日,则是在刚刚结束的第52周。

  

它有12月28日。因此,上周最早可能从12月21日星期一延长至1月28日星期日,最后一周可能从12月28日星期一延长至1月3日星期日(下一个格里高利年)。

因此,如果您想查看一年中的周数,您可以只请求YYYY-12-28的周数。如果你在Ruby中这样做,它将如下所示:

2014.upto(2016) do |i|
  date = Date.new(i, 12, 28)
  puts "#{date} - in week #{date.cweek}"
end

# 2014-12-28 - in week 52
# 2015-12-28 - in week 53
# 2016-12-28 - in week 52

正如您所看到的,2016年没有53周,所以您获得的日期可能是错误的。我建议您联系您的API提供商,因为我认为推出自己处理此案例的方法并不明智。

如果您需要推出自己的解决方案,可以使用看起来像这样的东西(来自tompave示例的正则表达式):

class MyCustomDateParser
  FORMAT = /\A(?<year>\d{4})-W(?<week>\d{2})\z/

  def self.parse!(date)
    raise ArgumentError, "invalid date" unless m = date.match(FORMAT)
    Date.parse(date)
  rescue ArgumentError => e
    fail e unless m[2] == "53"
    Date.parse("#{m[1]}-W52")
  end
end