Index: date_api.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/date/date_api.module,v
retrieving revision 1.64.2.5.2.94
diff -u -r1.64.2.5.2.94 date_api.module
--- date_api.module	28 Feb 2010 21:12:41 -0000	1.64.2.5.2.94
+++ date_api.module	15 Mar 2010 17:12:55 -0000
@@ -32,7 +32,7 @@
 define('DATE_FORMAT_ICAL_DATE', "Ymd");
 define('DATE_FORMAT_DATE', 'Y-m-d');
 
-define('DATE_REGEX_ISO', '/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):?(\d{2})?/');
+define('DATE_REGEX_ISO', '/(\d{4})?(-(\d{2}))?(-(\d{2}))?([T\s](\d{2}))?(:(\d{2}))?(:(\d{2}))?/');
 define('DATE_REGEX_DATETIME', '/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2}):?(\d{2})?/');
 define('DATE_REGEX_LOOSE', '/(\d{4})-?(\d{1,2})-?(\d{1,2})([T\s]?(\d{2}):?(\d{2}):?(\d{2})?(\.\d+)?(Z|[\+\-]\d{2}:?\d{2})?)?/');
 define('DATE_REGEX_ICAL_DATE', '/(\d{4})(\d{2})(\d{2})/');
@@ -131,10 +131,10 @@
 /**
  * Helper function for getting the format string for a date type.
  */
-function date_type_format($type) {
+function date_type_format($type, $granularity = NULL) {
   switch ($type) {
     case DATE_ISO:
-      return DATE_FORMAT_ISO;
+      return is_array($granularity) ? date_limit_format(DATE_FORMAT_ISO, $granularity) : DATE_FORMAT_ISO;
     case DATE_UNIX:
       return DATE_FORMAT_UNIX;
     case DATE_DATETIME:
@@ -666,6 +666,9 @@
     default:
       $format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
   }
+  if (is_array($date->granularity)) {
+    $format = date_limit_format($format, $date->granularity);
+  }
   $max = strlen($format);
   $datestring = '';
   for ($i = 0; $i < $max; $i++) {
@@ -759,7 +762,7 @@
 /**
  *  Convert a date of any type or an array of date parts into a valid date
  *  object.
-
+ *
  *  @param $date
  *    A date in any format or the string 'now'.
  *  @param $timezone
@@ -788,11 +791,12 @@
     
   // Special handling for partial dates that don't need precision.
   $max_granularity = array_pop($granularity);
+  $granularity = NULL;
   if (in_array($max_granularity, array('year', 'month')) || $type == DATE_ISO || $type == DATE_ARRAY) {
     if ($type == DATE_UNIX) {
       $date = date_convert($date, $type, DATE_DATETIME);
     }
-    $date = date_fuzzy_datetime($date);
+    $date = date_fuzzy_datetime($date, $granularity);
     $type = DATE_DATETIME;
   }
   
@@ -805,7 +809,9 @@
       return date_create('now', timezone_open($timezone));
     }
     elseif ($datetime = date_convert($date, $type, DATE_DATETIME, $timezone)) {
-      return date_create($datetime, timezone_open($timezone));
+      $obj = date_create($datetime, timezone_open($timezone));
+      $obj->granularity = $granularity;
+      return $obj;
     }
   }
   return NULL;
@@ -1148,14 +1154,55 @@
             ':'. date_pad(intval($date['minute'])) .':'. date_pad(intval($date['second']));
       switch ($to_type) {
         case DATE_ISO:
-          return str_replace(' ', 'T', $datetime);
+          $datetime = '';
+          if ($date['year'] !== '') {
+            $datetime = date_pad(intval($date['year']), 4);
+            if ($date['month'] !== '') {
+              $datetime .= '-'. date_pad(intval($date['month']));
+              if ($date['day'] !== '') { 
+                $datetime .= '-'. date_pad(intval($date['day']));
+              }
+            }
+          }
+          if ($date['hour'] !== '') {
+            $datetime .= $datetime ? 'T' : '';
+            $datetime .= date_pad(intval($date['hour']));
+            if ($date['minute'] !== '') {
+              $datetime.= ':'. date_pad(intval($date['minute']));
+              if ($date['second'] !== '') {
+                $datetime .= ':'. date_pad(intval($date['second']));
+              }
+            }
+          }
+          return $datetime;
         case DATE_DATETIME:
           return $datetime;
         case DATE_ICAL:
           $replace = array(' ' => 'T', '-' => '', ':' => '');
           return strtr($datetime, $replace);
-        case DATE_OBJECT:    
-          return date_create($datetime, timezone_open($tz));
+        case DATE_OBJECT:
+          $obj = date_create($datetime, timezone_open($tz));
+          $granularity = array();
+          if ($date['year'] !== '') {
+            $granularity[] = 'year';
+          }
+          if ($date['month'] !== '') {
+            $granularity[] = 'month';
+          }
+          if ($date['day'] !== '') {
+            $granularity[] = 'day';
+          }
+          if ($date['hour'] !== '') {
+            $granularity[] = 'hour';
+          }
+          if ($date['minute'] !== '') {
+            $granularity[] = 'minute';
+          }
+          if ($date['second'] !== '') {
+            $granularity[] = 'second';
+          }
+          $obj->granularity = $granularity;
+          return $obj;
         case DATE_UNIX:
           $obj = date_create($datetime, timezone_open($tz));
           return date_format($obj, 'U');  
@@ -1167,9 +1214,11 @@
       break;
     case DATE_DATETIME:
     case DATE_ISO:
-      if (!preg_match(DATE_REGEX_LOOSE, $date)) return NULL;
-      $date = date_fuzzy_datetime($date);
+      if (!preg_match(DATE_REGEX_ISO, $date)) return NULL;
+      $granularity = NULL;
+      $date = date_fuzzy_datetime($date, $granularity);
       $obj = date_create($date, timezone_open($tz));
+      $obj->granularity = $granularity;
       break;
     case DATE_ICAL:
       if (!preg_match(DATE_REGEX_LOOSE, $date)) return NULL;
@@ -1193,7 +1242,12 @@
     case DATE_DATETIME:
       return date_format($obj, DATE_FORMAT_DATETIME);
     case DATE_ISO:
-      return date_format($obj, DATE_FORMAT_ISO);
+      $format = DATE_FORMAT_ISO;
+      if (is_array($obj->granularity)) {
+        // We know how much of this data is valid
+        return date_format($obj, date_limit_format($format, $obj->granularity));
+      }
+      return date_format($obj, $format);
     case DATE_ICAL:
       return date_format($obj, DATE_FORMAT_ICAL);
     case DATE_UNIX:
@@ -1202,9 +1256,11 @@
       $date_array = date_array($obj);
       // ISO dates may contain zero values for some date parts,
       // make sure they don't get lost in the conversion.
-      if ($from_type == DATE_ISO) {
-        $date_array = array_merge($date_array, date_iso_array($date));
-      }
+      
+      // Breaks dynamic granularity patch and seems unnecessary
+      //if ($from_type == DATE_ISO) {
+      //  $date_array = array_merge($date_array, date_iso_array($date));
+      //}
       return $date_array;
     default:
       return NULL;
@@ -1214,7 +1270,7 @@
 /**
  * Create valid datetime value from incomplete ISO dates or arrays.
  */
-function date_fuzzy_datetime($date) {
+function date_fuzzy_datetime($date, &$granularity = NULL) {
   // A text ISO date, like MMMM-YY-DD HH:MM:SS
   if (!is_array($date)) {
     $date = date_iso_array($date);
@@ -1226,6 +1282,25 @@
     $time_part = array_key_exists('time', $date) ? $date['time'] : '';
     $date = date_iso_array(trim($date_part .' '. $time_part));
   }
+  // Set the granularity array.
+  $granularity = array();
+  $granularity[] = 'year';
+  if ($date['month'] !== '') {
+    $granularity[] = 'month';
+  }
+  if ($date['day'] !== '') {
+    $granularity[] = 'day';
+  }
+  if ($date['hour'] !== '') {
+    $granularity[] = 'hour';
+  }
+  if ($date['minute'] !== '') {
+    $granularity[] = 'minute';
+  }
+  if ($date['second'] !== '') {
+    $granularity[] = 'second';
+  }
+  
   // Otherwise date must in in format:
   //  array('year' => YYYY, 'month' => MM, 'day' => DD).
   if (empty($date['year'])) {
@@ -1247,15 +1322,15 @@
  * Create an array of date parts from an ISO date.
  */
 function date_iso_array($date) {
-  preg_match(DATE_REGEX_LOOSE, $date, $regs);
+  preg_match(DATE_REGEX_ISO, $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]) : '',
-    );       
+    'month' => isset($regs[3]) ? intval($regs[3]) : '',
+    'day' => isset($regs[5]) ? intval($regs[5]) : '',
+    'hour' => isset($regs[7]) ? intval($regs[7]) : '',
+    'minute' => isset($regs[9]) ? intval($regs[9]) : '',
+    'second' => isset($regs[11]) ? intval($regs[11]) : '',
+    );
 }
 
 /**
@@ -1267,22 +1342,25 @@
  */
 function date_array($obj) {
   $year = intval(date_format($obj, 'Y'));
-  $dow = date_format($obj, 'w');
+  $dow = date_lacking_granularity($obj, 'day') ? '' : date_format($obj, 'w');
   $days = date_week_days();
   return array(
-    'second' => (integer) date_format($obj, 's'),
-    'minute' => (integer) date_format($obj, 'i'),
-    'hour' => date_format($obj, 'G'),
-    'day' => date_format($obj, 'j'),
+    'second' => date_lacking_granularity($obj, 'second') ? '' : (integer) date_format($obj, 's'),
+    'minute' => date_lacking_granularity($obj, 'minute') ? '' : (integer) date_format($obj, 'i'),
+    'hour' => date_lacking_granularity($obj, 'hour') ? '' : date_format($obj, 'G'),
+    'day' => date_lacking_granularity($obj, 'day') ? '' : date_format($obj, 'j'),
     'wday' => $dow,
-    'month' => date_format($obj, 'n'),
+    'month' => date_lacking_granularity($obj, 'month') ? '' : date_format($obj, 'n'),
     'year' => date_format($obj, 'Y'),
     'yday' => date_format($obj, 'z'),
-    'weekday' => $days[$dow],
-    'month_name' => date_format($obj, 'F'),
+    'weekday' => date_lacking_granularity($obj, 'day') ? '' : $days[$dow],
+    'month_name' => date_lacking_granularity($obj, 'month') ? '' : date_format($obj, 'F'),
     0 => date_format($obj, 'U'));
 }
 
+function date_lacking_granularity($date, $gran) {
+  return is_array($date->granularity) && !in_array($gran, $date->granularity);
+}
 /**
  * Extract integer value of any date part from any type of date.
  *
@@ -1342,7 +1420,8 @@
   // Check that the value is properly structured.
   if (empty($date)) return FALSE;
   if ($type == DATE_OBJECT && !is_object($date)) return FALSE;
-  if (($type == DATE_ISO || $type == DATE_DATETIME) && (!is_string($date) || !preg_match(DATE_REGEX_LOOSE, $date))) return FALSE;
+  if ($type == DATE_DATETIME && (!is_string($date) || !preg_match(DATE_REGEX_LOOSE, $date))) return FALSE;
+  if ($type == DATE_ISO && (!is_string($date) || !preg_match(DATE_REGEX_ISO, $date))) return FALSE;
   if ($type == DATE_UNIX and !is_numeric($date)) return FALSE;
   if ($type == DATE_ARRAY and !is_array($date)) return FALSE;
 
@@ -1475,19 +1554,19 @@
     foreach ($nongranularity as $level) {
       switch ($level) {
         case 'second':
-          $date['second'] = 0;
+          $date['second'] = $type != DATE_ISO ? 0 : '';
           break;
         case 'minute':
-          $date['minute'] = 0;
+          $date['minute'] = $type != DATE_ISO ? 0 : '';
           break;
         case 'hour':
-          $date['hour'] = 0;
+          $date['hour'] = $type != DATE_ISO ? 0 : '';
           break;
         case 'month':
-          $date['month'] = $type != DATE_ISO ? 1 : 0;
+          $date['month'] = $type != DATE_ISO ? 1 : '';
           break;
         case 'day':
-          $date['day'] = $type != DATE_ISO ? 1 : 0;
+          $date['day'] = $type != DATE_ISO ? 1 : '';
           break;
        }
     }
Index: date_api_elements.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/date/date_api_elements.inc,v
retrieving revision 1.49.2.1.2.53
diff -u -r1.49.2.1.2.53 date_api_elements.inc
--- date_api_elements.inc	16 Sep 2009 10:56:28 -0000	1.49.2.1.2.53
+++ date_api_elements.inc	15 Mar 2010 17:12:55 -0000
@@ -63,6 +63,7 @@
     '#input' => TRUE, '#tree' => TRUE,
     '#date_timezone' => date_default_timezone_name(),
     '#date_format' => variable_get('date_format_short', 'm/d/Y - H:i'),
+    '#date_flexible' => 0,
     '#date_text_parts' => array(),
     '#date_increment' => 1,
     '#date_year_range' => '-3:+3',
@@ -135,17 +136,17 @@
   // We sometimes get $edit without $edit['date'] from
   // Views exposed filters.
   if (!empty($edit) && is_array($edit) && !empty($edit['date'])) {
-    $datetime = date_convert_from_custom($edit['date'], $element['#date_format']);
-    $date = date_make_date($datetime, $element['#date_timezone'], DATE_DATETIME, $granularity);
+    $datetime = _date_text_input_value($edit['date'], $element['#date_format'], $element['#field']['type'], $element['#date_flexible']);
+    $date = date_make_date($datetime, $element['#date_timezone'], DATE_ISO, $granularity);
   }
   elseif (!empty($element['#value'])) {
-    $date = date_make_date($element['#value'], $element['#date_timezone'], DATE_DATETIME, $granularity);
+    $date = date_make_date($element['#value'], $element['#date_timezone'], ($element['#field']['type'] == DATE_ISO ? DATE_ISO : DATE_DATETIME), $granularity);
   }
   $element['#tree'] = TRUE;
 
   $element['date']['#type'] = 'textfield';
   $element['date']['#weight'] = !empty($element['date']['#weight']) ? $element['date']['#weight'] : $element['#weight'];
-  $element['date']['#default_value'] = is_object($date) ? date_format($date , $element['#date_format']) : '';
+  $element['date']['#default_value'] = is_object($date) ? date_format($date , is_array($date->granularity) ? date_limit_format($element['#date_format'], $date->granularity) : $element['#date_format']) : '';
   $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())));
   
@@ -192,7 +193,7 @@
     $date = date_make_date($edit, $element['#date_timezone'], DATE_ARRAY, $granularity);
   }
   elseif (!empty($element['#value'])) {
-    $date = date_make_date($element['#value'], $element['#date_timezone'], DATE_DATETIME, $granularity);
+    $date = date_make_date($element['#value'], $element['#date_timezone'], DATE_ISO, $granularity);
   }
   $element['#tree'] = TRUE;
   date_increment_round($date, $element['#date_increment']);
@@ -205,7 +206,7 @@
     if ($field != 'timezone') {
       $element[$field] = array(
         '#type' => 'hidden',
-        '#value' => 0,
+        '#value' => $element['#field']['type'] == DATE_ISO ? '' : 0,
         );
     }
   }
@@ -259,39 +260,39 @@
         $min_year = $range[0];
         $max_year = $range[1];
         
-        $sub_element[$field]['#default_value'] = is_object($date) ? date_format($date, 'Y') : '';
+        $sub_element[$field]['#default_value'] = is_object($date) && !date_lacking_granularity($date, 'year') ? date_format($date, 'Y') : '';
         if ($part_type == 'select') {
           $sub_element[$field]['#options'] = drupal_map_assoc(date_years($min_year, $max_year, $part_required));
         }
         break;
       case 'month':
-        $sub_element[$field]['#default_value'] = is_object($date) ? date_format($date, 'n') : '';
+        $sub_element[$field]['#default_value'] = is_object($date) && !date_lacking_granularity($date, 'month') ? date_format($date, 'n') : '';
         if ($part_type == 'select') {
           $sub_element[$field]['#options'] = $month_function($part_required);
         }
         break;
       case 'day':
-        $sub_element[$field]['#default_value'] = is_object($date) ? date_format($date, 'j') : '';
+        $sub_element[$field]['#default_value'] = is_object($date) && !date_lacking_granularity($date, 'day') ? date_format($date, 'j') : '';
         if ($part_type == 'select') {
           $sub_element[$field]['#options'] = drupal_map_assoc(date_days($part_required));
         }
         break;
       case 'hour':
-        $sub_element[$field]['#default_value'] = is_object($date) ? date_format($date, $hours_format) : '';
+        $sub_element[$field]['#default_value'] = is_object($date) && !date_lacking_granularity($date, 'hour') ? date_format($date, $hours_format) : '';
         if ($part_type == 'select') {
           $sub_element[$field]['#options'] = drupal_map_assoc(date_hours($hours_format, $part_required));
         }
         $sub_element[$field]['#prefix'] = theme('date_part_hour_prefix', $element);
         break;
       case 'minute':
-        $sub_element[$field]['#default_value'] = is_object($date) ? date_format($date, 'i') : '';
+        $sub_element[$field]['#default_value'] = is_object($date) && !date_lacking_granularity($date, 'minute') ? date_format($date, 'i') : '';
         if ($part_type == 'select') {
           $sub_element[$field]['#options'] = drupal_map_assoc(date_minutes('i', $part_required, $element['#date_increment']));
         }
         $sub_element[$field]['#prefix'] = theme('date_part_minsec_prefix', $element);
         break;
       case 'second':
-        $sub_element[$field]['#default_value'] = is_object($date) ? date_format($date, 's') : '';
+        $sub_element[$field]['#default_value'] = is_object($date) && !date_lacking_granularity($date, 'second') ? date_format($date, 's') : '';
         if ($part_type == 'select') {
           $sub_element[$field]['#options'] = drupal_map_assoc(date_seconds('s', $part_required, $element['#date_increment']));
         }
@@ -489,6 +490,9 @@
     // Use fuzzy_datetime here to be sure year-only dates
     // aren't inadvertantly shifted to the wrong year by trying
     // to save '2009-00-00 00:00:00'.
+    if($element['#field']['type'] == DATE_ISO && $element['#date_flexible']) {
+      return date_convert($element['#value'], DATE_ARRAY, DATE_ISO);
+    }
     return date_fuzzy_datetime(date_convert($element['#value'], DATE_ARRAY, DATE_DATETIME));
   }
   return NULL;
@@ -521,25 +525,36 @@
   }
 }
 
+function date_text_input_value($element) {
+  return _date_text_input_value($element['#value']['date'], $element['#date_format'], $element['#field']['type'], $element['#date_flexible']);
+}
 /**
  * Helper function for extracting a date value out of user input.
  */
-function date_text_input_value($element) {
-  $form_values = $element['#value'];
-  $granularity = date_format_order($element['#date_format']);
-  $input = $form_values['date'];
-  if (!$element['#required'] && trim($input) == '') return NULL;
-
-  $value = date_limit_value(date_convert_from_custom($input, $element['#date_format']), $granularity);
+function _date_text_input_value($input, $format, $type, $flexible = FALSE) {
+  $granularity = date_format_order($format);
+  $value = date_limit_value(date_convert_from_custom($input, $format), $granularity, DATE_ISO);
 
   // If it creates a valid date, use it.
-  if (date_is_valid($value, DATE_DATETIME, $granularity)) {
+  if (date_is_valid($value, DATE_ISO, $granularity)) {
     return $value;
   }
-  // TODO come back and try to find a way to use strtotime to guess
-  // a valid value. Previous attempts to do it were too forgiving and
-  // invalid input was just silently converted to 'now'.
-  // See http://drupal.org/node/265076.
+  
+  if ($type != DATE_ISO || !$flexible) {
+    // Give up, only care for flexible ISO dates. @TODO, this could work for other dates and just stick in 0s
+    return NULL;
+  }
+  $removeorder = array('second', 'minute', 'hour', 'day', 'month');
+  for ($a = 0; $a < count($removeorder); $a ++) {
+    if($key = array_search($removeorder[$a], $granularity)) {
+      unset($granularity[$key]);
+      $format = date_limit_format($format, $granularity);
+      $value = date_limit_value(date_convert_from_custom($input, $format), $granularity, DATE_ISO);
+      if (date_is_valid($value, DATE_ISO, $granularity)) {
+        return $value;
+      }
+    }
+  }
 
   return NULL;
 }
@@ -569,7 +584,7 @@
  * @param string $format
  *   a date format string that describes the format of the input value
  * @return mixed
- *   input value converted to a DATE_DATETIME value
+ *   input value converted to a DATE_ISO value
  */
 function date_convert_from_custom($date, $format) {
   $array = date_format_patterns();
@@ -602,8 +617,8 @@
   if (count($letters) != count($values)) {
     return  NULL;
   }
-  $final_date = array('hour' => 0, 'minute' => 0, 'second' => 0,
-    'month' => 0, 'day' => 0, 'year' => 0);
+  $final_date = array('hour' => '', 'minute' => '', 'second' => '',
+    'month' => '', 'day' => '', 'year' => '');
   foreach ($letters as $i => $letter) {
     $value = $values[$i];
     switch ($letter) {
@@ -658,5 +673,5 @@
   }
   // Don't test for valid date, we might use this to extract
   // incomplete date part info from user input.
-  return date_convert($final_date, DATE_ARRAY, DATE_DATETIME);
+  return date_convert($final_date, DATE_ARRAY, DATE_ISO);
 }
\ No newline at end of file
Index: date_api_ical.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/date/date_api_ical.inc,v
retrieving revision 1.35.4.28
diff -u -r1.35.4.28 date_api_ical.inc
--- date_api_ical.inc	10 May 2009 12:44:14 -0000	1.35.4.28
+++ date_api_ical.inc	15 Mar 2010 17:12:55 -0000
@@ -392,6 +392,8 @@
     // Fix commonly used alternatives like US-Eastern which should be US/Eastern.
     $tz = str_replace('-', '/', $tz);
     // Unset invalid timezone names.
+    require_once(drupal_get_path('module', 'date_timezone') .'/date_timezone.install');
+    $tz = _date_timezone_replacement($tz);
     if (!date_timezone_is_valid($tz)) {
       $tz = '';
     }
Index: date/date.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/date/date/date.module,v
retrieving revision 1.61.2.4.2.64
diff -u -r1.61.2.4.2.64 date.module
--- date/date.module	8 Mar 2010 22:56:46 -0000	1.61.2.4.2.64
+++ date/date.module	15 Mar 2010 17:12:55 -0000
@@ -347,10 +347,12 @@
     else {
       // create a date object with a gmt timezone from the database value
       $value = $item[$processed];
+      $granularity = NULL;
       if ($field['type'] == DATE_ISO) {
-        $value = str_replace(' ', 'T', date_fuzzy_datetime($value));
+        $value = str_replace(' ', 'T', date_fuzzy_datetime($value, $granularity));
       }
       $date = date_make_date($value, $timezone_db, $field['type'], $field['granularity']);
+      $date->granularity = $granularity;
       $dates[$processed] = array();
       $dates[$processed]['db']['object'] = $date;
       $dates[$processed]['db']['datetime'] = date_format($date, DATE_FORMAT_DATETIME);
Index: date/date.theme
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/date/date/date.theme,v
retrieving revision 1.6.4.51
diff -u -r1.6.4.51 date.theme
--- date/date.theme	9 Jun 2009 17:20:31 -0000	1.6.4.51
+++ date/date.theme	15 Mar 2010 17:12:55 -0000
@@ -279,7 +279,7 @@
   }
   
   // Time runs from midnight to the maximum time -- call it 'All day'.
-  if (date_has_time($field['granularity']) && $min_comp && $max_comp) {
+  if (date_has_time($field['granularity']) && ((is_array($$which->granularity) && !date_has_time($$which->granularity) && $date2 == $date1) || (!is_array($$which->granularity) && $min_comp && $max_comp))) {
     $format = date_limit_format($format, array('year', 'month', 'day'));
     return trim(date_format_date($$which, 'custom', $format) .' '. theme('date_all_day_label'));
   }
Index: date/date_admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/date/date/date_admin.inc,v
retrieving revision 1.40.2.3.2.39
diff -u -r1.40.2.3.2.39 date_admin.inc
--- date/date_admin.inc	8 Jun 2009 16:49:36 -0000	1.40.2.3.2.39
+++ date/date_admin.inc	15 Mar 2010 17:12:55 -0000
@@ -245,7 +245,7 @@
 
     case 'save':
 
-      $options = array('granularity', 'timezone_db', 'tz_handling', 'todate', 'repeat', 'repeat_collapsed', 'default_format');
+      $options = array('granularity', 'granularity_flexible', 'timezone_db', 'tz_handling', 'todate', 'repeat', 'repeat_collapsed', 'default_format');
       return $options;
 
     case 'database columns':
@@ -359,6 +359,18 @@
     '#multiple' => TRUE,
     '#description' => t('Set the date elements to be stored (at least a year is required).'),
     );
+  if ($field['type'] == 'date' && !$field['repeat']) {
+    //@TODO figure out how to make flexible granularity work with repeating dates
+    //IE all day events can sensibly repeat every month
+    $form['input']['granularity_flexible'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Flexible Granularity'),
+      '#default_value' => $field['granularity_flexible'] ? 1 : 0,
+      '#description' => t('Allow entry of granularity from year only up to the maximum setting for this field. Only works with certain widgets.'),
+      );
+  } else {
+    $form['input']['granularity_flexible'] = array('#type' => 'hidden', '#value' => 0);
+  }
   $format_types = array();  
   foreach (date_get_format_types('', TRUE) as $name => $info) {
     $format_types[$name] = $info['title'];
@@ -486,6 +498,7 @@
       'hour' => 'hour',
       'minute' => 'minute',
       ),
+    'granularity_flexible' => 0,
     'timezone_db' => date_get_timezone_db($tz_handling),
     'tz_handling' => $tz_handling,
     'todate' => 'optional',
Index: date/date_elements.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/date/date/date_elements.inc,v
retrieving revision 1.46.2.2.2.67
diff -u -r1.46.2.2.2.67 date_elements.inc
--- date/date_elements.inc	5 Mar 2010 15:02:47 -0000	1.46.2.2.2.67
+++ date/date_elements.inc	15 Mar 2010 17:12:55 -0000
@@ -135,7 +135,7 @@
       $items[$delta][$processed] = '';
     }
     $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) : '';
+    $items[$delta][$processed] = is_object($date) ? date_format($date, (($items[$delta]['date_type'] == DATE_ISO && is_array($date->granularity)) ? date_limit_format(DATE_FORMAT_DATETIME, $date->granularity) : DATE_FORMAT_DATETIME)) : '';
   }
 
   $element = array(
@@ -207,16 +207,18 @@
   }
   else {
     $value = $item[$part];
-     
+
     // Special case for ISO dates to create a valid date object for formatting.
+    $granularity = NULL;
     if ($field['type'] == DATE_ISO) {
-      $value = date_fuzzy_datetime($value);
+      $value = date_fuzzy_datetime($value, $granularity);
     }
     else {
       $db_timezone = date_get_timezone_db($field['tz_handling']);
       $value = date_convert($value, $field['type'], DATE_DATETIME, $db_timezone);
     }
-    $date = date_make_date($value, date_get_timezone_db($field['tz_handling']), DATE_DATETIME, $field['granularity']);
+    $date = date_make_date($value, (is_array($granularity) && !date_has_time($granularity)) ? $timezone :date_get_timezone_db($field['tz_handling']), DATE_DATETIME, $field['granularity']);
+    $date->granularity = $granularity;
     if (empty($date)) {
       return NULL;
     }
@@ -282,6 +284,7 @@
     '#delta'         => $delta,
     '#date_timezone' => $element['#date_timezone'],
     '#date_format'      => date_limit_format(date_input_format($element, $field), $field['granularity']),
+    '#date_flexible' => $field['granularity_flexible'],
     '#date_text_parts'  => (array) $field['widget']['text_parts'],
     '#date_increment'   => $field['widget']['increment'],
     '#date_year_range'  => $field['widget']['year_range'],
@@ -487,18 +490,26 @@
       $timezone_db = date_get_timezone_db($field['tz_handling']);
       $item[$tz_field] = $timezone;
 
-      $from_date = date_make_date($from_date, $timezone);
+      $from_date = date_make_date($from_date, $timezone, $field['type'] == DATE_ISO ? DATE_ISO : DATE_DATETIME);
       $item[$offset_field] = date_offset_get($from_date);
       
-      $to_date = date_make_date($to_date, $timezone);
+      $to_date = date_make_date($to_date, $timezone, $field['type'] == DATE_ISO ? DATE_ISO : DATE_DATETIME);
       $test_from = date_format($from_date, 'r');
       $test_to = date_format($to_date, 'r');
       
+      // New stuff for dynamic granularity.
+      if ($from_date->granularity != $to_date->granularity) {
+        $errors[] = t('The From date and the To date must have the same granularity');
+      }
+      if ($from_date->granularity && !date_has_time($from_date->granularity)) {
+        $timezone_db = $timezone;
+      }
+      
       $item[$offset_field2] = date_offset_get($to_date);
       date_timezone_set($from_date, timezone_open($timezone_db));
       date_timezone_set($to_date, timezone_open($timezone_db));
-      $item[$from_field] = date_format($from_date, date_type_format($field['type']));
-      $item[$to_field] = date_format($to_date, date_type_format($field['type']));
+      $item[$from_field] = date_format($from_date, date_type_format($field['type'], $from_date->granularity));
+      $item[$to_field] = date_format($to_date, date_type_format($field['type'], $to_date->granularity));
       if (isset($form_values[$field_name]['rrule'])) {
         $item['rrule'] = $form_values[$field['field_name']]['rrule'];
       }
Index: date_popup/date_popup.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/date/date_popup/date_popup.module,v
retrieving revision 1.42.2.1.2.52
diff -u -r1.42.2.1.2.52 date_popup.module
--- date_popup/date_popup.module	13 Aug 2009 16:17:29 -0000	1.42.2.1.2.52
+++ date_popup/date_popup.module	15 Mar 2010 17:12:55 -0000
@@ -144,6 +144,7 @@
       '#tree' => TRUE,
       '#date_timezone' => date_default_timezone_name(),
       '#date_format' => variable_get('date_format_short', 'm/d/Y - H:i'),
+      '#date_flexible' => 0,
       '#date_increment' => 1,
       '#date_year_range' => '-3:+3',
       '#process' => array('date_popup_process'),
@@ -174,11 +175,11 @@
   
   if (!empty($edit) && is_array($edit) && !empty($edit['date'])) {
     $input = $edit['date'] . (!empty($edit['time']) ? ' '. $edit['time'] : '');
-    $datetime = date_convert_from_custom($input, $element['#date_format']);
-    $date = date_make_date($datetime, $element['#date_timezone'], DATE_DATETIME, $granularity);
+    $datetime = _date_text_input_value($input, $element['#date_format'], $element['#field']['type'], $element['#date_flexible']);
+    $date = date_make_date($datetime, $element['#date_timezone'], DATE_ISO, $granularity);
   }
   elseif (!empty($element['#value'])) {
-    $date = date_make_date($element['#value'], $element['#date_timezone'], DATE_DATETIME, $granularity);
+    $date = date_make_date($element['#value'], $element['#date_timezone'], ($element['#field']['type'] == DATE_ISO ? DATE_ISO : DATE_DATETIME), $granularity);
   }
   
   date_increment_round($date, $element['#date_increment']);
@@ -282,7 +283,7 @@
   $id = date_popup_js_settings_id($element['#id'], 'timeEntry', $settings);
   $sub_element = array(
     '#type' => 'textfield',
-    '#default_value' => (!empty($element['#value']['time']) || !empty($edit['time'])) && is_object($date) ? date_format($date, $time_format) : '',
+    '#default_value' => (!empty($element['#value']['time']) || !empty($edit['time'])) && is_object($date) ? date_format($date, is_array($date->granularity) ? date_limit_format($time_format, $date->granularity) : $time_format) : '',
     '#id' => $id,
     '#size' => 10,
     '#maxlength' => 10,
@@ -374,17 +375,10 @@
   // how they're combined in the complete date format.
   $time_format = date_limit_format($format, array('hour', 'minute', 'second'));
   $date_format = date_limit_format($format, array('year', 'month', 'day'));
-  $value = '';
   if (is_array($element['#value']) && !empty($element['#value']['date'])) {
-    $date = date_convert_from_custom(trim(!empty($element['#value']['date']) ? $element['#value']['date'] : ''), $date_format);
-    $time = date_convert_from_custom(trim(!empty($element['#value']['time']) ? $element['#value']['time'] : ''), $time_format);
-    $value = trim(substr($date, 0, 10) .' '. substr($time, 11, 8));
-  }
-  
-  if (date_is_valid($value, DATE_DATETIME, $granularity)) {
-    $date = date_make_date($value, $element['#date_timezone'], DATE_DATETIME, $granularity);
-    $value = date_convert($date, DATE_OBJECT, DATE_DATETIME);
-    return $value;
+    $value = trim($element['#value']['date'] .' '. $element['#value']['time']);
+    $useformat = trim($date_format .' '. $time_format);
+    return _date_text_input_value($value, $useformat, $element['#field']['type'] == DATE_ISO ? DATE_ISO : DATE_DATETIME, $element['#date_flexible']);
   }
   return NULL;
 }
