3 namespace Drupal\Tests\Component\Datetime;
5 use Drupal\Component\Datetime\DateTimePlus;
6 use PHPUnit\Framework\TestCase;
9 * @coversDefaultClass \Drupal\Component\Datetime\DateTimePlus
12 class DateTimePlusTest extends TestCase {
15 * Test creating dates from string and array input.
18 * Input argument for DateTimePlus.
19 * @param string $timezone
20 * Timezone argument for DateTimePlus.
21 * @param string $expected
22 * Expected output from DateTimePlus::format().
24 * @dataProvider providerTestDates
26 public function testDates($input, $timezone, $expected) {
27 $date = new DateTimePlus($input, $timezone);
28 $value = $date->format('c');
30 if (is_array($input)) {
31 $input = var_export($input, TRUE);
33 $this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $timezone, $expected, $value));
37 * Test creating dates from string and array input.
40 * Input argument for DateTimePlus.
41 * @param string $timezone
42 * Timezone argument for DateTimePlus.
43 * @param string $expected
44 * Expected output from DateTimePlus::format().
46 * @dataProvider providerTestDateArrays
48 public function testDateArrays($input, $timezone, $expected) {
49 $date = DateTimePlus::createFromArray($input, $timezone);
50 $value = $date->format('c');
52 if (is_array($input)) {
53 $input = var_export($input, TRUE);
55 $this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $timezone, $expected, $value));
61 * @param mixed $input1
62 * A DateTimePlus object.
63 * @param mixed $input2
64 * Date argument for DateTimePlus::diff method.
65 * @param bool $absolute
66 * Absolute flag for DateTimePlus::diff method.
67 * @param \DateInterval $expected
68 * The expected result of the DateTimePlus::diff operation.
70 * @dataProvider providerTestDateDiff
72 public function testDateDiff($input1, $input2, $absolute, \DateInterval $expected) {
73 $interval = $input1->diff($input2, $absolute);
74 $this->assertEquals($interval, $expected);
78 * Test date diff exception caused by invalid input.
80 * @param mixed $input1
81 * A DateTimePlus object.
82 * @param mixed $input2
83 * Date argument for DateTimePlus::diff method.
84 * @param bool $absolute
85 * Absolute flag for DateTimePlus::diff method.
87 * @dataProvider providerTestInvalidDateDiff
89 public function testInvalidDateDiff($input1, $input2, $absolute) {
90 if (method_exists($this, 'expectException')) {
91 $this->expectException(\BadMethodCallException::class);
92 $this->expectExceptionMessage('Method Drupal\Component\Datetime\DateTimePlus::diff expects parameter 1 to be a \DateTime or \Drupal\Component\Datetime\DateTimePlus object');
95 $this->setExpectedException(\BadMethodCallException::class, 'Method Drupal\Component\Datetime\DateTimePlus::diff expects parameter 1 to be a \DateTime or \Drupal\Component\Datetime\DateTimePlus object');
97 $interval = $input1->diff($input2, $absolute);
101 * Test creating dates from invalid array input.
103 * @param mixed $input
104 * Input argument for DateTimePlus.
105 * @param string $timezone
106 * Timezone argument for DateTimePlus.
107 * @param string $class
108 * The Exception subclass to expect to be thrown.
110 * @dataProvider providerTestInvalidDateArrays
112 public function testInvalidDateArrays($input, $timezone, $class) {
113 if (method_exists($this, 'expectException')) {
114 $this->expectException($class);
117 $this->setExpectedException($class);
119 $this->assertInstanceOf(
120 '\Drupal\Component\DateTimePlus',
121 DateTimePlus::createFromArray($input, $timezone)
126 * Test creating dates from timestamps, and manipulating timezones.
129 * Input argument for DateTimePlus::createFromTimestamp().
130 * @param array $initial
131 * An array containing:
132 * - 'timezone_initial' - Timezone argument for DateTimePlus.
133 * - 'format_initial' - Format argument for DateTimePlus.
134 * - 'expected_initial_date' - Expected output from DateTimePlus::format().
135 * - 'expected_initial_timezone' - Expected output from
136 * DateTimePlus::getTimeZone()::getName().
137 * - 'expected_initial_offset' - Expected output from DateTimePlus::getOffset().
138 * @param array $transform
139 * An array containing:
140 * - 'timezone_transform' - Argument to transform date to another timezone via
141 * DateTimePlus::setTimezone().
142 * - 'format_transform' - Format argument to use when transforming date to
144 * - 'expected_transform_date' - Expected output from DateTimePlus::format(),
145 * after timezone transform.
146 * - 'expected_transform_timezone' - Expected output from
147 * DateTimePlus::getTimeZone()::getName(), after timezone transform.
148 * - 'expected_transform_offset' - Expected output from
149 * DateTimePlus::getOffset(), after timezone transform.
151 * @dataProvider providerTestTimestamp
153 public function testTimestamp($input, array $initial, array $transform) {
154 // Initialize a new date object.
155 $date = DateTimePlus::createFromTimestamp($input, $initial['timezone']);
156 $this->assertDateTimestamp($date, $input, $initial, $transform);
160 * Test creating dates from datetime strings.
162 * @param string $input
163 * Input argument for DateTimePlus().
164 * @param array $initial
165 * @see testTimestamp()
166 * @param array $transform
167 * @see testTimestamp()
169 * @dataProvider providerTestDateTimestamp
171 public function testDateTimestamp($input, array $initial, array $transform) {
172 // Initialize a new date object.
173 $date = new DateTimePlus($input, $initial['timezone']);
174 $this->assertDateTimestamp($date, $input, $initial, $transform);
178 * Assertion helper for testTimestamp and testDateTimestamp since they need
179 * different dataProviders.
181 * @param \Drupal\Component\Datetime\DateTimePlus $date
182 * DateTimePlus to test.
183 * @input mixed $input
184 * The original input passed to the test method.
185 * @param array $initial
186 * @see testTimestamp()
187 * @param array $transform
188 * @see testTimestamp()
190 public function assertDateTimestamp($date, $input, $initial, $transform) {
192 $value = $date->format($initial['format']);
193 $this->assertEquals($initial['expected_date'], $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $initial['timezone'], $initial['expected_date'], $value));
195 // Check timezone name.
196 $value = $date->getTimeZone()->getName();
197 $this->assertEquals($initial['expected_timezone'], $value, sprintf("The current timezone is %s: should be %s.", $value, $initial['expected_timezone']));
200 $value = $date->getOffset();
201 $this->assertEquals($initial['expected_offset'], $value, sprintf("The current offset is %s: should be %s.", $value, $initial['expected_offset']));
203 // Transform the date to another timezone.
204 $date->setTimezone(new \DateTimeZone($transform['timezone']));
206 // Check transformed format.
207 $value = $date->format($transform['format']);
208 $this->assertEquals($transform['expected_date'], $value, sprintf("Test \$date->setTimezone(new \\DateTimeZone(%s)): should be %s, found %s.", $transform['timezone'], $transform['expected_date'], $value));
210 // Check transformed timezone.
211 $value = $date->getTimeZone()->getName();
212 $this->assertEquals($transform['expected_timezone'], $value, sprintf("The current timezone should be %s, found %s.", $transform['expected_timezone'], $value));
214 // Check transformed offset.
215 $value = $date->getOffset();
216 $this->assertEquals($transform['expected_offset'], $value, sprintf("The current offset should be %s, found %s.", $transform['expected_offset'], $value));
220 * Test creating dates from format strings.
222 * @param string $input
223 * Input argument for DateTimePlus.
224 * @param string $timezone
225 * Timezone argument for DateTimePlus.
226 * @param string $format_date
227 * Format argument for DateTimePlus::format().
228 * @param string $expected
229 * Expected output from DateTimePlus::format().
231 * @dataProvider providerTestDateFormat
233 public function testDateFormat($input, $timezone, $format, $format_date, $expected) {
234 $date = DateTimePlus::createFromFormat($format, $input, $timezone);
235 $value = $date->format($format_date);
236 $this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s, %s): should be %s, found %s.", $input, $timezone, $format, $expected, $value));
240 * Test invalid date handling.
242 * @param mixed $input
243 * Input argument for DateTimePlus.
244 * @param string $timezone
245 * Timezone argument for DateTimePlus.
246 * @param string $format
247 * Format argument for DateTimePlus.
248 * @param string $message
249 * Message to print if no errors are thrown by the invalid dates.
250 * @param string $class
251 * The Exception subclass to expect to be thrown.
253 * @dataProvider providerTestInvalidDates
255 public function testInvalidDates($input, $timezone, $format, $message, $class) {
256 if (method_exists($this, 'expectException')) {
257 $this->expectException($class);
260 $this->setExpectedException($class);
262 DateTimePlus::createFromFormat($format, $input, $timezone);
266 * Tests that DrupalDateTime can detect the right timezone to use.
267 * When specified or not.
269 * @param mixed $input
270 * Input argument for DateTimePlus.
271 * @param mixed $timezone
272 * Timezone argument for DateTimePlus.
273 * @param string $expected_timezone
274 * Expected timezone returned from DateTimePlus::getTimezone::getName().
275 * @param string $message
276 * Message to print on test failure.
278 * @dataProvider providerTestDateTimezone
280 public function testDateTimezone($input, $timezone, $expected_timezone, $message) {
281 $date = new DateTimePlus($input, $timezone);
282 $timezone = $date->getTimezone()->getName();
283 $this->assertEquals($timezone, $expected_timezone, $message);
287 * Test that DrupalDateTime can detect the right timezone to use when
288 * constructed from a datetime object.
290 public function testDateTimezoneWithDateTimeObject() {
291 // Create a date object with another date object.
292 $input = new \DateTime('now', new \DateTimeZone('Pacific/Midway'));
294 $expected_timezone = 'Pacific/Midway';
295 $message = 'DateTimePlus uses the specified timezone if provided.';
297 $date = DateTimePlus::createFromDateTime($input, $timezone);
298 $timezone = $date->getTimezone()->getName();
299 $this->assertEquals($timezone, $expected_timezone, $message);
303 * Provides data for date tests.
306 * An array of arrays, each containing the input parameters for
307 * DateTimePlusTest::testDates().
309 * @see DateTimePlusTest::testDates()
311 public function providerTestDates() {
314 // Create date object from datetime string.
315 ['2009-03-07 10:30', 'America/Chicago', '2009-03-07T10:30:00-06:00'],
316 // Same during daylight savings time.
317 ['2009-06-07 10:30', 'America/Chicago', '2009-06-07T10:30:00-05:00'],
318 // Create date object from date string.
319 ['2009-03-07', 'America/Chicago', '2009-03-07T00:00:00-06:00'],
320 // Same during daylight savings time.
321 ['2009-06-07', 'America/Chicago', '2009-06-07T00:00:00-05:00'],
322 // Create date object from date string.
323 ['2009-03-07 10:30', 'Australia/Canberra', '2009-03-07T10:30:00+11:00'],
324 // Same during daylight savings time.
325 ['2009-06-07 10:30', 'Australia/Canberra', '2009-06-07T10:30:00+10:00'],
328 // On 32-bit systems, timestamps are limited to 1901-2038.
329 if (PHP_INT_SIZE > 4) {
330 // Create a date object in the distant past.
331 // @see https://www.drupal.org/node/2795489#comment-12127088
332 if (version_compare(PHP_VERSION, '5.6.15', '>=')) {
333 $dates[] = ['1809-02-12 10:30', 'America/Chicago', '1809-02-12T10:30:00-06:00'];
335 // Create a date object in the far future.
336 $dates[] = ['2345-01-02 02:04', 'UTC', '2345-01-02T02:04:00+00:00'];
343 * Provides data for date tests.
346 * An array of arrays, each containing the input parameters for
347 * DateTimePlusTest::testDates().
349 * @see DateTimePlusTest::testDates()
351 public function providerTestDateArrays() {
354 // Create date object from date array, date only.
355 [['year' => 2010, 'month' => 2, 'day' => 28], 'America/Chicago', '2010-02-28T00:00:00-06:00'],
356 // Create date object from date array with hour.
357 [['year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 10], 'America/Chicago', '2010-02-28T10:00:00-06:00'],
358 // Create date object from date array, date only.
359 [['year' => 2010, 'month' => 2, 'day' => 28], 'Europe/Berlin', '2010-02-28T00:00:00+01:00'],
360 // Create date object from date array with hour.
361 [['year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 10], 'Europe/Berlin', '2010-02-28T10:00:00+01:00'],
364 // On 32-bit systems, timestamps are limited to 1901-2038.
365 if (PHP_INT_SIZE > 4) {
366 // Create a date object in the distant past.
367 // @see https://www.drupal.org/node/2795489#comment-12127088
368 if (version_compare(PHP_VERSION, '5.6.15', '>=')) {
369 $dates[] = [['year' => 1809, 'month' => 2, 'day' => 12], 'America/Chicago', '1809-02-12T00:00:00-06:00'];
371 // Create a date object in the far future.
372 $dates[] = [['year' => 2345, 'month' => 1, 'day' => 2], 'UTC', '2345-01-02T00:00:00+00:00'];
379 * Provides data for testDateFormats.
382 * An array of arrays, each containing:
383 * - 'input' - Input to DateTimePlus.
384 * - 'timezone' - Timezone for DateTimePlus.
385 * - 'format' - Date format for DateTimePlus.
386 * - 'format_date' - Date format for use in $date->format() method.
387 * - 'expected' - The expected return from DateTimePlus.
389 * @see testDateFormats()
391 public function providerTestDateFormat() {
393 // Create a year-only date.
394 ['2009', NULL, 'Y', 'Y', '2009'],
395 // Create a month and year-only date.
396 ['2009-10', NULL, 'Y-m', 'Y-m', '2009-10'],
397 // Create a time-only date.
398 ['T10:30:00', NULL, '\TH:i:s', 'H:i:s', '10:30:00'],
399 // Create a time-only date.
400 ['10:30:00', NULL, 'H:i:s', 'H:i:s', '10:30:00'],
405 * Provides data for testInvalidDates.
408 * An array of arrays, each containing:
409 * - 'input' - Input for DateTimePlus.
410 * - 'timezone' - Timezone for DateTimePlus.
411 * - 'format' - Format for DateTimePlus.
412 * - 'message' - Message to display on failure.
414 * @see testInvalidDates
416 public function providerTestInvalidDates() {
418 // Test for invalid month names when we are using a short version
420 ['23 abc 2012', NULL, 'd M Y', "23 abc 2012 contains an invalid month name and did not produce errors.", \InvalidArgumentException::class],
421 // Test for invalid hour.
422 ['0000-00-00T45:30:00', NULL, 'Y-m-d\TH:i:s', "0000-00-00T45:30:00 contains an invalid hour and did not produce errors.", \UnexpectedValueException::class],
423 // Test for invalid day.
424 ['0000-00-99T05:30:00', NULL, 'Y-m-d\TH:i:s', "0000-00-99T05:30:00 contains an invalid day and did not produce errors.", \UnexpectedValueException::class],
425 // Test for invalid month.
426 ['0000-75-00T15:30:00', NULL, 'Y-m-d\TH:i:s', "0000-75-00T15:30:00 contains an invalid month and did not produce errors.", \UnexpectedValueException::class],
427 // Test for invalid year.
428 ['11-08-01T15:30:00', NULL, 'Y-m-d\TH:i:s', "11-08-01T15:30:00 contains an invalid year and did not produce errors.", \UnexpectedValueException::class],
434 * Data provider for testInvalidDateArrays.
437 * An array of arrays, each containing:
438 * - 'input' - Input for DateTimePlus.
439 * - 'timezone' - Timezone for DateTimePlus.
441 * @see testInvalidDateArrays
443 public function providerTestInvalidDateArrays() {
445 // One year larger than the documented upper limit of checkdate().
446 [['year' => 32768, 'month' => 1, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
447 // One year smaller than the documented lower limit of checkdate().
448 [['year' => 0, 'month' => 1, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
449 // Test for invalid month from date array.
450 [['year' => 2010, 'month' => 27, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
451 // Test for invalid hour from date array.
452 [['year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 80, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
453 // Test for invalid minute from date array.
454 [['year' => 2010, 'month' => 7, 'day' => 8, 'hour' => 8, 'minute' => 88, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
455 // Regression test for https://www.drupal.org/node/2084455.
456 [['hour' => 59, 'minute' => 1, 'second' => 1], 'America/Chicago', \InvalidArgumentException::class],
461 * Provides data for testDateTimezone.
464 * An array of arrays, each containing:
465 * - 'date' - Date string or object for DateTimePlus.
466 * - 'timezone' - Timezone string for DateTimePlus.
467 * - 'expected' - Expected return from DateTimePlus::getTimezone()::getName().
468 * - 'message' - Message to display on test failure.
470 * @see testDateTimezone
472 public function providerTestDateTimezone() {
473 // Use a common date for most of the tests.
474 $date_string = '2007-01-31 21:00:00';
476 // Detect the system timezone.
477 $system_timezone = date_default_timezone_get();
480 // Create a date object with an unspecified timezone, which should
481 // end up using the system timezone.
482 [$date_string, NULL, $system_timezone, 'DateTimePlus uses the system timezone when there is no site timezone.'],
483 // Create a date object with a specified timezone name.
484 [$date_string, 'America/Yellowknife', 'America/Yellowknife', 'DateTimePlus uses the specified timezone if provided.'],
485 // Create a date object with a timezone object.
486 [$date_string, new \DateTimeZone('Australia/Canberra'), 'Australia/Canberra', 'DateTimePlus uses the specified timezone if provided.'],
487 // Create a date object with another date object.
488 [new DateTimePlus('now', 'Pacific/Midway'), NULL, 'Pacific/Midway', 'DateTimePlus uses the specified timezone if provided.'],
493 * Provides data for testTimestamp.
496 * An array of arrays, each containing the arguments required for
497 * self::testTimestamp().
499 * @see testTimestamp()
501 public function providerTestTimestamp() {
503 // Create date object from a unix timestamp and display it in
510 'expected_date' => '1970-01-01T00:00:00+00:00',
511 'expected_timezone' => 'UTC',
512 'expected_offset' => 0,
515 'timezone' => 'America/Los_Angeles',
517 'expected_date' => '1969-12-31T16:00:00-08:00',
518 'expected_timezone' => 'America/Los_Angeles',
519 'expected_offset' => '-28800',
522 // Create a date using the timestamp of zero, then display its
523 // value both in UTC and the local timezone.
527 'timezone' => 'America/Los_Angeles',
529 'expected_date' => '1969-12-31T16:00:00-08:00',
530 'expected_timezone' => 'America/Los_Angeles',
531 'expected_offset' => '-28800',
536 'expected_date' => '1970-01-01T00:00:00+00:00',
537 'expected_timezone' => 'UTC',
538 'expected_offset' => 0,
545 * Provides data for testDateTimestamp.
548 * An array of arrays, each containing the arguments required for
549 * self::testDateTimestamp().
551 * @see testDateTimestamp()
553 public function providerTestDateTimestamp() {
555 // Create date object from datetime string in UTC, and convert
556 // it to a local date.
558 'input' => '1970-01-01 00:00:00',
562 'expected_date' => '1970-01-01T00:00:00+00:00',
563 'expected_timezone' => 'UTC',
564 'expected_offset' => 0,
567 'timezone' => 'America/Los_Angeles',
569 'expected_date' => '1969-12-31T16:00:00-08:00',
570 'expected_timezone' => 'America/Los_Angeles',
571 'expected_offset' => '-28800',
574 // Convert the local time to UTC using string input.
576 'input' => '1969-12-31 16:00:00',
578 'timezone' => 'America/Los_Angeles',
580 'expected_date' => '1969-12-31T16:00:00-08:00',
581 'expected_timezone' => 'America/Los_Angeles',
582 'expected_offset' => '-28800',
587 'expected_date' => '1970-01-01T00:00:00+00:00',
588 'expected_timezone' => 'UTC',
589 'expected_offset' => 0,
592 // Convert the local time to UTC using string input.
594 'input' => '1969-12-31 16:00:00',
596 'timezone' => 'Europe/Warsaw',
598 'expected_date' => '1969-12-31T16:00:00+01:00',
599 'expected_timezone' => 'Europe/Warsaw',
600 'expected_offset' => '+3600',
605 'expected_date' => '1969-12-31T15:00:00+00:00',
606 'expected_timezone' => 'UTC',
607 'expected_offset' => 0,
614 * Provides data for date tests.
617 * An array of arrays, each containing the input parameters for
618 * DateTimePlusTest::testDateDiff().
620 * @see DateTimePlusTest::testDateDiff()
622 public function providerTestDateDiff() {
624 $empty_interval = new \DateInterval('PT0S');
626 $positive_19_hours = new \DateInterval('PT19H');
628 $positive_18_hours = new \DateInterval('PT18H');
630 $positive_1_hour = new \DateInterval('PT1H');
632 $negative_1_hour = new \DateInterval('PT1H');
633 $negative_1_hour->invert = 1;
636 // There should be a 19 hour time interval between
637 // new years in Sydney and new years in LA in year 2000.
639 'input2' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('Australia/Sydney')),
640 'input1' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles')),
642 'expected' => $positive_19_hours,
644 // In 1970 Sydney did not observe daylight savings time
645 // So there is only a 18 hour time interval.
647 'input2' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('Australia/Sydney')),
648 'input1' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles')),
650 'expected' => $positive_18_hours,
653 'input1' => DateTimePlus::createFromFormat('U', 3600, new \DateTimeZone('America/Los_Angeles')),
654 'input2' => DateTimePlus::createFromFormat('U', 0, new \DateTimeZone('UTC')),
656 'expected' => $negative_1_hour,
659 'input1' => DateTimePlus::createFromFormat('U', 3600),
660 'input2' => DateTimePlus::createFromFormat('U', 0),
662 'expected' => $negative_1_hour,
665 'input1' => DateTimePlus::createFromFormat('U', 3600),
666 'input2' => \DateTime::createFromFormat('U', 0),
668 'expected' => $negative_1_hour,
671 'input1' => DateTimePlus::createFromFormat('U', 3600),
672 'input2' => DateTimePlus::createFromFormat('U', 0),
674 'expected' => $positive_1_hour,
677 'input1' => DateTimePlus::createFromFormat('U', 3600),
678 'input2' => \DateTime::createFromFormat('U', 0),
680 'expected' => $positive_1_hour,
683 'input1' => DateTimePlus::createFromFormat('U', 0),
684 'input2' => DateTimePlus::createFromFormat('U', 0),
686 'expected' => $empty_interval,
692 * Provides data for date tests.
695 * An array of arrays, each containing the input parameters for
696 * DateTimePlusTest::testInvalidDateDiff().
698 * @see DateTimePlusTest::testInvalidDateDiff()
700 public function providerTestInvalidDateDiff() {
703 'input1' => DateTimePlus::createFromFormat('U', 3600),
704 'input2' => '1970-01-01 00:00:00',
708 'input1' => DateTimePlus::createFromFormat('U', 3600),
716 * Tests invalid values passed to constructor.
718 * @param string $time
719 * A date/time string.
720 * @param string[] $errors
721 * An array of error messages.
723 * @covers ::__construct
725 * @dataProvider providerTestInvalidConstructor
727 public function testInvalidConstructor($time, array $errors) {
728 $date = new DateTimePlus($time);
730 $this->assertEquals(TRUE, $date->hasErrors());
731 $this->assertEquals($errors, $date->getErrors());
735 * Provider for testInvalidConstructor().
738 * An array of invalid date/time strings, and corresponding error messages.
740 public function providerTestInvalidConstructor() {
745 'The timezone could not be found in the database',
746 'Unexpected character',
747 'Double timezone specification',
753 'Unexpected character',
754 'The timezone could not be found in the database',
760 'The timezone could not be found in the database',
761 'Unexpected character',
762 'Double timezone specification',
768 'The timezone could not be found in the database',
769 'Unexpected character',
770 'Double timezone specification',
776 'Unexpected character',
780 'YYYY-MM-DD hh:mm:ss',
782 'The timezone could not be found in the database',
783 'Unexpected character',
784 'Double timezone specification',
788 '2017-03-07 25:70:80',
790 'Unexpected character',
791 'Double time specification',
795 'lorem ipsum dolor sit amet',
797 'The timezone could not be found in the database',
798 'Double timezone specification',
805 * Tests the $settings['validate_format'] parameter in ::createFromFormat().
807 public function testValidateFormat() {
808 // Check that an input that does not strictly follow the input format will
809 // produce the desired date. In this case the year string '11' doesn't
810 // precisely match the 'Y' formatter parameter, but PHP will parse it
811 // regardless. However, when formatted with the same string, the year will
812 // be output with four digits. With the ['validate_format' => FALSE]
813 // $settings, this will not thrown an exception.
814 $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '11-03-31 17:44:00', 'UTC', ['validate_format' => FALSE]);
815 $this->assertEquals('0011-03-31 17:44:00', $date->format('Y-m-d H:i:s'));
817 // Parse the same date with ['validate_format' => TRUE] and make sure we
818 // get the expected exception.
819 if (method_exists($this, 'expectException')) {
820 $this->expectException(\UnexpectedValueException::class);
823 $this->setExpectedException(\UnexpectedValueException::class);
825 $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '11-03-31 17:44:00', 'UTC', ['validate_format' => TRUE]);
829 * Tests setting the default time for date-only objects.
831 public function testDefaultDateTime() {
832 $utc = new \DateTimeZone('UTC');
834 $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '2017-05-23 22:58:00', $utc);
835 $this->assertEquals('22:58:00', $date->format('H:i:s'));
836 $date->setDefaultDateTime();
837 $this->assertEquals('12:00:00', $date->format('H:i:s'));
841 * Tests that object methods are chainable.
845 public function testChainable() {
846 $date = new DateTimePlus('now', 'Australia/Sydney');
848 $date->setTimestamp(12345678);
849 $rendered = $date->render();
850 $this->assertEquals('1970-05-24 07:21:18 Australia/Sydney', $rendered);
852 $date->setTimestamp(23456789);
853 $rendered = $date->setTimezone(new \DateTimeZone('America/New_York'))->render();
854 $this->assertEquals('1970-09-29 07:46:29 America/New_York', $rendered);
856 $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-05-24 07:21:18', new \DateTimeZone('Australia/Sydney'))
857 ->setTimezone(new \DateTimeZone('America/New_York'));
858 $rendered = $date->render();
859 $this->assertInstanceOf(DateTimePlus::class, $date);
860 $this->assertEquals(12345678, $date->getTimestamp());
861 $this->assertEquals('1970-05-23 17:21:18 America/New_York', $rendered);
865 * Tests that non-chainable methods work.
869 public function testChainableNonChainable() {
870 $datetime1 = new DateTimePlus('2009-10-11 12:00:00');
871 $datetime2 = new DateTimePlus('2009-10-13 12:00:00');
872 $interval = $datetime1->diff($datetime2);
873 $this->assertInstanceOf(\DateInterval::class, $interval);
874 $this->assertEquals('+2 days', $interval->format('%R%a days'));
878 * Tests that chained calls to non-existent functions throw an exception.
882 public function testChainableNonCallable() {
883 if (method_exists($this, 'expectException')) {
884 $this->expectException(\BadMethodCallException::class);
885 $this->expectExceptionMessage('Call to undefined method Drupal\Component\Datetime\DateTimePlus::nonexistent()');
888 $this->setExpectedException(\BadMethodCallException::class, 'Call to undefined method Drupal\Component\Datetime\DateTimePlus::nonexistent()');
890 $date = new DateTimePlus('now', 'Australia/Sydney');
891 $date->setTimezone(new \DateTimeZone('America/New_York'))->nonexistent();
895 * @covers ::getPhpDateTime
897 public function testGetPhpDateTime() {
898 $new_york = new \DateTimeZone('America/New_York');
899 $berlin = new \DateTimeZone('Europe/Berlin');
901 // Test retrieving a cloned copy of the wrapped \DateTime object, and that
902 // altering it does not change the DateTimePlus object.
903 $datetimeplus = DateTimePlus::createFromFormat('Y-m-d H:i:s', '2017-07-13 22:40:00', $new_york, ['langcode' => 'en']);
904 $this->assertEquals(1500000000, $datetimeplus->getTimestamp());
905 $this->assertEquals('America/New_York', $datetimeplus->getTimezone()->getName());
907 $datetime = $datetimeplus->getPhpDateTime();
908 $this->assertInstanceOf('DateTime', $datetime);
909 $this->assertEquals(1500000000, $datetime->getTimestamp());
910 $this->assertEquals('America/New_York', $datetime->getTimezone()->getName());
912 $datetime->setTimestamp(1400000000)->setTimezone($berlin);
913 $this->assertEquals(1400000000, $datetime->getTimestamp());
914 $this->assertEquals('Europe/Berlin', $datetime->getTimezone()->getName());
915 $this->assertEquals(1500000000, $datetimeplus->getTimestamp());
916 $this->assertEquals('America/New_York', $datetimeplus->getTimezone()->getName());