]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/Utils/DateTime.pm
LP#1552778: copy some date/time utils from OpenSRF
[Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / Utils / DateTime.pm
1 package OpenILS::Utils::DateTime;
2
3 use Time::Local;
4 use Errno;
5 use POSIX;
6 use FileHandle;
7 use Digest::MD5 qw(md5 md5_hex md5_base64);
8 use Exporter;
9 use DateTime;
10 use DateTime::Format::ISO8601;
11 use DateTime::TimeZone;
12
13 =head1 NAME
14
15 OpenILS::Utils::DateTime;
16
17 =head1 DESCRIPTION
18
19 This contains several routines for doing date and time calculation. This
20 is derived from the date/time routines from OpenSRF::Utils.
21
22 =head1 VERSION
23
24 =cut
25
26 our $VERSION = 1.000;
27
28 use vars qw/@ISA $AUTOLOAD %EXPORT_TAGS @EXPORT_OK @EXPORT/;
29 push @ISA, 'Exporter';
30
31 %EXPORT_TAGS = (
32         datetime        => [qw(clean_ISO8601 gmtime_ISO8601 interval_to_seconds seconds_to_interval)],
33 );
34 Exporter::export_ok_tags('datetime');  # add aa, cc and dd to @EXPORT_OK
35
36 our $date_parser = DateTime::Format::ISO8601->new;
37
38 =head1 METHODS
39
40
41 =cut
42
43 sub AUTOLOAD {
44         my $self = shift;
45         my $type = ref($self) or return undef;
46
47         my $name = $AUTOLOAD;
48         $name =~ s/.*://;   # strip fully-qualified portion
49
50         if (defined($_[0])) {
51                 return $self->{$name} = shift;
52         }
53         return $self->{$name};
54 }
55
56 =head2 $thing->interval_to_seconds('interval') OR interval_to_seconds('interval')
57
58 =head2 $thing->seconds_to_interval($seconds) OR seconds_to_interval($seconds)
59
60 Returns the number of seconds for any interval passed, or the interval for the seconds.
61 This is the generic version of B<interval> listed below.
62
63 The interval must match the regex I</\s*\+?\s*(\d+)\s*(\w{1})\w*\s*/g>, for example
64 B<2 weeks, 3 d and 1hour + 17 Months> or
65 B<1 year, 5 Months, 2 weeks, 3 days and 1 hour of seconds> meaning 46148400 seconds.
66
67         my $expire_time = time() + $thing->interval_to_seconds('17h 9m');
68
69 The time size indicator may be one of
70
71 =over 2
72
73 =item s[econd[s]]
74
75 for seconds
76
77 =item m[inute[s]]
78
79 for minutes
80
81 =item h[our[s]]
82
83 for hours
84
85 =item d[ay[s]]
86
87 for days
88
89 =item w[eek[s]]
90
91 for weeks
92
93 =item M[onth[s]]
94
95 for months (really (365 * 1d)/12 ... that may get smarter, though)
96
97 =item y[ear[s]]
98
99 for years (this is 365 * 1d)
100
101 =back
102
103 =cut
104 sub interval_to_seconds {
105         my $self = shift;
106         my $interval = shift || $self;
107
108         $interval =~ s/(\d{2}):(\d{2}):(\d{2})/ $1 h $2 min $3 s /go;
109
110         $interval =~ s/and/,/g;
111         $interval =~ s/,/ /g;
112
113         my $amount = 0;
114         while ($interval =~ /\s*([\+-]?)\s*(\d+)\s*(\w+)\s*/g) {
115                 my ($sign, $count, $type) = ($1, $2, $3);
116                 $count = "$sign$count" if ($sign);
117                 $amount += $count if ($type =~ /^s/);
118                 $amount += 60 * $count if ($type =~ /^m(?!o)/oi);
119                 $amount += 60 * 60 * $count if ($type =~ /^h/);
120                 $amount += 60 * 60 * 24 * $count if ($type =~ /^d/oi);
121                 $amount += 60 * 60 * 24 * 7 * $count if ($type =~ /^w/oi);
122                 $amount += ((60 * 60 * 24 * 365)/12) * $count if ($type =~ /^mo/io);
123                 $amount += 60 * 60 * 24 * 365 * $count if ($type =~ /^y/oi);
124         }
125         return $amount;
126 }
127
128 sub seconds_to_interval {
129         my $self = shift;
130         my $interval = shift || $self;
131
132         my $limit = shift || 's';
133         $limit =~ s/^(.)/$1/o;
134
135         my ($y,$ym,$M,$Mm,$w,$wm,$d,$dm,$h,$hm,$m,$mm,$s,$string);
136         my ($year, $month, $week, $day, $hour, $minute, $second) =
137                 ('year','Month','week','day', 'hour', 'minute', 'second');
138
139         if ($y = int($interval / (60 * 60 * 24 * 365))) {
140                 $string = "$y $year". ($y > 1 ? 's' : '');
141                 $ym = $interval % (60 * 60 * 24 * 365);
142         } else {
143                 $ym = $interval;
144         }
145         return $string if ($limit eq 'y');
146
147         if ($M = int($ym / ((60 * 60 * 24 * 365)/12))) {
148                 $string .= ($string ? ', ':'')."$M $month". ($M > 1 ? 's' : '');
149                 $Mm = $ym % ((60 * 60 * 24 * 365)/12);
150         } else {
151                 $Mm = $ym;
152         }
153         return $string if ($limit eq 'M');
154
155         if ($w = int($Mm / 604800)) {
156                 $string .= ($string ? ', ':'')."$w $week". ($w > 1 ? 's' : '');
157                 $wm = $Mm % 604800;
158         } else {
159                 $wm = $Mm;
160         }
161         return $string if ($limit eq 'w');
162
163         if ($d = int($wm / 86400)) {
164                 $string .= ($string ? ', ':'')."$d $day". ($d > 1 ? 's' : '');
165                 $dm = $wm % 86400;
166         } else {
167                 $dm = $wm;
168         }
169         return $string if ($limit eq 'd');
170
171         if ($h = int($dm / 3600)) {
172                 $string .= ($string ? ', ' : '')."$h $hour". ($h > 1 ? 's' : '');
173                 $hm = $dm % 3600;
174         } else {
175                 $hm = $dm;
176         }
177         return $string if ($limit eq 'h');
178
179         if ($m = int($hm / 60)) {
180                 $string .= ($string ? ', ':'')."$m $minute". ($m > 1 ? 's' : '');
181                 $mm = $hm % 60;
182         } else {
183                 $mm = $hm;
184         }
185         return $string if ($limit eq 'm');
186
187         if ($s = int($mm)) {
188                 $string .= ($string ? ', ':'')."$s $second". ($s > 1 ? 's' : '');
189         } else {
190                 $string = "0s" unless ($string);
191         }
192         return $string;
193 }
194
195 sub gmtime_ISO8601 {
196         my $self = shift;
197         my @date = gmtime;
198
199         my $y = $date[5] + 1900;
200         my $M = $date[4] + 1;
201         my $d = $date[3];
202         my $h = $date[2];
203         my $m = $date[1];
204         my $s = $date[0];
205
206         return sprintf('%d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d+00:00', $y, $M, $d, $h, $m, $s);
207 }
208
209 sub clean_ISO8601 {
210         my $self = shift;
211         my $date = shift || $self;
212         if ($date =~ /^\s*(\d{4})-?(\d{2})-?(\d{2})/o) {
213                 my $new_date = "$1-$2-$3";
214
215                 if ($date =~/(\d{2}):(\d{2}):(\d{2})/o) {
216                         $new_date .= "T$1:$2:$3";
217
218                         my $z;
219                         if ($date =~ /([-+]{1})([0-9]{1,2})(?::?([0-9]{1,2}))*\s*$/o) {
220                                 $z = sprintf('%s%0.2d%0.2d',$1,$2,$3)
221                         } else {
222                                 $z =  DateTime::TimeZone::offset_as_string(
223                                         DateTime::TimeZone
224                                                 ->new( name => 'local' )
225                                                 ->offset_for_datetime(
226                                                         $date_parser->parse_datetime($new_date)
227                                                 )
228                                 );
229                         }
230
231                         if (length($z) > 3 && index($z, ':') == -1) {
232                                 substr($z,3,0) = ':';
233                                 substr($z,6,0) = ':' if (length($z) > 6);
234                         }
235                 
236                         $new_date .= $z;
237                 } else {
238                         $new_date .= "T00:00:00";
239                 }
240
241                 return $new_date;
242         }
243         return $date;
244 }
245
246 1;