Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
94.17% |
113 / 120 |
|
75.00% |
12 / 16 |
CRAP | |
0.00% |
0 / 1 |
ConvertibleTimestamp | |
94.17% |
113 / 120 |
|
75.00% |
12 / 16 |
64.81 | |
0.00% |
0 / 1 |
time | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
microtime | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
3 | |||
hrtime | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
setFakeTime | |
93.75% |
15 / 16 |
|
0.00% |
0 / 1 |
7.01 | |||
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setTimestamp | |
95.24% |
40 / 42 |
|
0.00% |
0 / 1 |
25 | |||
convert | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
now | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getTimestamp | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
8 | |||
__toString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
diff | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
add | |
66.67% |
4 / 6 |
|
0.00% |
0 / 1 |
3.33 | |||
sub | |
66.67% |
4 / 6 |
|
0.00% |
0 / 1 |
3.33 | |||
setTimezone | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getTimezone | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
format | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Timestamp |
4 | * |
5 | * Copyright (C) 2012 Tyler Romeo <tylerromeo@gmail.com> |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | * |
12 | * This program is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | * GNU General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU General Public License along |
18 | * with this program; if not, write to the Free Software Foundation, Inc., |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
20 | * http://www.gnu.org/copyleft/gpl.html |
21 | * |
22 | * @file |
23 | * @author Tyler Romeo <tylerromeo@gmail.com> |
24 | */ |
25 | |
26 | namespace Wikimedia\Timestamp; |
27 | |
28 | use DateInterval; |
29 | use DateTime; |
30 | use DateTimeZone; |
31 | use Exception; |
32 | use InvalidArgumentException; |
33 | use ValueError; |
34 | |
35 | /** |
36 | * Library for creating, parsing, and converting timestamps. |
37 | */ |
38 | class ConvertibleTimestamp { |
39 | /** |
40 | * Standard gmdate() formats for the different timestamp types. |
41 | * @var string[] |
42 | */ |
43 | private static $formats = [ |
44 | TS_UNIX => 'U', |
45 | TS_MW => 'YmdHis', |
46 | TS_DB => 'Y-m-d H:i:s', |
47 | TS_ISO_8601 => 'Y-m-d\TH:i:s\Z', |
48 | TS_ISO_8601_BASIC => 'Ymd\THis\Z', |
49 | // This shouldn't ever be used, but is included for completeness |
50 | TS_EXIF => 'Y:m:d H:i:s', |
51 | TS_RFC2822 => 'D, d M Y H:i:s', |
52 | // Was 'd-M-y h.i.s A' . ' +00:00' before r51500 |
53 | TS_ORACLE => 'd-m-Y H:i:s.u', |
54 | // Formerly 'Y-m-d H:i:s' . ' GMT' |
55 | TS_POSTGRES => 'Y-m-d H:i:s+00', |
56 | TS_UNIX_MICRO => 'U.u', |
57 | ]; |
58 | |
59 | /** |
60 | * Regexes for setTimestamp(). Named capture groups correspond to format codes for |
61 | * DateTime::createFromFormat(). Unnamed groups are ignored. |
62 | * @var string[] |
63 | */ |
64 | private static $regexes = [ |
65 | // 'TS_DB' => subset of TS_ISO_8601 (with no 'T') |
66 | 'TS_MW' => '/^(?<Y>\d{4})(?<m>\d\d)(?<d>\d\d)(?<H>\d\d)(?<i>\d\d)(?<s>\d\d)$/D', |
67 | 'TS_ISO_8601' => |
68 | '/^(?<Y>\d{4})-(?<m>\d{2})-(?<d>\d{2})[T ]' . |
69 | '(?<H>\d{2}):(?<i>\d{2}):(?<s>\d{2})(?:[.,](?<u>\d{1,6}))?' . |
70 | '(?<O>Z|[+\-]\d{2}(?::?\d{2})?)?$/', |
71 | 'TS_ISO_8601_BASIC' => |
72 | '/^(?<Y>\d{4})(?<m>\d{2})(?<d>\d{2})T(?<H>\d{2})(?<i>\d{2})(?<s>\d{2})(?:[.,](?<u>\d{1,6}))?' . |
73 | '(?<O>Z|[+\-]\d{2}(?::?\d{2})?)?$/', |
74 | 'TS_UNIX' => '/^(?<U>-?\d{1,13})$/D', |
75 | 'TS_UNIX_MICRO' => '/^(?<U>-?\d{1,13})\.(?<u>\d{1,6})$/D', |
76 | 'TS_ORACLE' => |
77 | '/^(?<d>\d{2})-(?<m>\d{2})-(?<Y>\d{4}) (?<H>\d{2}):(?<i>\d{2}):(?<s>\d{2})\.(?<u>\d{6})$/', |
78 | // TS_POSTGRES is almost redundant to TS_ISO_8601 (with no 'T'), but accepts a space in place of |
79 | // a `+` before the timezone. |
80 | 'TS_POSTGRES' => |
81 | '/^(?<Y>\d{4})-(?<m>\d\d)-(?<d>\d\d) (?<H>\d\d):(?<i>\d\d):(?<s>\d\d)(?:\.(?<u>\d{1,6}))?' . |
82 | '(?<O>[\+\- ]\d\d)$/', |
83 | 'old TS_POSTGRES' => |
84 | '/^(?<Y>\d{4})-(?<m>\d\d)-(?<d>\d\d) (?<H>\d\d):(?<i>\d\d):(?<s>\d\d)(?:\.(?<u>\d{1,6}))? GMT$/', |
85 | 'TS_EXIF' => '/^(?<Y>\d{4}):(?<m>\d\d):(?<d>\d\d) (?<H>\d\d):(?<i>\d\d):(?<s>\d\d)$/D', |
86 | |
87 | 'TS_RFC2822' => |
88 | # Day of the week |
89 | '/^[ \t\r\n]*(?:(?<D>[A-Z][a-z]{2}),[ \t\r\n]*)?' . |
90 | # dd Mon yyyy |
91 | '(?<d>\d\d?)[ \t\r\n]+(?<M>[A-Z][a-z]{2})[ \t\r\n]+(?<Y>\d{2,})' . |
92 | # hh:mm:ss |
93 | '[ \t\r\n]+(?<H>\d\d)[ \t\r\n]*:[ \t\r\n]*(?<i>\d\d)[ \t\r\n]*:[ \t\r\n]*(?<s>\d\d)' . |
94 | # zone, optional for hysterical raisins |
95 | '(?:[ \t\r\n]+(?<O>[+-]\d{4}|UT|GMT|[ECMP][SD]T|[A-IK-Za-ik-z]))?' . |
96 | # optional trailing comment |
97 | # See http://www.squid-cache.org/mail-archive/squid-users/200307/0122.html / r77171 |
98 | '(?:[ \t\r\n]*;|$)/S', |
99 | |
100 | 'TS_RFC850' => |
101 | '/^(?<D>[A-Z][a-z]{5,8}), (?<d>\d\d)-(?<M>[A-Z][a-z]{2})-(?<y>\d{2}) ' . |
102 | '(?<H>\d\d):(?<i>\d\d):(?<s>\d\d)' . |
103 | # timezone optional for hysterical raisins. RFC just says "worldwide time zone abbreviations". |
104 | # https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations lists strings of up to 5 |
105 | # uppercase letters. PHP 7.2's DateTimeZone::listAbbreviations() lists strings of up to 4 |
106 | # letters. |
107 | '(?: (?<O>[+\-]\d{2}(?::?\d{2})?|[A-Z]{1,5}))?$/', |
108 | |
109 | 'asctime' => '/^(?<D>[A-Z][a-z]{2}) (?<M>[A-Z][a-z]{2}) +(?<d>\d{1,2}) ' . |
110 | '(?<H>\d\d):(?<i>\d\d):(?<s>\d\d) (?<Y>\d{4})\s*$/', |
111 | ]; |
112 | |
113 | /** |
114 | * @var callback|null |
115 | * @see setFakeTime() |
116 | */ |
117 | protected static $fakeTimeCallback = null; |
118 | |
119 | /** |
120 | * Get the current time in the same form that PHP's built-in time() function uses. |
121 | * |
122 | * This is used by now() through setTimestamp( false ) instead of the built in time() function. |
123 | * The output of this method can be overwritten for testing purposes by calling setFakeTime(). |
124 | * |
125 | * @return int UNIX epoch |
126 | */ |
127 | public static function time() { |
128 | return static::$fakeTimeCallback ? (int)call_user_func( static::$fakeTimeCallback ) : \time(); |
129 | } |
130 | |
131 | /** |
132 | * Get the current time as seconds since the epoch, with sub-second precision. |
133 | * |
134 | * This is equivalent to calling PHP's built-in `microtime(true)`. |
135 | * The exact precision depends on the underlying operating system. |
136 | * |
137 | * You can overwrite microtime for testing purposes by calling setFakeTime(). |
138 | * In that case, this will re-use the fake time() value in seconds, and add a |
139 | * fake microsecond fraction based on an increasing counter. |
140 | * |
141 | * Repeated calls to microtime() are likely to return unique and increasing values. |
142 | * But, there is no guarantee of this, due to clock skew and clock drift correction. |
143 | * To measure time spent between two points in the code, use the ::hrtime() instead. |
144 | * |
145 | * @deprecated To measure relative duration, use ::hrtime(). For timestamps, use ::time(). |
146 | * @return float Seconds since the epoch |
147 | */ |
148 | public static function microtime(): float { |
149 | trigger_error( __METHOD__ . ' use ::hrtime() instead.', E_USER_DEPRECATED ); |
150 | |
151 | static $fakeSecond = 0; |
152 | static $fakeOffset = 0.0; |
153 | if ( static::$fakeTimeCallback ) { |
154 | $sec = static::time(); |
155 | if ( $sec !== $fakeSecond ) { |
156 | $fakeSecond = $sec; |
157 | $fakeOffset = 0.0; |
158 | } else { |
159 | $fakeOffset++; |
160 | } |
161 | return $fakeSecond + $fakeOffset * 0.000001; |
162 | } else { |
163 | return microtime( true ); |
164 | } |
165 | } |
166 | |
167 | /** |
168 | * Get the value of a monotonic clock in nanoseconds. |
169 | * |
170 | * This is equivalent to calling PHP's built-in `hrtime(true)`. Repeated calls to |
171 | * hrtime() are guruanteed to be equal to or higher than the previous call. It is |
172 | * designed to measure time between two calls, and is not affected by clock |
173 | * drift correction, and thus does not represent any kind of date or timestamp. |
174 | * |
175 | * The output of this method can be controlled for testing purposes by calling |
176 | * setFakeTime() with any value. In that case, hrtime() will always increment |
177 | * its arbitrary starting amount by 1 millisecond every time the function is |
178 | * called. |
179 | * |
180 | * @see https://www.php.net/hrtime |
181 | * @return int|float Monotonic nanoseconds since an arbitrary starting point |
182 | */ |
183 | public static function hrtime() { |
184 | static $fakeNanoMono = 41_999_000_000; |
185 | |
186 | if ( static::$fakeTimeCallback ) { |
187 | // Use a fake start. We ignore the actual fake time because: |
188 | // 1. Using it would make it easy to mistakenly use it as a timestamp. |
189 | // 2. Multiplying time() by 1e9 for nanoseconds since 1970 is larger than |
190 | // fits in float precision, thus causing all kinds of accuracy problems. |
191 | // |
192 | // We add 1 millisecond each time this method is called, so that it is always |
193 | // increasing, and ensures `b - a` gives a deterministic end result that can |
194 | // be strictly asserted in a unit test. |
195 | $fakeNanoMono += 1_000_000; |
196 | return $fakeNanoMono; |
197 | } else { |
198 | return hrtime( true ); |
199 | } |
200 | } |
201 | |
202 | /** |
203 | * Set a fake time value or clock callback. |
204 | * |
205 | * @param callable|ConvertibleTimestamp|string|int|false $fakeTime a fixed time given as a string, |
206 | * or as a number representing seconds since the UNIX epoch; or a callback that returns an int. |
207 | * or false to disable fake time and go back to real time. |
208 | * @param int|float $step The number of seconds by which to increment the clock each time |
209 | * the time() method is called. Must not be smaller than zero. |
210 | * Ignored if $fakeTime is a callback or false. |
211 | * |
212 | * @return callable|null the previous fake time callback, if any. |
213 | * |
214 | * @phan-param callable():int|ConvertibleTimestamp|string|int|false $fakeTime |
215 | */ |
216 | public static function setFakeTime( $fakeTime, $step = 0 ) { |
217 | if ( $fakeTime instanceof ConvertibleTimestamp ) { |
218 | $fakeTime = (int)$fakeTime->getTimestamp(); |
219 | } |
220 | |
221 | if ( is_string( $fakeTime ) ) { |
222 | $fakeTime = (int)static::convert( TS_UNIX, $fakeTime ); |
223 | } |
224 | |
225 | if ( is_int( $fakeTime ) ) { |
226 | $clock = $fakeTime; |
227 | $fakeTime = static function () use ( &$clock, $step ) { |
228 | $t = $clock; |
229 | $clock += $step; |
230 | return (int)$t; |
231 | }; |
232 | } |
233 | |
234 | if ( $fakeTime && !is_callable( $fakeTime ) ) { |
235 | throw new InvalidArgumentException( 'Bad fake time' ); |
236 | } |
237 | |
238 | $old = static::$fakeTimeCallback; |
239 | static::$fakeTimeCallback = $fakeTime ?: null; |
240 | return $old; |
241 | } |
242 | |
243 | /** |
244 | * The actual timestamp being wrapped (DateTime object). |
245 | * @var DateTime |
246 | */ |
247 | public $timestamp; |
248 | |
249 | /** |
250 | * Make a new timestamp and set it to the specified time, |
251 | * or the current time if unspecified. |
252 | * |
253 | * @param string|int|float|null|false|DateTime $timestamp Timestamp to set. |
254 | * If any falsy value is provided, the timestamp uses the current time instead. |
255 | * @throws TimestampException |
256 | */ |
257 | public function __construct( $timestamp = false ) { |
258 | if ( $timestamp instanceof DateTime ) { |
259 | $this->timestamp = $timestamp; |
260 | } else { |
261 | $this->setTimestamp( $timestamp ); |
262 | } |
263 | } |
264 | |
265 | /** |
266 | * Set the timestamp to the specified time, or the current time if unspecified. |
267 | * |
268 | * Parse the given timestamp into either a DateTime object or a Unix timestamp, |
269 | * and then store it. |
270 | * |
271 | * @param string|int|float|null|false $ts Timestamp to store. |
272 | * If any falsy value is provided, the timestamp uses the current time instead. |
273 | * @throws TimestampException |
274 | */ |
275 | public function setTimestamp( $ts = false ) { |
276 | $format = null; |
277 | $strtime = ''; |
278 | |
279 | // We want to catch 0, '', null... but not date strings starting with a letter. |
280 | if ( !$ts || $ts === "\0\0\0\0\0\0\0\0\0\0\0\0\0\0" ) { |
281 | $strtime = (string)self::time(); |
282 | $format = 'U'; |
283 | } else { |
284 | foreach ( self::$regexes as $name => $regex ) { |
285 | if ( !preg_match( $regex, $ts, $m ) ) { |
286 | continue; |
287 | } |
288 | |
289 | // Apply RFC 2626 § 11.2 rules for fixing a 2-digit year. |
290 | // We apply by year as written, without regard for |
291 | // offset within the year or timezone of the input date. |
292 | if ( isset( $m['y'] ) ) { |
293 | $pivot = (int)gmdate( 'Y', static::time() ) + 50; |
294 | $m['Y'] = $pivot - ( $pivot % 100 ) + (int)$m['y']; |
295 | if ( $m['Y'] > $pivot ) { |
296 | $m['Y'] -= 100; |
297 | } |
298 | unset( $m['y'] ); |
299 | } |
300 | |
301 | // TS_POSTGRES's match for 'O' can begin with a space, which PHP doesn't accept |
302 | if ( $name === 'TS_POSTGRES' && isset( $m['O'] ) && $m['O'][0] === ' ' ) { |
303 | $m['O'][0] = '+'; |
304 | } |
305 | |
306 | if ( $name === 'TS_RFC2822' ) { |
307 | // RFC 2822 rules for two- and three-digit years |
308 | if ( $m['Y'] < 1000 ) { |
309 | $m['Y'] += $m['Y'] < 50 ? 2000 : 1900; |
310 | } |
311 | |
312 | // TS_RFC2822 timezone fixups |
313 | if ( isset( $m['O'] ) ) { |
314 | // obs-zone value not recognized by PHP |
315 | if ( $m['O'] === 'UT' ) { |
316 | $m['O'] = 'UTC'; |
317 | } |
318 | |
319 | // RFC 2822 says all these should be treated as +0000 due to an error in RFC 822 |
320 | if ( strlen( $m['O'] ) === 1 ) { |
321 | $m['O'] = '+0000'; |
322 | } |
323 | } |
324 | } |
325 | |
326 | if ( $name === 'TS_UNIX_MICRO' && $m['U'] < 0 && $m['u'] > 0 ) { |
327 | // createFromFormat()'s componentwise behavior is counterintuitive in this case, "-1.2" gets |
328 | // interpreted as "-1 seconds + 200000 microseconds = -0.8 seconds" rather than as a decimal |
329 | // "-1.2 seconds" like we want. So correct the values to match the componentwise |
330 | // interpretation. |
331 | $m['U']--; |
332 | $m['u'] = 1000000 - (int)str_pad( $m['u'], 6, '0' ); |
333 | } |
334 | |
335 | $filtered = []; |
336 | foreach ( $m as $k => $v ) { |
337 | if ( !is_int( $k ) && $v !== '' ) { |
338 | $filtered[$k] = $v; |
339 | } |
340 | } |
341 | $format = implode( ' ', array_keys( $filtered ) ); |
342 | $strtime = implode( ' ', array_values( $filtered ) ); |
343 | |
344 | break; |
345 | } |
346 | } |
347 | |
348 | if ( $format === null ) { |
349 | throw new TimestampException( __METHOD__ . ": Invalid timestamp - $ts" ); |
350 | } |
351 | |
352 | try { |
353 | $final = DateTime::createFromFormat( "!$format", $strtime, new DateTimeZone( 'UTC' ) ); |
354 | } catch ( ValueError $e ) { |
355 | throw new TimestampException( __METHOD__ . ': Invalid timestamp format.', $e->getCode(), $e ); |
356 | } |
357 | |
358 | if ( $final === false ) { |
359 | throw new TimestampException( __METHOD__ . ': Invalid timestamp format.' ); |
360 | } |
361 | |
362 | $this->timestamp = $final; |
363 | } |
364 | |
365 | /** |
366 | * Converts any timestamp to the given string format. |
367 | * This is identical to `( new ConvertibleTimestamp() )->getTimestamp()`, |
368 | * except it returns false instead of throwing an exception. |
369 | * |
370 | * @param int $style Constant Output format for timestamp |
371 | * @param string|int|float|null|false|DateTime $ts Timestamp |
372 | * @return string|false Formatted timestamp or false on failure |
373 | */ |
374 | public static function convert( $style, $ts ) { |
375 | try { |
376 | $ct = new static( $ts ); |
377 | return $ct->getTimestamp( $style ); |
378 | } catch ( TimestampException $e ) { |
379 | return false; |
380 | } |
381 | } |
382 | |
383 | /** |
384 | * Get the current time in the given format |
385 | * |
386 | * @param int $style Constant Output format for timestamp |
387 | * @return string |
388 | */ |
389 | public static function now( $style = TS_MW ) { |
390 | return static::convert( $style, false ); |
391 | } |
392 | |
393 | /** |
394 | * Get the timestamp represented by this object in a certain form. |
395 | * |
396 | * Convert the internal timestamp to the specified format and then |
397 | * return it. |
398 | * |
399 | * @param int $style Constant Output format for timestamp |
400 | * @throws TimestampException |
401 | * @return string The formatted timestamp |
402 | */ |
403 | public function getTimestamp( $style = TS_UNIX ) { |
404 | if ( !isset( self::$formats[$style] ) ) { |
405 | throw new TimestampException( __METHOD__ . ': Illegal timestamp output type.' ); |
406 | } |
407 | |
408 | // All our formats are in UTC, so make sure to use that timezone |
409 | $timestamp = clone $this->timestamp; |
410 | $timestamp->setTimezone( new DateTimeZone( 'UTC' ) ); |
411 | |
412 | if ( $style === TS_UNIX_MICRO ) { |
413 | $seconds = (int)$timestamp->format( 'U' ); |
414 | $microseconds = (int)$timestamp->format( 'u' ); |
415 | if ( $seconds < 0 && $microseconds > 0 ) { |
416 | // Adjust components to properly create a decimal number for TS_UNIX_MICRO and negative |
417 | // timestamps. See the comment in setTimestamp() for details. |
418 | $seconds++; |
419 | $microseconds = 1000000 - $microseconds; |
420 | } |
421 | return sprintf( "%d.%06d", $seconds, $microseconds ); |
422 | } |
423 | |
424 | $output = $timestamp->format( self::$formats[$style] ); |
425 | |
426 | if ( $style == TS_RFC2822 ) { |
427 | $output .= ' GMT'; |
428 | } |
429 | |
430 | if ( $style == TS_MW && strlen( $output ) !== 14 ) { |
431 | throw new TimestampException( __METHOD__ . ': The timestamp cannot be represented in ' . |
432 | 'the specified format' ); |
433 | } |
434 | |
435 | return $output; |
436 | } |
437 | |
438 | /** |
439 | * @return string |
440 | * @throws TimestampException |
441 | */ |
442 | public function __toString() { |
443 | return $this->getTimestamp(); |
444 | } |
445 | |
446 | /** |
447 | * Calculate the difference between two ConvertibleTimestamp objects. |
448 | * |
449 | * @param ConvertibleTimestamp $relativeTo Base time to calculate difference from |
450 | * @return DateInterval|false The DateInterval object representing the |
451 | * difference between the two dates or false on failure |
452 | */ |
453 | public function diff( ConvertibleTimestamp $relativeTo ) { |
454 | return $this->timestamp->diff( $relativeTo->timestamp ); |
455 | } |
456 | |
457 | /** |
458 | * Add an interval to the timestamp. |
459 | * @param DateInterval|string $interval DateInterval or DateInterval specification (such as "P2D") |
460 | * @return $this |
461 | * @throws TimestampException |
462 | */ |
463 | public function add( $interval ) { |
464 | if ( is_string( $interval ) ) { |
465 | try { |
466 | $interval = new DateInterval( $interval ); |
467 | } catch ( Exception $e ) { |
468 | throw new TimestampException( __METHOD__ . ': Invalid interval.', $e->getCode(), $e ); |
469 | } |
470 | } |
471 | $this->timestamp->add( $interval ); |
472 | return $this; |
473 | } |
474 | |
475 | /** |
476 | * Subtract an interval from the timestamp. |
477 | * @param DateInterval|string $interval DateInterval or DateInterval specification (such as "P2D") |
478 | * @return $this |
479 | * @throws TimestampException |
480 | */ |
481 | public function sub( $interval ) { |
482 | if ( is_string( $interval ) ) { |
483 | try { |
484 | $interval = new DateInterval( $interval ); |
485 | } catch ( Exception $e ) { |
486 | throw new TimestampException( __METHOD__ . ': Invalid interval.', $e->getCode(), $e ); |
487 | } |
488 | } |
489 | $this->timestamp->sub( $interval ); |
490 | return $this; |
491 | } |
492 | |
493 | /** |
494 | * Set the timezone of this timestamp to the specified timezone. |
495 | * |
496 | * @param string $timezone Timezone to set |
497 | * @throws TimestampException |
498 | */ |
499 | public function setTimezone( $timezone ) { |
500 | try { |
501 | $this->timestamp->setTimezone( new DateTimeZone( $timezone ) ); |
502 | } catch ( Exception $e ) { |
503 | throw new TimestampException( __METHOD__ . ': Invalid timezone.', $e->getCode(), $e ); |
504 | } |
505 | } |
506 | |
507 | /** |
508 | * Get the timezone of this timestamp. |
509 | * |
510 | * @return DateTimeZone The timezone |
511 | */ |
512 | public function getTimezone() { |
513 | return $this->timestamp->getTimezone(); |
514 | } |
515 | |
516 | /** |
517 | * Format the timestamp in a given format. |
518 | * |
519 | * @param string $format Pattern to format in |
520 | * @return string The formatted timestamp |
521 | */ |
522 | public function format( $format ) { |
523 | return $this->timestamp->format( $format ); |
524 | } |
525 | } |