← Index
NYTProf Performance Profile   « block view • line view • sub view »
For /usr/local/src/github.com/foswiki/core/bin/view
  Run on Sun Dec 4 17:17:59 2011
Reported on Sun Dec 4 17:27:06 2011

Filename/usr/local/src/github.com/foswiki/core/lib/Foswiki/Time.pm
StatementsExecuted 756 statements in 8.25ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
23633.07ms4.12msFoswiki::Time::::formatTimeFoswiki::Time::formatTime
1112.04ms3.06msFoswiki::Time::::parseTimeFoswiki::Time::parseTime
371191728µs728µsFoswiki::Time::::CORE:substFoswiki::Time::CORE:subst (opcode)
7261166µs166µsFoswiki::Time::::CORE:matchFoswiki::Time::CORE:match (opcode)
4671113µs113µsFoswiki::Time::::CORE:substcontFoswiki::Time::CORE:substcont (opcode)
11125µs33µsFoswiki::Time::::BEGIN@35Foswiki::Time::BEGIN@35
11118µs42µsFoswiki::Time::::BEGIN@36Foswiki::Time::BEGIN@36
11117µs60µsFoswiki::Time::::BEGIN@38Foswiki::Time::BEGIN@38
1119µs9µsFoswiki::Time::::BEGIN@39Foswiki::Time::BEGIN@39
0000s0sFoswiki::Time::::_daysInYearFoswiki::Time::_daysInYear
0000s0sFoswiki::Time::::_parseDurationFoswiki::Time::_parseDuration
0000s0sFoswiki::Time::::_tzOffsetFoswiki::Time::_tzOffset
0000s0sFoswiki::Time::::_weekNumberFoswiki::Time::_weekNumber
0000s0sFoswiki::Time::::formatDeltaFoswiki::Time::formatDelta
0000s0sFoswiki::Time::::parseIntervalFoswiki::Time::parseInterval
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2
3=begin TML
4
5---+ package Foswiki::Time
6
7Time handling functions.
8
9API version $Date$ (revision $Rev$)
10
11*Since* _date_ indicates where functions or parameters have been added since
12the baseline of the API (TWiki release 4.2.3). The _date_ indicates the
13earliest date of a Foswiki release that will support that function or
14parameter.
15
16*Deprecated* _date_ indicates where a function or parameters has been
17[[http://en.wikipedia.org/wiki/Deprecation][deprecated]]. Deprecated
18functions will still work, though they should
19_not_ be called in new plugins and should be replaced in older plugins
20as soon as possible. Deprecated parameters are simply ignored in Foswiki
21releases after _date_.
22
23*Until* _date_ indicates where a function or parameter has been removed.
24The _date_ indicates the latest date at which Foswiki releases still supported
25the function or parameter.
26
27=cut
28
29# THIS PACKAGE IS PART OF THE PUBLISHED API USED BY EXTENSION AUTHORS.
30# DO NOT CHANGE THE EXISTING APIS (well thought out extensions are OK)
31# AND ENSURE ALL POD DOCUMENTATION IS COMPLETE AND ACCURATE.
32
33package Foswiki::Time;
34
35245µs241µs
# spent 33µs (25+8) within Foswiki::Time::BEGIN@35 which was called: # once (25µs+8µs) by Foswiki::BEGIN@609 at line 35
use strict;
# spent 33µs making 1 call to Foswiki::Time::BEGIN@35 # spent 8µs making 1 call to strict::import
36245µs266µs
# spent 42µs (18+24) within Foswiki::Time::BEGIN@36 which was called: # once (18µs+24µs) by Foswiki::BEGIN@609 at line 36
use warnings;
# spent 42µs making 1 call to Foswiki::Time::BEGIN@36 # spent 24µs making 1 call to warnings::import
37
38243µs2104µs
# spent 60µs (17+43) within Foswiki::Time::BEGIN@38 which was called: # once (17µs+43µs) by Foswiki::BEGIN@609 at line 38
use Assert;
# spent 60µs making 1 call to Foswiki::Time::BEGIN@38 # spent 43µs making 1 call to Assert::import
3923.80ms19µs
# spent 9µs within Foswiki::Time::BEGIN@39 which was called: # once (9µs+0s) by Foswiki::BEGIN@609 at line 39
use Foswiki ();
# spent 9µs making 1 call to Foswiki::Time::BEGIN@39
40
4113µsour $VERSION = '$Rev$'; # Subversion rev number
42
43# Constants
4416µsour @ISOMONTH = (
45 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
46 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
47);
48
4914µsour @MONTHLENS = ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
50
5113µsour @WEEKDAY = ( 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' );
52
5318µsour %MON2NUM = (
54 jan => 0,
55 feb => 1,
56 mar => 2,
57 apr => 3,
58 may => 4,
59 jun => 5,
60 jul => 6,
61 aug => 7,
62 sep => 8,
63 oct => 9,
64 nov => 10,
65 dec => 11
66);
67
6811µsour $TZSTRING; # timezone string for servertime; "Z" or "+01:00" etc.
69
70=begin TML
71
72---++ StaticMethod parseTime( $szDate, $defaultLocal ) -> $iSecs
73
74Convert string date/time string to seconds since epoch (1970-01-01T00:00:00Z).
75 * =$sDate= - date/time string
76
77Handles the following formats:
78
79Default Foswiki format
80 * 31 Dec 2001 - 23:59
81 * 31-Dec-2001 - 23:59
82
83Foswiki format without time (defaults to 00:00)
84 * 31 Dec 2001
85 * 31-Dec-2001
86
87Date separated by '/', '.' or '-', time with '.' or ':'
88Date and time separated by ' ', '.' and/or '-'
89 * 2001/12/31 23:59:59
90 * 2001.12.31.23.59.59
91 * 2001/12/31 23:59
92 * 2001.12.31.23.59
93 * 2001-12-31 23:59
94 * 2001-12-31 - 23:59
95 * 2009-1-12
96 * 2009-1
97 * 2009
98
99ISO format
100 * 2001-12-31T23:59:59
101 * 2001-12-31T
102
103ISO dates may have a timezone specifier, either Z or a signed difference
104in hh:mm format. For example:
105 * 2001-12-31T23:59:59+01:00
106 * 2001-12-31T23:59Z
107The default timezone is Z, unless $defaultLocal is true in which case
108the local timezone will be assumed.
109
110If the date format was not recognised, will return undef.
111
112=cut
113
114
# spent 3.06ms (2.04+1.02) within Foswiki::Time::parseTime which was called: # once (2.04ms+1.02ms) by Foswiki::Logger::PlainFile::_rotate at line 273 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Logger/PlainFile.pm
sub parseTime {
11514µs my ( $date, $defaultLocal ) = @_;
116
11715µs14µs ASSERT( defined $date ) if DEBUG;
# spent 4µs making 1 call to Assert::ASSERTS_OFF
118118µs18µs $date =~ s/^\s*//; #remove leading spaces without de-tainting.
# spent 8µs making 1 call to Foswiki::Time::CORE:subst
119115µs18µs $date =~ s/\s*$//;
# spent 8µs making 1 call to Foswiki::Time::CORE:subst
120
1211146µs require Time::Local;
122
123 # NOTE: This routine *will break* if input is not one of below formats!
12413µs my $timelocal =
125 $defaultLocal
126 ? \&Time::Local::timelocal
127 : \&Time::Local::timegm;
128
129 # try "31 Dec 2001 - 23:59" (Foswiki date)
130 # or "31 Dec 2001"
131 #TODO: allow /.: too
132119µs18µs if ( $date =~ /(\d+)[-\s]+([a-z]{3})[-\s]+(\d+)(?:[-\s]+(\d+):(\d+))?/i ) {
# spent 8µs making 1 call to Foswiki::Time::CORE:match
133 my $year = $3;
134 $year -= 1900 if ( $year > 1900 );
135
136 my $mon = $MON2NUM{ lc($2) };
137 return undef unless defined $mon;
138
139 #TODO: %MON2NUM needs to be updated to use i8n
140 #TODO: and should really work for long form of the month name too.
141 return &$timelocal( 0, $5 || 0, $4 || 0, $1, $mon, $year );
142 }
143
144 # ISO date 2001-12-31T23:59:59+01:00
145 # Sven is going to presume that _all_ ISO dated must have a 'T' in them.
14618µs12µs if (
# spent 2µs making 1 call to Foswiki::Time::CORE:match
147 ( $date =~ /T/ )
148 && ( $date =~
149/(\d\d\d\d)(?:-(\d\d)(?:-(\d\d))?)?(?:T(\d\d)(?::(\d\d)(?::(\d\d(?:\.\d+)?))?)?)?(Z|[-+]\d\d(?::\d\d)?)?/
150 )
151 )
152 {
153 my ( $Y, $M, $D, $h, $m, $s, $tz ) =
154 ( $1, $2 || 1, $3 || 1, $4 || 0, $5 || 0, $6 || 0, $7 || '' );
155 $M--;
156 $Y -= 1900 if ( $Y > 1900 );
157 if ($tz) {
158 my $tzadj = 0;
159 if ( $tz eq 'Z' ) {
160 $tzadj = 0; # Zulu
161 }
162 elsif ( $tz =~ /([-+])(\d\d)(?::(\d\d))?/ ) {
163 $tzadj = ( $1 || '' ) . ( ( ( $2 * 60 ) + ( $3 || 0 ) ) * 60 );
164 $tzadj -= 0;
165 }
166 return Time::Local::timegm( $s, $m, $h, $D, $M, $Y ) - $tzadj;
167 }
168 return &$timelocal( $s, $m, $h, $D, $M, $Y );
169 }
170
171 #any date that leads with a year (2 digit years too)
172124µs117µs if (
# spent 17µs making 1 call to Foswiki::Time::CORE:match
173 $date =~ m|^
174 (\d\d+) #year
175 (?:\s*[/\s.-]\s* #datesep
176 (\d\d?) #month
177 (?:\s*[/\s.-]\s* #datesep
178 (\d\d?) #day
179 (?:\s*[/\s.-]\s* #datetimesep
180 (\d\d?) #hour
181 (?:\s*[:.]\s* #timesep
182 (\d\d?) #min
183 (?:\s*[:.]\s* #timesep
184 (\d\d?)
185 )?
186 )?
187 )?
188 )?
189 )?
190 $|x
191 )
192 {
193
194 #no defaulting yet so we can detect the 2009--12 error
195112µs my ( $year, $M, $D, $h, $m, $s ) = ( $1, $2, $3, $4, $5, $6 );
196
197 # without range checking on the 12 Jan 2009 case above,
198 # there is ambiguity - what is 14 Jan 12 ?
199 # similarly, how would you decide what Jan 02 and 02 Jan are?
200 #$month_p = $MON2NUM{ lc($month_p) } if (defined($MON2NUM{ lc($month_p) }));
201
202 #TODO: unhappily, this means 09 == 1909 not 2009
20312µs $year -= 1900 if ( $year > 1900 );
204
205 #range checks
20612µs return undef if ( defined($M) && ( $M < 1 || $M > 12 ) );
20712µs my $month = ( $M || 1 ) - 1;
20812µs my $monthlength = $MONTHLENS[$month];
209
210 # If leap year, note February is month number 1 starting from 0
21112µs $monthlength = 29 if ( $month == 1 && _daysInYear($year) == 366 );
21212µs return undef if ( defined($D) && ( $D < 0 || $D > $monthlength ) );
21312µs return undef if ( defined($h) && ( $h < 0 || $h > 24 ) );
21412µs return undef if ( defined($m) && ( $m < 0 || $m > 60 ) );
21512µs return undef if ( defined($s) && ( $s < 0 || $s > 60 ) );
21612µs return undef if ( defined($year) && $year < 60 );
217
21812µs my $day = $D || 1;
21912µs my $hour = $h || 0;
22011µs my $min = $m || 0;
22111µs my $sec = $s || 0;
222
223113µs151µs return &$timelocal( $sec, $min, $hour, $day, $month, $year );
# spent 51µs making 1 call to Time::Local::timegm
224 }
225
226 # give up, return undef
227 return undef;
228}
229
230=begin TML
231
232---++ StaticMethod formatTime ($epochSeconds, $formatString, $outputTimeZone) -> $value
233
234 * =$epochSeconds= epochSecs GMT
235 * =$formatString= Foswiki time date format, default =$day $month $year - $hour:$min=
236 * =$outputTimeZone= timezone to display, =gmtime= or =servertime=, default is whatever is set in $Foswiki::cfg{DisplayTimeValues}
237
238=$formatString= supports:
239 | $seconds | secs |
240 | $minutes | mins |
241 | $hours | hours |
242 | $day | day |
243 | $wday | weekday name |
244 | $dow | day number (0 = Sunday) |
245 | $week | week number (ISO 8601) |
246 | $month | month name |
247 | $mo | month number |
248 | $year | 4-digit year |
249 | $ye | 2-digit year |
250 | $http | ful HTTP header format date/time |
251 | $email | full email format date/time |
252 | $rcs | full RCS format date/time |
253 | $epoch | seconds since 1st January 1970 |
254 | $tz | Timezone name (GMT or Local) |
255 | $isotz | ISO 8601 timezone specifier e.g. 'Z, '+07:15' |
256
257=cut
258
259# previous known as Foswiki::formatTime
260
261
# spent 4.12ms (3.07+1.04) within Foswiki::Time::formatTime which was called 23 times, avg 179µs/call: # 11 times (1.12ms+313µs) by Foswiki::Render::renderRevisionInfo at line 1793 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Render.pm, avg 130µs/call # 5 times (702µs+235µs) by Foswiki::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki.pm:209] at line 207 of /usr/local/src/github.com/foswiki/core/lib/Foswiki.pm, avg 187µs/call # 3 times (395µs+155µs) by Foswiki::Render::renderRevisionInfo at line 1790 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Render.pm, avg 183µs/call # 2 times (385µs+134µs) by Foswiki::Render::renderRevisionInfo at line 1788 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Render.pm, avg 259µs/call # once (249µs+115µs) by Foswiki::Logger::PlainFile::log at line 72 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Logger/PlainFile.pm # once (220µs+93µs) by Foswiki::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki.pm:277] at line 275 of /usr/local/src/github.com/foswiki/core/lib/Foswiki.pm
sub formatTime {
2622388µs my ( $epochSeconds, $formatString, $outputTimeZone ) = @_;
2632340µs my $value = $epochSeconds;
264
26523108µs2382µs ASSERT( defined $epochSeconds ) if DEBUG;
# spent 82µs making 23 calls to Assert::ASSERTS_OFF, avg 4µs/call
266
267 # use default Foswiki format "31 Dec 1999 - 23:59" unless specified
2682330µs $formatString ||= '$longdate';
2692342µs $outputTimeZone ||= $Foswiki::cfg{DisplayTimeValues};
270
27123157µs2354µs if ( $formatString =~ /http/i ) {
# spent 54µs making 23 calls to Foswiki::Time::CORE:match, avg 2µs/call
272 $outputTimeZone = 'gmtime';
273 }
274
2752350µs my ( $sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdst );
2762377µs if ( $outputTimeZone eq 'servertime' ) {
277122µs ( $sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdst ) =
278 localtime($epochSeconds);
279 }
280 else {
28122108µs ( $sec, $min, $hour, $day, $mon, $year, $wday, $yday ) =
282 gmtime($epochSeconds);
283 }
284
285 #standard Foswiki date time formats
286
287 # RCS format, example: "2001/12/31 23:59:59"
28823165µs2340µs $formatString =~ s/\$rcs/\$year\/\$mo\/\$day \$hour:\$min:\$sec/gi;
# spent 40µs making 23 calls to Foswiki::Time::CORE:subst, avg 2µs/call
289
290 # HTTP and email header format, e.g. "Thu, 23 Jul 1998 07:21:56 EST"
291 # RFC 822/2616/1123
29223166µs2362µs $formatString =~
# spent 62µs making 23 calls to Foswiki::Time::CORE:subst, avg 3µs/call
293 s/\$(http|email)/\$wday, \$day \$month \$year \$hour:\$min:\$sec \$tz/gi;
294
295 # ISO Format, see spec at http://www.w3.org/TR/NOTE-datetime
296 # e.g. "2002-12-31T19:30:12Z"
297 # Undocumented: formatString='iso'
2982350µs $formatString = '$year-$mo-$dayT$hour:$min:$sec$isotz'
299 if lc($formatString) eq 'iso';
300
301 # Undocumented, but used in renderers: formatString can contain '$iso'
30223178µs2353µs $formatString =~ s/\$iso\b/\$year-\$mo-\$dayT\$hour:\$min:\$sec\$isotz/gi;
# spent 53µs making 23 calls to Foswiki::Time::CORE:subst, avg 2µs/call
303
304 # longdate
30523175µs2342µs $formatString =~
# spent 42µs making 23 calls to Foswiki::Time::CORE:subst, avg 2µs/call
306 s/\$longdate/$Foswiki::cfg{DefaultDateFormat} - \$hour:\$min/gi;
307
3082337µs $value = $formatString;
30926217µs2976µs $value =~ s/\$seco?n?d?s?/sprintf('%.2u',$sec)/gei;
# spent 49µs making 23 calls to Foswiki::Time::CORE:subst, avg 2µs/call # spent 27µs making 6 calls to Foswiki::Time::CORE:substcont, avg 5µs/call
31027203µs3161µs $value =~ s/\$minu?t?e?s?/sprintf('%.2u',$min)/gei;
# spent 44µs making 23 calls to Foswiki::Time::CORE:subst, avg 2µs/call # spent 17µs making 8 calls to Foswiki::Time::CORE:substcont, avg 2µs/call
31127193µs3159µs $value =~ s/\$hour?s?/sprintf('%.2u',$hour)/gei;
# spent 42µs making 23 calls to Foswiki::Time::CORE:subst, avg 2µs/call # spent 17µs making 8 calls to Foswiki::Time::CORE:substcont, avg 2µs/call
31228224µs3371µs $value =~ s/\$day/sprintf('%.2u',$day)/gei;
# spent 48µs making 23 calls to Foswiki::Time::CORE:subst, avg 2µs/call # spent 23µs making 10 calls to Foswiki::Time::CORE:substcont, avg 2µs/call
31323139µs2332µs $value =~ s/\$wday/$WEEKDAY[$wday]/gi;
# spent 32µs making 23 calls to Foswiki::Time::CORE:subst, avg 1µs/call
31423121µs2332µs $value =~ s/\$dow/$wday/gi;
# spent 32µs making 23 calls to Foswiki::Time::CORE:subst, avg 1µs/call
31523121µs2334µs $value =~ s/\$week/_weekNumber($wday, $yday, $year + 1900)/egi;
# spent 34µs making 23 calls to Foswiki::Time::CORE:subst, avg 1µs/call
31623148µs2348µs $value =~ s/\$mont?h?/$ISOMONTH[$mon]/gi;
# spent 48µs making 23 calls to Foswiki::Time::CORE:subst, avg 2µs/call
31724155µs2551µs $value =~ s/\$mo/sprintf('%.2u',$mon+1)/gei;
# spent 47µs making 23 calls to Foswiki::Time::CORE:subst, avg 2µs/call # spent 4µs making 2 calls to Foswiki::Time::CORE:substcont, avg 2µs/call
31828220µs3369µs $value =~ s/\$year?/sprintf('%.4u',$year + 1900)/gei;
# spent 48µs making 23 calls to Foswiki::Time::CORE:subst, avg 2µs/call # spent 20µs making 10 calls to Foswiki::Time::CORE:substcont, avg 2µs/call
31923124µs2334µs $value =~ s/\$ye/sprintf('%.2u',$year%100)/gei;
# spent 34µs making 23 calls to Foswiki::Time::CORE:subst, avg 1µs/call
32023145µs2353µs $value =~ s/\$epoch/$epochSeconds/gi;
# spent 53µs making 23 calls to Foswiki::Time::CORE:subst, avg 2µs/call
321
32223140µs2348µs if ( $value =~ /\$tz/ ) {
# spent 48µs making 23 calls to Foswiki::Time::CORE:match, avg 2µs/call
323 my $tz_str;
324 if ( $outputTimeZone eq 'servertime' ) {
325 ( $sec, $min, $hour, $day, $mon, $year, $wday ) =
326 localtime($epochSeconds);
327
328 # SMELL: how do we get the different timezone strings (and when
329 # we add usertime, then what?)
330 $tz_str = 'Local';
331 }
332 else {
333 ( $sec, $min, $hour, $day, $mon, $year, $wday ) =
334 gmtime($epochSeconds);
335 $tz_str = 'GMT';
336 }
337 $value =~ s/\$tz/$tz_str/gei;
338 }
33923126µs2337µs if ( $value =~ /\$isotz/ ) {
# spent 37µs making 23 calls to Foswiki::Time::CORE:match, avg 2µs/call
34012µs my $tz_str = 'Z';
34112µs if ( $outputTimeZone ne 'gmtime' ) {
342
343 # servertime
344 # time zone designator (+hh:mm or -hh:mm)
345 # cached.
346 unless ( defined $TZSTRING ) {
347 my $offset = _tzOffset();
348 my $sign = ( $offset < 0 ) ? '-' : '+';
349 $offset = abs($offset);
350 my $hours = int( $offset / 3600 );
351 my $mins = int( ( $offset - $hours * 3600 ) / 60 );
352 if ( $hours || $mins ) {
353 $TZSTRING = sprintf( "$sign%02d:%02d", $hours, $mins );
354 }
355 else {
356 $TZSTRING = 'Z';
357 }
358 }
359 $tz_str = $TZSTRING;
360 }
361227µs39µs $value =~ s/\$isotz/$tz_str/gei;
# spent 5µs making 1 call to Foswiki::Time::CORE:subst # spent 4µs making 2 calls to Foswiki::Time::CORE:substcont, avg 2µs/call
362 }
363
36423173µs return $value;
365}
366
367# Get timezone offset from GMT in seconds
368# Code taken from CPAN module 'Time' - "David Muir Sharnoff disclaims
369# any copyright and puts his contribution to this module in the public
370# domain."
371# Note that unit tests rely on this function being here.
372sub _tzOffset {
373 my $time = time();
374 my @l = localtime($time);
375 my @g = gmtime($time);
376
377 my $off = $l[0] - $g[0] + ( $l[1] - $g[1] ) * 60 + ( $l[2] - $g[2] ) * 3600;
378
379 # subscript 7 is yday.
380
381 if ( $l[7] == $g[7] ) {
382
383 # done
384 }
385 elsif ( $l[7] == $g[7] + 1 ) {
386 $off += 86400;
387 }
388 elsif ( $l[7] == $g[7] - 1 ) {
389 $off -= 86400;
390 }
391 elsif ( $l[7] < $g[7] ) {
392
393 # crossed over a year boundary.
394 # localtime is beginning of year, gmt is end
395 # therefore local is ahead
396 $off += 86400;
397 }
398 else {
399 $off -= 86400;
400 }
401
402 return $off;
403}
404
405# Returns the ISO8601 week number for a date.
406# Year is the real year
407# Day of week is 0..6 where 0==Sunday
408# Day of year is 0..364 (or 365) where 0==Jan1
409# From http://www.perlmonks.org/?node_id=710571
410sub _weekNumber {
411 my ( $dayOfWeek, $dayOfYear, $year ) = @_;
412
413 # rebase dow to Monday==0
414 $dayOfWeek = ( $dayOfWeek + 6 ) % 7;
415
416 # Locate the nearest Thursday, by locating the Monday at
417 # or before and going forwards 3 days)
418 my $dayOfNearestThurs = $dayOfYear - $dayOfWeek + 3;
419
420 my $daysInThisYear = _daysInYear($year);
421
422#print STDERR "dow:$dayOfWeek, doy:$dayOfYear, $year = thu:$dayOfNearestThurs ($daysInThisYear)\n";
423
424 # Is nearest thursday in last year or next year?
425 if ( $dayOfNearestThurs < 0 ) {
426
427 # Nearest Thurs is last year
428 # We are at the start of the year
429 # Adjust by the number of days in LAST year
430 $dayOfNearestThurs += _daysInYear( $year - 1 );
431 }
432 if ( $dayOfNearestThurs >= $daysInThisYear ) {
433
434 # Nearest Thurs is next year
435 # We are at the end of the year
436 # Adjust by the number of days in THIS year
437 $dayOfNearestThurs -= $daysInThisYear;
438 }
439
440 # Which week does the Thurs fall into?
441 return int( $dayOfNearestThurs / 7 ) + 1;
442}
443
444# Returns the number of...
445sub _daysInYear {
446 return 366 unless $_[0] % 400;
447 return 365 unless $_[0] % 100;
448 return 366 unless $_[0] % 4;
449 return 365;
450}
451
452=begin TML
453
454---++ StaticMethod formatDelta( $s ) -> $string
455
456Format a time in seconds as a string. For example,
457"1 day, 3 hours, 2 minutes, 6 seconds"
458
459=cut
460
461sub formatDelta {
462 my $secs = shift;
463 my $language = shift;
464
465 ASSERT( defined $secs ) if DEBUG;
466 my $rem = $secs % ( 60 * 60 * 24 );
467 my $days = ( $secs - $rem ) / ( 60 * 60 * 24 );
468 $secs = $rem;
469
470 $rem = $secs % ( 60 * 60 );
471 my $hours = ( $secs - $rem ) / ( 60 * 60 );
472 $secs = $rem;
473
474 $rem = $secs % 60;
475 my $mins = ( $secs - $rem ) / 60;
476 $secs = $rem;
477
478 my $str = '';
479
480 if ($language) {
481
482 #format as in user's language
483 if ($days) {
484 $str .= $language->maketext( '[*,_1,day] ', $days );
485 }
486 if ($hours) {
487 $str .= $language->maketext( '[*,_1,hour] ', $hours );
488 }
489 if ($mins) {
490 $str .= $language->maketext( '[*,_1,minute] ', $mins );
491 }
492 if ($secs) {
493 $str .= $language->maketext( '[*,_1,second] ', $secs );
494 }
495 }
496 else {
497
498 #original code, harcoded English (BAD)
499 if ($days) {
500 $str .= $days . ' day' . ( $days > 1 ? 's ' : ' ' );
501 }
502 if ($hours) {
503 $str .= $hours . ' hour' . ( $hours > 1 ? 's ' : ' ' );
504 }
505 if ($mins) {
506 $str .= $mins . ' minute' . ( $mins > 1 ? 's ' : ' ' );
507 }
508 if ($secs) {
509 $str .= $secs . ' second' . ( $secs > 1 ? 's ' : ' ' );
510 }
511 }
512 $str =~ s/\s+$//;
513 return $str;
514}
515
516=begin TML
517
518---++ StaticMethod parseInterval( $szInterval ) -> [$iSecs, $iSecs]
519
520Convert string representing a time interval to a pair of integers
521representing the amount of seconds since epoch for the start and end
522extremes of the time interval.
523
524 * =$szInterval= - time interval string
525
526in yacc syntax, grammar and actions:
527<verbatim>
528interval ::= date { $$.start = fillStart($1); $$.end = fillEnd($1); }
529 | date '/' date { $$.start = fillStart($1); $$.end = fillEnd($3); }
530 | 'P' duration '/' date { $$.start = fillEnd($4)-$2; $$.end = fillEnd($4); }
531 | date '/' 'P' duration { $$.start = fillStart($1); $$.end = fillStart($1)+$4; }
532 ;
533</verbatim>
534an =interval= may be followed by a timezone specification string (this is not supported yet).
535
536=duration= has the form (regular expression):
537<verbatim>
538 P(<number><nameOfDuration>)+
539</verbatim>
540
541nameOfDuration may be one of:
542 * y(year), m(month), w(week), d(day), h(hour), M(minute), S(second)
543
544=date= follows ISO8601 and must include hyphens. (any amount of trailing
545 elements may be omitted and will be filled in differently on the
546 differents ends of the interval as to include the longest possible
547 interval):
548
549 * 2001-01-01T00:00:00
550 * 2001-12-31T23:59:59
551
552timezone is optional. Default is local time.
553
554If the format is not recognised, will return empty interval [0,0].
555
556=cut
557
558# TODO: timezone testing, especially on non valid strings
559
560sub parseInterval {
561 my ($interval) = @_;
562 my @lt = localtime();
563 my $today = sprintf( '%04d-%02d-%02d', $lt[5] + 1900, $lt[4] + 1, $lt[3] );
564 my $now = $today . sprintf( 'T%02d:%02d:%02d', $lt[2], $lt[1], $lt[0] );
565
566 ASSERT( defined $interval ) if DEBUG;
567
568 # replace $now and $today shortcuts
569 $interval =~ s/\$today/$today/g;
570 $interval =~ s/\$now/$now/g;
571
572 # if $theDate does not contain a '/': force it to do so.
573 $interval = $interval . '/' . $interval
574 unless ( $interval =~ /\// );
575
576 my ( $first, $last ) = split( /\//, $interval, 2 );
577 my ( $start, $end );
578
579 # first translate dates into seconds from epoch,
580 # in the second loop we will examine interval durations.
581
582 if ( $first !~ /^P/ ) {
583
584 # complete with parts from "-01-01T00:00:00"
585 if ( length($first) < length('0000-01-01T00:00:00') ) {
586 $first .= substr( '0000-01-01T00:00:00', length($first) );
587 }
588 $start = parseTime( $first, 1 );
589 }
590
591 if ( $last !~ /^P/ ) {
592
593 # complete with parts from "-12-31T23:59:60"
594 # check last day of month
595 if ( length($last) == 7 ) {
596 my $month = substr( $last, 5 );
597 my $year = substr( $last, 0, 4 );
598 my $monthlength = $MONTHLENS[ $month - 1 ];
599
600 # If leap year, note February is month number 2 here
601 $monthlength = 29 if ( $month == 2 && _daysInYear($year) == 366 );
602 $last .= '-' . $monthlength;
603 }
604 if ( length($last) < length('0000-12-31T23:59:59') ) {
605 $last .= substr( '0000-12-31T23:59:59', length($last) );
606 }
607 $end = parseTime( $last, 1 );
608 }
609
610 if ( !defined($start) ) {
611 $start = ( $end || 0 ) - _parseDuration($first);
612 }
613 if ( !defined($end) ) {
614 $end = $start + _parseDuration($last);
615 }
616 return ( $start || 0, $end || 0 );
617}
618
619sub _parseDuration {
620 my $s = shift;
621 my $d = 0;
622 $s =~ s/(\d+)y/$d += $1 * 31556925;''/gei; # tropical year
623 $s =~ s/(\d+)m/$d += $1 * 2592000; ''/ge; # 1m = 30 days
624 $s =~ s/(\d+)w/$d += $1 * 604800; ''/gei; # 1w = 7 days
625 $s =~ s/(\d+)d/$d += $1 * 86400; ''/gei; # 1d = 24 hours
626 $s =~ s/(\d+)h/$d += $1 * 3600; ''/gei; # 1 hour = 60 mins
627 $s =~ s/(\d+)M/$d += $1 * 60; ''/ge; # note: m != M
628 $s =~ s/(\d+)S/$d += $1 * 1; ''/gei;
629 return $d;
630}
631
632117µs1;
633__END__
 
# spent 166µs within Foswiki::Time::CORE:match which was called 72 times, avg 2µs/call: # 23 times (54µs+0s) by Foswiki::Time::formatTime at line 271, avg 2µs/call # 23 times (48µs+0s) by Foswiki::Time::formatTime at line 322, avg 2µs/call # 23 times (37µs+0s) by Foswiki::Time::formatTime at line 339, avg 2µs/call # once (17µs+0s) by Foswiki::Time::parseTime at line 172 # once (8µs+0s) by Foswiki::Time::parseTime at line 132 # once (2µs+0s) by Foswiki::Time::parseTime at line 146
sub Foswiki::Time::CORE:match; # opcode
# spent 728µs within Foswiki::Time::CORE:subst which was called 371 times, avg 2µs/call: # 23 times (62µs+0s) by Foswiki::Time::formatTime at line 292, avg 3µs/call # 23 times (53µs+0s) by Foswiki::Time::formatTime at line 320, avg 2µs/call # 23 times (53µs+0s) by Foswiki::Time::formatTime at line 302, avg 2µs/call # 23 times (49µs+0s) by Foswiki::Time::formatTime at line 309, avg 2µs/call # 23 times (48µs+0s) by Foswiki::Time::formatTime at line 318, avg 2µs/call # 23 times (48µs+0s) by Foswiki::Time::formatTime at line 316, avg 2µs/call # 23 times (48µs+0s) by Foswiki::Time::formatTime at line 312, avg 2µs/call # 23 times (47µs+0s) by Foswiki::Time::formatTime at line 317, avg 2µs/call # 23 times (44µs+0s) by Foswiki::Time::formatTime at line 310, avg 2µs/call # 23 times (42µs+0s) by Foswiki::Time::formatTime at line 305, avg 2µs/call # 23 times (42µs+0s) by Foswiki::Time::formatTime at line 311, avg 2µs/call # 23 times (40µs+0s) by Foswiki::Time::formatTime at line 288, avg 2µs/call # 23 times (34µs+0s) by Foswiki::Time::formatTime at line 315, avg 1µs/call # 23 times (34µs+0s) by Foswiki::Time::formatTime at line 319, avg 1µs/call # 23 times (32µs+0s) by Foswiki::Time::formatTime at line 313, avg 1µs/call # 23 times (32µs+0s) by Foswiki::Time::formatTime at line 314, avg 1µs/call # once (8µs+0s) by Foswiki::Time::parseTime at line 118 # once (8µs+0s) by Foswiki::Time::parseTime at line 119 # once (5µs+0s) by Foswiki::Time::formatTime at line 361
sub Foswiki::Time::CORE:subst; # opcode
# spent 113µs within Foswiki::Time::CORE:substcont which was called 46 times, avg 2µs/call: # 10 times (23µs+0s) by Foswiki::Time::formatTime at line 312, avg 2µs/call # 10 times (20µs+0s) by Foswiki::Time::formatTime at line 318, avg 2µs/call # 8 times (17µs+0s) by Foswiki::Time::formatTime at line 310, avg 2µs/call # 8 times (17µs+0s) by Foswiki::Time::formatTime at line 311, avg 2µs/call # 6 times (27µs+0s) by Foswiki::Time::formatTime at line 309, avg 5µs/call # 2 times (4µs+0s) by Foswiki::Time::formatTime at line 317, avg 2µs/call # 2 times (4µs+0s) by Foswiki::Time::formatTime at line 361, avg 2µs/call
sub Foswiki::Time::CORE:substcont; # opcode