diff --git a/core/lib/Drupal/Core/Datetime/DateFormatter.php b/core/lib/Drupal/Core/Datetime/DateFormatter.php
index 0b4f817..ff62e7e 100644
--- a/core/lib/Drupal/Core/Datetime/DateFormatter.php
+++ b/core/lib/Drupal/Core/Datetime/DateFormatter.php
@@ -169,11 +169,17 @@ public function format($timestamp, $type = 'medium', $format = '', $timezone = N
    *   (optional) How many different units to display in the string (2 by
    *   default).
    * @param string $langcode
-   *   (optional) Language code to translate to a language other than what is
-   *   used to display the page. Defaults to NULL.
+   *   (optional) Language code, to translate to a language other than what is
+   *   used to display the page.
    *
    * @return string
-   *   A translated string representation of the interval.
+   *   A translated string representation of the interval. Note that for
+   *   intervals over 30 days, the output is approximate: a "month" is always
+   *   exactly 30 days, and a "year" is always 365 days. It is not possible to
+   *   make a more exact representation, given that there is only one input in
+   *   seconds.
+   *
+   * @see \Drupal\Core\Datetime\DateFormatter::formatDiff()
    */
   public function formatInterval($interval, $granularity = 2, $langcode = NULL) {
     $output = '';
@@ -193,6 +199,109 @@ public function formatInterval($interval, $granularity = 2, $langcode = NULL) {
   }
 
   /**
+   * Formats a time interval between two timestamps.
+   *
+   * @param int $timestamp1
+   *   A UNIX timestamp, defining the from date and time.
+   * @param int $timestamp2
+   *   (optional) A UNIX timestamp, defining the to date and time. If NULL is
+   *   passed, the current request time is assumed. Defaults to NULL.
+   * @param array $options
+   *   (optional) An associative array with additional options. The following
+   *   keys can be used:
+   *   - granularity: An integer value that signals how many different units to
+   *   display in the string. Defaults to 2.
+   *   - langcode: A string representing a language code. It is used to
+   *   translate to a language other than what is used to display the page.
+   *   Defaults to NULL, which results in the current user language being used.
+   *   - prefix: A string that is prepended to the output.
+   *   - suffix: A string that is appended to the output.
+   *
+   * @return string
+   *   A translated string representation of the interval.
+   *
+   * @see \Drupal\Core\Datetime\DateFormatter::formatInterval()
+   */
+  public function formatDiff($timestamp1, $timestamp2 = NULL, $options = array()) {
+
+    if(is_null($timestamp2)) {
+      $timestamp2 = $_SERVER['REQUEST_TIME'];
+    }
+
+    $options += array(
+      'granularity' => 2,
+      'langcode' => NULL,
+      'prefix' => '',
+      'suffix' => '',
+    );
+
+    $date_time1 = new \DateTime();
+    $date_time1->setTimestamp($timestamp1);
+
+    $date_time2 = new \DateTime();
+    $date_time2->setTimestamp($timestamp2);
+
+    $interval = $date_time2->diff($date_time1);
+
+    $granularity = $options['granularity'];
+    $output = '';
+    foreach (array('y', 'm', 'd', 'h', 'i', 's') as $value) {
+      if ($interval->$value > 0) {
+        // Switch over the keys to call formatPlural() explicitly with literal
+        // strings for all different possibilities.
+        switch ($value) {
+          case 'y':
+            $thisoutput = $this->formatPlural($interval->y, '1 year', '@count years', array(), array('langcode' => $options['langcode']));
+            break;
+
+          case 'm':
+            $thisoutput = $this->formatPlural($interval->m, '1 month', '@count months', array(), array('langcode' => $options['langcode']));
+            break;
+
+          case 'd':
+            $thisoutput = $this->formatPlural($interval->d, '1 day', '@count days', array(), array('langcode' => $options['langcode']));
+            break;
+
+          case 'h':
+            $thisoutput = $this->formatPlural($interval->h, '1 hour', '@count hours', array(), array('langcode' => $options['langcode']));
+            break;
+
+          case 'i':
+            $thisoutput = $this->formatPlural($interval->i, '1 minute', '@count minutes', array(), array('langcode' => $options['langcode']));
+            break;
+
+          case 's':
+            $thisoutput = $this->formatPlural($interval->s, '1 second', '@count seconds', array(), array('langcode' => $options['langcode']));
+            break;
+
+          default:
+            break;
+        }
+        $output .= ($output ? ' ' : '') . $thisoutput;
+        $granularity--;
+      }
+
+      if ($granularity == 0) {
+        break;
+      }
+    }
+
+    if(empty($output)) {
+      $output = $this->t('0 seconds');
+    }
+
+    if($options['prefix'] != '') {
+      $output = $options['prefix'] . ' ' . $output;
+    }
+
+    if($options['suffix'] != '') {
+      $output .= ' ' . $options['suffix'];
+    }
+
+    return $output;
+  }
+
+  /**
    * Loads the given format pattern for the given langcode.
    *
    * @param string $format
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php
index 04f96fa..b5f5600 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php
@@ -87,7 +87,7 @@ public function viewElements(FieldItemListInterface $items) {
 
     foreach ($items as $delta => $item) {
       if ($item->value) {
-        $updated = $this->t('@time ago', array('@time' => $this->dateFormatter->formatInterval(REQUEST_TIME - $item->value)));
+        $updated = $this->dateFormatter->formatDiff($_SERVER['REQUEST_TIME'] - $item->value, $_SERVER['REQUEST_TIME'], array('suffix' => 'ago'));
       }
       else {
         $updated = $this->t('never');
diff --git a/core/modules/aggregator/src/Controller/AggregatorController.php b/core/modules/aggregator/src/Controller/AggregatorController.php
index 303b43c..e849218 100644
--- a/core/modules/aggregator/src/Controller/AggregatorController.php
+++ b/core/modules/aggregator/src/Controller/AggregatorController.php
@@ -127,8 +127,8 @@ public function adminOverview() {
       $row[] = $this->formatPlural($entity_manager->getStorage('aggregator_item')->getItemCount($feed), '1 item', '@count items');
       $last_checked = $feed->getLastCheckedTime();
       $refresh_rate = $feed->getRefreshRate();
-      $row[] = ($last_checked ? $this->t('@time ago', array('@time' => $this->dateFormatter->formatInterval(REQUEST_TIME - $last_checked))) : $this->t('never'));
-      $row[] = ($last_checked && $refresh_rate ? $this->t('%time left', array('%time' => $this->dateFormatter->formatInterval($last_checked + $refresh_rate - REQUEST_TIME))) : $this->t('never'));
+      $row[] = ($last_checked ? $this->dateFormatter->formatDiff($_SERVER['REQUEST_TIME'] - $last_checked, $_SERVER['REQUEST_TIME'], array('suffix' => 'ago')) : $this->t('never'));
+      $row[] = ($last_checked && $refresh_rate ? $this->dateFormatter->formatDiff($last_checked + $refresh_rate - $_SERVER['REQUEST_TIME'], $_SERVER['REQUEST_TIME'], array('suffix' => 'left')) : $this->t('never'));
       $links['edit'] = [
         'title' => $this->t('Edit'),
         'url' => Url::fromRoute('entity.aggregator_feed.edit_form', ['aggregator_feed' => $feed->id()]),
diff --git a/core/modules/contact/src/MessageForm.php b/core/modules/contact/src/MessageForm.php
index 78de6dd..cad1942 100644
--- a/core/modules/contact/src/MessageForm.php
+++ b/core/modules/contact/src/MessageForm.php
@@ -200,7 +200,7 @@ public function validate(array $form, FormStateInterface $form_state) {
       if (!$this->flood->isAllowed('contact', $limit, $interval)) {
         $form_state->setErrorByName('', $this->t('You cannot send more than %limit messages in @interval. Try again later.', array(
           '%limit' => $limit,
-          '@interval' => $this->dateFormatter->formatInterval($interval),
+          '@interval' => $this->dateFormatter->formatDiff($_SERVER['REQUEST_TIME'] + $interval),
         )));
       }
     }
diff --git a/core/modules/contact/src/Tests/ContactSitewideTest.php b/core/modules/contact/src/Tests/ContactSitewideTest.php
index df2816e..058ee62 100644
--- a/core/modules/contact/src/Tests/ContactSitewideTest.php
+++ b/core/modules/contact/src/Tests/ContactSitewideTest.php
@@ -231,7 +231,7 @@ function testSiteWideContact() {
     }
     // Submit contact form one over limit.
     $this->submitContact($this->randomMachineName(16), $recipients[0], $this->randomMachineName(16), $id, $this->randomMachineName(64));
-    $this->assertRaw(t('You cannot send more than %number messages in @interval. Try again later.', array('%number' => $this->config('contact.settings')->get('flood.limit'), '@interval' => \Drupal::service('date.formatter')->formatInterval(600))));
+    $this->assertRaw(t('You cannot send more than %number messages in @interval. Try again later.', array('%number' => $this->config('contact.settings')->get('flood.limit'), '@interval' => \Drupal::service('date.formatter')->formatDiff($_SERVER['REQUEST_TIME'] - 600))));
 
     // Test listing controller.
     $this->drupalLogin($admin_user);
diff --git a/core/modules/system/src/Form/CronForm.php b/core/modules/system/src/Form/CronForm.php
index f906c70..5591618 100644
--- a/core/modules/system/src/Form/CronForm.php
+++ b/core/modules/system/src/Form/CronForm.php
@@ -101,8 +101,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#value' => t('Run cron'),
       '#submit' => array('::submitCron'),
     );
-
-    $status = '<p>' . t('Last run: %cron-last ago.', array('%cron-last' => $this->dateFormatter->formatInterval(REQUEST_TIME - $this->state->get('system.cron_last')))) . '</p>';
+    $status = '<p>' . $this->dateFormatter->formatDiff($this->state->get('system.cron_last'), $_SERVER['REQUEST_TIME'], array('prefix' => 'Last run:', 'suffix' => 'ago')) . '</p>';
     $form['status'] = array(
       '#markup' => $status,
     );
diff --git a/core/modules/user/src/UserListBuilder.php b/core/modules/user/src/UserListBuilder.php
index e6ae82d..d36d5fb 100644
--- a/core/modules/user/src/UserListBuilder.php
+++ b/core/modules/user/src/UserListBuilder.php
@@ -151,10 +151,8 @@ public function buildRow(EntityInterface $entity) {
       '#theme' => 'item_list',
       '#items' => $users_roles,
     );
-    $row['member_for'] = $this->dateFormatter->formatInterval(REQUEST_TIME - $entity->getCreatedTime());
-    $row['access'] = $entity->access ? $this->t('@time ago', array(
-      '@time' => $this->dateFormatter->formatInterval(REQUEST_TIME - $entity->getLastAccessedTime()),
-    )) : t('never');
+    $row['member_for'] = $this->dateFormatter->formatDiff($entity->getCreatedTime());
+    $row['access'] = $entity->access ? $this->dateFormatter->formatDiff($entity->getLastAccessedTime(), REQUEST_TIME, array('suffix' => 'ago')) : t('never');
     return $row + parent::buildRow($entity);
   }
 
diff --git a/core/modules/views/src/Plugin/views/cache/Time.php b/core/modules/views/src/Plugin/views/cache/Time.php
index cd576c1..62f40a3 100644
--- a/core/modules/views/src/Plugin/views/cache/Time.php
+++ b/core/modules/views/src/Plugin/views/cache/Time.php
@@ -141,7 +141,7 @@ public function validateOptionsForm(&$form, FormStateInterface $form_state) {
   public function summaryTitle() {
     $results_lifespan = $this->getLifespan('results');
     $output_lifespan = $this->getLifespan('output');
-    return $this->dateFormatter->formatInterval($results_lifespan, 1) . '/' . $this->dateFormatter->formatInterval($output_lifespan, 1);
+    return $this->dateFormatter->formatDiff($results_lifespan, 1) . '/' . $this->dateFormatter->formatDiff($output_lifespan, 1);
   }
 
   protected function getLifespan($type) {
diff --git a/core/modules/views/src/Plugin/views/field/Date.php b/core/modules/views/src/Plugin/views/field/Date.php
index 8857c1a..6cce138 100644
--- a/core/modules/views/src/Plugin/views/field/Date.php
+++ b/core/modules/views/src/Plugin/views/field/Date.php
@@ -148,27 +148,48 @@ public function render(ResultRow $values) {
 
     if ($value) {
       $timezone = !empty($this->options['timezone']) ? $this->options['timezone'] : NULL;
-      $time_diff = REQUEST_TIME - $value; // will be positive for a datetime in the past (ago), and negative for a datetime in the future (hence)
+      $time_diff = $_SERVER['REQUEST_TIME'] - $value; // will be positive for a datetime in the past (ago), and negative for a datetime in the future (hence)
       switch ($format) {
         case 'raw time ago':
-          return $this->dateFormatter->formatInterval($time_diff, is_numeric($custom_format) ? $custom_format : 2);
+          return $this->dateFormatter->formatDiff($value, $_SERVER['REQUEST_TIME'], array('granularity' => is_numeric($custom_format) ? $custom_format : 2));
+
         case 'time ago':
-          return $this->t('%time ago', array('%time' => $this->dateFormatter->formatInterval($time_diff, is_numeric($custom_format) ? $custom_format : 2)));
+          $options = array(
+            'granularity' => isset($this->options['granularity']) ? $this->options['granularity'] : 2
+          );
+          return $this->t('%time ago', array('%time' => $this->dateFormatter->formatDiff($value, $_SERVER['REQUEST_TIME'], $options)));
+
         case 'raw time hence':
-          return $this->dateFormatter->formatInterval(-$time_diff, is_numeric($custom_format) ? $custom_format : 2);
+          return $this->dateFormatter->formatDiff($value, $_SERVER['REQUEST_TIME'], array('granularity' => is_numeric($custom_format) ? $custom_format : 2));
+
         case 'time hence':
-          return $this->t('%time hence', array('%time' => $this->dateFormatter->formatInterval(-$time_diff, is_numeric($custom_format) ? $custom_format : 2)));
+          return $this->t('%time hence', array('%time' => $this->dateFormatter->formatDiff($value, $_SERVER['REQUEST_TIME'], array('granularity' => is_numeric($custom_format) ? $custom_format : 2))));
+
         case 'raw time span':
-          return ($time_diff < 0 ? '-' : '') . $this->dateFormatter->formatInterval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2);
+          $options = array(
+            'granularity' => is_numeric($custom_format) ? $custom_format : 2,
+            'prefix' => ($time_diff < 0 ? '-' : ''),
+          );
+          return $this->dateFormatter->formatDiff(abs($time_diff), $_SERVER['REQUEST_TIME'], $options);
         case 'inverse time span':
-          return ($time_diff > 0 ? '-' : '') . $this->dateFormatter->formatInterval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2);
+          $options = array(
+            'granularity' => is_numeric($custom_format) ? $custom_format : 2,
+            'prefix' => ($time_diff < 0 ? '-' : ''),
+          );
+          return $this->dateFormatter->formatDiff(abs($time_diff), $_SERVER['REQUEST_TIME'], $options);
         case 'time span':
-          return $this->t(($time_diff < 0 ? '%time hence' : '%time ago'), array('%time' => $this->dateFormatter->formatInterval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2)));
+          $options = array(
+            'granularity' => is_numeric($custom_format) ? $custom_format : 2,
+            'suffix' => ($time_diff < 0 ? 'hence' : 'ago'),
+          );
+          return $this->dateFormatter->formatDiff(abs($time_diff), $_SERVER['REQUEST_TIME'], $options);
+
         case 'custom':
           if ($custom_format == 'r') {
             return format_date($value, $format, $custom_format, $timezone, 'en');
           }
           return format_date($value, $format, $custom_format, $timezone);
+
         default:
           return format_date($value, $format, '', $timezone);
       }
diff --git a/core/modules/views/src/Plugin/views/field/TimeInterval.php b/core/modules/views/src/Plugin/views/field/TimeInterval.php
index faf5fd6..9a538fd 100644
--- a/core/modules/views/src/Plugin/views/field/TimeInterval.php
+++ b/core/modules/views/src/Plugin/views/field/TimeInterval.php
@@ -87,7 +87,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    */
   public function render(ResultRow $values) {
     $value = $values->{$this->field_alias};
-    return $this->dateFormatter->formatInterval($value, isset($this->options['granularity']) ? $this->options['granularity'] : 2);
+    return $this->dateFormatter->formatDiff($value, $_SERVER['REQUEST_TIME'], array('granularity' => isset($this->options['granularity']) ? $this->options['granularity'] : 2));
   }
 
 }
diff --git a/core/modules/views_ui/src/ViewEditForm.php b/core/modules/views_ui/src/ViewEditForm.php
index 2fc7fc4..382e14b 100644
--- a/core/modules/views_ui/src/ViewEditForm.php
+++ b/core/modules/views_ui/src/ViewEditForm.php
@@ -147,7 +147,7 @@ public function form(array $form, FormStateInterface $form_state) {
       );
       $lock_message_substitutions = array(
         '!user' => drupal_render($username),
-        '!age' => $this->dateFormatter->formatInterval(REQUEST_TIME - $view->lock->updated),
+        '!age' => $this->dateFormatter->formatDiff($view->lock->updated),
         '@url' => $view->url('break-lock-form'),
       );
       $form['locked'] = array(
diff --git a/core/tests/Drupal/Tests/Core/Datetime/DateTest.php b/core/tests/Drupal/Tests/Core/Datetime/DateTest.php
index da9ebaa..a247248 100644
--- a/core/tests/Drupal/Tests/Core/Datetime/DateTest.php
+++ b/core/tests/Drupal/Tests/Core/Datetime/DateTest.php
@@ -128,4 +128,155 @@ public function testFormatIntervalZeroSecond() {
     $this->assertEquals('0 sec', $result);
   }
 
+  /**
+   * Tests the formatDiff method.
+   *
+   * @dataProvider providerTestFormatDiff
+   *
+   * @covers ::formatDiff
+   */
+  public function testformatDiff($expected, $timestamp1, $timestamp2 = NULL, $options = array()) {
+
+    // A formatted diff of two timestamps is dependent on the start and end
+    // date, so we set the request time fixed to 2013-12-11 10-09-08.
+    if ($timestamp2 == $_SERVER['REQUEST_TIME'] || is_null($timestamp2)) {
+      $timestamp2 = $this->createTimestamp('2013-12-11 10:09:08');
+    }
+
+    // Mocks a simple formatPlural implementation.
+    $this->stringTranslation->expects($this->any())
+      ->method('formatPlural')
+      ->with($this->anything(), $this->anything(), $this->anything(), array(), array('langcode' => isset($options['langcode']) ? $options['langcode'] : NULL))
+      ->will($this->returnCallback(function($count, $one, $multiple) {
+        return $count == 1 ? $one : str_replace('@count', $count, $multiple);
+      }));
+
+    // Mocks a simple translate implementation.
+    $this->stringTranslation->expects($this->any())
+      ->method('translate')
+      ->with($this->anything())
+      ->will($this->returnCallback(function($string, $args, $options) {
+        return $string;
+      }));
+
+    $this->assertEquals($expected, $this->dateFormatter->formatDiff($timestamp1, $timestamp2, $options), 'The method returned in the expected formatted diff string.');
+  }
+
+  /**
+   * Data provider for testformatDiff().
+   */
+  public function providerTestFormatDiff() {
+    // This is the fixed request time in the test.
+    $request_time = $this->createTimestamp('2013-12-11 10:09:08');
+
+    $granularity_3 = array('granularity' => 3);
+    $granularity_4 = array('granularity' => 4);
+
+    $langcode_en = array('langcode' => 'en');
+    $langcode_lolspeak = array('langcode' => 'xxx-lolspeak');
+
+    $prefix_abc = array('prefix' => 'abc');
+    $suffix_xyz = array('suffix' => 'xyz');
+
+    $data = array(
+      // Checks for equal timestamps.
+      array('0 seconds', $request_time, $request_time),
+
+      // Checks for seconds only.
+      array('1 second', $this->createTimestamp('2013-12-11 10:09:07')),
+      array('1 second', $this->createTimestamp('2013-12-11 10:09:07'), $request_time),
+      array('abc 1 second', $this->createTimestamp('2013-12-11 10:09:07'), $request_time, $prefix_abc),
+      array('1 second xyz', $this->createTimestamp('2013-12-11 10:09:07'), $request_time, $suffix_xyz),
+      array('1 second', $this->createTimestamp('2013-12-11 10:09:07'), $request_time, $granularity_3 + $langcode_en),
+      array('1 second', $this->createTimestamp('2013-12-11 10:09:07'), $request_time, $granularity_4 + $langcode_lolspeak),
+      array('2 seconds', $this->createTimestamp('2013-12-11 10:09:06'), $request_time),
+      array('59 seconds', $this->createTimestamp('2013-12-11 10:08:09')),
+      array('59 seconds', $this->createTimestamp('2013-12-11 10:08:09'), $request_time),
+
+      // Checks for minutes and possibly seconds.
+      array('1 minute', $this->createTimestamp('2013-12-11 10:08:08')),
+      array('1 minute', $this->createTimestamp('2013-12-11 10:08:08'), $request_time),
+      array('1 minute 1 second', $this->createTimestamp('2013-12-11 10:08:07'), $request_time),
+      array('1 minute 59 seconds', $this->createTimestamp('2013-12-11 10:07:09'), $request_time),
+      array('2 minutes', $this->createTimestamp('2013-12-11 10:07:08'), $request_time),
+      array('2 minutes 1 second', $this->createTimestamp('2013-12-11 10:07:07'), $request_time),
+      array('2 minutes 2 seconds', $this->createTimestamp('2013-12-11 10:07:06'), $request_time),
+      array('2 minutes 2 seconds', $this->createTimestamp('2013-12-11 10:07:06'), $request_time, $granularity_3),
+      array('2 minutes 2 seconds', $this->createTimestamp('2013-12-11 10:07:06'), $request_time, $granularity_4),
+      array('30 minutes', $this->createTimestamp('2013-12-11 09:39:08'), $request_time),
+      array('59 minutes 59 seconds', $this->createTimestamp('2013-12-11 09:09:09'), $request_time),
+      array('59 minutes 59 seconds', $this->createTimestamp('2013-12-11 09:09:09')),
+
+      // Checks for hours and possibly minutes or seconds.
+      array('1 hour', $this->createTimestamp('2013-12-11 09:09:08')),
+      array('1 hour', $this->createTimestamp('2013-12-11 09:09:08'), $request_time),
+      array('1 hour 1 second', $this->createTimestamp('2013-12-11 09:09:07')),
+      array('1 hour 2 seconds', $this->createTimestamp('2013-12-11 09:09:06')),
+      array('1 hour 1 minute', $this->createTimestamp('2013-12-11 09:08:08')),
+      array('1 hour 1 minute 1 second', $this->createTimestamp('2013-12-11 09:08:07'), $request_time, $granularity_3),
+      array('1 hour 1 minute 2 seconds', $this->createTimestamp('2013-12-11 09:08:06'), $request_time, $granularity_4),
+      array('1 hour 30 minutes', $this->createTimestamp('2013-12-11 08:39:08')),
+      array('2 hours', $this->createTimestamp('2013-12-11 08:09:08')),
+      array('23 hours 59 minutes', $this->createTimestamp('2013-12-10 10:10:08')),
+
+      // Checks for days and possibly hours, minutes or seconds.
+      array('1 day', $this->createTimestamp('2013-12-10 10:09:08')),
+      array('1 day 1 second', $this->createTimestamp('2013-12-10 10:09:07')),
+      array('1 day 1 hour', $this->createTimestamp('2013-12-10 09:09:08')),
+      array('1 day 1 hour 1 minute', $this->createTimestamp('2013-12-10 09:08:07'), $request_time, $granularity_3 + $langcode_en),
+      array('1 day 1 hour 1 minute 1 second', $this->createTimestamp('2013-12-10 09:08:07'), $request_time, $granularity_4 + $langcode_lolspeak),
+      array('1 day 2 hours 2 minutes 2 seconds', $this->createTimestamp('2013-12-10 08:07:06'), $request_time, $granularity_4),
+      array('2 days', $this->createTimestamp('2013-12-09 10:09:08')),
+      array('2 days 2 minutes', $this->createTimestamp('2013-12-09 10:07:08')),
+      array('2 days 2 hours', $this->createTimestamp('2013-12-09 08:09:08')),
+      array('2 days 2 hours 2 minutes', $this->createTimestamp('2013-12-09 08:07:06'), $request_time, $granularity_3 + $langcode_en),
+      array('2 days 2 hours 2 minutes 2 seconds', $this->createTimestamp('2013-12-09 08:07:06'), $request_time, $granularity_4 + $langcode_lolspeak),
+      array('29 days', $this->createTimestamp('2013-11-12 10:09:08')),
+
+      // Checks for months and possibly days, hours, minutes or seconds.
+      array('1 month', $this->createTimestamp('2013-11-11 10:09:08')),
+      array('1 month 1 second', $this->createTimestamp('2013-11-11 10:09:07')),
+      array('1 month 1 hour', $this->createTimestamp('2013-11-11 09:09:08')),
+      array('1 month 1 hour 1 minute', $this->createTimestamp('2013-11-11 09:08:07'), $request_time, $granularity_3),
+      array('1 month 1 hour 1 minute 1 second', $this->createTimestamp('2013-11-11 09:08:07'), $request_time, $granularity_4),
+      array('1 month 29 days', $this->createTimestamp('2013-10-13 10:09:08')),
+      array('1 month 30 days', $this->createTimestamp('2013-10-12 10:09:08')),
+      array('2 months', $this->createTimestamp('2013-10-11 10:09:08')),
+      array('abc 2 months 1 day xyz', $this->createTimestamp('2013-10-10 10:09:08'), $request_time, $prefix_abc + $suffix_xyz),
+      array('2 months 2 days', $this->createTimestamp('2013-10-09 08:07:06')),
+      array('2 months 2 days 2 hours', $this->createTimestamp('2013-10-09 08:07:06'), $request_time, $granularity_3),
+      array('2 months 2 days 2 hours 2 minutes', $this->createTimestamp('2013-10-09 08:07:06'), $request_time, $granularity_4),
+      array('6 months 2 days', $this->createTimestamp('2013-06-09 10:09:08')),
+      array('11 months 3 hours', $this->createTimestamp('2013-01-11 07:09:08')),
+      array('11 months 30 days', $this->createTimestamp('2012-12-12 10:09:08')),
+
+      // Checks for years and possibly months, days, hours, minutes or seconds.
+      array('1 year', $this->createTimestamp('2012-12-11 10:09:08')),
+      array('abc 1 year 1 minute', $this->createTimestamp('2012-12-11 10:08:08'), $request_time, $prefix_abc),
+      array('1 year 1 day xyz', $this->createTimestamp('2012-12-10 10:09:08'), $request_time, $suffix_xyz),
+      array('2 years', $this->createTimestamp('2011-12-11 10:09:08')),
+      array('2 years 2 minutes', $this->createTimestamp('2011-12-11 10:07:08')),
+      array('2 years 2 days', $this->createTimestamp('2011-12-09 10:09:08')),
+      array('2 years 2 months 2 days', $this->createTimestamp('2011-10-09 08:07:06'), $request_time, $granularity_3),
+      array('2 years 2 months 2 days 2 hours', $this->createTimestamp('2011-10-09 08:07:06'), $request_time, $granularity_4),
+      array('10 years', $this->createTimestamp('2003-12-11 10:09:08')),
+      array('100 years', $this->createTimestamp('1913-12-11 10:09:08')),
+    );
+
+    return $data;
+  }
+
+  /**
+   * Creates a UNIX timestamp given a date and time string in the format
+   * year-month-day hour:minute:seconds (e.g. 2013-12-11 10:09:08).
+   *
+   * @param string $dateTimeString
+   *   The formatted date and time string.
+   *
+   * @return int
+   *   The UNIX timestamp.
+   */
+  private function createTimestamp($dateTimeString) {
+    return \DateTime::createFromFormat('Y-m-d G:i:s', $dateTimeString)->getTimestamp();
+  }
 }
