I've released Date::WeekNumber, which provides two functions for generating week numbers in the format 2014-W09. This is based on code I wrote while creating the CPAN new dist a month contest. I approached the creation of this module slightly differently from my norm, writing the documentation first.
The key thing I wanted is illustrated with this year boundary:
December 2013 January 2014
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 7 1 2 3 4
8 9 10 11 12 13 14 5 6 7 8 9 10 11
15 16 17 18 19 20 21 12 13 14 15 16 17 18
22 23 24 25 26 27 28 19 20 21 22 23 24 25
29 30 31 26 27 28 29 30 31
The key requirement for a weeks function is:
week('2013-12-31') eq week(2014-01-01')
ISO 8601 defines weeks as starting on Monday, but the once a week contest has weeks starting on Sunday. So I decided my new module would provide two functions: one for ISO weeks, and one for 'CPAN weeks'.
I started by writing the section of documentation that described the week numbering schemes, including calendar extracts that showed the interesting year-boundary cases.
That naturally led to some test cases. I stubbed out the functions so I could run the tests and see them fail.
When writing my earlier code I'd had a quick look at CPAN, but now I had a more determined look for modules that had support for weeks, thinking I still might find a module that did what I wanted. None of them could do CPAN weeks any easier than my existing code, and most of them needed several lines to produce the CPAN week.
So, I was still going to create a new module. I created a SEE ALSO section based on the modules I'd found, and wrote a script to run a bake-off between them. This revealed that Date::WeekOfYear sometimes gets the date wrong (bug submitted).
I wrote the first versions of iso_week_number()
and
cpan_week_number()
using POSIX (thanks BOOK for %U
),
and released the module to CPAN.
The following is illustrative only (strftime's interface
isn't nearly so sane, for a start):
sub iso_week_number {
my ($year, $month, $day) = @_;
return POSIX::strftime('%G-W%V', $year, $month, $day);
}
sub cpan_week_number {
my ($year, $month, $day) = @_;
$week = POSIX::strftime('%U', $year, $month, $day);
# week 0 means the last week of the previous year
if ($week == 0) {
$year--;
$week = POSIX::strftime('%U', $year, 12, 31);
}
return sprintf('%.4d-W%.2d', $year, $week);
}
I also decided to try for some DWIMmery, so you can pass the date in one of 5 different formats.
At first blush,
CPAN Testers
seemed happy.
But the next day I discovered that all Windows tests were failing.
Turns out strftime's %G
and %V
don't work on Windows. WTF?!
So the next release used POSIX for cpan_week_number()
and Date::Calc for iso_week_number()
, with each function
requiring the module it uses. All looks good so far.
Next I'll benchmark the different modules I could have used, to
see which introduces the most dependencies, takes up most memory etc.
I could have used DateTime for both, but I think of it as a bit
of a bruiser. I figure any given user will want either
iso_week_number()
or cpan_week_number()
, and so will only
pull in one of POSIX or Date::Calc.