Reputation:
I am working with an api that returns a date string referencing the 53rd week for the year 2016.
I've found that the only calendar that works when parsing it is JULIAN
.
However, Date.parse("2016-W53", true, Date::JULIAN)
returns Mon, 27 Dec 2016
, which is the wrong date as it should be Mon, 26 Dec 2016
.
Why is this happening?
How can I get it to return the correct date?
Upvotes: 1
Views: 282
Reputation: 1767
The Julian Calendar is an entirely different calendar; on the Julian calendar, 12/27 is a Monday, because on the Julian calendar, January 1st, 2016 was a Thursday whereas on the Gregorian calendar it was a Friday.
Normally, I'd say use Date.strptime('2016-W53','%G-W%V')
... however that appears not to work. According to the documentation, when counting weeks of the year, Ruby uses the ISO 8601 week-based year and week number:
ISO 8601 week-based year and week number:
The week 1 of YYYY starts with a Monday and includes YYYY-01-04.
The days in the year before the first week are in the last week of the previous year.
%G - The week-based year
%V - Week number of the week-based year (01..53)
%g - The last 2 digits of the week-based year (00..99)
As stated, the first week of the year in this system is the week that contains January 4th; in the Julian calendar, Sunday the 4th was contained in the same week that contained January 1st; however, in the Gregorian calendar, the 4th was a Monday, and so that marked the first week that 'counts' as part of 2016 - the preceding week was the 53rd week of 2015.
The reason for this is (presumably) that one shouldn't count a week as being both the first week of 2016 and also the last week of 2015; because then if you wanted a period of, say, 10 weeks, you'd end up counting that week twice. The reason January 4th was chosen as a marker was presumably because if the week of January 1st includes January 4th, then the majority of that 7-day period falls in the new year, so the week counts as part of the new year.
Given the above, there is no 53rd week of 2016, at least per ISO rules for counting weeks. You could implement your own system for figuring out what date was meant, but you'd have to ask the provider of the API you're consuming how they're generating those numbers, otherwise you wouldn't know whether you were interpreting it correctly.
Upvotes: 1
Reputation: 711
Regarding last week according to Wikipedia:
If 31 December is on a Monday, Tuesday or Wednesday, it is in week 01 of the next year. If it is on a Thursday, it is in week 53 of the year just ending; if on a Friday it is in week 52 (or 53 if the year just ending is a leap year); if on a Saturday or Sunday, it is in week 52 of the year just ending.
and:
It has 28 December in it. Hence the earliest possible last week extends from Monday 21 December to Sunday 28 January, the latest possible last week extends from Monday 28 December to Sunday 3 January (next gregorian year).
So if you want to see how many weeks there in a year, you could just request the week number for YYYY-12-28
. If you do this in Ruby, it would look like this:
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
As you can see there aren't 53 weeks in 2016 so the date you're getting is probably wrong. I'd suggest to contact your API provider since I don't think it's wise to roll out your own method for handling this case.
In case you necessary need to roll out your own solution, you could go with something that looks like this (regex from tompave's example):
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
Upvotes: 0
Reputation: 12427
You should not interpret the date with the Julian calendar, as it does not correct the periodic "drift" in time that is accounted for in the more recent Gregorian calendar, which we use nowadays in the western countries.
Date.parse
should work if the input is valid, and it looks like yours isn't:
Date.parse("2016-W53")
# ArgumentError: invalid date
Date.parse("2020-W53")
# <Date: 2020-12-28 ((2459212j,0s,0n),+0s,2299161j)>
I would say that if the default parse
methods from the standard library can't make sense of that value, you should write your own parser.
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)>
Upvotes: 0