]> jfr.im git - irc/SurrealServices/srsv.git/blob - tags/0.4.3.1-pre2/SrSv/Time.pm
cut of branches/0.4.3
[irc/SurrealServices/srsv.git] / tags / 0.4.3.1-pre2 / SrSv / Time.pm
1 # This file is part of SurrealServices.
2 #
3 # SurrealServices is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
7 #
8 # SurrealServices is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with SurrealServices; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
17 package SrSv::Time;
18
19 use strict;
20 use integer;
21 use Time::Local;
22
23 use Exporter 'import';
24 BEGIN { our @EXPORT = qw( @months @days
25 gmtime2 tz_time gmt_date local_date
26 time_ago time_rel time_rel_long_all
27 parse_time split_time
28 get_nextday get_nextday_time get_monthdays
29 get_nexthour get_nexthour_time
30 )
31 }
32
33 our @months = ( 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' );
34 our @days = ( 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' );
35
36 sub _time_text($) {
37 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime(shift);
38 return $mday.'/'.$months[$mon].'/'. substr($year, -2, 2).' '.
39 sprintf("%02d:%02d", $hour, $min);
40 }
41
42 sub gmtime2(;$) {
43 my ($time) = @_;
44 $time = time() unless $time;
45 return _time_text($time) . ' GMT';
46 }
47
48 sub tz_time($;$) {
49 my ($tzoffset, $time) = @_;
50 return _time_text(($time ? $time : time()) + tz_to_offset($tzoffset));
51 }
52
53 sub tz_to_offset($) {
54 my ($offset) = @_;
55 # offset is a signed integer corresponding to 1/4 hr increments
56 # or 900 seconds (15 minutes)
57 return ($offset * 900);
58 }
59
60 sub _date_text($) {
61 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime(shift);
62 return (!wantarray ? ($year+1900).' '.$months[$mon].' '.$mday : ($year + 1900, $mon+1, $months[$mon], $mday));
63 }
64
65 sub gmt_date(;$) {
66 my ($time) = @_;
67 $time = time() unless $time;
68 return _date_text($time);
69 }
70
71 sub local_date($;$) {
72 my ($tzoffset, $time) = @_;
73 return _date_text(($time ? $time : time()) + tz_to_offset($tzoffset));
74 }
75
76 sub parse_time($) {
77 my ($str) = @_;
78 my $out;
79 $str =~ s/^\+//;
80 $str = lc($str);
81
82 my @vals = split(/(?<!\d)(?=\d+\w)/, $str);
83
84 foreach my $val (@vals) {
85 $val =~ /(\d+)(\w)/;
86 my ($num, $pos) = ($1, $2);
87
88 if($pos eq 'w') { $num *= (86400*7) }
89 elsif($pos eq 'd') { $num *= 86400 }
90 elsif($pos eq 'h') { $num *= 3600 }
91 elsif($pos eq 'm') { $num *= 60 }
92 elsif($pos ne 's') { return undef }
93
94 $out += $num;
95 }
96
97 return $out;
98 }
99
100 sub split_time($) {
101 no integer; # We might want to pass in a float value for $difference
102 my ($difference) = @_;
103 my ($weeks, $days, $hours, $minutes, $seconds);
104 $seconds = $difference % 60 + ($difference - int($difference));
105 $difference = ($difference - $seconds) / 60;
106 $minutes = $difference % 60;
107 $difference = ($difference - $minutes) / 60;
108 $hours = $difference % 24;
109 $difference = ($difference - $hours) / 24;
110 $days = $difference % 7;
111 $weeks = ($difference - $days) / 7;
112
113 return ($weeks, $days, $hours, $minutes, $seconds);
114 }
115
116 sub time_ago($;$) {
117 return time_rel(time() - $_[0], $_[1]);
118 }
119
120 sub time_rel($;$) {
121 my ($time, $all) = @_;
122
123 if ($time >= 2419200) { # 86400 * 7 * 4
124 my ($years, $months, $weeks, $days) = __time_rel_long(time() - $time);
125 if($years or $months or $weeks or $days) {
126 my $text = '';
127 if($years) {
128 $text = "$years year".($years !=1 ? 's' : '');
129 }
130 if($months) {
131 $text .= (length($text) ? ' ' : '')."$months month".($months !=1 ? 's' : '');
132 if ($years && !$all) {
133 return $text;
134 }
135 }
136 if($weeks) {
137 $text .= (length($text) ? ' ' : '')."$weeks week".($weeks !=1 ? 's' : '');
138 if ($months && !$all) {
139 return $text;
140 }
141 }
142 if($days) {
143 $text .= (length($text) ? ' ' : '')."$days day".($days !=1 ? 's' : '');
144 =cut
145 if ($weeks && !$all) {
146 return $text;
147 }
148 =cut
149 }
150 return $text;
151 =cut
152 return ( $years ? "$years year".($years !=1 ? 's' : '') : '' ).
153 ( $months ? ($years ? ', ' : '')."$months month".( $months!=1 ? 's' : '' ) : '').
154 ( $weeks ? (($years or $months) ? ', ' : '')."$weeks week".( $weeks!=1 ? 's' : '' ) : '').
155 ( $days ? (($months or $years or $weeks) ? ', ' : '')."$days day".($days!=1 ? 's' : '') : '' )
156 ;
157 =cut
158 }
159 }
160
161 my ($weeks, $days, $hours, $minutes, $seconds) = split_time($time);
162
163 my $text;
164 # if($time >= 604800) { # 86400 * 7 }
165 if($weeks) {
166 $text = "$weeks week".($weeks!=1 ? 's' : '');
167 =cut
168 return "$weeks week".
169 ($weeks!=1 ? 's' : '').
170 ", $days day".
171 ($days!=1 ? 's' : '');
172 =cut
173 }
174 if($days) {
175 $text .= (length($text) ? ' ' : '')."$days day".($days!=1 ? 's' : '');
176 return $text if $weeks && !$all;
177 =cut
178 return "$days day".
179 ($days!=1 ? 's' : '').
180 ", $hours hour".
181 ($hours!=1 ? 's' : '');
182 =cut
183 }
184 if($hours) {
185 $text .= (length($text) ? ' ' : '')."$hours hour".($hours!=1 ? 's' : '');
186 return $text if $days && !$all;
187 =cut
188 return "$hours hour".
189 ($hours!=1 ? 's' : '').
190 ", $minutes minute".
191 ($minutes!=1 ? 's' : '');
192 =cut
193 }
194 if($minutes) {
195 $text .= (length($text) ? ' ' : '')."$minutes minute".($minutes!=1 ? 's' : '');
196 return $text if $hours && !$all;
197 =cut return "$minutes minute".
198 ($minutes!=1 ? 's' : '').
199 ", $seconds second".
200 ($seconds!=1 ? 's' : '');
201 =cut
202 }
203 if($seconds) {
204 $text .= (length($text) ? ' ' : '')."$seconds second".($seconds!=1 ? 's' : '');
205 =cut
206 return "$seconds second".
207 ($seconds!=1 ? 's' : '');
208 =cut
209 }
210 if(!($weeks || $days || $hours || $minutes || $seconds) ) {
211 return '0 seconds';
212 }
213 return $text;
214 }
215
216 # This is for cases over 4 weeks, when we need years, months, weeks, and days
217 sub __time_rel_long($;$) {
218 my ($lesser_time, $greater_time) = @_;
219 $greater_time = time() unless $greater_time;
220
221 my ($sec1, $min1, $hour1, $mday1, $month1, $year1, undef, undef, undef) = gmtime($lesser_time);
222 my ($sec2, $min2, $hour2, $mday2, $month2, $year2, undef, undef, undef) = gmtime($greater_time);
223
224 my ($result_years, $result_months, $result_weeks, $result_days,
225 $result_hours, $result_mins, $result_secs);
226 $result_secs = $sec2 - $sec1;
227 $result_mins = $min2 - $min1;
228 if($result_secs < 0) {
229 $result_secs += 60; $result_mins--;
230 }
231 $result_hours = $hour2 - $hour1;
232 if($result_mins < 0) {
233 $result_mins += 60; $result_hours--;
234 }
235 $result_days = $mday2 - $mday1;
236 if($result_hours < 0) {
237 $result_hours += 24; $result_days--;
238 }
239 $result_months = $month2 - $month1;
240 if($result_days < 0) {
241 $result_days += get_monthdays(
242 ($month2 == 0 ? 11 : $month2 - 1),
243 ($month2 == 0 ? $year2 - 1: $year2));
244 $result_months--;
245 }
246 # The following division relies on integer division, as 'use integer' is decl'd above.
247 $result_weeks = $result_days / 7;
248 $result_days = $result_days % 7;
249 $result_years = $year2 - $year1;
250 if($result_months < 0) {
251 $result_months += 12; $result_years--
252 }
253 return ($result_years, $result_months, $result_weeks, $result_days, $result_hours, $result_mins, $result_secs);
254 }
255
256 # Apologize about the unreadability, but the alternative is about 4 times as long
257 # This is for use when we want as precise a time-difference as possible.
258 sub time_rel_long_all($;$) {
259 my ($lesser_time, $greater_time) = @_;
260 $greater_time = time() unless $greater_time;
261 my ($years, $months, $weeks, $days, $hours, $minutes, $seconds) = __time_rel_long($lesser_time);
262 return ( $years ? "$years year".($years !=1 ? 's' : '') : '' ).
263 ( $months ? ($years ? ', ' : '')."$months month".( $months!=1 ? 's' : '' ) : '').
264 ( $weeks ? (($years or $months) ? ', ' : '')."$weeks week".( $weeks!=1 ? 's' : '' ) : '').
265 ( $days ? (($months or $years or $weeks) ? ', ' : '')."$days day".($days!=1 ? 's' : '') : '' ).
266 ( $hours ? (($days or $months or $years or $weeks) ? ', ' : '')."$hours hour".($hours!=1 ? 's' : '') : '' ).
267 ( $minutes ? (($hours or $days or $months or $years or $weeks) ? ', ' : '')."$minutes minute".($minutes!=1 ? 's' : '') : '' ).
268 ( $seconds ? (($minutes or $days or $months or $years or $weeks) ? ', ' : '')."$seconds second".($seconds!=1 ? 's' : '') : '' )
269 ;
270
271 }
272
273 sub get_nextday($$$) {
274 my ($mday, $mon, $year) = @_;
275 $year += 1900 if $year < 1582; #Gregorian calendar was somewhere around here...
276
277 my $monthdays = get_monthdays($mon, $year);
278 $mday++;
279 if($mday > $monthdays) {
280 $mday %= $monthdays;
281 $mon++;
282 }
283 if($mon >= 12) {
284 $mon %= 12;
285 $year++;
286 }
287 return ($mday, $mon, $year);
288 }
289 sub get_nextday_time(;$) {
290 my ($time) = @_;
291 $time = time() unless $time;
292 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($time);
293 return Time::Local::timegm(0,0,0,get_nextday($mday, $mon, $year));
294 }
295
296 sub get_nexthour($$$$) {
297 my ($hour, $mday, $mon, $year) = @_;
298 # $minute++;
299 # if($minute >= 60) {
300 # $minute %= 60;
301 # $hour++;
302 # }
303 $hour++;
304 if($hour >= 24) {
305 $hour %= 24;
306 ($mday, $mon, $year) = get_nextday($mday, $mon, $year)
307 }
308 return ($hour, $mday, $mon, $year);
309 }
310 sub get_nexthour_time(;$) {
311 my ($time) = @_;
312 $time = time() unless $time;
313 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($time);
314 return Time::Local::timegm(0,0,get_nexthour($hour, $mday, $mon, $year));
315 }
316
317 # This function is only correct/valid for Gregorian dates.
318 # Not IVLIAN dates.
319 sub get_monthdays {
320 # $month is 0-11 not 1-12
321 my ($month, $year) = @_;
322 sub m30($) { return 30; }
323 sub m31($) { return 31; }
324 sub mFeb($) {
325 my ($year) = @_;
326 if(($year % 100 and !($year % 4)) or !($year % 400)) {
327 return 29;
328 } else {
329 return 28;
330 }
331 }
332 # this is the common table, but note +1 below
333 # as gmtime() and friends return months from 0-11 not 1-12
334 my %months = (
335 1 => \&m31,
336 3 => \&m31,
337 5 => \&m31,
338 7 => \&m31,
339 8 => \&m31,
340 10 => \&m31,
341 12 => \&m31,
342
343 4 => \&m30,
344 6 => \&m30,
345 9 => \&m30,
346 11 => \&m30,
347
348 2 => \&mFeb,
349 );
350
351 $year += 1900 if $year < 1582; #Gregorian calendar was somewhere around here...
352 return $months{$month+1}($year);
353 }
354
355 1;