annotate core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Tests\Component\Datetime;
Chris@0 4
Chris@0 5 use Drupal\Component\Datetime\DateTimePlus;
Chris@0 6 use PHPUnit\Framework\TestCase;
Chris@0 7
Chris@0 8 /**
Chris@0 9 * @coversDefaultClass \Drupal\Component\Datetime\DateTimePlus
Chris@0 10 * @group Datetime
Chris@0 11 */
Chris@0 12 class DateTimePlusTest extends TestCase {
Chris@0 13
Chris@0 14 /**
Chris@0 15 * Test creating dates from string and array input.
Chris@0 16 *
Chris@0 17 * @param mixed $input
Chris@0 18 * Input argument for DateTimePlus.
Chris@0 19 * @param string $timezone
Chris@0 20 * Timezone argument for DateTimePlus.
Chris@0 21 * @param string $expected
Chris@0 22 * Expected output from DateTimePlus::format().
Chris@0 23 *
Chris@0 24 * @dataProvider providerTestDates
Chris@0 25 */
Chris@0 26 public function testDates($input, $timezone, $expected) {
Chris@0 27 $date = new DateTimePlus($input, $timezone);
Chris@0 28 $value = $date->format('c');
Chris@0 29
Chris@0 30 if (is_array($input)) {
Chris@0 31 $input = var_export($input, TRUE);
Chris@0 32 }
Chris@0 33 $this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $timezone, $expected, $value));
Chris@0 34 }
Chris@0 35
Chris@0 36 /**
Chris@0 37 * Test creating dates from string and array input.
Chris@0 38 *
Chris@0 39 * @param mixed $input
Chris@0 40 * Input argument for DateTimePlus.
Chris@0 41 * @param string $timezone
Chris@0 42 * Timezone argument for DateTimePlus.
Chris@0 43 * @param string $expected
Chris@0 44 * Expected output from DateTimePlus::format().
Chris@0 45 *
Chris@0 46 * @dataProvider providerTestDateArrays
Chris@0 47 */
Chris@0 48 public function testDateArrays($input, $timezone, $expected) {
Chris@0 49 $date = DateTimePlus::createFromArray($input, $timezone);
Chris@0 50 $value = $date->format('c');
Chris@0 51
Chris@0 52 if (is_array($input)) {
Chris@0 53 $input = var_export($input, TRUE);
Chris@0 54 }
Chris@0 55 $this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $timezone, $expected, $value));
Chris@0 56 }
Chris@0 57
Chris@0 58 /**
Chris@0 59 * Test date diffs.
Chris@0 60 *
Chris@0 61 * @param mixed $input1
Chris@0 62 * A DateTimePlus object.
Chris@0 63 * @param mixed $input2
Chris@0 64 * Date argument for DateTimePlus::diff method.
Chris@0 65 * @param bool $absolute
Chris@0 66 * Absolute flag for DateTimePlus::diff method.
Chris@0 67 * @param \DateInterval $expected
Chris@0 68 * The expected result of the DateTimePlus::diff operation.
Chris@0 69 *
Chris@0 70 * @dataProvider providerTestDateDiff
Chris@0 71 */
Chris@0 72 public function testDateDiff($input1, $input2, $absolute, \DateInterval $expected) {
Chris@0 73 $interval = $input1->diff($input2, $absolute);
Chris@0 74 $this->assertEquals($interval, $expected);
Chris@0 75 }
Chris@0 76
Chris@0 77 /**
Chris@0 78 * Test date diff exception caused by invalid input.
Chris@0 79 *
Chris@0 80 * @param mixed $input1
Chris@0 81 * A DateTimePlus object.
Chris@0 82 * @param mixed $input2
Chris@0 83 * Date argument for DateTimePlus::diff method.
Chris@0 84 * @param bool $absolute
Chris@0 85 * Absolute flag for DateTimePlus::diff method.
Chris@0 86 *
Chris@0 87 * @dataProvider providerTestInvalidDateDiff
Chris@0 88 */
Chris@0 89 public function testInvalidDateDiff($input1, $input2, $absolute) {
Chris@14 90 if (method_exists($this, 'expectException')) {
Chris@14 91 $this->expectException(\BadMethodCallException::class);
Chris@14 92 $this->expectExceptionMessage('Method Drupal\Component\Datetime\DateTimePlus::diff expects parameter 1 to be a \DateTime or \Drupal\Component\Datetime\DateTimePlus object');
Chris@14 93 }
Chris@14 94 else {
Chris@14 95 $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 96 }
Chris@0 97 $interval = $input1->diff($input2, $absolute);
Chris@0 98 }
Chris@0 99
Chris@0 100 /**
Chris@0 101 * Test creating dates from invalid array input.
Chris@0 102 *
Chris@0 103 * @param mixed $input
Chris@0 104 * Input argument for DateTimePlus.
Chris@0 105 * @param string $timezone
Chris@0 106 * Timezone argument for DateTimePlus.
Chris@0 107 * @param string $class
Chris@0 108 * The Exception subclass to expect to be thrown.
Chris@0 109 *
Chris@0 110 * @dataProvider providerTestInvalidDateArrays
Chris@0 111 */
Chris@0 112 public function testInvalidDateArrays($input, $timezone, $class) {
Chris@14 113 if (method_exists($this, 'expectException')) {
Chris@14 114 $this->expectException($class);
Chris@14 115 }
Chris@14 116 else {
Chris@14 117 $this->setExpectedException($class);
Chris@14 118 }
Chris@0 119 $this->assertInstanceOf(
Chris@0 120 '\Drupal\Component\DateTimePlus',
Chris@0 121 DateTimePlus::createFromArray($input, $timezone)
Chris@0 122 );
Chris@0 123 }
Chris@0 124
Chris@0 125 /**
Chris@0 126 * Test creating dates from timestamps, and manipulating timezones.
Chris@0 127 *
Chris@0 128 * @param int $input
Chris@0 129 * Input argument for DateTimePlus::createFromTimestamp().
Chris@0 130 * @param array $initial
Chris@0 131 * An array containing:
Chris@0 132 * - 'timezone_initial' - Timezone argument for DateTimePlus.
Chris@0 133 * - 'format_initial' - Format argument for DateTimePlus.
Chris@0 134 * - 'expected_initial_date' - Expected output from DateTimePlus::format().
Chris@0 135 * - 'expected_initial_timezone' - Expected output from
Chris@0 136 * DateTimePlus::getTimeZone()::getName().
Chris@0 137 * - 'expected_initial_offset' - Expected output from DateTimePlus::getOffset().
Chris@0 138 * @param array $transform
Chris@0 139 * An array containing:
Chris@0 140 * - 'timezone_transform' - Argument to transform date to another timezone via
Chris@0 141 * DateTimePlus::setTimezone().
Chris@0 142 * - 'format_transform' - Format argument to use when transforming date to
Chris@0 143 * another timezone.
Chris@0 144 * - 'expected_transform_date' - Expected output from DateTimePlus::format(),
Chris@0 145 * after timezone transform.
Chris@0 146 * - 'expected_transform_timezone' - Expected output from
Chris@0 147 * DateTimePlus::getTimeZone()::getName(), after timezone transform.
Chris@0 148 * - 'expected_transform_offset' - Expected output from
Chris@0 149 * DateTimePlus::getOffset(), after timezone transform.
Chris@0 150 *
Chris@0 151 * @dataProvider providerTestTimestamp
Chris@0 152 */
Chris@0 153 public function testTimestamp($input, array $initial, array $transform) {
Chris@0 154 // Initialize a new date object.
Chris@0 155 $date = DateTimePlus::createFromTimestamp($input, $initial['timezone']);
Chris@0 156 $this->assertDateTimestamp($date, $input, $initial, $transform);
Chris@0 157 }
Chris@0 158
Chris@0 159 /**
Chris@0 160 * Test creating dates from datetime strings.
Chris@0 161 *
Chris@0 162 * @param string $input
Chris@0 163 * Input argument for DateTimePlus().
Chris@0 164 * @param array $initial
Chris@0 165 * @see testTimestamp()
Chris@0 166 * @param array $transform
Chris@0 167 * @see testTimestamp()
Chris@0 168 *
Chris@0 169 * @dataProvider providerTestDateTimestamp
Chris@0 170 */
Chris@0 171 public function testDateTimestamp($input, array $initial, array $transform) {
Chris@0 172 // Initialize a new date object.
Chris@0 173 $date = new DateTimePlus($input, $initial['timezone']);
Chris@0 174 $this->assertDateTimestamp($date, $input, $initial, $transform);
Chris@0 175 }
Chris@0 176
Chris@0 177 /**
Chris@0 178 * Assertion helper for testTimestamp and testDateTimestamp since they need
Chris@0 179 * different dataProviders.
Chris@0 180 *
Chris@12 181 * @param \Drupal\Component\Datetime\DateTimePlus $date
Chris@0 182 * DateTimePlus to test.
Chris@0 183 * @input mixed $input
Chris@0 184 * The original input passed to the test method.
Chris@0 185 * @param array $initial
Chris@0 186 * @see testTimestamp()
Chris@0 187 * @param array $transform
Chris@0 188 * @see testTimestamp()
Chris@0 189 */
Chris@0 190 public function assertDateTimestamp($date, $input, $initial, $transform) {
Chris@0 191 // Check format.
Chris@0 192 $value = $date->format($initial['format']);
Chris@0 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));
Chris@0 194
Chris@0 195 // Check timezone name.
Chris@0 196 $value = $date->getTimeZone()->getName();
Chris@0 197 $this->assertEquals($initial['expected_timezone'], $value, sprintf("The current timezone is %s: should be %s.", $value, $initial['expected_timezone']));
Chris@0 198
Chris@0 199 // Check offset.
Chris@0 200 $value = $date->getOffset();
Chris@0 201 $this->assertEquals($initial['expected_offset'], $value, sprintf("The current offset is %s: should be %s.", $value, $initial['expected_offset']));
Chris@0 202
Chris@0 203 // Transform the date to another timezone.
Chris@0 204 $date->setTimezone(new \DateTimeZone($transform['timezone']));
Chris@0 205
Chris@0 206 // Check transformed format.
Chris@0 207 $value = $date->format($transform['format']);
Chris@0 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));
Chris@0 209
Chris@0 210 // Check transformed timezone.
Chris@0 211 $value = $date->getTimeZone()->getName();
Chris@0 212 $this->assertEquals($transform['expected_timezone'], $value, sprintf("The current timezone should be %s, found %s.", $transform['expected_timezone'], $value));
Chris@0 213
Chris@0 214 // Check transformed offset.
Chris@0 215 $value = $date->getOffset();
Chris@0 216 $this->assertEquals($transform['expected_offset'], $value, sprintf("The current offset should be %s, found %s.", $transform['expected_offset'], $value));
Chris@0 217 }
Chris@0 218
Chris@0 219 /**
Chris@0 220 * Test creating dates from format strings.
Chris@0 221 *
Chris@0 222 * @param string $input
Chris@0 223 * Input argument for DateTimePlus.
Chris@0 224 * @param string $timezone
Chris@0 225 * Timezone argument for DateTimePlus.
Chris@0 226 * @param string $format_date
Chris@0 227 * Format argument for DateTimePlus::format().
Chris@0 228 * @param string $expected
Chris@0 229 * Expected output from DateTimePlus::format().
Chris@0 230 *
Chris@0 231 * @dataProvider providerTestDateFormat
Chris@0 232 */
Chris@0 233 public function testDateFormat($input, $timezone, $format, $format_date, $expected) {
Chris@0 234 $date = DateTimePlus::createFromFormat($format, $input, $timezone);
Chris@0 235 $value = $date->format($format_date);
Chris@0 236 $this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s, %s): should be %s, found %s.", $input, $timezone, $format, $expected, $value));
Chris@0 237 }
Chris@0 238
Chris@0 239 /**
Chris@0 240 * Test invalid date handling.
Chris@0 241 *
Chris@0 242 * @param mixed $input
Chris@0 243 * Input argument for DateTimePlus.
Chris@0 244 * @param string $timezone
Chris@0 245 * Timezone argument for DateTimePlus.
Chris@0 246 * @param string $format
Chris@0 247 * Format argument for DateTimePlus.
Chris@0 248 * @param string $message
Chris@0 249 * Message to print if no errors are thrown by the invalid dates.
Chris@0 250 * @param string $class
Chris@0 251 * The Exception subclass to expect to be thrown.
Chris@0 252 *
Chris@0 253 * @dataProvider providerTestInvalidDates
Chris@0 254 */
Chris@0 255 public function testInvalidDates($input, $timezone, $format, $message, $class) {
Chris@14 256 if (method_exists($this, 'expectException')) {
Chris@14 257 $this->expectException($class);
Chris@14 258 }
Chris@14 259 else {
Chris@14 260 $this->setExpectedException($class);
Chris@14 261 }
Chris@0 262 DateTimePlus::createFromFormat($format, $input, $timezone);
Chris@0 263 }
Chris@0 264
Chris@0 265 /**
Chris@0 266 * Tests that DrupalDateTime can detect the right timezone to use.
Chris@0 267 * When specified or not.
Chris@0 268 *
Chris@0 269 * @param mixed $input
Chris@0 270 * Input argument for DateTimePlus.
Chris@0 271 * @param mixed $timezone
Chris@0 272 * Timezone argument for DateTimePlus.
Chris@0 273 * @param string $expected_timezone
Chris@0 274 * Expected timezone returned from DateTimePlus::getTimezone::getName().
Chris@0 275 * @param string $message
Chris@0 276 * Message to print on test failure.
Chris@0 277 *
Chris@0 278 * @dataProvider providerTestDateTimezone
Chris@0 279 */
Chris@0 280 public function testDateTimezone($input, $timezone, $expected_timezone, $message) {
Chris@0 281 $date = new DateTimePlus($input, $timezone);
Chris@0 282 $timezone = $date->getTimezone()->getName();
Chris@0 283 $this->assertEquals($timezone, $expected_timezone, $message);
Chris@0 284 }
Chris@0 285
Chris@0 286 /**
Chris@0 287 * Test that DrupalDateTime can detect the right timezone to use when
Chris@0 288 * constructed from a datetime object.
Chris@0 289 */
Chris@0 290 public function testDateTimezoneWithDateTimeObject() {
Chris@0 291 // Create a date object with another date object.
Chris@0 292 $input = new \DateTime('now', new \DateTimeZone('Pacific/Midway'));
Chris@0 293 $timezone = NULL;
Chris@0 294 $expected_timezone = 'Pacific/Midway';
Chris@0 295 $message = 'DateTimePlus uses the specified timezone if provided.';
Chris@0 296
Chris@0 297 $date = DateTimePlus::createFromDateTime($input, $timezone);
Chris@0 298 $timezone = $date->getTimezone()->getName();
Chris@0 299 $this->assertEquals($timezone, $expected_timezone, $message);
Chris@0 300 }
Chris@0 301
Chris@0 302 /**
Chris@0 303 * Provides data for date tests.
Chris@0 304 *
Chris@0 305 * @return array
Chris@0 306 * An array of arrays, each containing the input parameters for
Chris@0 307 * DateTimePlusTest::testDates().
Chris@0 308 *
Chris@0 309 * @see DateTimePlusTest::testDates()
Chris@0 310 */
Chris@0 311 public function providerTestDates() {
Chris@0 312 $dates = [
Chris@0 313 // String input.
Chris@0 314 // Create date object from datetime string.
Chris@0 315 ['2009-03-07 10:30', 'America/Chicago', '2009-03-07T10:30:00-06:00'],
Chris@0 316 // Same during daylight savings time.
Chris@0 317 ['2009-06-07 10:30', 'America/Chicago', '2009-06-07T10:30:00-05:00'],
Chris@0 318 // Create date object from date string.
Chris@0 319 ['2009-03-07', 'America/Chicago', '2009-03-07T00:00:00-06:00'],
Chris@0 320 // Same during daylight savings time.
Chris@0 321 ['2009-06-07', 'America/Chicago', '2009-06-07T00:00:00-05:00'],
Chris@0 322 // Create date object from date string.
Chris@0 323 ['2009-03-07 10:30', 'Australia/Canberra', '2009-03-07T10:30:00+11:00'],
Chris@0 324 // Same during daylight savings time.
Chris@0 325 ['2009-06-07 10:30', 'Australia/Canberra', '2009-06-07T10:30:00+10:00'],
Chris@0 326 ];
Chris@0 327
Chris@0 328 // On 32-bit systems, timestamps are limited to 1901-2038.
Chris@0 329 if (PHP_INT_SIZE > 4) {
Chris@0 330 // Create a date object in the distant past.
Chris@0 331 // @see https://www.drupal.org/node/2795489#comment-12127088
Chris@0 332 if (version_compare(PHP_VERSION, '5.6.15', '>=')) {
Chris@17 333 // Note that this date is after the United States standardized its
Chris@17 334 // timezones.
Chris@17 335 $dates[] = ['1883-11-19 10:30', 'America/Chicago', '1883-11-19T10:30:00-06:00'];
Chris@0 336 }
Chris@0 337 // Create a date object in the far future.
Chris@0 338 $dates[] = ['2345-01-02 02:04', 'UTC', '2345-01-02T02:04:00+00:00'];
Chris@0 339 }
Chris@0 340
Chris@0 341 return $dates;
Chris@0 342 }
Chris@0 343
Chris@0 344 /**
Chris@0 345 * Provides data for date tests.
Chris@0 346 *
Chris@0 347 * @return array
Chris@0 348 * An array of arrays, each containing the input parameters for
Chris@0 349 * DateTimePlusTest::testDates().
Chris@0 350 *
Chris@0 351 * @see DateTimePlusTest::testDates()
Chris@0 352 */
Chris@0 353 public function providerTestDateArrays() {
Chris@0 354 $dates = [
Chris@0 355 // Array input.
Chris@0 356 // Create date object from date array, date only.
Chris@0 357 [['year' => 2010, 'month' => 2, 'day' => 28], 'America/Chicago', '2010-02-28T00:00:00-06:00'],
Chris@0 358 // Create date object from date array with hour.
Chris@0 359 [['year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 10], 'America/Chicago', '2010-02-28T10:00:00-06:00'],
Chris@0 360 // Create date object from date array, date only.
Chris@0 361 [['year' => 2010, 'month' => 2, 'day' => 28], 'Europe/Berlin', '2010-02-28T00:00:00+01:00'],
Chris@0 362 // Create date object from date array with hour.
Chris@0 363 [['year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 10], 'Europe/Berlin', '2010-02-28T10:00:00+01:00'],
Chris@0 364 ];
Chris@0 365
Chris@0 366 // On 32-bit systems, timestamps are limited to 1901-2038.
Chris@0 367 if (PHP_INT_SIZE > 4) {
Chris@0 368 // Create a date object in the distant past.
Chris@0 369 // @see https://www.drupal.org/node/2795489#comment-12127088
Chris@0 370 if (version_compare(PHP_VERSION, '5.6.15', '>=')) {
Chris@17 371 // Note that this date is after the United States standardized its
Chris@17 372 // timezones.
Chris@17 373 $dates[] = [['year' => 1883, 'month' => 11, 'day' => 19], 'America/Chicago', '1883-11-19T00:00:00-06:00'];
Chris@0 374 }
Chris@0 375 // Create a date object in the far future.
Chris@0 376 $dates[] = [['year' => 2345, 'month' => 1, 'day' => 2], 'UTC', '2345-01-02T00:00:00+00:00'];
Chris@0 377 }
Chris@0 378
Chris@0 379 return $dates;
Chris@0 380 }
Chris@0 381
Chris@0 382 /**
Chris@0 383 * Provides data for testDateFormats.
Chris@0 384 *
Chris@0 385 * @return array
Chris@0 386 * An array of arrays, each containing:
Chris@0 387 * - 'input' - Input to DateTimePlus.
Chris@0 388 * - 'timezone' - Timezone for DateTimePlus.
Chris@0 389 * - 'format' - Date format for DateTimePlus.
Chris@0 390 * - 'format_date' - Date format for use in $date->format() method.
Chris@0 391 * - 'expected' - The expected return from DateTimePlus.
Chris@0 392 *
Chris@0 393 * @see testDateFormats()
Chris@0 394 */
Chris@0 395 public function providerTestDateFormat() {
Chris@0 396 return [
Chris@0 397 // Create a year-only date.
Chris@0 398 ['2009', NULL, 'Y', 'Y', '2009'],
Chris@0 399 // Create a month and year-only date.
Chris@0 400 ['2009-10', NULL, 'Y-m', 'Y-m', '2009-10'],
Chris@0 401 // Create a time-only date.
Chris@0 402 ['T10:30:00', NULL, '\TH:i:s', 'H:i:s', '10:30:00'],
Chris@0 403 // Create a time-only date.
Chris@0 404 ['10:30:00', NULL, 'H:i:s', 'H:i:s', '10:30:00'],
Chris@0 405 ];
Chris@0 406 }
Chris@0 407
Chris@0 408 /**
Chris@0 409 * Provides data for testInvalidDates.
Chris@0 410 *
Chris@0 411 * @return array
Chris@0 412 * An array of arrays, each containing:
Chris@0 413 * - 'input' - Input for DateTimePlus.
Chris@0 414 * - 'timezone' - Timezone for DateTimePlus.
Chris@0 415 * - 'format' - Format for DateTimePlus.
Chris@0 416 * - 'message' - Message to display on failure.
Chris@0 417 *
Chris@0 418 * @see testInvalidDates
Chris@0 419 */
Chris@0 420 public function providerTestInvalidDates() {
Chris@0 421 return [
Chris@0 422 // Test for invalid month names when we are using a short version
Chris@0 423 // of the month.
Chris@0 424 ['23 abc 2012', NULL, 'd M Y', "23 abc 2012 contains an invalid month name and did not produce errors.", \InvalidArgumentException::class],
Chris@0 425 // Test for invalid hour.
Chris@0 426 ['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 427 // Test for invalid day.
Chris@0 428 ['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 429 // Test for invalid month.
Chris@0 430 ['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 431 // Test for invalid year.
Chris@0 432 ['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 433
Chris@0 434 ];
Chris@0 435 }
Chris@0 436
Chris@0 437 /**
Chris@0 438 * Data provider for testInvalidDateArrays.
Chris@0 439 *
Chris@0 440 * @return array
Chris@0 441 * An array of arrays, each containing:
Chris@0 442 * - 'input' - Input for DateTimePlus.
Chris@0 443 * - 'timezone' - Timezone for DateTimePlus.
Chris@0 444 *
Chris@0 445 * @see testInvalidDateArrays
Chris@0 446 */
Chris@0 447 public function providerTestInvalidDateArrays() {
Chris@0 448 return [
Chris@0 449 // One year larger than the documented upper limit of checkdate().
Chris@0 450 [['year' => 32768, 'month' => 1, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
Chris@0 451 // One year smaller than the documented lower limit of checkdate().
Chris@0 452 [['year' => 0, 'month' => 1, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
Chris@0 453 // Test for invalid month from date array.
Chris@0 454 [['year' => 2010, 'month' => 27, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
Chris@0 455 // Test for invalid hour from date array.
Chris@0 456 [['year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 80, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
Chris@0 457 // Test for invalid minute from date array.
Chris@0 458 [['year' => 2010, 'month' => 7, 'day' => 8, 'hour' => 8, 'minute' => 88, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
Chris@0 459 // Regression test for https://www.drupal.org/node/2084455.
Chris@0 460 [['hour' => 59, 'minute' => 1, 'second' => 1], 'America/Chicago', \InvalidArgumentException::class],
Chris@0 461 ];
Chris@0 462 }
Chris@0 463
Chris@0 464 /**
Chris@0 465 * Provides data for testDateTimezone.
Chris@0 466 *
Chris@0 467 * @return array
Chris@0 468 * An array of arrays, each containing:
Chris@0 469 * - 'date' - Date string or object for DateTimePlus.
Chris@0 470 * - 'timezone' - Timezone string for DateTimePlus.
Chris@0 471 * - 'expected' - Expected return from DateTimePlus::getTimezone()::getName().
Chris@0 472 * - 'message' - Message to display on test failure.
Chris@0 473 *
Chris@0 474 * @see testDateTimezone
Chris@0 475 */
Chris@0 476 public function providerTestDateTimezone() {
Chris@0 477 // Use a common date for most of the tests.
Chris@0 478 $date_string = '2007-01-31 21:00:00';
Chris@0 479
Chris@0 480 // Detect the system timezone.
Chris@0 481 $system_timezone = date_default_timezone_get();
Chris@0 482
Chris@0 483 return [
Chris@0 484 // Create a date object with an unspecified timezone, which should
Chris@0 485 // end up using the system timezone.
Chris@0 486 [$date_string, NULL, $system_timezone, 'DateTimePlus uses the system timezone when there is no site timezone.'],
Chris@0 487 // Create a date object with a specified timezone name.
Chris@0 488 [$date_string, 'America/Yellowknife', 'America/Yellowknife', 'DateTimePlus uses the specified timezone if provided.'],
Chris@0 489 // Create a date object with a timezone object.
Chris@0 490 [$date_string, new \DateTimeZone('Australia/Canberra'), 'Australia/Canberra', 'DateTimePlus uses the specified timezone if provided.'],
Chris@0 491 // Create a date object with another date object.
Chris@0 492 [new DateTimePlus('now', 'Pacific/Midway'), NULL, 'Pacific/Midway', 'DateTimePlus uses the specified timezone if provided.'],
Chris@0 493 ];
Chris@0 494 }
Chris@0 495
Chris@0 496 /**
Chris@0 497 * Provides data for testTimestamp.
Chris@0 498 *
Chris@0 499 * @return array
Chris@0 500 * An array of arrays, each containing the arguments required for
Chris@0 501 * self::testTimestamp().
Chris@0 502 *
Chris@0 503 * @see testTimestamp()
Chris@0 504 */
Chris@0 505 public function providerTestTimestamp() {
Chris@0 506 return [
Chris@0 507 // Create date object from a unix timestamp and display it in
Chris@0 508 // local time.
Chris@0 509 [
Chris@0 510 'input' => 0,
Chris@0 511 'initial' => [
Chris@0 512 'timezone' => 'UTC',
Chris@0 513 'format' => 'c',
Chris@0 514 'expected_date' => '1970-01-01T00:00:00+00:00',
Chris@0 515 'expected_timezone' => 'UTC',
Chris@0 516 'expected_offset' => 0,
Chris@0 517 ],
Chris@0 518 'transform' => [
Chris@0 519 'timezone' => 'America/Los_Angeles',
Chris@0 520 'format' => 'c',
Chris@0 521 'expected_date' => '1969-12-31T16:00:00-08:00',
Chris@0 522 'expected_timezone' => 'America/Los_Angeles',
Chris@0 523 'expected_offset' => '-28800',
Chris@0 524 ],
Chris@0 525 ],
Chris@0 526 // Create a date using the timestamp of zero, then display its
Chris@0 527 // value both in UTC and the local timezone.
Chris@0 528 [
Chris@0 529 'input' => 0,
Chris@0 530 'initial' => [
Chris@0 531 'timezone' => 'America/Los_Angeles',
Chris@0 532 'format' => 'c',
Chris@0 533 'expected_date' => '1969-12-31T16:00:00-08:00',
Chris@0 534 'expected_timezone' => 'America/Los_Angeles',
Chris@0 535 'expected_offset' => '-28800',
Chris@0 536 ],
Chris@0 537 'transform' => [
Chris@0 538 'timezone' => 'UTC',
Chris@0 539 'format' => 'c',
Chris@0 540 'expected_date' => '1970-01-01T00:00:00+00:00',
Chris@0 541 'expected_timezone' => 'UTC',
Chris@0 542 'expected_offset' => 0,
Chris@0 543 ],
Chris@0 544 ],
Chris@0 545 ];
Chris@0 546 }
Chris@0 547
Chris@0 548 /**
Chris@0 549 * Provides data for testDateTimestamp.
Chris@0 550 *
Chris@0 551 * @return array
Chris@0 552 * An array of arrays, each containing the arguments required for
Chris@0 553 * self::testDateTimestamp().
Chris@0 554 *
Chris@0 555 * @see testDateTimestamp()
Chris@0 556 */
Chris@0 557 public function providerTestDateTimestamp() {
Chris@0 558 return [
Chris@0 559 // Create date object from datetime string in UTC, and convert
Chris@0 560 // it to a local date.
Chris@0 561 [
Chris@0 562 'input' => '1970-01-01 00:00:00',
Chris@0 563 'initial' => [
Chris@0 564 'timezone' => 'UTC',
Chris@0 565 'format' => 'c',
Chris@0 566 'expected_date' => '1970-01-01T00:00:00+00:00',
Chris@0 567 'expected_timezone' => 'UTC',
Chris@0 568 'expected_offset' => 0,
Chris@0 569 ],
Chris@0 570 'transform' => [
Chris@0 571 'timezone' => 'America/Los_Angeles',
Chris@0 572 'format' => 'c',
Chris@0 573 'expected_date' => '1969-12-31T16:00:00-08:00',
Chris@0 574 'expected_timezone' => 'America/Los_Angeles',
Chris@0 575 'expected_offset' => '-28800',
Chris@0 576 ],
Chris@0 577 ],
Chris@0 578 // Convert the local time to UTC using string input.
Chris@0 579 [
Chris@0 580 'input' => '1969-12-31 16:00:00',
Chris@0 581 'initial' => [
Chris@0 582 'timezone' => 'America/Los_Angeles',
Chris@0 583 'format' => 'c',
Chris@0 584 'expected_date' => '1969-12-31T16:00:00-08:00',
Chris@0 585 'expected_timezone' => 'America/Los_Angeles',
Chris@0 586 'expected_offset' => '-28800',
Chris@0 587 ],
Chris@0 588 'transform' => [
Chris@0 589 'timezone' => 'UTC',
Chris@0 590 'format' => 'c',
Chris@0 591 'expected_date' => '1970-01-01T00:00:00+00:00',
Chris@0 592 'expected_timezone' => 'UTC',
Chris@0 593 'expected_offset' => 0,
Chris@0 594 ],
Chris@0 595 ],
Chris@0 596 // Convert the local time to UTC using string input.
Chris@0 597 [
Chris@0 598 'input' => '1969-12-31 16:00:00',
Chris@0 599 'initial' => [
Chris@0 600 'timezone' => 'Europe/Warsaw',
Chris@0 601 'format' => 'c',
Chris@0 602 'expected_date' => '1969-12-31T16:00:00+01:00',
Chris@0 603 'expected_timezone' => 'Europe/Warsaw',
Chris@0 604 'expected_offset' => '+3600',
Chris@0 605 ],
Chris@0 606 'transform' => [
Chris@0 607 'timezone' => 'UTC',
Chris@0 608 'format' => 'c',
Chris@0 609 'expected_date' => '1969-12-31T15:00:00+00:00',
Chris@0 610 'expected_timezone' => 'UTC',
Chris@0 611 'expected_offset' => 0,
Chris@0 612 ],
Chris@0 613 ],
Chris@0 614 ];
Chris@0 615 }
Chris@0 616
Chris@0 617 /**
Chris@0 618 * Provides data for date tests.
Chris@0 619 *
Chris@0 620 * @return array
Chris@0 621 * An array of arrays, each containing the input parameters for
Chris@0 622 * DateTimePlusTest::testDateDiff().
Chris@0 623 *
Chris@0 624 * @see DateTimePlusTest::testDateDiff()
Chris@0 625 */
Chris@0 626 public function providerTestDateDiff() {
Chris@0 627
Chris@0 628 $empty_interval = new \DateInterval('PT0S');
Chris@0 629
Chris@0 630 $positive_19_hours = new \DateInterval('PT19H');
Chris@0 631
Chris@0 632 $positive_18_hours = new \DateInterval('PT18H');
Chris@0 633
Chris@0 634 $positive_1_hour = new \DateInterval('PT1H');
Chris@0 635
Chris@0 636 $negative_1_hour = new \DateInterval('PT1H');
Chris@0 637 $negative_1_hour->invert = 1;
Chris@0 638
Chris@0 639 return [
Chris@0 640 // There should be a 19 hour time interval between
Chris@0 641 // new years in Sydney and new years in LA in year 2000.
Chris@0 642 [
Chris@0 643 'input2' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('Australia/Sydney')),
Chris@0 644 'input1' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles')),
Chris@0 645 'absolute' => FALSE,
Chris@0 646 'expected' => $positive_19_hours,
Chris@0 647 ],
Chris@0 648 // In 1970 Sydney did not observe daylight savings time
Chris@0 649 // So there is only a 18 hour time interval.
Chris@0 650 [
Chris@0 651 'input2' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('Australia/Sydney')),
Chris@0 652 'input1' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles')),
Chris@0 653 'absolute' => FALSE,
Chris@0 654 'expected' => $positive_18_hours,
Chris@0 655 ],
Chris@0 656 [
Chris@0 657 'input1' => DateTimePlus::createFromFormat('U', 3600, new \DateTimeZone('America/Los_Angeles')),
Chris@0 658 'input2' => DateTimePlus::createFromFormat('U', 0, new \DateTimeZone('UTC')),
Chris@0 659 'absolute' => FALSE,
Chris@0 660 'expected' => $negative_1_hour,
Chris@0 661 ],
Chris@0 662 [
Chris@0 663 'input1' => DateTimePlus::createFromFormat('U', 3600),
Chris@0 664 'input2' => DateTimePlus::createFromFormat('U', 0),
Chris@0 665 'absolute' => FALSE,
Chris@0 666 'expected' => $negative_1_hour,
Chris@0 667 ],
Chris@0 668 [
Chris@0 669 'input1' => DateTimePlus::createFromFormat('U', 3600),
Chris@0 670 'input2' => \DateTime::createFromFormat('U', 0),
Chris@0 671 'absolute' => FALSE,
Chris@0 672 'expected' => $negative_1_hour,
Chris@0 673 ],
Chris@0 674 [
Chris@0 675 'input1' => DateTimePlus::createFromFormat('U', 3600),
Chris@0 676 'input2' => DateTimePlus::createFromFormat('U', 0),
Chris@0 677 'absolute' => TRUE,
Chris@0 678 'expected' => $positive_1_hour,
Chris@0 679 ],
Chris@0 680 [
Chris@0 681 'input1' => DateTimePlus::createFromFormat('U', 3600),
Chris@0 682 'input2' => \DateTime::createFromFormat('U', 0),
Chris@0 683 'absolute' => TRUE,
Chris@0 684 'expected' => $positive_1_hour,
Chris@0 685 ],
Chris@0 686 [
Chris@0 687 'input1' => DateTimePlus::createFromFormat('U', 0),
Chris@0 688 'input2' => DateTimePlus::createFromFormat('U', 0),
Chris@0 689 'absolute' => FALSE,
Chris@0 690 'expected' => $empty_interval,
Chris@0 691 ],
Chris@0 692 ];
Chris@0 693 }
Chris@0 694
Chris@0 695 /**
Chris@0 696 * Provides data for date tests.
Chris@0 697 *
Chris@0 698 * @return array
Chris@0 699 * An array of arrays, each containing the input parameters for
Chris@0 700 * DateTimePlusTest::testInvalidDateDiff().
Chris@0 701 *
Chris@0 702 * @see DateTimePlusTest::testInvalidDateDiff()
Chris@0 703 */
Chris@0 704 public function providerTestInvalidDateDiff() {
Chris@0 705 return [
Chris@0 706 [
Chris@0 707 'input1' => DateTimePlus::createFromFormat('U', 3600),
Chris@0 708 'input2' => '1970-01-01 00:00:00',
Chris@0 709 'absolute' => FALSE,
Chris@0 710 ],
Chris@0 711 [
Chris@0 712 'input1' => DateTimePlus::createFromFormat('U', 3600),
Chris@0 713 'input2' => NULL,
Chris@0 714 'absolute' => FALSE,
Chris@0 715 ],
Chris@0 716 ];
Chris@0 717 }
Chris@0 718
Chris@0 719 /**
Chris@0 720 * Tests invalid values passed to constructor.
Chris@0 721 *
Chris@0 722 * @param string $time
Chris@0 723 * A date/time string.
Chris@0 724 * @param string[] $errors
Chris@0 725 * An array of error messages.
Chris@0 726 *
Chris@0 727 * @covers ::__construct
Chris@0 728 *
Chris@0 729 * @dataProvider providerTestInvalidConstructor
Chris@0 730 */
Chris@0 731 public function testInvalidConstructor($time, array $errors) {
Chris@0 732 $date = new DateTimePlus($time);
Chris@0 733
Chris@0 734 $this->assertEquals(TRUE, $date->hasErrors());
Chris@0 735 $this->assertEquals($errors, $date->getErrors());
Chris@0 736 }
Chris@0 737
Chris@0 738 /**
Chris@0 739 * Provider for testInvalidConstructor().
Chris@0 740 *
Chris@0 741 * @return array
Chris@0 742 * An array of invalid date/time strings, and corresponding error messages.
Chris@0 743 */
Chris@0 744 public function providerTestInvalidConstructor() {
Chris@0 745 return [
Chris@0 746 [
Chris@0 747 'YYYY-MM-DD',
Chris@0 748 [
Chris@0 749 'The timezone could not be found in the database',
Chris@0 750 'Unexpected character',
Chris@0 751 'Double timezone specification',
Chris@0 752 ],
Chris@0 753 ],
Chris@0 754 [
Chris@0 755 '2017-MM-DD',
Chris@0 756 [
Chris@0 757 'Unexpected character',
Chris@0 758 'The timezone could not be found in the database',
Chris@0 759 ],
Chris@0 760 ],
Chris@0 761 [
Chris@0 762 'YYYY-03-DD',
Chris@0 763 [
Chris@0 764 'The timezone could not be found in the database',
Chris@0 765 'Unexpected character',
Chris@0 766 'Double timezone specification',
Chris@0 767 ],
Chris@0 768 ],
Chris@0 769 [
Chris@0 770 'YYYY-MM-07',
Chris@0 771 [
Chris@0 772 'The timezone could not be found in the database',
Chris@0 773 'Unexpected character',
Chris@0 774 'Double timezone specification',
Chris@0 775 ],
Chris@0 776 ],
Chris@0 777 [
Chris@0 778 '2017-13-55',
Chris@0 779 [
Chris@0 780 'Unexpected character',
Chris@0 781 ],
Chris@0 782 ],
Chris@0 783 [
Chris@0 784 'YYYY-MM-DD hh:mm:ss',
Chris@0 785 [
Chris@0 786 'The timezone could not be found in the database',
Chris@0 787 'Unexpected character',
Chris@0 788 'Double timezone specification',
Chris@0 789 ],
Chris@0 790 ],
Chris@0 791 [
Chris@0 792 '2017-03-07 25:70:80',
Chris@0 793 [
Chris@0 794 'Unexpected character',
Chris@0 795 'Double time specification',
Chris@0 796 ],
Chris@0 797 ],
Chris@0 798 [
Chris@0 799 'lorem ipsum dolor sit amet',
Chris@0 800 [
Chris@0 801 'The timezone could not be found in the database',
Chris@0 802 'Double timezone specification',
Chris@0 803 ],
Chris@0 804 ],
Chris@0 805 ];
Chris@0 806 }
Chris@0 807
Chris@0 808 /**
Chris@0 809 * Tests the $settings['validate_format'] parameter in ::createFromFormat().
Chris@0 810 */
Chris@0 811 public function testValidateFormat() {
Chris@0 812 // Check that an input that does not strictly follow the input format will
Chris@0 813 // produce the desired date. In this case the year string '11' doesn't
Chris@17 814 // precisely match the 'Y' formatter parameter, but PHP will parse it
Chris@0 815 // regardless. However, when formatted with the same string, the year will
Chris@0 816 // be output with four digits. With the ['validate_format' => FALSE]
Chris@0 817 // $settings, this will not thrown an exception.
Chris@0 818 $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '11-03-31 17:44:00', 'UTC', ['validate_format' => FALSE]);
Chris@0 819 $this->assertEquals('0011-03-31 17:44:00', $date->format('Y-m-d H:i:s'));
Chris@0 820
Chris@0 821 // Parse the same date with ['validate_format' => TRUE] and make sure we
Chris@0 822 // get the expected exception.
Chris@14 823 if (method_exists($this, 'expectException')) {
Chris@14 824 $this->expectException(\UnexpectedValueException::class);
Chris@14 825 }
Chris@14 826 else {
Chris@14 827 $this->setExpectedException(\UnexpectedValueException::class);
Chris@14 828 }
Chris@0 829 $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '11-03-31 17:44:00', 'UTC', ['validate_format' => TRUE]);
Chris@0 830 }
Chris@0 831
Chris@0 832 /**
Chris@14 833 * Tests setting the default time for date-only objects.
Chris@14 834 */
Chris@14 835 public function testDefaultDateTime() {
Chris@14 836 $utc = new \DateTimeZone('UTC');
Chris@14 837
Chris@14 838 $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '2017-05-23 22:58:00', $utc);
Chris@14 839 $this->assertEquals('22:58:00', $date->format('H:i:s'));
Chris@14 840 $date->setDefaultDateTime();
Chris@14 841 $this->assertEquals('12:00:00', $date->format('H:i:s'));
Chris@14 842 }
Chris@14 843
Chris@14 844 /**
Chris@0 845 * Tests that object methods are chainable.
Chris@0 846 *
Chris@0 847 * @covers ::__call
Chris@0 848 */
Chris@0 849 public function testChainable() {
Chris@0 850 $date = new DateTimePlus('now', 'Australia/Sydney');
Chris@0 851
Chris@0 852 $date->setTimestamp(12345678);
Chris@0 853 $rendered = $date->render();
Chris@0 854 $this->assertEquals('1970-05-24 07:21:18 Australia/Sydney', $rendered);
Chris@0 855
Chris@0 856 $date->setTimestamp(23456789);
Chris@0 857 $rendered = $date->setTimezone(new \DateTimeZone('America/New_York'))->render();
Chris@0 858 $this->assertEquals('1970-09-29 07:46:29 America/New_York', $rendered);
Chris@0 859
Chris@0 860 $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-05-24 07:21:18', new \DateTimeZone('Australia/Sydney'))
Chris@0 861 ->setTimezone(new \DateTimeZone('America/New_York'));
Chris@0 862 $rendered = $date->render();
Chris@0 863 $this->assertInstanceOf(DateTimePlus::class, $date);
Chris@0 864 $this->assertEquals(12345678, $date->getTimestamp());
Chris@0 865 $this->assertEquals('1970-05-23 17:21:18 America/New_York', $rendered);
Chris@0 866 }
Chris@0 867
Chris@0 868 /**
Chris@0 869 * Tests that non-chainable methods work.
Chris@0 870 *
Chris@0 871 * @covers ::__call
Chris@0 872 */
Chris@0 873 public function testChainableNonChainable() {
Chris@0 874 $datetime1 = new DateTimePlus('2009-10-11 12:00:00');
Chris@0 875 $datetime2 = new DateTimePlus('2009-10-13 12:00:00');
Chris@0 876 $interval = $datetime1->diff($datetime2);
Chris@0 877 $this->assertInstanceOf(\DateInterval::class, $interval);
Chris@0 878 $this->assertEquals('+2 days', $interval->format('%R%a days'));
Chris@0 879 }
Chris@0 880
Chris@0 881 /**
Chris@0 882 * Tests that chained calls to non-existent functions throw an exception.
Chris@0 883 *
Chris@0 884 * @covers ::__call
Chris@0 885 */
Chris@0 886 public function testChainableNonCallable() {
Chris@14 887 if (method_exists($this, 'expectException')) {
Chris@14 888 $this->expectException(\BadMethodCallException::class);
Chris@14 889 $this->expectExceptionMessage('Call to undefined method Drupal\Component\Datetime\DateTimePlus::nonexistent()');
Chris@14 890 }
Chris@14 891 else {
Chris@14 892 $this->setExpectedException(\BadMethodCallException::class, 'Call to undefined method Drupal\Component\Datetime\DateTimePlus::nonexistent()');
Chris@14 893 }
Chris@0 894 $date = new DateTimePlus('now', 'Australia/Sydney');
Chris@0 895 $date->setTimezone(new \DateTimeZone('America/New_York'))->nonexistent();
Chris@0 896 }
Chris@0 897
Chris@17 898 /**
Chris@17 899 * @covers ::getPhpDateTime
Chris@17 900 */
Chris@17 901 public function testGetPhpDateTime() {
Chris@17 902 $new_york = new \DateTimeZone('America/New_York');
Chris@17 903 $berlin = new \DateTimeZone('Europe/Berlin');
Chris@17 904
Chris@17 905 // Test retrieving a cloned copy of the wrapped \DateTime object, and that
Chris@17 906 // altering it does not change the DateTimePlus object.
Chris@17 907 $datetimeplus = DateTimePlus::createFromFormat('Y-m-d H:i:s', '2017-07-13 22:40:00', $new_york, ['langcode' => 'en']);
Chris@17 908 $this->assertEquals(1500000000, $datetimeplus->getTimestamp());
Chris@17 909 $this->assertEquals('America/New_York', $datetimeplus->getTimezone()->getName());
Chris@17 910
Chris@17 911 $datetime = $datetimeplus->getPhpDateTime();
Chris@17 912 $this->assertInstanceOf('DateTime', $datetime);
Chris@17 913 $this->assertEquals(1500000000, $datetime->getTimestamp());
Chris@17 914 $this->assertEquals('America/New_York', $datetime->getTimezone()->getName());
Chris@17 915
Chris@17 916 $datetime->setTimestamp(1400000000)->setTimezone($berlin);
Chris@17 917 $this->assertEquals(1400000000, $datetime->getTimestamp());
Chris@17 918 $this->assertEquals('Europe/Berlin', $datetime->getTimezone()->getName());
Chris@17 919 $this->assertEquals(1500000000, $datetimeplus->getTimestamp());
Chris@17 920 $this->assertEquals('America/New_York', $datetimeplus->getTimezone()->getName());
Chris@17 921 }
Chris@17 922
Chris@0 923 }