File Coverage

blib/lib/Labyrinth/DTUtils.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package Labyrinth::DTUtils;
2              
3 7     7   15849 use warnings;
  7         8  
  7         219  
4 7     7   24 use strict;
  7         8  
  7         188  
5              
6 7     7   22 use vars qw($VERSION @ISA %EXPORT_TAGS @EXPORT @EXPORT_OK);
  7         7  
  7         797  
7             $VERSION = '5.31';
8              
9             =head1 NAME
10              
11             Labyrinth::DTUtils - Date & Time Utilities for Labyrinth
12              
13             =head1 SYNOPSIS
14              
15             use Labyrinth::DTUtils;
16              
17             =head1 DESCRIPTION
18              
19             Various date & time utilities.
20              
21             =head1 EXPORT
22              
23             everything
24              
25             =cut
26              
27             # -------------------------------------
28             # Export Details
29              
30             require Exporter;
31             @ISA = qw(Exporter);
32              
33             %EXPORT_TAGS = (
34             'all' => [ qw(
35             DaySelect MonthSelect YearSelect PeriodSelect
36             formatDate unformatDate isMonth
37             ) ]
38             );
39              
40             @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
41             @EXPORT = ( @{ $EXPORT_TAGS{'all'} } );
42              
43             #############################################################################
44             #Libraries
45             #############################################################################
46              
47 7     7   5487 use DateTime;
  7         739917  
  7         244  
48 7     7   3492 use Time::Local;
  7         9148  
  7         388  
49 7     7   360 use Labyrinth::Audit;
  7         14  
  7         999  
50 7     7   3510 use Labyrinth::MLUtils;
  0            
  0            
51             use Labyrinth::Variables;
52              
53             #############################################################################
54             #Variables
55             #############################################################################
56              
57             my @months = (
58             { 'id' => 1, 'value' => "January", },
59             { 'id' => 2, 'value' => "February", },
60             { 'id' => 3, 'value' => "March", },
61             { 'id' => 4, 'value' => "April", },
62             { 'id' => 5, 'value' => "May", },
63             { 'id' => 6, 'value' => "June", },
64             { 'id' => 7, 'value' => "July", },
65             { 'id' => 8, 'value' => "August", },
66             { 'id' => 9, 'value' => "September", },
67             { 'id' => 10, 'value' => "October", },
68             { 'id' => 11, 'value' => "November", },
69             { 'id' => 12, 'value' => "December" },
70             );
71              
72             my @dotw = ( "Sunday", "Monday", "Tuesday", "Wednesday",
73             "Thursday", "Friday", "Saturday" );
74              
75             my @days = map {{'id'=>$_,'value'=> $_}} (1..31);
76             my @periods = (
77             {act => 'evnt-month', value => 'Month'},
78             {act => 'evnt-week', value => 'Week'},
79             {act => 'evnt-day', value => 'Day'}
80             );
81              
82             my %formats = (
83             1 => 'YYYY',
84             2 => 'MONTH YYYY',
85             3 => 'DD/MM/YYYY',
86             4 => 'DABV MABV DD TIME24 YYYY',
87             5 => 'DAY, DD MONTH YYYY',
88             6 => 'DAY, DDEXT MONTH YYYY',
89             7 => 'DAY, DD MONTH YYYY (TIME12)',
90             8 => 'DAY, DDEXT MONTH YYYY (TIME12)',
91             9 => 'YYYY/MM/DD',
92             10 => 'DDEXT MONTH YYYY',
93             11 => 'YYYYMMDDThhmmss', # iCal date string
94             12 => 'YYYY-MM-DDThh:mm:ssZ', # RSS date string
95             13 => 'YYYYMMDD', # backwards date
96             14 => 'DABV, DDEXT MONTH YYYY',
97             15 => 'DD MABV YYYY',
98             16 => 'DABV, dd MABV YYYY hh:mm:ss TZ', # RFC-822 date string
99             17 => 'DAY, DD MONTH YYYY hh:mm:ss',
100             18 => 'DD/MM/YYYY hh:mm:ss',
101             19 => 'DDEXT MONTH YYYY',
102             20 => 'DABV, DD MABV YYYY hh:mm:ss',
103             21 => 'YYYY-MM-DD hh:mm:ss',
104             22 => 'YYYYMMDDhhmm',
105             );
106              
107             my %unformats = (
108             11 => '(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})', # iCal date string
109             12 => '(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z', # ISO 8601 date string
110             13 => '(\d{4})(\d{2})(\d{2})', # backwards date
111             22 => '(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})',
112             );
113              
114             # decrees whether the date format above should be UTC
115             # time based, or allow for any Summer Time variations.
116             my %zonetime = (12 => 1, 16 => 1);
117              
118             #############################################################################
119             #Subroutines
120             #############################################################################
121              
122             =head1 FUNCTIONS
123              
124             =head2 Dropdown Boxes
125              
126             =over 4
127              
128             =item DaySelect($opt,$blank)
129              
130             Provides a Day dropdown selection box.
131              
132             The option $opt allows the given day (numerical 1 - 31) to be the selected
133             option in the dropdown. If blank is true, a 'Select Day' option is added as
134             the first option to the dropdown.
135              
136             =item MonthSelect($opt,$blank)
137              
138             Provides a Month dropdown selection box.
139              
140             The option $opt allows the given month (numerical 1 - 12) to be the selected
141             option in the dropdown. If blank is true, a 'Select Month' option is added as
142             the first option to the dropdown.
143              
144             =item YearSelect($opt,$range,$blank,$dates)
145              
146             Provides a Year dropdown selection box.
147              
148             The option $opt allows the given month (numerical 1 - 12) to be the selected
149             option in the dropdown. If blank is true, a 'Select Month' option is added as
150             the first option to the dropdown.
151              
152             If is specified, then the following criteria is used:
153              
154             0 - default
155             1 - given dates, see $dates list
156             2 - oldest year to current year
157             3 - current year to future year
158              
159             For oldest year, this is determined by the configuration setting
160             'year_past_offset' or 'year_past'. For the future year, this is determined by
161             the configuration setting 'year_future_offset'.
162              
163             If the range is set to 1, the list of dates given in the $dates array
164             reference will be used.
165              
166             =item PeriodSelect($opt,$blank)
167              
168             Provides a Period dropdown selection box.
169              
170             The option $opt allows the given period to be the selected option in the
171             dropdown. If blank is true, a 'Select Period' option is added as the first
172             option to the dropdown.
173              
174             Current valid periods are:
175              
176             opt value
177             -------------------
178             evnt-month Month
179             evnt-week Week
180             evnt-day Day
181              
182             =back
183              
184             =cut
185              
186             sub DaySelect {
187             my ($opt,$blank) = @_;
188             my @list = @days;
189             unshift @list, {id=>0,value=>'Select Day'} if(defined $blank && $blank == 1);
190             DropDownRows($opt,'day','id','value',@list);
191             }
192              
193             sub MonthSelect {
194             my ($opt,$blank) = @_;
195             my @list = @months;
196             unshift @list, {id=>0,value=>'Select Month'} if(defined $blank && $blank == 1);
197             DropDownRows($opt,'month','id','value',@list);
198             }
199              
200             sub YearSelect {
201             my ($opt,$range,$blank,$dates) = @_;
202             my $year = formatDate(1);
203            
204             my $past_offset = $settings{year_past_offset} || 0;
205             my $future_offset = defined $settings{year_future_offset} ? $settings{year_future_offset} : 4;
206             my $past = $past_offset ? $year - $past_offset : $settings{year_past};
207             my $future = $year + $future_offset;
208             $past ||= $year;
209              
210             my @range = ($past .. $future);
211             if(defined $range) {
212             if($range == 1) { @range = @$dates }
213             elsif($range == 2) { @range = ($past .. $year) }
214             elsif($range == 3) { @range = ($year .. $future) }
215             }
216              
217             my @years = map {{'id'=>$_,'value'=> $_}} @range;
218             unshift @years, {id=>0,value=>'Select Year'} if(defined $blank && $blank == 1);
219             DropDownRows($opt,'year','id','value',@years);
220             }
221              
222             sub PeriodSelect {
223             my ($opt,$blank) = @_;
224             my @list = @periods;
225             unshift @list, {act=>'',value=>'Select Period'} if(defined $blank && $blank == 1);
226             DropDownRowsText($opt,'period','act','value',@list);
227             }
228              
229             ## ------------------------------------
230             ## Date Functions
231              
232             =head2 Date Formatting
233              
234             =over 4
235              
236             =item formatDate
237              
238             =item unformatDate
239              
240             =item isMonth
241              
242             =back
243              
244             =cut
245              
246             sub formatDate {
247             my ($format,$time) = @_;
248             my $now = $time ? 0 : 1;
249              
250             my $dt;
251             my $timezone = $settings{timezone} || 'Europe/London';
252             if($time) {
253             $dt = DateTime->from_epoch( epoch => $time, time_zone => $timezone );
254             } else {
255             $dt = DateTime->now( time_zone => $timezone );
256             }
257              
258             return $dt->epoch unless($format);
259              
260             #LogDebug("formatDate format=$format, time=".$dt->epoch);
261              
262             # create date mini strings
263             my $fmonth = $dt->month_name;
264             my $amonth = $dt->month_abbr;
265             my $fdotw = $dt->day_name;
266             my $adotw = $dt->day_abbr;
267             my $fsday = sprintf "%d", $dt->day; # short form, ie 6
268             my $fday = sprintf "%02d", $dt->day; # long form, ie 06
269             my $fmon = sprintf "%02d", $dt->month;
270             my $fyear = sprintf "%04d", $dt->year;
271             my $fddext = sprintf "%d%s", $dt->day, _ext($dt->day);
272             my $time12 = sprintf "%d:%02d%s", $dt->hour_12, $dt->minute, lc $dt->am_or_pm;
273             my $time24 = sprintf "%d:%02d:%02d", $dt->hour, $dt->minute, $dt->second;
274             my $fhour = sprintf "%02d", $dt->hour;
275             my $fminute = sprintf "%02d", $dt->minute;
276             my $fsecond = sprintf "%02d", $dt->second;
277             my $tz = 'UTC';
278             eval { $tz = $dt->time_zone->short_name_for_datetime };
279              
280             my $fmt = $formats{$format};
281              
282             # transpose format string into a date string
283             $fmt =~ s/hh/$fhour/;
284             $fmt =~ s/mm/$fminute/;
285             $fmt =~ s/ss/$fsecond/;
286             $fmt =~ s/DMY/$fday-$fmon-$fyear/;
287             $fmt =~ s/MDY/$fmon-$fday-$fyear/;
288             $fmt =~ s/YMD/$fyear-$fmon-$fday/;
289             $fmt =~ s/MABV/$amonth/;
290             $fmt =~ s/DABV/$adotw/;
291             $fmt =~ s/MONTH/$fmonth/;
292             $fmt =~ s/DAY/$fdotw/;
293             $fmt =~ s/DDEXT/$fddext/;
294             $fmt =~ s/YYYY/$fyear/;
295             $fmt =~ s/MM/$fmon/;
296             $fmt =~ s/DD/$fday/;
297             $fmt =~ s/dd/$fsday/;
298             $fmt =~ s/TIME12/$time12/;
299             $fmt =~ s/TIME24/$time24/;
300             $fmt =~ s/TZ/$tz/;
301              
302             return $fmt;
303             }
304              
305             sub unformatDate {
306             my ($format,$time) = @_;
307              
308             return time unless($format && $time);
309              
310             my (@fields,@values);
311             my @basic = qw(ss mm hh DD MM YYYY);
312             my %forms = map {$_ => 0 } @basic, 'dd';
313              
314             if($unformats{$format}) {
315             @fields = reverse @basic;
316             @values = $time =~ /$unformats{$format}/;
317             } else {
318             my $pattern = $formats{$format};
319             $pattern =~ s!TIME24!hh::mm:ss!;
320             $pattern =~ s!TIME12!hh::ampm!;
321              
322             @fields = split(qr![ ,/:()-]+!,$pattern);
323             @values = split(qr![ ,/:()-]+!,$time);
324             }
325              
326             @forms{@fields} = @values;
327             $forms{$_} = int($forms{$_}||0) for(@basic);
328              
329             #use Data::Dumper;
330             #LogDebug("format=[$format], time=[$time]");
331             #LogDebug("fields=[@fields], values=[@values]");
332             #LogDebug("before=".Dumper(\%forms));
333              
334             ($forms{DD}) = $forms{dd} =~ /(\d+)/ if($forms{dd});
335             ($forms{DD}) = $forms{DDEXT} =~ /(\d+)/ if($forms{DDEXT});
336             $forms{MM} = isMonth($forms{MONTH}) if($forms{MONTH});
337             $forms{MM} = isMonth($forms{MABV}) if($forms{MABV});
338             ($forms{mm},$forms{AMPM}) = ($forms{ampm} =~ /(\d+)(am|pm)/) if($forms{ampm});
339             $forms{hh}+=12 if($forms{AMPM} && $forms{AMPM} eq 'pm');
340              
341             @values = map {$forms{$_}||0} @basic;
342              
343             my $timezone = $settings{timezone} || 'Europe/London';
344             my $dt = DateTime->new(
345             year => $values[5], month => $values[4] || 1, day => $values[3] || 1,
346             hour => $values[2], minute => $values[1], second => $values[0],
347             time_zone => $timezone );
348              
349             return $dt->epoch;
350             }
351              
352             sub _ext {
353             my $day = shift;
354             my $ext = "th";
355             if($day == 1 || $day == 21 || $day == 31) { $ext = "st" }
356             elsif($day == 2 || $day == 22) { $ext = "nd" }
357             elsif($day == 3 || $day == 23) { $ext = "rd" }
358             return $ext;
359             }
360              
361             sub isMonth {
362             my $month = shift;
363             return (localtime)[4]+1 unless(defined $month && $month);
364              
365             foreach (@months) {
366             return $_->{id} if($_->{value} =~ /$month/);
367             return $_->{value} if($month eq $_->{id});
368             }
369             return 0;
370             }
371              
372             1;
373              
374             __END__