1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Tests legacy Moodle date/time functions.
19 *
20 * @package   core
21 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
22 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 * @author    Petr Skoda <petr.skoda@totaralms.com>
24 */
25
26defined('MOODLE_INTERNAL') || die();
27
28/**
29 * Tests legacy Moodle date/time functions.
30 *
31 * @package   core
32 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
33 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34 * @author    Petr Skoda <petr.skoda@totaralms.com>
35 */
36class core_date_legacy_testcase extends advanced_testcase {
37    public function test_settings() {
38        global $CFG;
39        $this->resetAfterTest();
40
41        $this->assertNotEmpty($CFG->timezone);
42
43        $this->assertSame('99', $CFG->forcetimezone);
44
45        $user = $this->getDataGenerator()->create_user();
46        $this->assertSame('99', $user->timezone);
47    }
48
49    public function test_get_user_timezone() {
50        global $CFG, $USER;
51
52        $this->resetAfterTest();
53
54        $user = $this->getDataGenerator()->create_user();
55        $this->setUser($user);
56
57        // All set to something.
58
59        $this->setTimezone('Pacific/Auckland', 'Pacific/Auckland');
60        $USER->timezone = 'Europe/Prague';
61
62        $tz = get_user_timezone();
63        $this->assertSame('Europe/Prague', $tz);
64
65        $tz = get_user_timezone(99);
66        $this->assertSame('Europe/Prague', $tz);
67        $tz = get_user_timezone('99');
68        $this->assertSame('Europe/Prague', $tz);
69
70        $tz = get_user_timezone('Europe/Berlin');
71        $this->assertSame('Europe/Berlin', $tz);
72
73        // User timezone not set.
74
75        $this->setTimezone('Pacific/Auckland', 'Pacific/Auckland');
76        $USER->timezone = '99';
77
78        $tz = get_user_timezone();
79        $this->assertSame('Pacific/Auckland', $tz);
80
81        $tz = get_user_timezone(99);
82        $this->assertSame('Pacific/Auckland', $tz);
83        $tz = get_user_timezone('99');
84        $this->assertSame('Pacific/Auckland', $tz);
85
86        $tz = get_user_timezone('Europe/Berlin');
87        $this->assertSame('Europe/Berlin', $tz);
88
89        // Server timezone not set.
90
91        $this->setTimezone('99', 'Pacific/Auckland');
92        $USER->timezone = 'Europe/Prague';
93
94        $tz = get_user_timezone();
95        $this->assertSame('Europe/Prague', $tz);
96
97        $tz = get_user_timezone(99);
98        $this->assertSame('Europe/Prague', $tz);
99        $tz = get_user_timezone('99');
100        $this->assertSame('Europe/Prague', $tz);
101
102        $tz = get_user_timezone('Europe/Berlin');
103        $this->assertSame('Europe/Berlin', $tz);
104
105        // Server and user timezone not set.
106
107        $this->setTimezone('99', 'Pacific/Auckland');
108        $USER->timezone = '99';
109
110        $tz = get_user_timezone();
111        $this->assertSame(99.0, $tz);
112
113        $tz = get_user_timezone(99);
114        $this->assertSame(99.0, $tz);
115        $tz = get_user_timezone('99');
116        $this->assertSame(99.0, $tz);
117
118        $tz = get_user_timezone('Europe/Berlin');
119        $this->assertSame('Europe/Berlin', $tz);
120    }
121
122    public function test_dst_offset_on() {
123        $time = gmmktime(1, 1, 1, 3, 1, 2015);
124        $this->assertSame(3600, dst_offset_on($time, 'Pacific/Auckland'));
125        $this->assertSame(0, dst_offset_on($time, 'Australia/Perth'));
126        $this->assertSame(1800, dst_offset_on($time, 'Australia/Lord_Howe'));
127        $this->assertSame(0, dst_offset_on($time, 'Europe/Prague'));
128        $this->assertSame(0, dst_offset_on($time, 'America/New_York'));
129
130        $time = gmmktime(1, 1, 1, 5, 1, 2015);
131        $this->assertSame(0, dst_offset_on($time, 'Pacific/Auckland'));
132        $this->assertSame(0, dst_offset_on($time, 'Australia/Perth'));
133        $this->assertSame(0, dst_offset_on($time, 'Australia/Lord_Howe'));
134        $this->assertSame(3600, dst_offset_on($time, 'Europe/Prague'));
135        $this->assertSame(3600, dst_offset_on($time, 'America/New_York'));
136    }
137
138    public function test_make_timestamp() {
139        global $CFG;
140
141        $this->resetAfterTest();
142
143        // There are quite a lot of problems, let's pick some less problematic zones for now.
144        $timezones = array('Europe/Prague', 'Europe/London', 'Australia/Perth', 'Pacific/Auckland', 'America/New_York', '99');
145
146        $dates = array(
147            array(2, 1, 0, 40, 40),
148            array(4, 3, 0, 30, 22),
149            array(9, 5, 0, 20, 19),
150            array(11, 28, 0, 10, 45),
151        );
152        $years = array(1999, 2009, 2014, 2018);
153
154        $this->setTimezone('Pacific/Auckland', 'Pacific/Auckland');
155        foreach ($timezones as $tz) {
156            foreach ($years as $year) {
157                foreach ($dates as $date) {
158                    $result = make_timestamp($year, $date[0], $date[1], $date[2], $date[3], $date[4], $tz, true);
159                    $expected = new DateTime('now', new DateTimeZone(($tz == 99 ? 'Pacific/Auckland' : $tz)));
160                    $expected->setDate($year, $date[0], $date[1]);
161                    $expected->setTime($date[2], $date[3], $date[4]);
162                    $this->assertSame($expected->getTimestamp(), $result,
163                        'Incorrect result for data ' . $expected->format("D, d M Y H:i:s O") . ' ' . $tz);
164                }
165            }
166        }
167
168        $this->setTimezone('99', 'Pacific/Auckland');
169        foreach ($timezones as $tz) {
170            foreach ($years as $year) {
171                foreach ($dates as $date) {
172                    $result = make_timestamp($year, $date[0], $date[1], $date[2], $date[3], $date[4], $tz, true);
173                    $expected = new DateTime('now', new DateTimeZone(($tz == 99 ? 'Pacific/Auckland' : $tz)));
174                    $expected->setDate($year, $date[0], $date[1]);
175                    $expected->setTime($date[2], $date[3], $date[4]);
176                    $this->assertSame($expected->getTimestamp(), $result,
177                        'Incorrect result for data ' . $expected->format("D, d M Y H:i:s O") . ' ' . $tz);
178                }
179            }
180        }
181    }
182
183    public function test_usergetdate() {
184        global $CFG;
185
186        $this->resetAfterTest();
187
188        // There are quite a lot of problems, let's pick some less problematic zones for now.
189        $timezones = array('Europe/Prague', 'Europe/London', 'Australia/Perth', 'Pacific/Auckland', 'America/New_York', '99');
190
191        $dates = array(
192            array(2, 1, 0, 40, 40),
193            array(4, 3, 0, 30, 22),
194            array(9, 5, 0, 20, 19),
195            array(11, 28, 0, 10, 45),
196        );
197        $years = array(1999, 2009, 2014, 2018);
198
199        $this->setTimezone('Pacific/Auckland', 'Pacific/Auckland');
200        foreach ($timezones as $tz) {
201            foreach ($years as $year) {
202                foreach ($dates as $date) {
203                    $expected = new DateTime('now', new DateTimeZone(($tz == 99 ? 'Pacific/Auckland' : $tz)));
204                    $expected->setDate($year, $date[0], $date[1]);
205                    $expected->setTime($date[2], $date[3], $date[4]);
206                    $result = usergetdate($expected->getTimestamp(), $tz);
207                    unset($result[0]); // Extra introduced by getdate().
208                    $ex = array(
209                        'seconds' => $date[4],
210                        'minutes' => $date[3],
211                        'hours' => $date[2],
212                        'mday' => $date[1],
213                        'wday' => (int)$expected->format('w'),
214                        'mon' => $date[0],
215                        'year' => $year,
216                        'yday' => (int)$expected->format('z'),
217                        'weekday' => $expected->format('l'),
218                        'month' => $expected->format('F'),
219                    );
220                    $this->assertSame($ex, $result,
221                        'Incorrect result for data ' . $expected->format("D, d M Y H:i:s O") . ' ' . $tz);
222                }
223            }
224        }
225
226        $this->setTimezone('99', 'Pacific/Auckland');
227        foreach ($timezones as $tz) {
228            foreach ($years as $year) {
229                foreach ($dates as $date) {
230                    $expected = new DateTime('now', new DateTimeZone(($tz == 99 ? 'Pacific/Auckland' : $tz)));
231                    $expected->setDate($year, $date[0], $date[1]);
232                    $expected->setTime($date[2], $date[3], $date[4]);
233                    $result = usergetdate($expected->getTimestamp(), $tz);
234                    unset($result[0]); // Extra introduced by getdate().
235                    $ex = array(
236                        'seconds' => $date[4],
237                        'minutes' => $date[3],
238                        'hours' => $date[2],
239                        'mday' => $date[1],
240                        'wday' => (int)$expected->format('w'),
241                        'mon' => $date[0],
242                        'year' => $year,
243                        'yday' => (int)$expected->format('z'),
244                        'weekday' => $expected->format('l'),
245                        'month' => $expected->format('F'),
246                    );
247                    $this->assertSame($ex, $result,
248                        'Incorrect result for data ' . $expected->format("D, d M Y H:i:s O") . ' ' . $tz);
249                }
250            }
251        }
252    }
253
254    public function test_userdate() {
255        global $CFG;
256
257        $this->resetAfterTest();
258
259        $dates = array(
260            array(2, 1, 0, 40, 40),
261            array(4, 3, 0, 30, 22),
262            array(9, 5, 0, 20, 19),
263            array(11, 28, 0, 10, 45),
264        );
265        $years = array(1999, 2009, 2014, 2018);
266
267        $users = array();
268        $users[] = $this->getDataGenerator()->create_user(array('timezone' => 99));
269        $users[] = $this->getDataGenerator()->create_user(array('timezone' => 'Europe/Prague'));
270        $users[] = $this->getDataGenerator()->create_user(array('timezone' => 'Pacific/Auckland'));
271        $users[] = $this->getDataGenerator()->create_user(array('timezone' => 'Australia/Perth'));
272        $users[] = $this->getDataGenerator()->create_user(array('timezone' => 'America/New_York'));
273
274        $format = get_string('strftimedaydatetime', 'langconfig');
275
276        $this->setTimezone('Pacific/Auckland', 'Pacific/Auckland');
277        foreach ($years as $year) {
278            foreach ($dates as $date) {
279                $expected = new DateTime('now', new DateTimeZone('UTC'));
280                $expected->setDate($year, $date[0], $date[1]);
281                $expected->setTime($date[2], $date[3], $date[4]);
282
283                foreach ($users as $user) {
284                    $this->setUser($user);
285                    $expected->setTimezone(new DateTimeZone(($user->timezone == 99 ? 'Pacific/Auckland' : $user->timezone)));
286                    $result = userdate($expected->getTimestamp(), '', 99, false, false);
287                    date_default_timezone_set($expected->getTimezone()->getName());
288                    $ex = strftime($format, $expected->getTimestamp());
289                    date_default_timezone_set($CFG->timezone);
290                    $this->assertSame($ex, $result);
291                }
292            }
293        }
294    }
295
296    public function test_usertime() {
297        // This is a useless bad hack, it needs to be completely eliminated.
298
299        $time = gmmktime(1, 1, 1, 3, 1, 2015);
300        $this->assertSame($time - (60 * 60 * 1), usertime($time, '1'));
301        $this->assertSame($time - (60 * 60 * -1), usertime($time, '-1'));
302        $this->assertSame($time - (60 * 60 * 1), usertime($time, 'Europe/Prague'));
303        $this->assertSame($time - (60 * 60 * 8), usertime($time, 'Australia/Perth'));
304        $this->assertSame($time - (60 * 60 * 12), usertime($time, 'Pacific/Auckland'));
305        $this->assertSame($time - (60 * 60 * -5), usertime($time, 'America/New_York'));
306
307        $time = gmmktime(1, 1, 1, 5, 1, 2015);
308        $this->assertSame($time - (60 * 60 * 1), usertime($time, '1'));
309        $this->assertSame($time - (60 * 60 * -1), usertime($time, '-1'));
310        $this->assertSame($time - (60 * 60 * 1), usertime($time, 'Europe/Prague'));
311        $this->assertSame($time - (60 * 60 * 8), usertime($time, 'Australia/Perth'));
312        $this->assertSame($time - (60 * 60 * 12), usertime($time, 'Pacific/Auckland'));
313        $this->assertSame($time - (60 * 60 * -5), usertime($time, 'America/New_York'));
314    }
315
316    public function test_usertimezone() {
317        global $USER;
318        $this->resetAfterTest();
319
320        $this->setTimezone('Pacific/Auckland', 'Pacific/Auckland');
321
322        $USER->timezone = 'Europe/Prague';
323        $this->assertSame('Europe/Prague', usertimezone());
324
325        $USER->timezone = '1';
326        $this->assertSame('UTC+1', usertimezone());
327
328        $USER->timezone = '0';
329        $this->assertSame('UTC', usertimezone());
330
331        $USER->timezone = '99';
332        $this->assertSame('Pacific/Auckland', usertimezone());
333
334        $USER->timezone = '99';
335        $this->assertSame('Europe/Berlin', usertimezone('Europe/Berlin'));
336
337        $USER->timezone = '99';
338        $this->assertSame('Pacific/Auckland', usertimezone('99'));
339
340        $USER->timezone = 'Europe/Prague';
341        $this->assertSame('Europe/Prague', usertimezone('99'));
342
343        // When passed an unknown non-whole hour TZ, verify we round to closest
344        // hour. (Possible for legacy reasons when old timezones go away).
345        $USER->timezone = '-9.23';
346        $this->assertSame('UTC-9', usertimezone('99'));
347    }
348}
349