Merge pull request #19069 from eileenmcnaughton/test
[civicrm-core.git] / CRM / Utils / Time.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17
18 /**
19 * Date time utilties
20 */
21 class CRM_Utils_Time {
22
23 /**
24 * A function which determines the current time.
25 * Only used during testing (with mocked time).
26 *
27 * The normal value, NULL, indicates the use of real time.
28 *
29 * @var callable|null
30 */
31 static private $callback = NULL;
32
33 /**
34 * Evaluate a time expression (relative to current time).
35 *
36 * @param string $str
37 * Ex: '2001-02-03 04:05:06' or '+2 days'
38 * @param string|int $now
39 * For relative time strings, $now determines the base time.
40 * @return false|int
41 * The indicated time (seconds since epoch)
42 * @see strtotime()
43 */
44 public static function strtotime($str, $now = 'time()') {
45 if ($now === NULL || $now === 'time()') {
46 $now = self::time();
47 }
48 return strtotime($str, $now);
49 }
50
51 /**
52 * Format a date/time expression.
53 *
54 * @param string $format
55 * Ex: 'Y-m-d H:i:s'
56 * @param null|int $timestamp
57 * The time (seconds since epoch). NULL will use current time.
58 * @return string
59 * Ex: '2001-02-03 04:05:06'
60 * @see date()
61 */
62 public static function date($format, $timestamp = NULL) {
63 return date($format, $timestamp ?: self::time());
64 }
65
66 /**
67 * Get the time.
68 *
69 * @return int
70 * seconds since epoch
71 * @see time()
72 */
73 public static function time() {
74 return self::$callback === NULL ? time() : call_user_func(self::$callback);
75 }
76
77 /**
78 * Get the time.
79 *
80 * @param string $returnFormat
81 * Format in which date is to be retrieved.
82 *
83 * @return string
84 * @deprecated
85 * Prefer CRM_Utils_Time::date(), whose name looks similar to the stdlib work-a-like.
86 */
87 public static function getTime($returnFormat = 'YmdHis') {
88 return date($returnFormat, self::time());
89 }
90
91 /**
92 * Get the time.
93 *
94 * @return int
95 * seconds since epoch
96 * @deprecated
97 * Prefer CRM_Utils_Time::time(), whose name looks similar to the stdlib work-a-like.
98 */
99 public static function getTimeRaw() {
100 return self::time();
101 }
102
103 /**
104 * Set the given time.
105 *
106 * @param string $newDateTime
107 * A date formatted with strtotime.
108 * @param string $returnFormat
109 * Format in which date is to be retrieved.
110 *
111 * Note: The progression of time will be influenced by TIME_FUNC, which may be:
112 * - 'frozen' (time does not move)
113 * - 'natural' (time moves naturally)
114 * - 'linear:XXX' (time moves in increments of XXX milliseconds - with every lookup)
115 * - 'prng:XXX' (time moves by random increments, between 0 and XXX milliseconds)
116 * @return string
117 */
118 public static function setTime($newDateTime, $returnFormat = 'YmdHis') {
119 $mode = getenv('TIME_FUNC') ? getenv('TIME_FUNC') : 'natural';
120
121 list ($modeName, $modeNum) = explode(":", "$mode:");
122
123 switch ($modeName) {
124 case 'frozen':
125 // Every getTime() will produce the same value (ie $newDateTime).
126 $now = strtotime($newDateTime);
127 self::$callback = function () use ($now) {
128 return $now;
129 };
130 break;
131
132 case 'natural':
133 // Time changes to $newDateTime and then proceeds naturally.
134 $delta = strtotime($newDateTime) - time();
135 self::$callback = function () use ($delta) {
136 return time() + $delta;
137 };
138 break;
139
140 case 'linear':
141 // Time changes to $newDateTime and then proceeds in fixed increments ($modeNum milliseconds).
142 $incr = ($modeNum / 1000.0);
143 $now = (float) strtotime($newDateTime) - $incr;
144 self::$callback = function () use (&$now, $incr) {
145 $now += $incr;
146 return floor($now);
147 };
148 break;
149
150 case 'prng':
151 // Time changes to $newDateTime and then proceeds using deterministic pseudorandom increments (of up to $modeNum milliseconds).
152 $seed = md5($newDateTime . chr(0) . $mode, TRUE);
153 $now = (float) strtotime($newDateTime);
154 self::$callback = function () use (&$seed, &$now, $modeNum) {
155 $mod = gmp_strval(gmp_mod(gmp_import($seed), "$modeNum"));
156 $seed = md5($seed . $now, TRUE);
157 $now = $now + ($mod / 1000.0);
158 return floor($now);
159 };
160 break;
161
162 default:
163 throw new \RuntimeException("Unrecognized TIME_FUNC ($mode)");
164 }
165
166 return self::getTime($returnFormat);
167 }
168
169 /**
170 * Remove any time overrides.
171 */
172 public static function resetTime() {
173 self::$callback = NULL;
174 }
175
176 /**
177 * Approximate time-comparison. $a and $b are considered equal if they
178 * are within $threshold seconds of each other.
179 *
180 * @param string $a
181 * Time which can be parsed by strtotime.
182 * @param string $b
183 * Time which can be parsed by strtotime.
184 * @param int $threshold
185 * Maximum allowed difference (in seconds).
186 * @return bool
187 */
188 public static function isEqual($a, $b, $threshold = 0) {
189 $diff = strtotime($b) - strtotime($a);
190 return (abs($diff) <= $threshold);
191 }
192
193 }