File Coverage

blib/lib/Date/WeekOfYear.pm
Criterion Covered Total %
statement 77 77 100.0
branch 31 34 91.1
condition 22 24 91.6
subroutine 14 14 100.0
pod 8 8 100.0
total 152 157 96.8


line stmt bran cond sub pod time code
1             #
2             # WeekOfYear.pm
3             #
4             # Synopsis: see POD at end of file
5             #
6             package Date::WeekOfYear;
7            
8 1     1   67601 use strict;
  1         2  
  1         27  
9 1     1   5 use warnings;
  1         2  
  1         23  
10 1     1   477 use Time::Local;
  1         2329  
  1         57  
11 1     1   7 use parent 'Exporter';
  1         2  
  1         4  
12 1     1   596 use integer; # Integer math, so we don't need floor
  1         14  
  1         5  
13            
14             our $VERSION = '1.07';
15            
16             our @ISA = qw(Exporter);
17             our %EXPORT_TAGS = (
18             'mode' => [ qw( WeekOfYear WOY_OLD_MODE WOY_ISO_MODE ) ],
19             'all' => [ qw( WeekOfYear WOY_OLD_MODE WOY_ISO_MODE is_leap_year day_of_year jan1week_day WeekOfYear week_day week_number ) ],
20             );
21             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
22             our @EXPORT = qw( WeekOfYear );
23            
24             # Weekday constants
25             use constant {
26 1         842 SUNDAY => 0,
27             MONDAY => 1,
28             TUESDAY => 2,
29             WEDNESDAY => 3,
30             THURSDAY => 4,
31             FRIDAY => 5,
32             SATURDAY => 6,
33 1     1   124 };
  1         2  
34            
35             # Pseudo constants
36 192     192 1 2256 sub WOY_OLD_MODE { 1 }
37 365     365 1 966 sub WOY_ISO_MODE { 2 }
38            
39            
40             sub is_leap_year
41             {
42              
43 464     464 1 587 my $year = shift; # eg 2014
44            
45             # See POD for details of the algorithm.
46 464 100 100     1607 my $is_ly = ((($year % 4 == 0) && ($year % 100 != 0)) || ($year % 400 == 0)) ? 1 : 0;
47             #print STDERR "is_ly=$is_ly $year\n";
48            
49 464         841 return $is_ly;
50             }
51            
52             sub day_of_year
53             {
54             # Return the day of the year, 1 being the first day (unlike localtime()) based on day of month and month
55 356     356 1 544 my ($year, $month, $day) = @_; # year is YYYY (eg 2014), month is 1-12, Jan=1, day is day of month
56            
57             # Days to mth start Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
58 356         668 my @days_in_month = (undef, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334);
59            
60 356         504 my $doy = $day + $days_in_month[$month];
61            
62             # Need to adjust for leap year if after Feb
63 356 100 100     694 $doy++ if ($month > 2 && is_leap_year($year));
64            
65 356         608 return $doy;
66             }
67            
68             sub jan1week_day
69             {
70 363     363 1 557 my ($year) = @_; # year is YYYY (eg 2014)
71            
72             # Here weekday 1=Mon, 2=Tue, 7=Sun (not 0 as localtime())
73 363         501 my $yy = ($year - 1) % 100;
74 363         616 my $jan1wd = 1 + (((($year -1 - $yy) / 100 ) % 4) * 5 + $yy + $yy/4) % 7;
75            
76 363         528 return $jan1wd;
77             }
78            
79             sub week_day
80             {
81             # Here weekday 1=Mon, 2=Tue, 3=Wed, 4=Thu,...7=Sun (not 0 as localtime())
82 178     178 1 262 my ($year, $month, $day) = @_; # year is YYYY (eg 2014), month is 1-12, Jan=1, day is day of month
83            
84 178         281 my $doy = day_of_year($year, $month, $day);
85 178         314 my $jan1wd = jan1week_day($year, $month, $day);
86            
87 178         275 my $wd = 1 + (($doy + $jan1wd - 2) % 7);
88            
89 178         267 return $wd;
90             }
91            
92             sub week_number
93             {
94 178     178 1 331 my ($year, $month, $day) = @_; # year is YYYY (eg 2014), month is 1-12, Jan=1, day is day of month
95            
96 178         242 my $year_number = $year;
97 178         230 my $week_number;
98            
99            
100 178         309 my $is_leap_y = is_leap_year($year);
101 178         311 my $is_leap_prev_y = is_leap_year($year - 1);
102 178         336 my $doy = day_of_year($year, $month, $day);
103 178         315 my $jan1wd = jan1week_day($year, $month, $day);
104 178         314 my $wd = week_day($year, $month, $day);
105            
106             #print STDERR "year=$year, month=$month, day=$day, is_leap_y=$is_leap_y, is_leap_prev_y=$is_leap_prev_y, doy=$doy, jan1wd=$jan1wd, wd=$wd\n";
107            
108             # Does YYYYMMDD fall in year YYYY-1, weeknumber 52 or 53
109 178 100 100     458 if ($doy <= (8 - $jan1wd) && $jan1wd > 4)
110             {
111 46         67 $year_number--;
112 46 100 100     163 if ($jan1wd == 5 || ($jan1wd == 6 && $is_leap_prev_y))
      100        
113             {
114 10         16 $week_number = 53;
115             }
116             else
117             {
118 36         50 $week_number = 52;
119             }
120             }
121            
122             # Does YYYYMMDD fall in YYYY+1, weeknumber 1
123 178 100       314 if ($year_number == $year)
124             {
125 132 100       251 my $days_in_year = $is_leap_y ? 366 : 365;
126            
127 132 100       270 if (($days_in_year - $doy) < (4 - $wd))
128             {
129 8         11 $year_number++;
130 8         13 $week_number = 1;
131             }
132             }
133            
134             # Does YYYYMMDD fall in YYYY weeknumber 1 -> 53
135 178 100       338 if ($year_number == $year)
136             {
137 124         179 $week_number = ($doy + 6 - $wd + $jan1wd)/7;
138 124 100       229 $week_number-- if ($jan1wd > 4);
139             }
140            
141 178         315 return ($week_number, $year_number);
142             }
143            
144             sub WeekOfYear
145             {
146 185     185 1 143931 my ($time, $mode) = @_;
147            
148             # Make sure we have a mode
149 185 100       465 $mode = 0 unless defined $mode;
150            
151 185         260 my ($tm_day, $tm_mth, $tm_year, $wkday, $yrday);
152            
153             # Post version 1.4 can be passed a hash ref for the time
154             # The hash ref must have a year, month and day
155             # This allows working past or before dates that can be handled by localtime
156 185 100 100     698 if (($mode == 0 || $mode == WOY_ISO_MODE) && ref($time) eq 'HASH')
      100        
157             {
158 2         4 $tm_day = $time->{day};
159 2         4 $tm_mth = $time->{month} - 1;
160 2         3 $tm_year = $time->{year} - 1900;
161             }
162             else
163             {
164             # Set to the current time if nothing provided
165 183 50 33     1159 $time = time unless (defined($time) && $time =~ /^\s*\d+\s*$/);
166            
167             # wkday is the day of the week, 0=Sunday, 1=Monday.. 4=Thursday
168 183         3246 ($tm_day, $tm_mth, $tm_year, $wkday, $yrday) = (localtime($time))[3..7];
169             }
170            
171 185         503 my $wkNo;
172            
173 185 100       381 if ($mode == WOY_OLD_MODE)
174             {
175             # Pre version 1.4 functionality
176            
177             # What is the week day for 1 Jan of the year in question
178 7         16 my ($soywkday) = jan1week_day($tm_year + 1900);
179            
180 7 100       18 $wkNo = int($yrday / 7) + 1 + (($wkday < $soywkday)? 1:0);
181            
182 7 50       51 return wantarray ? ($wkNo, $tm_year + 1900) : $wkNo;
183             }
184             else
185             {
186 178         440 my ($w, $y) = week_number($tm_year + 1900, $tm_mth + 1, $tm_day);
187            
188 178 100       322 if ($mode == WOY_ISO_MODE)
189             {
190             # YYYY-WXX where YYYY is the year, W denotes the week, and XX is the week number, eg 1970-W53
191 90         542 return sprintf('%d-W%02d', $y, $w);
192             }
193             else
194             {
195             # The new default output
196 88 50       366 return wantarray ? ($w, $y) : $w;
197             }
198             }
199             }
200            
201            
202             1;
203             __END__