1 package OpenILS::Utils::DateTime;
7 use Digest::MD5 qw(md5 md5_hex md5_base64);
10 use DateTime::Format::ISO8601;
11 use DateTime::TimeZone;
15 OpenILS::Utils::DateTime;
19 This contains several routines for doing date and time calculation. This
20 is derived from the date/time routines from OpenSRF::Utils.
28 use vars qw/@ISA $AUTOLOAD %EXPORT_TAGS @EXPORT_OK @EXPORT/;
29 push @ISA, 'Exporter';
32 datetime => [qw(clean_ISO8601 gmtime_ISO8601 interval_to_seconds seconds_to_interval)],
34 Exporter::export_ok_tags('datetime'); # add aa, cc and dd to @EXPORT_OK
36 our $date_parser = DateTime::Format::ISO8601->new;
45 my $type = ref($self) or return undef;
48 $name =~ s/.*://; # strip fully-qualified portion
51 return $self->{$name} = shift;
53 return $self->{$name};
56 =head2 $thing->interval_to_seconds('interval') OR interval_to_seconds('interval')
58 =head2 $thing->seconds_to_interval($seconds) OR seconds_to_interval($seconds)
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.
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.
67 my $expire_time = time() + $thing->interval_to_seconds('17h 9m');
69 The time size indicator may be one of
95 for months (really (365 * 1d)/12 ... that may get smarter, though)
99 for years (this is 365 * 1d)
104 sub interval_to_seconds {
106 my $interval = shift || $self;
108 $interval =~ s/(\d{2}):(\d{2}):(\d{2})/ $1 h $2 min $3 s /go;
110 $interval =~ s/and/,/g;
111 $interval =~ s/,/ /g;
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);
128 sub seconds_to_interval {
130 my $interval = shift || $self;
132 my $limit = shift || 's';
133 $limit =~ s/^(.)/$1/o;
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');
139 if ($y = int($interval / (60 * 60 * 24 * 365))) {
140 $string = "$y $year". ($y > 1 ? 's' : '');
141 $ym = $interval % (60 * 60 * 24 * 365);
145 return $string if ($limit eq 'y');
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);
153 return $string if ($limit eq 'M');
155 if ($w = int($Mm / 604800)) {
156 $string .= ($string ? ', ':'')."$w $week". ($w > 1 ? 's' : '');
161 return $string if ($limit eq 'w');
163 if ($d = int($wm / 86400)) {
164 $string .= ($string ? ', ':'')."$d $day". ($d > 1 ? 's' : '');
169 return $string if ($limit eq 'd');
171 if ($h = int($dm / 3600)) {
172 $string .= ($string ? ', ' : '')."$h $hour". ($h > 1 ? 's' : '');
177 return $string if ($limit eq 'h');
179 if ($m = int($hm / 60)) {
180 $string .= ($string ? ', ':'')."$m $minute". ($m > 1 ? 's' : '');
185 return $string if ($limit eq 'm');
188 $string .= ($string ? ', ':'')."$s $second". ($s > 1 ? 's' : '');
190 $string = "0s" unless ($string);
199 my $y = $date[5] + 1900;
200 my $M = $date[4] + 1;
206 return sprintf('%d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d+00:00', $y, $M, $d, $h, $m, $s);
211 my $date = shift || $self;
212 if ($date =~ /^\s*(\d{4})-?(\d{2})-?(\d{2})/o) {
213 my $new_date = "$1-$2-$3";
215 if ($date =~/(\d{2}):(\d{2}):(\d{2})/o) {
216 $new_date .= "T$1:$2:$3";
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)
222 $z = DateTime::TimeZone::offset_as_string(
224 ->new( name => 'local' )
225 ->offset_for_datetime(
226 $date_parser->parse_datetime($new_date)
231 if (length($z) > 3 && index($z, ':') == -1) {
232 substr($z,3,0) = ':';
233 substr($z,6,0) = ':' if (length($z) > 6);
238 $new_date .= "T00:00:00";