--- ./modules/date/date/date.module	2009-12-31 19:06:02.000000000 +0100
+++ ./modules/date/date/date.module	2009-12-31 20:13:12.000000000 +0100
@@ -120,6 +120,10 @@
       'label' => 'Date',
       'description' => t('Store a date in the database as an ISO date, recommended for historical or partial dates.'),
       ),
+    'dateflex' => array(
+      'label' => 'Dateflex',
+      'description' => t('Store a date in the database as a flexible text date, recommended for natural language, partial dates.'),
+      ),
     'datestamp' => array(
       'label' => 'Datestamp',
       'description' => t('Store a date in the database as a timestamp, deprecated format to suppport legacy data.'),
@@ -154,7 +158,7 @@
     ),
     'date_text' => array(
       'label' =>  t('Text Field with custom input format'),
-      'field types' => array('date', 'datestamp', 'datetime'),
+      'field types' => array('date', 'datestamp', 'datetime', 'dateflex'),
       'multiple values' => CONTENT_HANDLE_CORE,
       'callbacks' => array(
         'default value' => CONTENT_CALLBACK_CUSTOM,
@@ -162,7 +166,7 @@
       ),
     'date_text_repeat' => array(
       'label' =>  t('Text Field with Repeat options'),
-      'field types' => array('date', 'datestamp', 'datetime'),
+      'field types' => array('date', 'dateflex', 'datestamp', 'datetime'),
       'multiple values' => CONTENT_HANDLE_MODULE,
       'callbacks' => array(
         'default value' => CONTENT_CALLBACK_CUSTOM,
@@ -219,10 +223,10 @@
 function date_field_formatter_info() {
   $formatters = array(
     'default' => array('label' => t('Default'),
-      'field types' => array('date', 'datestamp', 'datetime'),
+      'field types' => array('date', 'dateflex', 'datestamp', 'datetime'),
       'multiple values' => CONTENT_HANDLE_CORE),
     'format_interval' => array('label' => t('As Time Ago'),
-      'field types' => array('date', 'datestamp', 'datetime'),
+      'field types' => array('date', 'dateflex', 'datestamp', 'datetime'),
       'multiple values' => CONTENT_HANDLE_CORE),
   );
 
--- ./modules/date/date/date_admin.inc	2009-12-31 19:06:03.000000000 +0100
+++ ./modules/date/date/date_admin.inc	2009-12-31 20:13:12.000000000 +0100
@@ -296,6 +333,9 @@
   if ($field['type'] == 'date') {
     $db_columns['value'] = array('type' => 'varchar', 'length' => 20, 'not null' => FALSE, 'sortable' => TRUE, 'views' => TRUE);
   }
+  else if ($field['type'] == 'dateflex') {
+    $db_columns['value'] = array('type' => 'varchar', 'length' => 21, 'not null' => FALSE, 'sortable' => TRUE);
+  }
   elseif ($field['type'] == 'datestamp') {
     $db_columns['value'] = array('type' => 'int', 'not null' => FALSE, 'sortable' => TRUE, 'views' => TRUE);
   }
--- ./modules/date/date/date_elements.inc	2009-12-31 19:06:03.000000000 +0100
+++ ./modules/date/date/date_elements.inc	2009-12-31 20:13:12.000000000 +0100
@@ -138,8 +143,11 @@
     if (!isset($items[$delta][$processed])) {
       $items[$delta][$processed] = '';
     }
+    // Flex date stay as is.
+    if ($field['type'] != DATE_FLEX) {
     $date = date_local_date($form, $form_state, $delta, $items[$delta], $timezone, $field, $processed);
     $items[$delta][$processed] = is_object($date) ? date_format($date, DATE_FORMAT_DATETIME) : '';
+    }
   }
 
   $element = array(
@@ -328,6 +357,7 @@
     $element[$to_field]['#default_value'] = isset($element['#value'][$to_field]) ? $element['#value'][$to_field] : '';
     $element[$to_field]['#required'] = false;
     $element[$to_field]['#weight'] += .1;
+    $element[$to_field]['#secondary'] = TRUE;
     if ($field['widget']['type'] == 'date_select') {
       $description .= ' '. t("Empty 'To date' values will use the 'From date' values.");
     }
@@ -434,6 +472,7 @@
                 
     // Check todate input for blank values and substitute in fromdate
     // values where needed, then re-compute the todate with those values.
+    $to_date_empty = TRUE;
     if ($field['todate']) {
       $merged_date = array();
       $to_date_empty = TRUE;
@@ -460,10 +499,18 @@
       
       // Call the right function to turn this altered user input into
       // a new value for the todate.
+      // Dateflexes are all-or-nothing (a single field to rule them all). Thus we either have a valid field, or an empty one. First case should remain untouched, second one requires a simple copy (instead of a rotten array-copy).
+      if($field['type'] != DATE_FLEX) {
       $item[$to_field] = $merged_date;
+        $to_date_empty = FALSE;
+      }
     }
-    else {
+    if ($to_date_empty) {
       $item[$to_field] = $item[$from_field];
+      if($field['type'] == DATE_FLEX) {
+        $flexarray = date_dbtext2flexarray($item[$to_field]);
+        $item[$to_field] = date_flexarray2dbtext($flexarray, TRUE);
+      }
     }
     
     $from_date = date_input_value($field, $element[$from_field]);
@@ -485,6 +532,8 @@
       $errors[] = t('The To date must be greater than the From date.');
     }
     else {
+      // Flex dates are never converted to real dates, so there is no need to reconvert them back.
+      if ($field['type'] != DATE_FLEX) {
       // Convert input dates back to their UTC values and re-format to ISO
       // or UNIX instead of the DATETIME format used in element processing.
       $timezone = !empty($item[$tz_field]) ? $item[$tz_field] : $element['#date_timezone'];
@@ -518,6 +567,11 @@
       if ($test_to != date_format($to_date, 'r')) {
         $errors[] = t('The To date is invalid.');
       }
+      }
+      else {
+        $item[$from_field] = strtr($item[$from_field], ' ', 'T');
+        $item[$to_field] = strtr($item[$to_field], ' ', 'T');
+      }
       if (empty($errors)) {
         form_set_value($element, $item, $form_state);
       }
--- ./modules/date/date_api.module	2009-12-31 19:06:04.000000000 +0100
+++ ./modules/date/date_api.module	2009-12-31 20:13:12.000000000 +0100
@@ -8,6 +8,9 @@
  * are not loaded unless other modules specifically include them.
  */
 
+require_once('./'. drupal_get_path('module', 'date_api') .'/includes/gui_date.inc');
+require_once('./'. drupal_get_path('module', 'date_api') .'/includes/gui_periode.inc');
+
 /**
  * Set up some constants.
  *
@@ -20,6 +23,7 @@
  */
 define('DATE_ISO',  'date');
 define('DATE_UNIX', 'datestamp');
+define('DATE_FLEX', 'dateflex');
 define('DATE_DATETIME', 'datetime');
 define('DATE_ARRAY', 'array');
 define('DATE_OBJECT', 'object');
@@ -1168,6 +1172,7 @@
     case DATE_DATETIME:
     case DATE_ISO:
       if (!preg_match(DATE_REGEX_LOOSE, $date)) return NULL;
+    case DATE_FLEX:
       $date = date_fuzzy_datetime($date);
       $obj = date_create($date, timezone_open($tz));
       break;
@@ -1247,15 +1252,7 @@
  * Create an array of date parts from an ISO date.
  */
 function date_iso_array($date) {
-  preg_match(DATE_REGEX_LOOSE, $date, $regs);
-  return array(
-    'year' => isset($regs[1]) ? intval($regs[1]) : '',
-    'month' => isset($regs[2]) ? intval($regs[2]) : '',
-    'day' => isset($regs[3]) ? intval($regs[3]) : '',
-    'hour' => isset($regs[5]) ? intval($regs[5]) : '',
-    'minute' => isset($regs[6]) ? intval($regs[6]) : '',
-    'second' => isset($regs[7]) ? intval($regs[7]) : '',
-    );       
+  return date_flex2fullarray($date);
 }
 
 /**
@@ -1283,6 +1280,70 @@
     0 => date_format($obj, 'U'));
 }
 
+function date_flex2fullarray(&$flex, &$flexarray = NULL, $end_date = FALSE) {
+  if (!$flexarray) {
+    $flexarray = date_dbtext2flexarray($flex);
+  }
+  if ($flexarray === NULL) return 'ERROR';
+  $now = Date::obtenir(time());
+  for ($n = 0; $n < 6 && $flexarray[$n] === NULL; ++$n) {
+    $flexarray[$n] = $now[$n];
+  }
+  $flex = date_flexarray2dbtext($flexarray, $end_date);
+  foreach (array('year', 'month', 'day', 'hour', 'minute', 'second') as $n => $c) {
+    $array[$c] = $flexarray[$n];
+  }
+  return $array;
+}
+
+function date_flexarray2dbtext(&$flexarray, $end_date) {
+  $flexa2 = Date::completer(array(1970, 1, 1, 0, 0, 0), $flexarray);
+  for ($y = 6; --$y >= 0 && $flexarray[$y] === NULL;) {
+  }
+  ++$y;
+  if ($end_date && $y <= 3) { // If this is an end date, for interval searches, we need to push it to its limit. If this is an end hour, we don't modify it. Example: end date 2008-05-01 is 2008-05-01 23:59:59. End date 2008-05-01 15h is 2008-05-01 15:00:00 (not 15:59:59).
+    ++$flexa2[$y - 1];
+    $flexa2 = Date::corriger($flexa2);
+    --$flexa2[5];
+    $flexa2 = Date::corriger($flexa2);
+  }
+  $flex = Date::aff($flexa2).'|'.$y;
+  return $flex;
+}
+
+function date_date2flextext($d) {
+  if (isset($d->flexparts)) {
+    $flex = $d->flexparts;
+  }
+  else if (isset($d->parts)) {
+    $flex = array();
+    foreach(array('year', 'mon', 'mday', 'hours', 'minutes', 'seconds') as $field) {
+      $flex[] = $d->parts[$field];
+    }
+  }
+  else if(is_array($d)) {
+    $flex = $d;
+  }
+  else {
+    date_flex2fullarray($d, $flex);
+  }
+  $now = Date::obtenir(time());
+  for ($i = 6; --$i >= 3;) {
+    $now[$i] = NULL; // Hours, minutes and seconds is something we want to show, even if they match exactly with the current time. So we clear those fields from the thing that will clear them for display.
+  }
+  // Find our least significant member and protect it from $now, so that a strictly matching date will at least show as its least significant element.
+  for ($i = 6; --$i >= 0;) {
+    if (isset($flex[$i])) { // If we have at least a value, that's OK.
+      break;
+    }
+  }
+  if ($i >= 0) {
+    $now[$i] = NULL; // Else tell now to let at least this element
+  }
+  $flex = Date::decompleter($flex, $now);
+  return Periode::affDate($flex, Periode::$NUMERIQUE);
+}
+
 /**
  * Extract integer value of any date part from any type of date.
  *
@@ -1311,6 +1372,7 @@
       return (integer) array_key_exists($part, $date) ? $date[$part] : NULL;
     case DATE_DATETIME:
     case DATE_ISO:
+    case DATE_FLEX:
       return (integer) substr($date, $positions[$part], $part == 'year' ? 4 : 2);
     case DATE_ICAL:
       return (integer) substr($date, $ipositions[$part], $part == 'year' ? 4 : 2);
@@ -1345,6 +1407,7 @@
   if (($type == DATE_ISO || $type == DATE_DATETIME) && (!is_string($date) || !preg_match(DATE_REGEX_LOOSE, $date))) return FALSE;
   if ($type == DATE_UNIX and !is_numeric($date)) return FALSE;
   if ($type == DATE_ARRAY and !is_array($date)) return FALSE;
+  if ($type == DATE_FLEX and Date::calculer(date_dbtext2flexarray($date)) < 0) return FALSE;
 
   // Make sure integer values are sent to checkdate.
   $year = intval(date_part_extract($date, 'year', $type));
@@ -1389,6 +1452,21 @@
   return TRUE;
 }
 
+function date_dbtext2flexarray($dbtext) {
+  $dbtext = explode('|', $dbtext);
+  $flexarray = Date::lire($dbtext[0]);
+  if ($flexarray && isset($dbtext[1])) {
+    $z = 0 + $dbtext[1];
+    if ($z < 0) {
+      $z = 0;
+    }
+    while ($z < 6) {
+      $flexarray[$z++] = NULL;
+    }
+  }
+  return $flexarray;
+}
+
 function date_valid_year($year) {
   if (variable_get('date_max_year', 4000) < $year || variable_get('date_min_year', 1) > $year) {
     return FALSE;
--- ./modules/date/date_api_elements.inc	2009-12-31 19:06:04.000000000 +0100
+++ ./modules/date/date_api_elements.inc	2009-12-31 20:13:12.000000000 +0100
@@ -130,6 +130,11 @@
  */
 function date_text_process($element, $edit, $form_state, $form) {
   $date = NULL;
+  if ($element['#field']['type'] == DATE_FLEX) {
+    $date = !empty($edit) ? $edit['date'] : (!empty($element['#value']) ? $element['#value'] : NULL);
+    $date = isset($date) ? date_date2flextext($date) : '';
+  }
+  else {
   $granularity = date_format_order($element['#date_format']);
   
   // We sometimes get $edit without $edit['date'] from
@@ -141,11 +146,18 @@
   elseif (!empty($element['#value'])) {
     $date = date_make_date($element['#value'], $element['#date_timezone'], DATE_DATETIME, $granularity);
   }
+  }
+  
   $element['#tree'] = TRUE;
 
   $element['date']['#type'] = 'textfield';
   $element['date']['#weight'] = !empty($element['date']['#weight']) ? $element['date']['#weight'] : $element['#weight'];
+  if ($element['#field']['type'] != DATE_FLEX) {
   $element['date']['#default_value'] = is_object($date) ? date_format($date , $element['#date_format']) : '';
+  }
+  else {
+    $element['date']['#default_value'] = $date;
+  }
   $element['date']['#attributes'] = array('class' => (isset($element['#attributes']['class']) ? $element['#attributes']['class'] : '') .' date-date');
   $element['date']['#description'] = ' '. t('Format: @date', array('@date' => date($element['#date_format'], time())));
   
@@ -153,6 +165,9 @@
   // We'll set our own message on the parent element.
   //$element['date']['#required'] = $element['#required'];
   $element['date']['#theme'] = 'date_textfield_element';
+  if ($element['#field']['type'] == DATE_FLEX) {
+    $element['date']['#size'] = 16; // Really, dateflex is made to give an even shorter input.
+  }
   if (isset($element['#element_validate'])) {
     array_push($element['#element_validate'], 'date_text_validate');
   }
@@ -530,7 +545,12 @@
   $input = $form_values['date'];
   if (!$element['#required'] && trim($input) == '') return NULL;
 
+  if ($element['#field']['type'] == DATE_FLEX) {
+    return date_flex2fullarray($input, $nomatter, isset($element['#secondary'])) ? $input : NULL;
+  }
+  else {
   $value = date_limit_value(date_convert_from_custom($input, $element['#date_format']), $granularity);
+  }
 
   // If it creates a valid date, use it.
   if (date_is_valid($value, DATE_DATETIME, $granularity)) {
--- ./modules/date/date_api_sql.inc	2009-12-31 19:06:04.000000000 +0100
+++ ./modules/date/date_api_sql.inc	2009-12-31 20:13:12.000000000 +0100
@@ -175,7 +175,7 @@
             break;
           case DATE_ISO:
             if (version_compare(db_version(), '4.1.1', '>=')) {
-              $field = "STR_TO_DATE($field, '%Y-%m-%%dT%T')";
+              $field = "STR_TO_DATE(SUBSTR($field, 1, 19), '%Y-%m-%%dT%T')";
             }
             else {
               $field = "REPLACE($field, 'T', ' ')";
--- ./modules/date/includes/date_api_fields.inc	2009-12-31 19:06:14.000000000 +0100
+++ ./modules/date/includes/date_api_fields.inc	2009-12-31 20:13:12.000000000 +0100
@@ -39,6 +39,9 @@
       if ($handler->content_field['type'] == 'date') {
         $type = 'cck_string';
       }
+      elseif ($handler->content_field['type'] == 'dateflex') {
+        $type = 'cck_string';
+      }
       elseif ($handler->content_field['type'] == 'datestamp') {
         $type = 'cck_timestamp';
       }
