3 namespace Drupal\Tests\Core\Datetime;
5 use Drupal\Core\Cache\CacheableMetadata;
6 use Drupal\Core\Datetime\DateFormatter;
7 use Drupal\Core\Datetime\FormattedDateDiff;
8 use Drupal\Core\DependencyInjection\ContainerBuilder;
9 use Drupal\Tests\UnitTestCase;
10 use Symfony\Component\HttpFoundation\Request;
11 use Drupal\Core\StringTranslation\TranslatableMarkup;
14 * @coversDefaultClass \Drupal\Core\Datetime\DateFormatter
17 class DateTest extends UnitTestCase {
20 * The mocked entity manager.
22 * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
24 protected $entityManager;
27 * The mocked language manager.
29 * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
31 protected $languageManager;
34 * The mocked string translation.
36 * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
38 protected $stringTranslation;
41 * The mocked string translation.
43 * @var \Symfony\Component\HttpFoundation\RequestStack|\PHPUnit_Framework_MockObject_MockObject
45 protected $requestStack;
48 * The mocked date formatter class.
50 * @var \Drupal\Core\Datetime\DateFormatter
52 protected $dateFormatter;
55 * The date formatter class where methods can be stubbed.
57 * @var \Drupal\Core\Datetime\DateFormatter|\PHPUnit_Framework_MockObject_MockObject
59 protected $dateFormatterStub;
61 protected function setUp() {
64 $entity_storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
66 $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
67 $this->entityManager->expects($this->any())->method('getStorage')->with('date_format')->willReturn($entity_storage);
69 $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
70 $this->stringTranslation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface');
71 $this->requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack');
73 $config_factory = $this->getConfigFactoryStub(['system.date' => ['country' => ['default' => 'GB']]]);
74 $container = new ContainerBuilder();
75 $container->set('config.factory', $config_factory);
76 $container->set('string_translation', $this->getStringTranslationStub());
77 \Drupal::setContainer($container);
79 $this->dateFormatter = new DateFormatter($this->entityManager, $this->languageManager, $this->stringTranslation, $this->getConfigFactoryStub(), $this->requestStack);
81 $this->dateFormatterStub = $this->getMockBuilder('\Drupal\Core\Datetime\DateFormatter')
82 ->setConstructorArgs([$this->entityManager, $this->languageManager, $this->stringTranslation, $this->getConfigFactoryStub(), $this->requestStack])
83 ->setMethods(['formatDiff'])
88 * Tests the formatInterval method.
90 * @dataProvider providerTestFormatInterval
92 * @covers ::formatInterval
94 public function testFormatInterval($interval, $granularity, $expected, $langcode = NULL) {
95 // Mocks a simple formatPlural implementation.
96 $this->stringTranslation->expects($this->any())
97 ->method('translateString')
98 ->willReturnCallback(function (TranslatableMarkup $arg) {
99 return $arg->getUntranslatedString();
102 // Check if the granularity is specified.
104 $result = $this->dateFormatter->formatInterval($interval, $granularity, $langcode);
107 $result = $this->dateFormatter->formatInterval($interval);
110 $this->assertEquals(new TranslatableMarkup($expected, [], ['langcode' => $langcode], $this->stringTranslation), $result);
114 * Provides some test data for the format interval test.
116 public function providerTestFormatInterval() {
118 // Checks for basic seconds.
123 // Checks for minutes with seconds.
125 [61, 2, '1 min 1 sec'],
126 [62, 2, '1 min 2 sec'],
128 [121, 2, '2 min 1 sec'],
129 // Check for hours with minutes and seconds.
132 // Check for higher units.
134 [604800, 1, '1 week'],
135 [2592000 * 2, 1, '2 months'],
136 [31536000 * 2, 1, '2 years'],
137 // Check for a complicated one with months weeks and days.
138 [2592000 * 2 + 604800 * 3 + 86400 * 4, 3, '2 months 3 weeks 4 days'],
139 // Check for the langcode.
140 [61, 1, '1 min', 'xxx-lolspeak'],
141 // Check with an unspecified granularity.
142 [61, NULL, '1 min 1 sec'],
149 * Tests the formatInterval method for 0 second.
151 public function testFormatIntervalZeroSecond() {
152 $result = $this->dateFormatter->formatInterval(0, 1, 'xxx-lolspeak');
153 $this->assertEquals(new TranslatableMarkup('0 sec', [], ['langcode' => 'xxx-lolspeak'], $this->stringTranslation), $result);
157 * Tests the getSampleDateFormats method.
159 * @covers \Drupal\Core\Datetime\DateFormatter::getSampleDateFormats
161 public function testGetSampleDateFormats() {
162 $timestamp = strtotime('2015-03-22 14:23:00');
163 $expected = $this->dateFormatter->getSampleDateFormats('en', $timestamp, 'Australia/Sydney');
165 // Removed characters related to timezone 'e' and 'T', as test does not have
167 $date_characters = 'dDjlNSwzWFmMntLoYyaABgGhHisuIOPZcrU';
168 $date_chars = str_split($date_characters);
170 foreach ($date_chars as $val) {
171 $this->assertEquals($expected[$val], date($val, $timestamp));
176 * Tests the formatTimeDiffUntil method.
178 * @covers ::formatTimeDiffUntil
180 public function testFormatTimeDiffUntil() {
181 $expected = '1 second';
182 $request_time = $this->createTimestamp('2013-12-11 10:09:08');
183 $timestamp = $this->createTimestamp('2013-12-11 10:09:09');
186 // Mocks the formatDiff function of the dateformatter object.
187 $this->dateFormatterStub
188 ->expects($this->at(0))
189 ->method('formatDiff')
190 ->with($timestamp, $request_time, $options)
191 ->will($this->returnValue($expected));
193 $this->dateFormatterStub
194 ->expects($this->at(1))
195 ->method('formatDiff')
196 ->with($timestamp, $request_time, $options + ['return_as_object' => TRUE])
197 ->will($this->returnValue(new FormattedDateDiff('1 second', 1)));
199 $request = Request::createFromGlobals();
200 $request->server->set('REQUEST_TIME', $request_time);
201 // Mocks a the request stack getting the current request.
202 $this->requestStack->expects($this->any())
203 ->method('getCurrentRequest')
204 ->willReturn($request);
206 $this->assertEquals($expected, $this->dateFormatterStub->formatTimeDiffSince($timestamp, $options));
207 $options['return_as_object'] = TRUE;
208 $expected_object = new FormattedDateDiff('1 second', 1);
209 $this->assertEquals($expected_object, $this->dateFormatterStub->formatTimeDiffSince($timestamp, $options));
213 * Tests the formatTimeDiffSince method.
215 * @covers ::formatTimeDiffSince
217 public function testFormatTimeDiffSince() {
218 $expected = '1 second';
219 $timestamp = $this->createTimestamp('2013-12-11 10:09:07');
220 $request_time = $this->createTimestamp('2013-12-11 10:09:08');
223 // Mocks the formatDiff function of the dateformatter object.
224 $this->dateFormatterStub
225 ->expects($this->at(0))
226 ->method('formatDiff')
227 ->with($request_time, $timestamp, $options)
228 ->will($this->returnValue($expected));
230 $this->dateFormatterStub
231 ->expects($this->at(1))
232 ->method('formatDiff')
233 ->with($request_time, $timestamp, $options + ['return_as_object' => TRUE])
234 ->will($this->returnValue(new FormattedDateDiff('1 second', 1)));
236 $request = Request::createFromGlobals();
237 $request->server->set('REQUEST_TIME', $request_time);
238 // Mocks a the request stack getting the current request.
239 $this->requestStack->expects($this->any())
240 ->method('getCurrentRequest')
241 ->willReturn($request);
243 $this->assertEquals($expected, $this->dateFormatterStub->formatTimeDiffUntil($timestamp, $options));
244 $options['return_as_object'] = TRUE;
245 $expected_object = new FormattedDateDiff('1 second', 1);
246 $this->assertEquals($expected_object, $this->dateFormatterStub->formatTimeDiffUntil($timestamp, $options));
250 * Tests the formatDiff method.
252 * @dataProvider providerTestFormatDiff
254 * @covers ::formatDiff
256 public function testformatDiff($expected, $max_age, $timestamp1, $timestamp2, $options = []) {
257 // Mocks a simple translateString implementation.
258 $this->stringTranslation->expects($this->any())
259 ->method('translateString')
260 ->willReturnCallback(function (TranslatableMarkup $arg) {
261 return $arg->getUntranslatedString();
264 if (isset($options['langcode'])) {
265 $expected_markup = new TranslatableMarkup($expected, [], ['langcode' => $options['langcode']], $this->stringTranslation);
268 $expected_markup = new TranslatableMarkup($expected, [], [], $this->stringTranslation);
270 $this->assertEquals($expected_markup, $this->dateFormatter->formatDiff($timestamp1, $timestamp2, $options));
272 $options['return_as_object'] = TRUE;
273 $expected_object = new FormattedDateDiff($expected, $max_age);
274 $this->assertEquals($expected_object, $this->dateFormatter->formatDiff($timestamp1, $timestamp2, $options));
278 * Data provider for testformatDiff().
280 public function providerTestFormatDiff() {
281 // This is the fixed request time in the test.
282 $request_time = $this->createTimestamp('2013-12-11 10:09:08');
284 $granularity_3 = ['granularity' => 3];
285 $granularity_4 = ['granularity' => 4];
287 $langcode_en = ['langcode' => 'en'];
288 $langcode_lolspeak = ['langcode' => 'xxx-lolspeak'];
290 $non_strict = ['strict' => FALSE];
293 // Checks for equal timestamps.
294 ['0 seconds', 0, $request_time, $request_time],
296 // Checks for seconds only.
297 ['1 second', 1, $this->createTimestamp('2013-12-11 10:09:07'), $request_time],
298 ['1 second', 1, $this->createTimestamp('2013-12-11 10:09:07'), $request_time],
299 ['1 second', 1, $this->createTimestamp('2013-12-11 10:09:07'), $request_time, $granularity_3 + $langcode_en],
300 ['1 second', 1, $this->createTimestamp('2013-12-11 10:09:07'), $request_time, $granularity_4 + $langcode_lolspeak],
301 ['2 seconds', 1, $this->createTimestamp('2013-12-11 10:09:06'), $request_time],
302 ['59 seconds', 1, $this->createTimestamp('2013-12-11 10:08:09'), $request_time],
303 ['59 seconds', 1, $this->createTimestamp('2013-12-11 10:08:09'), $request_time],
305 // Checks for minutes and possibly seconds.
306 ['1 minute', 60, $this->createTimestamp('2013-12-11 10:08:08'), $request_time],
307 ['1 minute', 60, $this->createTimestamp('2013-12-11 10:08:08'), $request_time],
308 ['1 minute 1 second', 1, $this->createTimestamp('2013-12-11 10:08:07'), $request_time],
309 ['1 minute 59 seconds', 1, $this->createTimestamp('2013-12-11 10:07:09'), $request_time],
310 ['2 minutes', 60, $this->createTimestamp('2013-12-11 10:07:08'), $request_time],
311 ['2 minutes 1 second', 1, $this->createTimestamp('2013-12-11 10:07:07'), $request_time],
312 ['2 minutes 2 seconds', 1, $this->createTimestamp('2013-12-11 10:07:06'), $request_time],
313 ['2 minutes 2 seconds', 1, $this->createTimestamp('2013-12-11 10:07:06'), $request_time, $granularity_3],
314 ['2 minutes 2 seconds', 1, $this->createTimestamp('2013-12-11 10:07:06'), $request_time, $granularity_4],
315 ['30 minutes', 60, $this->createTimestamp('2013-12-11 09:39:08'), $request_time],
316 ['59 minutes 59 seconds', 1, $this->createTimestamp('2013-12-11 09:09:09'), $request_time],
317 ['59 minutes 59 seconds', 1, $this->createTimestamp('2013-12-11 09:09:09'), $request_time],
319 // Checks for hours and possibly minutes or seconds.
320 ['1 hour', 3600, $this->createTimestamp('2013-12-11 09:09:08'), $request_time],
321 ['1 hour', 3600, $this->createTimestamp('2013-12-11 09:09:08'), $request_time],
322 ['1 hour', 3600, $this->createTimestamp('2013-12-11 09:09:07'), $request_time],
323 ['1 hour', 3600, $this->createTimestamp('2013-12-11 09:09:06'), $request_time],
324 ['1 hour 1 minute', 60, $this->createTimestamp('2013-12-11 09:08:08'), $request_time],
325 ['1 hour 1 minute 1 second', 1, $this->createTimestamp('2013-12-11 09:08:07'), $request_time, $granularity_3],
326 ['1 hour 1 minute 2 seconds', 1, $this->createTimestamp('2013-12-11 09:08:06'), $request_time, $granularity_4],
327 ['1 hour 30 minutes', 60, $this->createTimestamp('2013-12-11 08:39:08'), $request_time],
328 ['2 hours', 3600, $this->createTimestamp('2013-12-11 08:09:08'), $request_time],
329 ['23 hours 59 minutes', 60, $this->createTimestamp('2013-12-10 10:10:08'), $request_time],
331 // Checks for days and possibly hours, minutes or seconds.
332 ['1 day', 86400, $this->createTimestamp('2013-12-10 10:09:08'), $request_time],
333 ['1 day', 86400, $this->createTimestamp('2013-12-10 10:09:07'), $request_time],
334 ['1 day 1 hour', 3600, $this->createTimestamp('2013-12-10 09:09:08'), $request_time],
335 ['1 day 1 hour 1 minute', 60, $this->createTimestamp('2013-12-10 09:08:07'), $request_time, $granularity_3 + $langcode_en],
336 ['1 day 1 hour 1 minute 1 second', 1, $this->createTimestamp('2013-12-10 09:08:07'), $request_time, $granularity_4 + $langcode_lolspeak],
337 ['1 day 2 hours 2 minutes 2 seconds', 1, $this->createTimestamp('2013-12-10 08:07:06'), $request_time, $granularity_4],
338 ['2 days', 86400, $this->createTimestamp('2013-12-09 10:09:08'), $request_time],
339 ['2 days', 86400, $this->createTimestamp('2013-12-09 10:07:08'), $request_time],
340 ['2 days 2 hours', 3600, $this->createTimestamp('2013-12-09 08:09:08'), $request_time],
341 ['2 days 2 hours 2 minutes', 60, $this->createTimestamp('2013-12-09 08:07:06'), $request_time, $granularity_3 + $langcode_en],
342 ['2 days 2 hours 2 minutes 2 seconds', 1, $this->createTimestamp('2013-12-09 08:07:06'), $request_time, $granularity_4 + $langcode_lolspeak],
344 // Checks for weeks and possibly days, hours, minutes or seconds.
345 ['1 week', 7 * 86400, $this->createTimestamp('2013-12-04 10:09:08'), $request_time],
346 ['1 week 1 day', 86400, $this->createTimestamp('2013-12-03 10:09:08'), $request_time],
347 ['2 weeks', 7 * 86400, $this->createTimestamp('2013-11-27 10:09:08'), $request_time],
348 ['2 weeks 2 days', 86400, $this->createTimestamp('2013-11-25 08:07:08'), $request_time],
349 ['2 weeks 2 days 2 hours 2 minutes', 60, $this->createTimestamp('2013-11-25 08:07:08'), $request_time, $granularity_4],
350 ['4 weeks', 7 * 86400, $this->createTimestamp('2013-11-13 10:09:08'), $request_time],
351 ['4 weeks 1 day', 86400, $this->createTimestamp('2013-11-12 10:09:08'), $request_time],
353 // Checks for months and possibly days, hours, minutes or seconds.
354 ['1 month', 30 * 86400, $this->createTimestamp('2013-11-11 10:09:08'), $request_time],
355 ['1 month', 30 * 86400, $this->createTimestamp('2013-11-11 10:09:07'), $request_time],
356 ['1 month', 30 * 86400, $this->createTimestamp('2013-11-11 09:09:08'), $request_time],
357 ['1 month', 30 * 86400, $this->createTimestamp('2013-11-11 09:08:07'), $request_time, $granularity_3],
358 ['1 month', 30 * 86400, $this->createTimestamp('2013-11-11 09:08:07'), $request_time, $granularity_4],
359 ['1 month 4 weeks', 7 * 86400, $this->createTimestamp('2013-10-13 10:09:08'), $request_time],
360 ['1 month 4 weeks 1 day', 86400, $this->createTimestamp('2013-10-13 10:09:08'), $request_time, $granularity_3],
361 ['1 month 4 weeks', 7 * 86400, $this->createTimestamp('2013-10-12 10:09:08'), $request_time],
362 ['1 month 4 weeks 2 days', 86400, $this->createTimestamp('2013-10-12 10:09:08'), $request_time, $granularity_3],
363 ['2 months', 30 * 86400, $this->createTimestamp('2013-10-11 10:09:08'), $request_time],
364 ['2 months', 30 * 86400, $this->createTimestamp('2013-10-10 10:09:08'), $request_time],
365 ['2 months', 30 * 86400, $this->createTimestamp('2013-10-09 08:07:06'), $request_time],
366 ['2 months', 30 * 86400, $this->createTimestamp('2013-10-09 08:07:06'), $request_time, $granularity_3],
367 ['2 months', 30 * 86400, $this->createTimestamp('2013-10-09 08:07:06'), $request_time, $granularity_4],
368 ['6 months', 30 * 86400, $this->createTimestamp('2013-06-09 10:09:08'), $request_time],
369 ['11 months', 30 * 86400, $this->createTimestamp('2013-01-11 07:09:08'), $request_time],
370 ['11 months 4 weeks', 7 * 86400, $this->createTimestamp('2012-12-12 10:09:08'), $request_time],
371 ['11 months 4 weeks 2 days', 86400, $this->createTimestamp('2012-12-12 10:09:08'), $request_time, $granularity_3],
373 // Checks for years and possibly months, days, hours, minutes or seconds.
374 ['1 year', 365 * 86400, $this->createTimestamp('2012-12-11 10:09:08'), $request_time],
375 ['1 year', 365 * 86400, $this->createTimestamp('2012-12-11 10:08:08'), $request_time],
376 ['1 year', 365 * 86400, $this->createTimestamp('2012-12-10 10:09:08'), $request_time],
377 ['2 years', 365 * 86400, $this->createTimestamp('2011-12-11 10:09:08'), $request_time],
378 ['2 years', 365 * 86400, $this->createTimestamp('2011-12-11 10:07:08'), $request_time],
379 ['2 years', 365 * 86400, $this->createTimestamp('2011-12-09 10:09:08'), $request_time],
380 ['2 years 2 months', 30 * 86400, $this->createTimestamp('2011-10-09 08:07:06'), $request_time, $granularity_3],
381 ['2 years 2 months', 30 * 86400, $this->createTimestamp('2011-10-09 08:07:06'), $request_time, $granularity_4],
382 ['10 years', 365 * 86400, $this->createTimestamp('2003-12-11 10:09:08'), $request_time],
383 ['100 years', 365 * 86400, $this->createTimestamp('1913-12-11 10:09:08'), $request_time],
385 // Checks the non-strict option vs. strict (default).
386 ['1 second', 1, $this->createTimestamp('2013-12-11 10:09:08'), $this->createTimestamp('2013-12-11 10:09:07'), $non_strict],
387 ['0 seconds', 0, $this->createTimestamp('2013-12-11 10:09:08'), $this->createTimestamp('2013-12-11 10:09:07')],
389 // Checks granularity limit.
390 ['2 years 3 months 1 week', 7 * 86400, $this->createTimestamp('2011-08-30 11:15:57'), $request_time, $granularity_3],
397 * Tests FormattedDateDiff.
399 * @covers \Drupal\Core\Datetime\FormattedDateDiff::toRenderable
400 * @covers \Drupal\Core\Datetime\FormattedDateDiff::getString
401 * @covers \Drupal\Core\Datetime\FormattedDateDiff::getCacheMaxAge
403 public function testFormattedDateDiff() {
404 $string = '10 minutes';
406 $object = new FormattedDateDiff($string, $max_age);
408 // Test conversion to a render array.
410 '#markup' => $string,
412 'max-age' => $max_age,
415 $this->assertArrayEquals($expected, $object->toRenderable());
417 // Test retrieving the formatted time difference string.
418 $this->assertEquals($string, $object->getString());
420 // Test applying cacheability data to an existing build.
422 CacheableMetadata::createFromObject($object)->applyTo($build);
423 $this->assertEquals($max_age, $build['#cache']['max-age']);
424 // Test the BC layer.
425 $this->assertSame($object->getCacheMaxAge(), $object->getMaxAge());
429 * Creates a UNIX timestamp given a date and time string in the format
430 * year-month-day hour:minute:seconds (e.g. 2013-12-11 10:09:08).
432 * @param string $dateTimeString
433 * The formatted date and time string.
436 * The UNIX timestamp.
438 private function createTimestamp($dateTimeString) {
439 return \DateTime::createFromFormat('Y-m-d G:i:s', $dateTimeString)->getTimestamp();