File Coverage

blib/lib/No/Dato.pm
Criterion Covered Total %
statement 88 98 89.8
branch 29 38 76.3
condition 11 18 61.1
subroutine 12 12 100.0
pod 4 7 57.1
total 144 173 83.2


line stmt bran cond sub pod time code
1             package No::Dato;
2              
3 1     1   14577 use Time::Local qw(timelocal);
  1         2891  
  1         97  
4 1     1   9 use Carp ();
  1         2  
  1         45  
5              
6             require Exporter;
7             @ISA=qw(Exporter);
8             @EXPORT_OK = qw(tekstdato helligdag hverdag helligdager @UKEDAGER @MANEDER);
9              
10 1     1   6 use strict;
  1         7  
  1         30  
11 1     1   387 use vars qw(%SPECIAL_DAYS @UKEDAGER @MANEDER $VERSION);
  1         10  
  1         1314  
12              
13             $VERSION = sprintf("%d.%02d", q$Revision: 1.10 $ =~ /(\d+)\.(\d+)/);
14              
15              
16             =head1 NAME
17              
18             No::Dato - Norwegian dates
19              
20             =head1 SYNOPSIS
21              
22             use No::Dato qw(tekstdato helligdag helligdager);
23              
24             print tekstdato(time), "\n";
25             if (helligdag(time)) {
26             print "Idag er det ", helligdag(time), "\n";
27             }
28              
29             for (helligdager()) {
30             print "$_\n";
31             }
32              
33              
34             =head1 DESCRIPTION
35              
36             B
37              
38             Denne modulen tilbyr funksjoner for å håndtere det som er spesielt med
39             datoer på norsk. Dette gjelder blant annet å identifisere offentlige
40             høytidsdager.
41              
42             Følgende funksjoner er tilgjengelig:
43              
44             =over
45              
46             =cut
47              
48              
49              
50              
51             %SPECIAL_DAYS = (
52             "nyttårsdag" => '01-01',
53             "1. mai" => '05-01',
54             "grunnlovsdag" => '05-17',
55             "juledag" => '12-25',
56             "2. juledag" => '12-26',
57              
58             # relative to easter day
59             "skjærtorsdag" => -3,
60             "langfredag" => -2,
61             "påskedag" => 0,
62             "2. påskedag" => +1,
63             "kristi himmelfartsdag" => +39,
64             "pinsedag" => +49,
65             "2. pinsedag" => +50,
66             );
67              
68             @UKEDAGER = qw(søndag mandag tirsdag onsdag torsdag fredag lørdag);
69             @MANEDER = qw(januar februar mars april mai juni
70             juli august september oktober november desember);
71              
72             my %hellig_cache = ();
73              
74              
75             =item tekstdato($time)
76              
77             Denne rutinen returnerer en dato formatert på formen:
78              
79             fredag, 7. februar 2004
80              
81             Argumentet er en vanlig perl $time verdi. Hvis argumentet utelates så
82             benyttes dagens dato.
83              
84             =cut
85              
86             sub tekstdato (;$)
87             {
88 2   66 2 1 156 my $time = shift || time;
89 2         37 my($d,$m,$y,$wd) = (localtime $time)[3,4,5,6];
90 2         18 sprintf "%s, %d. %s %d", $UKEDAGER[$wd], $d, $MANEDER[$m], $y+1900;
91             }
92              
93              
94             =item helligdag($time)
95              
96             Rutinen avgjør om en gitt dato er en norsk helligdag eller ikke. Hvis
97             det er en helligdag så vil navnet på helligdagen bli returnert. Hvis
98             det er en vanlig hverdag eller lørdag så vil en tom streng (som er
99             FALSE i perl) bli returnert.
100              
101             Argumentet kan være en vanlig $time verdi eller en streng på formen
102             "ÅÅÅÅ-MM-DD".
103              
104             For denne funksjonen er "helligdag" definert til å være det samme som
105             norsk offentlig høytidsdag samt søndager, dvs de dagene som er røde på
106             kalenderen. Dette inkluderer nyttårsdagen, samt 1. og 17. mai selv om
107             disse egentlig ikke er hellige.
108              
109             =cut
110              
111             sub helligdag (;$$)
112             {
113 21   66 21 1 145 my $date = shift || time;
114 21         23 my $year;
115             my $weekday;
116 21 100       134 if ($date =~ /^\d+$/) {
    50          
117 1         17 my($d,$m,$y,$w) = (localtime $date)[3,4,5,6];
118 1         4 $year = $y+1900;
119 1         2 $weekday = $w;
120 1         5 $date = sprintf "%02d-%02d", $m+1, $d;
121             } elsif ($date =~ s/^(\d{4})-(\d\d-\d\d)$/$2/) {
122 20         32 $year = $1;
123             } else {
124 0         0 Carp::croak("Bad date '$date'");
125             }
126 21 100       55 helligdager($year) unless exists $hellig_cache{$year};
127 21         22 my $day = "";
128 21 100       39 if (exists $hellig_cache{$year}{$date}) {
129 12         19 $day = $hellig_cache{$year}{$date};
130             } else {
131             # sjekk om det er søndag
132 9 100       19 unless (defined $weekday) {
133 8         22 my($m, $d) = split(/-/, $date);
134 8         27 $weekday = (localtime(timelocal(12,0,0,$d, $m-1, $year-1900)))[6];
135             }
136 9 50       491 $day = "søndag" if $weekday == 0;
137 9 100 66     26 $day = "lørdag" if $weekday == 6 && $_[0];
138             }
139 21         43 $day;
140             }
141              
142             =item hverdag($time)
143              
144             Rutinen avgjør om en gitt date er en hverdag eller ikke. Lørdag er
145             her ikke regnet som hverdag.
146              
147             Argumentet kan være en vanlig $time verdi eller en streng på formen
148             "ÅÅÅÅ-MM-DD".
149              
150             =cut
151              
152             sub hverdag {
153 6     6 1 51 return !helligdag(shift, 1);
154             }
155              
156             =item helligdager($year)
157              
158             Denne rutinen vil returnere en liste av datostrenger, én for hver
159             helligdag i året gitt som argument. Hvis argumentet mangler vil vi
160             bruke inneværende år. Datostrengene er på formen:
161              
162             "ÅÅÅÅ-MM-DD skjærtorsdag"
163              
164             Dvs. datoen formatert i henhold til ISO 8601 etterfulgt av navnet på
165             helligdagen. Listen vil være sortert på dato.
166              
167             For denne funksjonen er "helligdag" definert til å være det samme som
168             norsk offentlig høytidsdag. Søndagene er ikke tatt med selv om
169             funksjonen helligdag(), beskrevet over, er TRUE for disse.
170              
171             =cut
172              
173             sub helligdager (;$)
174             {
175 4   66 4 1 30 my $year = shift || (localtime)[5] + 1900;
176              
177 4 100       10 unless (exists $hellig_cache{$year}) {
178 3         7 my $easter = easter_day($year);
179              
180 3         4 my ($text, $date);
181 3         12 while (($text, $date) = each %SPECIAL_DAYS) {
182 36         37 my($month, $mday);
183 36 100       93 if ($date =~ /^(\d+)-(\d+)$/) {
184             # a fixed date
185 15         29 ($month, $mday) = ($1, $2);
186             } else {
187 21         38 ($month, $mday) = dayno_to_date($year, $easter + $date);
188             }
189 36         175 $hellig_cache{$year}{sprintf "%02d-%02d", $month, $mday} = $text;
190             }
191             }
192              
193             # we want to return a sorted array
194 4         6 my @days;
195 4         5 for (sort keys %{$hellig_cache{$year}}) {
  4         40  
196 48         104 push(@days, "$year-$_ $hellig_cache{$year}{$_}");
197             }
198 4         17 @days;
199             }
200              
201              
202              
203             sub easter_day ($)
204             {
205 1     1   1646 use integer;
  1         83  
  1         6  
206             # The algoritm is taken from LaTeX calendar macros by C. E. Chew, which
207             # has taken the algoritm from "The Calculation of Easter", D.E.Knuth,
208             # CACM April 1962 p 209.
209              
210 3     3 0 5 my $year = shift;
211 3         4 my $golden; # year in Mentonic cycle
212             my $easter; # easter sunday
213 0         0 my $grCor; # Gregorian correction
214 0         0 my $clCor; # Clavian correction
215 0         0 my $epact; # age of calendar moon at start of year
216 0         0 my $century;
217 0         0 my $extra; # when Sunday occurs in March
218              
219 3         7 $golden = ($year / 19) * -19 + $year + 1;
220 3 50       6 if ($year > 1582) {
221 3         5 $century = ($year / 100) + 1;
222 3         3 $grCor = ($century * 3) / -4 + 12;
223 3         6 $clCor = (($century - 18)/ -25 + $century - 16) / 3;
224 3         5 $extra = ($year * 5) / 4 + $grCor - 10;
225 3         4 $epact = $golden * 11 + 20 + $clCor + $grCor;
226 3         6 $epact += ($epact / 30) * -30;
227 3 50       7 $epact += 30 if $epact <= 0;
228 3 50       6 if ($epact == 25) {
229 0 0       0 $epact++ if $golden > 11;
230             } else {
231 3 50       8 $epact++ if $epact == 24;
232             }
233             } else { # year <= 1582
234 0         0 $extra = ($year * 5) / 4;
235 0         0 $epact = ($golden * 11) - 4;
236 0         0 $epact += ($epact / 30) * -30 + 1;
237             }
238 3         6 $easter = -$epact + 44;
239 3 100       6 $easter += 30 if $easter < 21;
240 3         4 $extra += $easter;
241 3         4 $extra += ($extra / 7) * -7;
242 3         3 $extra = -$extra;
243 3         4 $easter += $extra + 7;
244             # easter is now a date in march
245              
246             # convert to a dayno relative to 1. jan
247 3         4 $easter += 31 + 28; # days in january and february
248 3 100       7 $easter++ if leap_year($year);
249 3         5 $easter;
250             }
251              
252              
253             sub leap_year ($)
254             {
255 24     24 0 26 my $year = shift;
256 24 100 66     138 (($year % 4 == 0) && ($year % 100 != 0)) || ($year % 400 == 0);
257             }
258              
259              
260             sub dayno_to_date($$)
261             {
262 21     21 0 26 my($year, $dayno) = @_;
263 21         53 my @days_pr_month = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
264 21         22 my $maxdayno = 365;
265 21 100       50 if (leap_year($year)) {
266 7         7 $days_pr_month[1]++;
267 7         8 $maxdayno++;
268             }
269 21 50 33     80 die "Dayno $dayno out of range" if $dayno < 1 || $dayno > $maxdayno;
270              
271 21         19 my $month = 1;
272 21         46 while ($dayno > $days_pr_month[0]) {
273 70         62 $month++;
274 70         123 $dayno -= shift @days_pr_month;
275             }
276              
277 21         47 ($month, $dayno);
278             }
279              
280             1;
281             __END__