Filename | /usr/local/src/github.com/foswiki/core/lib/Foswiki/Time.pm |
Statements | Executed 756 statements in 8.25ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
23 | 6 | 3 | 3.07ms | 4.12ms | formatTime | Foswiki::Time::
1 | 1 | 1 | 2.04ms | 3.06ms | parseTime | Foswiki::Time::
371 | 19 | 1 | 728µs | 728µs | CORE:subst (opcode) | Foswiki::Time::
72 | 6 | 1 | 166µs | 166µs | CORE:match (opcode) | Foswiki::Time::
46 | 7 | 1 | 113µs | 113µs | CORE:substcont (opcode) | Foswiki::Time::
1 | 1 | 1 | 25µs | 33µs | BEGIN@35 | Foswiki::Time::
1 | 1 | 1 | 18µs | 42µs | BEGIN@36 | Foswiki::Time::
1 | 1 | 1 | 17µs | 60µs | BEGIN@38 | Foswiki::Time::
1 | 1 | 1 | 9µs | 9µs | BEGIN@39 | Foswiki::Time::
0 | 0 | 0 | 0s | 0s | _daysInYear | Foswiki::Time::
0 | 0 | 0 | 0s | 0s | _parseDuration | Foswiki::Time::
0 | 0 | 0 | 0s | 0s | _tzOffset | Foswiki::Time::
0 | 0 | 0 | 0s | 0s | _weekNumber | Foswiki::Time::
0 | 0 | 0 | 0s | 0s | formatDelta | Foswiki::Time::
0 | 0 | 0 | 0s | 0s | parseInterval | Foswiki::Time::
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 | |||||
7 | Time handling functions. | ||||
8 | |||||
9 | API version $Date$ (revision $Rev$) | ||||
10 | |||||
11 | *Since* _date_ indicates where functions or parameters have been added since | ||||
12 | the baseline of the API (TWiki release 4.2.3). The _date_ indicates the | ||||
13 | earliest date of a Foswiki release that will support that function or | ||||
14 | parameter. | ||||
15 | |||||
16 | *Deprecated* _date_ indicates where a function or parameters has been | ||||
17 | [[http://en.wikipedia.org/wiki/Deprecation][deprecated]]. Deprecated | ||||
18 | functions will still work, though they should | ||||
19 | _not_ be called in new plugins and should be replaced in older plugins | ||||
20 | as soon as possible. Deprecated parameters are simply ignored in Foswiki | ||||
21 | releases after _date_. | ||||
22 | |||||
23 | *Until* _date_ indicates where a function or parameter has been removed. | ||||
24 | The _date_ indicates the latest date at which Foswiki releases still supported | ||||
25 | the 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 | |||||
33 | package Foswiki::Time; | ||||
34 | |||||
35 | 2 | 45µs | 2 | 41µ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 # spent 33µs making 1 call to Foswiki::Time::BEGIN@35
# spent 8µs making 1 call to strict::import |
36 | 2 | 45µs | 2 | 66µ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 # spent 42µs making 1 call to Foswiki::Time::BEGIN@36
# spent 24µs making 1 call to warnings::import |
37 | |||||
38 | 2 | 43µs | 2 | 104µ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 # spent 60µs making 1 call to Foswiki::Time::BEGIN@38
# spent 43µs making 1 call to Assert::import |
39 | 2 | 3.80ms | 1 | 9µs | # spent 9µs within Foswiki::Time::BEGIN@39 which was called:
# once (9µs+0s) by Foswiki::BEGIN@609 at line 39 # spent 9µs making 1 call to Foswiki::Time::BEGIN@39 |
40 | |||||
41 | 1 | 3µs | our $VERSION = '$Rev$'; # Subversion rev number | ||
42 | |||||
43 | # Constants | ||||
44 | 1 | 6µs | our @ISOMONTH = ( | ||
45 | 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', | ||||
46 | 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' | ||||
47 | ); | ||||
48 | |||||
49 | 1 | 4µs | our @MONTHLENS = ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ); | ||
50 | |||||
51 | 1 | 3µs | our @WEEKDAY = ( 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ); | ||
52 | |||||
53 | 1 | 8µs | our %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 | |||||
68 | 1 | 1µs | our $TZSTRING; # timezone string for servertime; "Z" or "+01:00" etc. | ||
69 | |||||
70 | =begin TML | ||||
71 | |||||
72 | ---++ StaticMethod parseTime( $szDate, $defaultLocal ) -> $iSecs | ||||
73 | |||||
74 | Convert string date/time string to seconds since epoch (1970-01-01T00:00:00Z). | ||||
75 | * =$sDate= - date/time string | ||||
76 | |||||
77 | Handles the following formats: | ||||
78 | |||||
79 | Default Foswiki format | ||||
80 | * 31 Dec 2001 - 23:59 | ||||
81 | * 31-Dec-2001 - 23:59 | ||||
82 | |||||
83 | Foswiki format without time (defaults to 00:00) | ||||
84 | * 31 Dec 2001 | ||||
85 | * 31-Dec-2001 | ||||
86 | |||||
87 | Date separated by '/', '.' or '-', time with '.' or ':' | ||||
88 | Date 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 | |||||
99 | ISO format | ||||
100 | * 2001-12-31T23:59:59 | ||||
101 | * 2001-12-31T | ||||
102 | |||||
103 | ISO dates may have a timezone specifier, either Z or a signed difference | ||||
104 | in hh:mm format. For example: | ||||
105 | * 2001-12-31T23:59:59+01:00 | ||||
106 | * 2001-12-31T23:59Z | ||||
107 | The default timezone is Z, unless $defaultLocal is true in which case | ||||
108 | the local timezone will be assumed. | ||||
109 | |||||
110 | If 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 | ||||
115 | 25 | 291µs | my ( $date, $defaultLocal ) = @_; | ||
116 | |||||
117 | 1 | 4µs | ASSERT( defined $date ) if DEBUG; # spent 4µs making 1 call to Assert::ASSERTS_OFF | ||
118 | 1 | 8µs | $date =~ s/^\s*//; #remove leading spaces without de-tainting. # spent 8µs making 1 call to Foswiki::Time::CORE:subst | ||
119 | 1 | 8µs | $date =~ s/\s*$//; # spent 8µs making 1 call to Foswiki::Time::CORE:subst | ||
120 | |||||
121 | require Time::Local; | ||||
122 | |||||
123 | # NOTE: This routine *will break* if input is not one of below formats! | ||||
124 | 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 | ||||
132 | 1 | 8µ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. | ||||
146 | 1 | 2µ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) | ||||
172 | 1 | 17µ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 | ||||
195 | 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 | ||||
203 | $year -= 1900 if ( $year > 1900 ); | ||||
204 | |||||
205 | #range checks | ||||
206 | return undef if ( defined($M) && ( $M < 1 || $M > 12 ) ); | ||||
207 | my $month = ( $M || 1 ) - 1; | ||||
208 | my $monthlength = $MONTHLENS[$month]; | ||||
209 | |||||
210 | # If leap year, note February is month number 1 starting from 0 | ||||
211 | $monthlength = 29 if ( $month == 1 && _daysInYear($year) == 366 ); | ||||
212 | return undef if ( defined($D) && ( $D < 0 || $D > $monthlength ) ); | ||||
213 | return undef if ( defined($h) && ( $h < 0 || $h > 24 ) ); | ||||
214 | return undef if ( defined($m) && ( $m < 0 || $m > 60 ) ); | ||||
215 | return undef if ( defined($s) && ( $s < 0 || $s > 60 ) ); | ||||
216 | return undef if ( defined($year) && $year < 60 ); | ||||
217 | |||||
218 | my $day = $D || 1; | ||||
219 | my $hour = $h || 0; | ||||
220 | my $min = $m || 0; | ||||
221 | my $sec = $s || 0; | ||||
222 | |||||
223 | 1 | 51µ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 | ||||
262 | 716 | 3.97ms | my ( $epochSeconds, $formatString, $outputTimeZone ) = @_; | ||
263 | my $value = $epochSeconds; | ||||
264 | |||||
265 | 23 | 82µ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 | ||||
268 | $formatString ||= '$longdate'; | ||||
269 | $outputTimeZone ||= $Foswiki::cfg{DisplayTimeValues}; | ||||
270 | |||||
271 | 23 | 54µ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 | |||||
275 | my ( $sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdst ); | ||||
276 | if ( $outputTimeZone eq 'servertime' ) { | ||||
277 | ( $sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdst ) = | ||||
278 | localtime($epochSeconds); | ||||
279 | } | ||||
280 | else { | ||||
281 | ( $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" | ||||
288 | 23 | 40µ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 | ||||
292 | 23 | 62µ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' | ||||
298 | $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' | ||||
302 | 23 | 53µ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 | ||||
305 | 23 | 42µ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 | |||||
308 | $value = $formatString; | ||||
309 | 29 | 76µ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 | ||
310 | 31 | 61µ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 | ||
311 | 31 | 59µ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 | ||
312 | 33 | 71µ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 | ||
313 | 23 | 32µs | $value =~ s/\$wday/$WEEKDAY[$wday]/gi; # spent 32µs making 23 calls to Foswiki::Time::CORE:subst, avg 1µs/call | ||
314 | 23 | 32µs | $value =~ s/\$dow/$wday/gi; # spent 32µs making 23 calls to Foswiki::Time::CORE:subst, avg 1µs/call | ||
315 | 23 | 34µ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 | ||
316 | 23 | 48µs | $value =~ s/\$mont?h?/$ISOMONTH[$mon]/gi; # spent 48µs making 23 calls to Foswiki::Time::CORE:subst, avg 2µs/call | ||
317 | 25 | 51µ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 | ||
318 | 33 | 69µ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 | ||
319 | 23 | 34µs | $value =~ s/\$ye/sprintf('%.2u',$year%100)/gei; # spent 34µs making 23 calls to Foswiki::Time::CORE:subst, avg 1µs/call | ||
320 | 23 | 53µs | $value =~ s/\$epoch/$epochSeconds/gi; # spent 53µs making 23 calls to Foswiki::Time::CORE:subst, avg 2µs/call | ||
321 | |||||
322 | 23 | 48µ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 | } | ||||
339 | 23 | 37µs | if ( $value =~ /\$isotz/ ) { # spent 37µs making 23 calls to Foswiki::Time::CORE:match, avg 2µs/call | ||
340 | my $tz_str = 'Z'; | ||||
341 | 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 | } | ||||
361 | 3 | 9µ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 | |||||
364 | 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. | ||||
372 | sub _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 | ||||
410 | sub _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... | ||||
445 | sub _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 | |||||
456 | Format a time in seconds as a string. For example, | ||||
457 | "1 day, 3 hours, 2 minutes, 6 seconds" | ||||
458 | |||||
459 | =cut | ||||
460 | |||||
461 | sub 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 | |||||
520 | Convert string representing a time interval to a pair of integers | ||||
521 | representing the amount of seconds since epoch for the start and end | ||||
522 | extremes of the time interval. | ||||
523 | |||||
524 | * =$szInterval= - time interval string | ||||
525 | |||||
526 | in yacc syntax, grammar and actions: | ||||
527 | <verbatim> | ||||
528 | interval ::= 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> | ||||
534 | an =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 | |||||
541 | nameOfDuration 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 | |||||
552 | timezone is optional. Default is local time. | ||||
553 | |||||
554 | If 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 | |||||
560 | sub 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 | |||||
619 | sub _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 | |||||
632 | 1 | 17µs | 1; | ||
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 | |||||
# 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 | |||||
# 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 |