diff --git a/core/includes/form.inc b/core/includes/form.inc
index a12fb34..1216ed9 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -90,6 +90,11 @@
  */
 
 /**
+* The default cache lifetime for forms.
+*/
+define('FORM_CACHE_DEFAULT_LIFETIME', 21600);
+
+/**
  * Wrapper for drupal_build_form() for use when $form_state is not needed.
  *
  * @param $form_id
@@ -512,22 +517,50 @@ function form_get_cache($form_build_id, &$form_state) {
 
 /**
  * Store a form in the cache.
+ *
+ * The form cache expiration (set in $form_state['cache_expire']) may be either
+ * a numeric value (in seconds) or a relative date format (for use with the PHP
+ * strtotime() function). Using a relative date format allows the cache
+ * expiration to be set independently of the cache build time. For example, if
+ * the expiration time is set to 'tomorrow', the cache will expire at midnight,
+ * regardless of whether the the cache was built at 10:00 a.m. or 11:55 p.m.
+ *
+ * @see http://www.php.net/manual/en/datetime.formats.relative.php
+ *
  */
 function form_set_cache($form_build_id, $form, $form_state) {
-  // 6 hours cache life time for forms should be plenty.
-  $expire = 21600;
+  // If this form has a custom cache expiration set, use that.
+  if (!empty($form_state['cache_expire'])) {
+    $expire = $form_state['cache_expire'];
+  }
+  // Otherwise, retrieve the configured cache lifetime.
+  else {
+    $expire = variable_get('form_cache_expire', FORM_CACHE_DEFAULT_LIFETIME);
+  }
+  // If the expiration is non-numeric, assume it is a relative datetime.
+  if (!is_numeric($expire) && ($strtotime = strtotime($exipre))) {
+    $expire = $strtotime;
+  }
+  // A numeric expiration time is given as seconds from now.
+  elseif (is_numeric($expire)) {
+    $expire = REQUEST_TIME + $expire;
+  }
+  // Fall back to the default.
+  else {
+    $expire = REQUEST_TIME + FORM_CACHE_DEFAULT_LIFETIME;
+  }
 
   // Cache form structure.
   if (isset($form)) {
     if ($GLOBALS['user']->uid) {
       $form['#cache_token'] = drupal_get_token();
     }
-    cache('form')->set('form_' . $form_build_id, $form, REQUEST_TIME + $expire);
+    cache('form')->set('form_' . $form_build_id, $form, $expire);
   }
 
   // Cache form state.
   if ($data = array_diff_key($form_state, array_flip(form_state_keys_no_cache()))) {
-    cache('form')->set('form_state_' . $form_build_id, $data, REQUEST_TIME + $expire);
+    cache('form')->set('form_state_' . $form_build_id, $data, $expire);
   }
 }
 
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index a5a8885..f205517 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -1674,6 +1674,33 @@ function system_performance_settings() {
     '#description' => t('The maximum time an external cache can use an old version of a page.')
   );
 
+  $form_cache_expire = variable_get('form_cache_expire', 21600);
+  $form['caching']['form_cache_expire'] = array(
+    '#type' => 'select',
+    '#title' => t('Expiration of cached forms'),
+    '#default_value' => $form_cache_expire,
+    '#options' => array('custom' => '<' . t('custom') . '>') + $period,
+    '#description' => t('The maximum time form and form_state are cached.')
+  );
+  $form_cache_expire_custom = (is_numeric($form_cache_expire)) ? REQUEST_TIME + $form_cache_expire : strtotime($form_cache_expire);
+  $form['caching']['form_cache_expire_custom'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Custom form cache expiration'),
+    '#default_value' => $form_cache_expire,
+    '#field_suffix' => t(
+      '%expire evaluates to %expiration_date',
+      array(
+        '%expire' => $form_cache_expire,
+        '%expiration_date' => date('Y/m/d - H:i:s', $form_cache_expire_custom),
+      )
+    ),
+    '#description' => t('Enter a custom expiration time using a <a href="http://www.php.net/manual/en/datetime.formats.relative.php">relative datetime format</a>.'),
+    '#states' => array(
+      'visible' => array(':input[name="form_cache_expire"]' => array('value' => 'custom')),
+    ),
+    '#element_validate' => array('_system_performance_settings_validate_form_caching'),
+  );
+
   $directory = 'public://';
   $is_writable = is_dir($directory) && is_writable($directory);
   $disabled = !$is_writable;
@@ -1719,6 +1746,36 @@ function system_performance_settings() {
 }
 
 /**
+ * Validates the form cache part of the system performance form.
+ *
+ * Validates the custom form expiration and stores it back into the used
+ * variable name.
+ *
+ * @see system_performance_settings()
+ */
+function _system_performance_settings_validate_form_caching($element, &$form_state) {
+  if (!empty($element['#value']) && $form_state['values']['form_cache_expire'] == 'custom') {
+    $expire = $element['#value'];
+    if (!is_numeric($expire)) {
+      $expire = (int) strtotime($element['#value']);
+    }
+    else {
+      $expire += REQUEST_TIME;
+    }
+    if ($expire < REQUEST_TIME) {
+      form_error(
+        $element,
+        t('The form\'s custom expiration is set to a time in the past.')
+      );
+    }
+    else {
+      $form_state['values']['form_cache_expire'] = $element['#value'];
+    }
+  }
+  unset($form_state['values']['form_cache_expire_custom']);
+}
+
+/**
  * Submit callback; clear system caches.
  *
  * @ingroup forms
