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