Mercurial > hg > isophonics-drupal-site
diff core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 7a779792577d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php Wed Nov 29 16:09:58 2017 +0000 @@ -0,0 +1,855 @@ +<?php + +namespace Drupal\Tests\Component\Datetime; + +use Drupal\Component\Datetime\DateTimePlus; +use PHPUnit\Framework\TestCase; + +/** + * @coversDefaultClass \Drupal\Component\Datetime\DateTimePlus + * @group Datetime + */ +class DateTimePlusTest extends TestCase { + + /** + * Test creating dates from string and array input. + * + * @param mixed $input + * Input argument for DateTimePlus. + * @param string $timezone + * Timezone argument for DateTimePlus. + * @param string $expected + * Expected output from DateTimePlus::format(). + * + * @dataProvider providerTestDates + */ + public function testDates($input, $timezone, $expected) { + $date = new DateTimePlus($input, $timezone); + $value = $date->format('c'); + + if (is_array($input)) { + $input = var_export($input, TRUE); + } + $this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $timezone, $expected, $value)); + } + + /** + * Test creating dates from string and array input. + * + * @param mixed $input + * Input argument for DateTimePlus. + * @param string $timezone + * Timezone argument for DateTimePlus. + * @param string $expected + * Expected output from DateTimePlus::format(). + * + * @dataProvider providerTestDateArrays + */ + public function testDateArrays($input, $timezone, $expected) { + $date = DateTimePlus::createFromArray($input, $timezone); + $value = $date->format('c'); + + if (is_array($input)) { + $input = var_export($input, TRUE); + } + $this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $timezone, $expected, $value)); + } + + /** + * Test date diffs. + * + * @param mixed $input1 + * A DateTimePlus object. + * @param mixed $input2 + * Date argument for DateTimePlus::diff method. + * @param bool $absolute + * Absolute flag for DateTimePlus::diff method. + * @param \DateInterval $expected + * The expected result of the DateTimePlus::diff operation. + * + * @dataProvider providerTestDateDiff + */ + public function testDateDiff($input1, $input2, $absolute, \DateInterval $expected) { + $interval = $input1->diff($input2, $absolute); + $this->assertEquals($interval, $expected); + } + + /** + * Test date diff exception caused by invalid input. + * + * @param mixed $input1 + * A DateTimePlus object. + * @param mixed $input2 + * Date argument for DateTimePlus::diff method. + * @param bool $absolute + * Absolute flag for DateTimePlus::diff method. + * + * @dataProvider providerTestInvalidDateDiff + */ + public function testInvalidDateDiff($input1, $input2, $absolute) { + $this->setExpectedException(\BadMethodCallException::class, 'Method Drupal\Component\Datetime\DateTimePlus::diff expects parameter 1 to be a \DateTime or \Drupal\Component\Datetime\DateTimePlus object'); + $interval = $input1->diff($input2, $absolute); + } + + /** + * Test creating dates from invalid array input. + * + * @param mixed $input + * Input argument for DateTimePlus. + * @param string $timezone + * Timezone argument for DateTimePlus. + * @param string $class + * The Exception subclass to expect to be thrown. + * + * @dataProvider providerTestInvalidDateArrays + */ + public function testInvalidDateArrays($input, $timezone, $class) { + $this->setExpectedException($class); + $this->assertInstanceOf( + '\Drupal\Component\DateTimePlus', + DateTimePlus::createFromArray($input, $timezone) + ); + } + + /** + * Test creating dates from timestamps, and manipulating timezones. + * + * @param int $input + * Input argument for DateTimePlus::createFromTimestamp(). + * @param array $initial + * An array containing: + * - 'timezone_initial' - Timezone argument for DateTimePlus. + * - 'format_initial' - Format argument for DateTimePlus. + * - 'expected_initial_date' - Expected output from DateTimePlus::format(). + * - 'expected_initial_timezone' - Expected output from + * DateTimePlus::getTimeZone()::getName(). + * - 'expected_initial_offset' - Expected output from DateTimePlus::getOffset(). + * @param array $transform + * An array containing: + * - 'timezone_transform' - Argument to transform date to another timezone via + * DateTimePlus::setTimezone(). + * - 'format_transform' - Format argument to use when transforming date to + * another timezone. + * - 'expected_transform_date' - Expected output from DateTimePlus::format(), + * after timezone transform. + * - 'expected_transform_timezone' - Expected output from + * DateTimePlus::getTimeZone()::getName(), after timezone transform. + * - 'expected_transform_offset' - Expected output from + * DateTimePlus::getOffset(), after timezone transform. + * + * @dataProvider providerTestTimestamp + */ + public function testTimestamp($input, array $initial, array $transform) { + // Initialize a new date object. + $date = DateTimePlus::createFromTimestamp($input, $initial['timezone']); + $this->assertDateTimestamp($date, $input, $initial, $transform); + } + + /** + * Test creating dates from datetime strings. + * + * @param string $input + * Input argument for DateTimePlus(). + * @param array $initial + * @see testTimestamp() + * @param array $transform + * @see testTimestamp() + * + * @dataProvider providerTestDateTimestamp + */ + public function testDateTimestamp($input, array $initial, array $transform) { + // Initialize a new date object. + $date = new DateTimePlus($input, $initial['timezone']); + $this->assertDateTimestamp($date, $input, $initial, $transform); + } + + /** + * Assertion helper for testTimestamp and testDateTimestamp since they need + * different dataProviders. + * + * @param DateTimePlus $date + * DateTimePlus to test. + * @input mixed $input + * The original input passed to the test method. + * @param array $initial + * @see testTimestamp() + * @param array $transform + * @see testTimestamp() + */ + public function assertDateTimestamp($date, $input, $initial, $transform) { + // Check format. + $value = $date->format($initial['format']); + $this->assertEquals($initial['expected_date'], $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $initial['timezone'], $initial['expected_date'], $value)); + + // Check timezone name. + $value = $date->getTimeZone()->getName(); + $this->assertEquals($initial['expected_timezone'], $value, sprintf("The current timezone is %s: should be %s.", $value, $initial['expected_timezone'])); + + // Check offset. + $value = $date->getOffset(); + $this->assertEquals($initial['expected_offset'], $value, sprintf("The current offset is %s: should be %s.", $value, $initial['expected_offset'])); + + // Transform the date to another timezone. + $date->setTimezone(new \DateTimeZone($transform['timezone'])); + + // Check transformed format. + $value = $date->format($transform['format']); + $this->assertEquals($transform['expected_date'], $value, sprintf("Test \$date->setTimezone(new \\DateTimeZone(%s)): should be %s, found %s.", $transform['timezone'], $transform['expected_date'], $value)); + + // Check transformed timezone. + $value = $date->getTimeZone()->getName(); + $this->assertEquals($transform['expected_timezone'], $value, sprintf("The current timezone should be %s, found %s.", $transform['expected_timezone'], $value)); + + // Check transformed offset. + $value = $date->getOffset(); + $this->assertEquals($transform['expected_offset'], $value, sprintf("The current offset should be %s, found %s.", $transform['expected_offset'], $value)); + } + + /** + * Test creating dates from format strings. + * + * @param string $input + * Input argument for DateTimePlus. + * @param string $timezone + * Timezone argument for DateTimePlus. + * @param string $format_date + * Format argument for DateTimePlus::format(). + * @param string $expected + * Expected output from DateTimePlus::format(). + * + * @dataProvider providerTestDateFormat + */ + public function testDateFormat($input, $timezone, $format, $format_date, $expected) { + $date = DateTimePlus::createFromFormat($format, $input, $timezone); + $value = $date->format($format_date); + $this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s, %s): should be %s, found %s.", $input, $timezone, $format, $expected, $value)); + } + + /** + * Test invalid date handling. + * + * @param mixed $input + * Input argument for DateTimePlus. + * @param string $timezone + * Timezone argument for DateTimePlus. + * @param string $format + * Format argument for DateTimePlus. + * @param string $message + * Message to print if no errors are thrown by the invalid dates. + * @param string $class + * The Exception subclass to expect to be thrown. + * + * @dataProvider providerTestInvalidDates + */ + public function testInvalidDates($input, $timezone, $format, $message, $class) { + $this->setExpectedException($class); + DateTimePlus::createFromFormat($format, $input, $timezone); + } + + /** + * Tests that DrupalDateTime can detect the right timezone to use. + * When specified or not. + * + * @param mixed $input + * Input argument for DateTimePlus. + * @param mixed $timezone + * Timezone argument for DateTimePlus. + * @param string $expected_timezone + * Expected timezone returned from DateTimePlus::getTimezone::getName(). + * @param string $message + * Message to print on test failure. + * + * @dataProvider providerTestDateTimezone + */ + public function testDateTimezone($input, $timezone, $expected_timezone, $message) { + $date = new DateTimePlus($input, $timezone); + $timezone = $date->getTimezone()->getName(); + $this->assertEquals($timezone, $expected_timezone, $message); + } + + /** + * Test that DrupalDateTime can detect the right timezone to use when + * constructed from a datetime object. + */ + public function testDateTimezoneWithDateTimeObject() { + // Create a date object with another date object. + $input = new \DateTime('now', new \DateTimeZone('Pacific/Midway')); + $timezone = NULL; + $expected_timezone = 'Pacific/Midway'; + $message = 'DateTimePlus uses the specified timezone if provided.'; + + $date = DateTimePlus::createFromDateTime($input, $timezone); + $timezone = $date->getTimezone()->getName(); + $this->assertEquals($timezone, $expected_timezone, $message); + } + + /** + * Provides data for date tests. + * + * @return array + * An array of arrays, each containing the input parameters for + * DateTimePlusTest::testDates(). + * + * @see DateTimePlusTest::testDates() + */ + public function providerTestDates() { + $dates = [ + // String input. + // Create date object from datetime string. + ['2009-03-07 10:30', 'America/Chicago', '2009-03-07T10:30:00-06:00'], + // Same during daylight savings time. + ['2009-06-07 10:30', 'America/Chicago', '2009-06-07T10:30:00-05:00'], + // Create date object from date string. + ['2009-03-07', 'America/Chicago', '2009-03-07T00:00:00-06:00'], + // Same during daylight savings time. + ['2009-06-07', 'America/Chicago', '2009-06-07T00:00:00-05:00'], + // Create date object from date string. + ['2009-03-07 10:30', 'Australia/Canberra', '2009-03-07T10:30:00+11:00'], + // Same during daylight savings time. + ['2009-06-07 10:30', 'Australia/Canberra', '2009-06-07T10:30:00+10:00'], + ]; + + // On 32-bit systems, timestamps are limited to 1901-2038. + if (PHP_INT_SIZE > 4) { + // Create a date object in the distant past. + // @see https://www.drupal.org/node/2795489#comment-12127088 + if (version_compare(PHP_VERSION, '5.6.15', '>=')) { + $dates[] = ['1809-02-12 10:30', 'America/Chicago', '1809-02-12T10:30:00-06:00']; + } + // Create a date object in the far future. + $dates[] = ['2345-01-02 02:04', 'UTC', '2345-01-02T02:04:00+00:00']; + } + + return $dates; + } + + /** + * Provides data for date tests. + * + * @return array + * An array of arrays, each containing the input parameters for + * DateTimePlusTest::testDates(). + * + * @see DateTimePlusTest::testDates() + */ + public function providerTestDateArrays() { + $dates = [ + // Array input. + // Create date object from date array, date only. + [['year' => 2010, 'month' => 2, 'day' => 28], 'America/Chicago', '2010-02-28T00:00:00-06:00'], + // Create date object from date array with hour. + [['year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 10], 'America/Chicago', '2010-02-28T10:00:00-06:00'], + // Create date object from date array, date only. + [['year' => 2010, 'month' => 2, 'day' => 28], 'Europe/Berlin', '2010-02-28T00:00:00+01:00'], + // Create date object from date array with hour. + [['year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 10], 'Europe/Berlin', '2010-02-28T10:00:00+01:00'], + ]; + + // On 32-bit systems, timestamps are limited to 1901-2038. + if (PHP_INT_SIZE > 4) { + // Create a date object in the distant past. + // @see https://www.drupal.org/node/2795489#comment-12127088 + if (version_compare(PHP_VERSION, '5.6.15', '>=')) { + $dates[] = [['year' => 1809, 'month' => 2, 'day' => 12], 'America/Chicago', '1809-02-12T00:00:00-06:00']; + } + // Create a date object in the far future. + $dates[] = [['year' => 2345, 'month' => 1, 'day' => 2], 'UTC', '2345-01-02T00:00:00+00:00']; + } + + return $dates; + } + + /** + * Provides data for testDateFormats. + * + * @return array + * An array of arrays, each containing: + * - 'input' - Input to DateTimePlus. + * - 'timezone' - Timezone for DateTimePlus. + * - 'format' - Date format for DateTimePlus. + * - 'format_date' - Date format for use in $date->format() method. + * - 'expected' - The expected return from DateTimePlus. + * + * @see testDateFormats() + */ + public function providerTestDateFormat() { + return [ + // Create a year-only date. + ['2009', NULL, 'Y', 'Y', '2009'], + // Create a month and year-only date. + ['2009-10', NULL, 'Y-m', 'Y-m', '2009-10'], + // Create a time-only date. + ['T10:30:00', NULL, '\TH:i:s', 'H:i:s', '10:30:00'], + // Create a time-only date. + ['10:30:00', NULL, 'H:i:s', 'H:i:s', '10:30:00'], + ]; + } + + /** + * Provides data for testInvalidDates. + * + * @return array + * An array of arrays, each containing: + * - 'input' - Input for DateTimePlus. + * - 'timezone' - Timezone for DateTimePlus. + * - 'format' - Format for DateTimePlus. + * - 'message' - Message to display on failure. + * + * @see testInvalidDates + */ + public function providerTestInvalidDates() { + return [ + // Test for invalid month names when we are using a short version + // of the month. + ['23 abc 2012', NULL, 'd M Y', "23 abc 2012 contains an invalid month name and did not produce errors.", \InvalidArgumentException::class], + // Test for invalid hour. + ['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], + // Test for invalid day. + ['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], + // Test for invalid month. + ['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], + // Test for invalid year. + ['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], + + ]; + } + + /** + * Data provider for testInvalidDateArrays. + * + * @return array + * An array of arrays, each containing: + * - 'input' - Input for DateTimePlus. + * - 'timezone' - Timezone for DateTimePlus. + * + * @see testInvalidDateArrays + */ + public function providerTestInvalidDateArrays() { + return [ + // One year larger than the documented upper limit of checkdate(). + [['year' => 32768, 'month' => 1, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class], + // One year smaller than the documented lower limit of checkdate(). + [['year' => 0, 'month' => 1, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class], + // Test for invalid month from date array. + [['year' => 2010, 'month' => 27, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class], + // Test for invalid hour from date array. + [['year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 80, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class], + // Test for invalid minute from date array. + [['year' => 2010, 'month' => 7, 'day' => 8, 'hour' => 8, 'minute' => 88, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class], + // Regression test for https://www.drupal.org/node/2084455. + [['hour' => 59, 'minute' => 1, 'second' => 1], 'America/Chicago', \InvalidArgumentException::class], + ]; + } + + /** + * Provides data for testDateTimezone. + * + * @return array + * An array of arrays, each containing: + * - 'date' - Date string or object for DateTimePlus. + * - 'timezone' - Timezone string for DateTimePlus. + * - 'expected' - Expected return from DateTimePlus::getTimezone()::getName(). + * - 'message' - Message to display on test failure. + * + * @see testDateTimezone + */ + public function providerTestDateTimezone() { + // Use a common date for most of the tests. + $date_string = '2007-01-31 21:00:00'; + + // Detect the system timezone. + $system_timezone = date_default_timezone_get(); + + return [ + // Create a date object with an unspecified timezone, which should + // end up using the system timezone. + [$date_string, NULL, $system_timezone, 'DateTimePlus uses the system timezone when there is no site timezone.'], + // Create a date object with a specified timezone name. + [$date_string, 'America/Yellowknife', 'America/Yellowknife', 'DateTimePlus uses the specified timezone if provided.'], + // Create a date object with a timezone object. + [$date_string, new \DateTimeZone('Australia/Canberra'), 'Australia/Canberra', 'DateTimePlus uses the specified timezone if provided.'], + // Create a date object with another date object. + [new DateTimePlus('now', 'Pacific/Midway'), NULL, 'Pacific/Midway', 'DateTimePlus uses the specified timezone if provided.'], + ]; + } + + /** + * Provides data for testTimestamp. + * + * @return array + * An array of arrays, each containing the arguments required for + * self::testTimestamp(). + * + * @see testTimestamp() + */ + public function providerTestTimestamp() { + return [ + // Create date object from a unix timestamp and display it in + // local time. + [ + 'input' => 0, + 'initial' => [ + 'timezone' => 'UTC', + 'format' => 'c', + 'expected_date' => '1970-01-01T00:00:00+00:00', + 'expected_timezone' => 'UTC', + 'expected_offset' => 0, + ], + 'transform' => [ + 'timezone' => 'America/Los_Angeles', + 'format' => 'c', + 'expected_date' => '1969-12-31T16:00:00-08:00', + 'expected_timezone' => 'America/Los_Angeles', + 'expected_offset' => '-28800', + ], + ], + // Create a date using the timestamp of zero, then display its + // value both in UTC and the local timezone. + [ + 'input' => 0, + 'initial' => [ + 'timezone' => 'America/Los_Angeles', + 'format' => 'c', + 'expected_date' => '1969-12-31T16:00:00-08:00', + 'expected_timezone' => 'America/Los_Angeles', + 'expected_offset' => '-28800', + ], + 'transform' => [ + 'timezone' => 'UTC', + 'format' => 'c', + 'expected_date' => '1970-01-01T00:00:00+00:00', + 'expected_timezone' => 'UTC', + 'expected_offset' => 0, + ], + ], + ]; + } + + /** + * Provides data for testDateTimestamp. + * + * @return array + * An array of arrays, each containing the arguments required for + * self::testDateTimestamp(). + * + * @see testDateTimestamp() + */ + public function providerTestDateTimestamp() { + return [ + // Create date object from datetime string in UTC, and convert + // it to a local date. + [ + 'input' => '1970-01-01 00:00:00', + 'initial' => [ + 'timezone' => 'UTC', + 'format' => 'c', + 'expected_date' => '1970-01-01T00:00:00+00:00', + 'expected_timezone' => 'UTC', + 'expected_offset' => 0, + ], + 'transform' => [ + 'timezone' => 'America/Los_Angeles', + 'format' => 'c', + 'expected_date' => '1969-12-31T16:00:00-08:00', + 'expected_timezone' => 'America/Los_Angeles', + 'expected_offset' => '-28800', + ], + ], + // Convert the local time to UTC using string input. + [ + 'input' => '1969-12-31 16:00:00', + 'initial' => [ + 'timezone' => 'America/Los_Angeles', + 'format' => 'c', + 'expected_date' => '1969-12-31T16:00:00-08:00', + 'expected_timezone' => 'America/Los_Angeles', + 'expected_offset' => '-28800', + ], + 'transform' => [ + 'timezone' => 'UTC', + 'format' => 'c', + 'expected_date' => '1970-01-01T00:00:00+00:00', + 'expected_timezone' => 'UTC', + 'expected_offset' => 0, + ], + ], + // Convert the local time to UTC using string input. + [ + 'input' => '1969-12-31 16:00:00', + 'initial' => [ + 'timezone' => 'Europe/Warsaw', + 'format' => 'c', + 'expected_date' => '1969-12-31T16:00:00+01:00', + 'expected_timezone' => 'Europe/Warsaw', + 'expected_offset' => '+3600', + ], + 'transform' => [ + 'timezone' => 'UTC', + 'format' => 'c', + 'expected_date' => '1969-12-31T15:00:00+00:00', + 'expected_timezone' => 'UTC', + 'expected_offset' => 0, + ], + ], + ]; + } + + /** + * Provides data for date tests. + * + * @return array + * An array of arrays, each containing the input parameters for + * DateTimePlusTest::testDateDiff(). + * + * @see DateTimePlusTest::testDateDiff() + */ + public function providerTestDateDiff() { + + $empty_interval = new \DateInterval('PT0S'); + + $positive_19_hours = new \DateInterval('PT19H'); + + $positive_18_hours = new \DateInterval('PT18H'); + + $positive_1_hour = new \DateInterval('PT1H'); + + $negative_1_hour = new \DateInterval('PT1H'); + $negative_1_hour->invert = 1; + + return [ + // There should be a 19 hour time interval between + // new years in Sydney and new years in LA in year 2000. + [ + 'input2' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('Australia/Sydney')), + 'input1' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles')), + 'absolute' => FALSE, + 'expected' => $positive_19_hours, + ], + // In 1970 Sydney did not observe daylight savings time + // So there is only a 18 hour time interval. + [ + 'input2' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('Australia/Sydney')), + 'input1' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles')), + 'absolute' => FALSE, + 'expected' => $positive_18_hours, + ], + [ + 'input1' => DateTimePlus::createFromFormat('U', 3600, new \DateTimeZone('America/Los_Angeles')), + 'input2' => DateTimePlus::createFromFormat('U', 0, new \DateTimeZone('UTC')), + 'absolute' => FALSE, + 'expected' => $negative_1_hour, + ], + [ + 'input1' => DateTimePlus::createFromFormat('U', 3600), + 'input2' => DateTimePlus::createFromFormat('U', 0), + 'absolute' => FALSE, + 'expected' => $negative_1_hour, + ], + [ + 'input1' => DateTimePlus::createFromFormat('U', 3600), + 'input2' => \DateTime::createFromFormat('U', 0), + 'absolute' => FALSE, + 'expected' => $negative_1_hour, + ], + [ + 'input1' => DateTimePlus::createFromFormat('U', 3600), + 'input2' => DateTimePlus::createFromFormat('U', 0), + 'absolute' => TRUE, + 'expected' => $positive_1_hour, + ], + [ + 'input1' => DateTimePlus::createFromFormat('U', 3600), + 'input2' => \DateTime::createFromFormat('U', 0), + 'absolute' => TRUE, + 'expected' => $positive_1_hour, + ], + [ + 'input1' => DateTimePlus::createFromFormat('U', 0), + 'input2' => DateTimePlus::createFromFormat('U', 0), + 'absolute' => FALSE, + 'expected' => $empty_interval, + ], + ]; + } + + /** + * Provides data for date tests. + * + * @return array + * An array of arrays, each containing the input parameters for + * DateTimePlusTest::testInvalidDateDiff(). + * + * @see DateTimePlusTest::testInvalidDateDiff() + */ + public function providerTestInvalidDateDiff() { + return [ + [ + 'input1' => DateTimePlus::createFromFormat('U', 3600), + 'input2' => '1970-01-01 00:00:00', + 'absolute' => FALSE, + ], + [ + 'input1' => DateTimePlus::createFromFormat('U', 3600), + 'input2' => NULL, + 'absolute' => FALSE, + ], + ]; + } + + /** + * Tests invalid values passed to constructor. + * + * @param string $time + * A date/time string. + * @param string[] $errors + * An array of error messages. + * + * @covers ::__construct + * + * @dataProvider providerTestInvalidConstructor + */ + public function testInvalidConstructor($time, array $errors) { + $date = new DateTimePlus($time); + + $this->assertEquals(TRUE, $date->hasErrors()); + $this->assertEquals($errors, $date->getErrors()); + } + + /** + * Provider for testInvalidConstructor(). + * + * @return array + * An array of invalid date/time strings, and corresponding error messages. + */ + public function providerTestInvalidConstructor() { + return [ + [ + 'YYYY-MM-DD', + [ + 'The timezone could not be found in the database', + 'Unexpected character', + 'Double timezone specification', + ], + ], + [ + '2017-MM-DD', + [ + 'Unexpected character', + 'The timezone could not be found in the database', + ], + ], + [ + 'YYYY-03-DD', + [ + 'The timezone could not be found in the database', + 'Unexpected character', + 'Double timezone specification', + ], + ], + [ + 'YYYY-MM-07', + [ + 'The timezone could not be found in the database', + 'Unexpected character', + 'Double timezone specification', + ], + ], + [ + '2017-13-55', + [ + 'Unexpected character', + ], + ], + [ + 'YYYY-MM-DD hh:mm:ss', + [ + 'The timezone could not be found in the database', + 'Unexpected character', + 'Double timezone specification', + ], + ], + [ + '2017-03-07 25:70:80', + [ + 'Unexpected character', + 'Double time specification', + ], + ], + [ + 'lorem ipsum dolor sit amet', + [ + 'The timezone could not be found in the database', + 'Double timezone specification', + ], + ], + ]; + } + + /** + * Tests the $settings['validate_format'] parameter in ::createFromFormat(). + */ + public function testValidateFormat() { + // Check that an input that does not strictly follow the input format will + // produce the desired date. In this case the year string '11' doesn't + // precisely match the 'Y' formater parameter, but PHP will parse it + // regardless. However, when formatted with the same string, the year will + // be output with four digits. With the ['validate_format' => FALSE] + // $settings, this will not thrown an exception. + $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '11-03-31 17:44:00', 'UTC', ['validate_format' => FALSE]); + $this->assertEquals('0011-03-31 17:44:00', $date->format('Y-m-d H:i:s')); + + // Parse the same date with ['validate_format' => TRUE] and make sure we + // get the expected exception. + $this->setExpectedException(\UnexpectedValueException::class); + $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '11-03-31 17:44:00', 'UTC', ['validate_format' => TRUE]); + } + + /** + * Tests that object methods are chainable. + * + * @covers ::__call + */ + public function testChainable() { + $date = new DateTimePlus('now', 'Australia/Sydney'); + + $date->setTimestamp(12345678); + $rendered = $date->render(); + $this->assertEquals('1970-05-24 07:21:18 Australia/Sydney', $rendered); + + $date->setTimestamp(23456789); + $rendered = $date->setTimezone(new \DateTimeZone('America/New_York'))->render(); + $this->assertEquals('1970-09-29 07:46:29 America/New_York', $rendered); + + $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-05-24 07:21:18', new \DateTimeZone('Australia/Sydney')) + ->setTimezone(new \DateTimeZone('America/New_York')); + $rendered = $date->render(); + $this->assertInstanceOf(DateTimePlus::class, $date); + $this->assertEquals(12345678, $date->getTimestamp()); + $this->assertEquals('1970-05-23 17:21:18 America/New_York', $rendered); + } + + /** + * Tests that non-chainable methods work. + * + * @covers ::__call + */ + public function testChainableNonChainable() { + $datetime1 = new DateTimePlus('2009-10-11 12:00:00'); + $datetime2 = new DateTimePlus('2009-10-13 12:00:00'); + $interval = $datetime1->diff($datetime2); + $this->assertInstanceOf(\DateInterval::class, $interval); + $this->assertEquals('+2 days', $interval->format('%R%a days')); + } + + /** + * Tests that chained calls to non-existent functions throw an exception. + * + * @covers ::__call + */ + public function testChainableNonCallable() { + $this->setExpectedException(\BadMethodCallException::class, 'Call to undefined method Drupal\Component\Datetime\DateTimePlus::nonexistent()'); + $date = new DateTimePlus('now', 'Australia/Sydney'); + $date->setTimezone(new \DateTimeZone('America/New_York'))->nonexistent(); + } + +}