diff --git a/core/includes/common.inc b/core/includes/common.inc
index 9601c76..af57937 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -1833,41 +1833,65 @@ function format_size($size, $langcode = NULL) {
 /**
  * Formats a time interval with the requested granularity.
  *
- * @param $interval
+ * @param number $interval
  *   The length of the interval in seconds.
- * @param $granularity
+ * @param number $granularity
  *   How many different units to display in the string.
- * @param $langcode
+ * @param text $langcode
  *   Optional language code to translate to a language other than
  *   what is used to display the page.
  *
- * @return
+ * @return string
  *   A translated string representation of the interval.
  */
 function format_interval($interval, $granularity = 2, $langcode = NULL) {
+  // Prepare variables.
+  $date_format = DateTime::ISO8601;
+  $now = time();
+  // Add 'ago' to past intervals.
+  $append = $interval < 0 ? ' ago' : '';
+  // Create a default response of 0 seconds.
+  $default = t("0 sec$append", array(), array('langcode' => $langcode));
+
+  // Create the current date's components and $interval's objects.
+  $dt_now = new DateTime(date($date_format, $now));
+  $dt_then = new DateTime(date($date_format, $now + abs((int) $interval)));
+  if ($dt_now === FALSE || $dt_then === FALSE) {
+    // If we can't parse the date then return the default.
+    return $default;
+  }
+
+  // What is the interval?
+  $dt_diff = $dt_now->diff($dt_then);
   $units = array(
-    '1 year|@count years' => 31536000,
-    '1 month|@count months' => 2592000,
-    '1 week|@count weeks' => 604800,
-    '1 day|@count days' => 86400,
-    '1 hour|@count hours' => 3600,
-    '1 min|@count min' => 60,
-    '1 sec|@count sec' => 1
+    '1 year|@count years' => $dt_diff->y,
+    '1 month|@count months' => $dt_diff->m,
+    '1 week|@count weeks' => 0,
+    '1 day|@count days' => $dt_diff->d,
+    '1 hour|@count hours' => $dt_diff->h,
+    '1 min|@count min' => $dt_diff->i,
+    '1 sec|@count sec' => $dt_diff->s,
   );
-  $output = '';
+  // Week is now a special case.
+  $units['1 week|@count weeks'] = floor($units['1 day|@count days'] / 7);
+  $units['1 day|@count days'] = $units['1 day|@count days'] % 7;
+
+  // Produce the output based on the granularity.
+  $output_set = array();
   foreach ($units as $key => $value) {
     $key = explode('|', $key);
-    if ($interval >= $value) {
-      $output .= ($output ? ' ' : '') . format_plural(floor($interval / $value), $key[0], $key[1], array(), array('langcode' => $langcode));
-      $interval %= $value;
+    if ($value > 0) {
+      $output_set[] = format_plural($value, $key[0], $key[1], array(), array('langcode' => $langcode));
       $granularity--;
-    }
-
-    if ($granularity == 0) {
-      break;
+      if ($granularity == 0) {
+        break;
+      }
     }
   }
-  return $output ? $output : t('0 sec', array(), array('langcode' => $langcode));
+  $output = empty($output_set) ? $default : implode(' ', $output_set) . $append;
+
+  // Return the pretty interval.
+  return $output;
 }
 
 /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/IntervalTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/IntervalTest.php
new file mode 100644
index 0000000..9eda73e
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Common/IntervalTest.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Common\IntervalTest.
+ */
+
+namespace Drupal\system\Tests\Common;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests format_interval().
+ */
+class IntervalTest extends WebTestBase {
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array();
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Interval functionality',
+      'description' => 'Tests the format_interval() functionality.',
+      'group' => 'Common',
+    );
+  }
+
+  /**
+   * Tests format_interval().
+   *
+   * These are trival tests based on static inputs.
+   */
+  function testFormatIntervalBasic() {
+    // A list of lengths of time in seconds which map to their words.
+    $intervals = array(
+      5 => '5 sec',
+      60 * 1 => '1 min',
+      60 * 10 => '10 min',
+      60 * 60 => '1 hour',
+      60 * 60 * 24 => '1 day',
+      60 * 60 * 24 * 7 => '1 week',
+    );
+    foreach ($intervals as $interval => $expected) {
+      $actual = format_interval($interval);
+      $this->assertEqual($expected, $actual, sprintf('%d seconds is %s (%s)', $interval, $actual, $expected));
+    }
+  }
+
+  /**
+   * Tests for format_interval().
+   *
+   * These are more complex tests based on the current timestamp.
+   */
+  function testFormatIntervalComplex() {
+    // Here we take some expected results and see if they match up.
+    // This text is formated the same as format_interval's results.
+    $results = array(
+      '2 weeks',
+      '4 weeks',
+      '1 month',
+      '3 years',
+      '1 month 2 days',
+      '1 week 4 hours',
+      '2 years 4 days',
+      '1 month 2 days',
+      '4 months 2 weeks 5 min',
+      '4 months 1 min',
+      // Below are the more complex tests that require proper date functions.
+      '3 months 6 days',
+      '3 months 2 weeks 5 days',
+      '2 months 1 day',
+      '11 months 4 weeks 1 day',
+    );
+    foreach ($results as $expected) {
+      // Find out how many seconds make up '2 weeks'.
+      $interval = strtotime('now + ' . $expected) - time();
+      $actual = format_interval($interval, 3);
+      $this->assertEqual($expected, $actual, sprintf('%d seconds is %s (%s)', $interval, $actual, $expected));
+    }
+  }
+}
