Index: components/time.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/components/time.inc,v
retrieving revision 1.24.2.3
diff -u -r1.24.2.3 time.inc
--- components/time.inc	16 Mar 2010 00:11:54 -0000	1.24.2.3
+++ components/time.inc	20 Mar 2010 01:37:18 -0000
@@ -6,6 +6,9 @@
  * Webform module time component.
  */
 
+// Time depends on functions provided by date.
+webform_component_include('date');
+
 /**
  * Implementation of _webform_defaults_component().
  */
@@ -54,7 +57,6 @@
     '#size' => 60,
     '#maxlength' => 127,
     '#weight' => 0,
-    '#element_validate' => array('webform_validate_time_string'),
   );
   $form['extra']['timezone'] = array(
     '#type' => 'radios',
@@ -137,7 +139,7 @@
   $hours[''] = t('hour');
   $minutes[''] = t('minute');
   for ($i = $first_hour; $i <= $last_hour; $i++) $hours[$i] = $i;
-  for ($i = 0; $i <= 59; $i++) $minutes[$i < 10 ? "0$i" : $i] = $i < 10 ? "0$i" : $i;
+  for ($i = 0; $i <= 59; $i++) $minutes[$i] = $i < 10 ? "0$i" : $i;
   $am_pms = array('am' => t('am'), 'pm' => t('pm'));
 
   $element = array(
@@ -149,6 +151,7 @@
     '#suffix' => '</div>',
     '#theme' => 'webform_time',
     '#element_validate' => array('webform_validate_time'),
+    '#hourformat' => $component['extra']['hourformat'],
     '#webform_component' => $component,
   );
 
@@ -172,19 +175,16 @@
     );
   }
 
-  if (isset($value)) {
+  if (isset($value[0]) && $value[0] !== '') {
+    $value = webform_date_array($value[0], 'time');
+    if ($component['extra']['hourformat'] == '12-hour') {
+      $value = webform_time_convert($value, '12-hour');
+    }
+
+    $element['hour']['#default_value'] = $value['hour'];
     $element['minute']['#default_value'] = $value['minute'];
-  
-    // Match the hourly data to the hour format.
-    if ($value['hour']) {
-      $timestamp = strtotime($value['hour'] . ':' . $value['minute'] . (isset($value['ampm']) ? $value['ampm'] : ''));
-      if ($component['extra']['hourformat'] == '24-hour') {
-        $element['hour']['#default_value'] = date('H', $timestamp);
-      }
-      else {
-        $element['hour']['#default_value'] = date('g', $timestamp);
-        $element['ampm']['#default_value'] = $value['ampm'];
-      }
+    if (isset($value['ampm'])) {
+      $element['ampm']['#default_value'] = $value['ampm'];
     }
   }
 
@@ -196,9 +196,9 @@
   $name = $element['#webform_component']['name'];
 
   // Check if the user filled the required fields.
-  foreach ($element['#webform_component'] == '12-hour' ? array('hour', 'minute', 'ampm') : array('hour', 'minute') as $field_type) {
+  foreach ($element['#hourformat'] == '12-hour' ? array('hour', 'minute', 'ampm') : array('hour', 'minute') as $field_type) {
     if (!is_numeric($element[$field_type]['#value']) && $element['#required']) {
-      form_set_error($form_key, t('%field field is required.', array('%field' => $name)));
+      form_set_error($element[$field_type], t('%field field is required.', array('%field' => $name)));
       return;
     }
   }
@@ -206,21 +206,33 @@
   // Check for a valid time.
   if ($element['hour']['#value'] !== '' || $element['minute']['#value'] !== '') {
     if (!is_numeric($element['hour']['#value']) || !is_numeric($element['minute']['#value']) || (isset($element['ampm']) && $element['ampm']['#value'] === '')) {
-      form_error($form_key, t('Entered %name is not a valid time.', array('%name' => $name)));
+      form_error($element, t('Entered %name is not a valid time.', array('%name' => $name)));
       return;
     }
   }
 }
 
 /**
+ * Implementation of _webform_submit_component().
+ */
+function _webform_submit_time($component, $value) {
+  // Convert to 24-hour time before string conversion.
+  if ($component['extra']['hourformat'] == '12-hour') {
+    $value = webform_time_convert($value, '24-hour');
+  }
+
+  // Convert the value into a ISO 8601 string.
+  return $value['hour'] !== '' ? webform_date_string($value, 'time') : '';
+}
+
+/**
  * Implementation of _webform_display_component().
  */
 function _webform_display_time($component, $value, $format = 'html') {
-  $value = array(
-    'hour' => isset($value['hour']) ? $value['hour'] : NULL,
-    'minute' => isset($value['minute']) ? $value['minute'] : NULL,
-    'ampm' => isset($value['ampm']) ? $value['ampm'] : NULL,
-  );
+  $value = webform_date_array(isset($value[0]) ? $value[0] : '', 'time');
+  if ($component['extra']['hourformat'] == '12-hour') {
+    $value = webform_time_convert($value, '12-hour');
+  }
 
   return array(
     '#title' => $component['name'],
@@ -242,10 +254,10 @@
   $output = ' ';
   if ($element['#value']['hour'] && $element['#value']['minute']) {
     if ($element['#hourformat'] == '24-hour') {
-      $output = $element['#value']['hour'] . ':' . $element['#value']['minute'];
+      $output = sprintf('%02d', $element['#value']['hour']) . ':' . sprintf('%02d', $element['#value']['minute']);
     }
     else {
-      $output = $element['#value']['hour'] . ':' . $element['#value']['minute'] . ' ' . $element['#value']['ampm'];
+      $output = $element['#value']['hour'] . ':' . sprintf('%02d', $element['#value']['minute']) . ' ' . $element['#value']['ampm'];
     }
   }
   return $output;
@@ -265,37 +277,17 @@
 
   $result = db_query($query, array_merge(array($component['nid'], $component['cid']), $sids));
 
-  // Build an array of timestamps from entered values.
-  $timestamps = array();
-  $submissions = 1;
+  $times = array();
+  $submissions = 0;
   while ($row = db_fetch_array($result)) {
-    if ($row['no'] == 'hour') {
-      $submissions++;
-      $hour = $row['data'];
-      if ($row = db_fetch_array($result)) {
-        if ($row['no'] == 'minute') {
-          $minute = $row['data'];
-          if ($row = db_fetch_array($result)) {
-            if ($row['no'] == 'ampm') {
-              $ampm = $row['data'];
-              // Build the full timestamp.
-              if (drupal_strlen($hour) > 0 && drupal_strlen($minute) > 0 ) {
-                $timestamps[] = strtotime($hour . ':' . $minute . ' ' . $ampm);
-              }
-            }
-            else {
-              // Build military time.
-              $timestamps[] = strtotime($hour . ':' . $minute);
-            }
-          }
-        }
-      }
+    $submissions++;
+    if ($row['data']) {
+      $times[] = webform_date_array($row['data']);
     }
   }
 
   // Display stats.
-  // TODO: display date statistics in javascript tabs.
-  $nonblanks = count($timestamps);
+  $nonblanks = count($times);
   $rows[0] = array(t('Left Blank'), ($submissions - $nonblanks));
   $rows[1] = array(t('User entered value'), $nonblanks);
   return $rows;
@@ -305,13 +297,14 @@
  * Implementation of _webform_table_component().
  */
 function _webform_table_time($component, $value) {
-  if (drupal_strlen($value['hour']) > 0 && drupal_strlen($value['minute']) > 0) {
-    $timestamp = strtotime($value['hour'] . ':' . $value['minute'] . (isset($value['ampm']) ? $value['ampm'] : ''));
+  if ($value[0]) {
+    $time = webform_date_array($value[0], 'time');
     if ($component['extra']['hourformat'] == '24-hour') {
-      return check_plain(date('H:i', $timestamp));
+      return sprintf('%02d', $time['hour']) . ':' . sprintf('%02d', $time['minute']);
     }
     else {
-      return check_plain(date('g:i a', $timestamp));
+      $time = webform_time_convert($time, '12-hour');
+      return $time['hour'] . ':' . sprintf('%02d', $time['minute']) . ' ' . $time['ampm'];
     }
   }
   else {
@@ -334,13 +327,14 @@
  * Implementation of _webform_csv_data_component().
  */
 function _webform_csv_data_time($component, $export_options, $value) {
-  if (drupal_strlen($value['hour']) > 0 && drupal_strlen($value['minute']) > 0) {
-    $timestamp = strtotime($value['hour'] . ':' . $value['minute'] . (isset($value['ampm']) ? $value['ampm'] : ''));
+  if ($value[0]) {
+    $time = webform_date_array($value[0], 'time');
     if ($component['extra']['hourformat'] == '24-hour') {
-      return date('H:i', $timestamp);
+      return sprintf('%02d', $time['hour']) . ':' . sprintf('%02d', $time['minute']);
     }
     else {
-      return date('g:i a', $timestamp);
+      $time = webform_time_convert($time, '12-hour');
+      return $time['hour'] . ':' . sprintf('%02d', $time['minute']) . ' ' . $time['ampm'];
     }
   }
   else {
@@ -357,3 +351,34 @@
 
   return theme('form_element', $element, $element['#children']);
 }
+
+/**
+ * Convert a time between a 24-hour and a 12-hour value.
+ *
+ * @param $array
+ *   An array of hour, minute, second, and optionally ampm.
+ * @param $format
+ *   Either 12-hour or 24-hour.
+ * @return
+ *   An array with hour, minute, second, and ampm (if using "12-hour").
+ */
+function webform_time_convert($array, $format) {
+  if ($array['hour'] !== '') {
+    if ($format == '12-hour') {
+      $array['ampm'] = ($array['hour'] >= 12 && $array['hour'] < 24) ? 'pm' : 'am';
+      $array['hour'] = ($array['hour'] > 12 || $array['hour'] == 0) ? abs($array['hour'] - 12) : (int) $array['hour'];
+    }
+    elseif ($format == '24-hour' && isset($array['ampm'])) {
+      $array['hour'] = ($array['hour'] < 12 && $array['ampm'] == 'pm') ? $array['hour'] + 12 : (int) $array['hour'];
+    }
+  }
+
+  if ($format == '12-hour' && !isset($array['ampm'])) {
+    $array['ampm'] = '';
+  }
+  elseif ($format == '24-hour' && isset($array['ampm'])) {
+    unset($array['ampm']);
+  }
+
+  return $array;
+}
Index: components/date.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/components/date.inc,v
retrieving revision 1.29.2.3
diff -u -r1.29.2.3 date.inc
--- components/date.inc	16 Mar 2010 00:11:54 -0000	1.29.2.3
+++ components/date.inc	20 Mar 2010 01:37:17 -0000
@@ -123,7 +123,8 @@
     '#element_validate' => array('webform_validate_date'),
   );
 
-  if (isset($value)) {
+  if (isset($value[0]) && $value[0] !== '') {
+    $value = webform_date_array($value[0], 'date');
     $element['#default_value'] = $value;
   }
 
@@ -255,20 +256,15 @@
  * Implementation of _webform_submit_component().
  */
 function _webform_submit_date($component, $value) {
-  // Webform stores dates in month/day/year rows.
-  // Ensure consistency when using international date formats.
-  return array('month' => $value['month'], 'day' => $value['day'], 'year' => $value['year']);
+  // Convert the date to an ISO 8601 format.
+  return ($value['year'] && $value['month'] && $value['day']) ? webform_date_string($value, 'date') : '';
 }
 
 /**
  * Implementation of _webform_display_component().
  */
 function _webform_display_date($component, $value, $format = 'html') {
-  $value = array(
-    'month' => isset($value['month']) ? $value['month'] : NULL,
-    'day' => isset($value['day']) ? $value['day'] : NULL,
-    'year' => isset($value['year']) ? $value['year'] : NULL,
-  );
+  $value = webform_date_array(isset($value[0]) ? $value[0] : '', 'date');
 
   return array(
     '#title' => $component['name'],
@@ -287,7 +283,7 @@
  */
 function theme_webform_display_date($element) {
   $output = ' ';
-  if ($element['#value']) {
+  if ($element['#value']['year'] && $element['#value']['month'] && $element['#value']['day']) {
     $timestamp = webform_strtotime($element['#value']['month'] . '/' . $element['#value']['day'] . '/' . $element['#value']['year']);
     $format = webform_date_format('medium');
     $output = format_date($timestamp, 'custom', $format, 0);
@@ -310,39 +306,17 @@
 
   $result = db_query($query, array_merge(array($component['nid'], $component['cid']), $sids));
 
-  // Build an array of timestamps from entered values.
-  $timestamps = array();
-  $submissions = 1;
+  $dates = array();
+  $submissions = 0;
   while ($row = db_fetch_array($result)) {
-    if ($row['no'] == '0') {
-      $submissions++;
-      $month = $row['data'];
-      if ($row = db_fetch_array($result)) {
-        if ($row['no'] == '1') {
-          $day = $row['data'];
-          if ($row = db_fetch_array($result)) {
-            if ($row['no'] == '2') {
-              $year = $row['data'];
-              // Build the full timestamp.
-              if (drupal_strlen($month) > 0  && drupal_strlen($day) > 0  && drupal_strlen($year) > 0) {
-                $timestamp = webform_strtotime($month . '/' . $day . '/' . $year);
-                // Add usefull information about this date into an array.
-                $timestamps[$timestamp] = array(
-                  date('l', $timestamp), // Day of the week (Monday, Tuesday, etc.).
-                  date('F', $timestamp), // Full Month name (January, February, etc.).
-                  $year, // Year.
-                  $day,  // Day of the month (1,2,...,31).
-                );
-              }
-            }
-          }
-        }
-      }
+    $submissions++;
+    if ($row['data']) {
+      $dates[] = webform_date_array($row['data']);
     }
   }
 
   // Display stats.
-  $nonblanks = count($timestamps);
+  $nonblanks = count($dates);
   $rows[0] = array(t('Left Blank'), ($submissions - $nonblanks));
   $rows[1] = array(t('User entered value'), $nonblanks);
   return $rows;
@@ -352,8 +326,8 @@
  * Implementation of _webform_table_component().
  */
 function _webform_table_date($component, $value) {
-  if (drupal_strlen($value['month']) > 0 && drupal_strlen($value['day']) > 0 && drupal_strlen($value['year']) > 0) {
-    $timestamp = webform_strtotime($value['month'] . '/' . $value['day'] . '/' . $value['year']);
+  if ($value[0]) {
+    $timestamp = webform_strtotime($value[0]);
     $format = webform_date_format('short');
     return format_date($timestamp, 'custom', $format, 0);
   }
@@ -377,8 +351,8 @@
  * Implementation of _webform_csv_data_component().
  */
 function _webform_csv_data_date($component, $export_options, $value) {
-  if (drupal_strlen($value['month']) > 0 && drupal_strlen($value['day']) > 0 && drupal_strlen($value['year']) > 0) {
-    $timestamp = webform_strtotime($value['month'] . '/' . $value['day'] . '/' . $value['year']);
+  if ($value[0]) {
+    $timestamp = webform_strtotime($value[0]);
     $format = webform_date_format('short');
     return format_date($timestamp, 'custom', $format, 0);
   }
@@ -388,14 +362,88 @@
 }
 
 /**
- * Get a timestamp in GMT time, ensuring timezone accuracy.
+ * Convert an ISO 8601 date or time into an array.
+ *
+ * This converst full format dates or times. Either a date or time may be
+ * provided, in which case only those portions will be returned. Dashes and
+ * colons must be used, never implied.
+ *
+ * Formats:
+ * Dates: YYYY-MM-DD
+ * Times: HH:MM:SS
+ * Datetimes: YYYY-MM-DDTHH:MM:SS
+ *
+ * @param $string
+ *   An ISO 8601 date, time, or datetime.
+ * @param $type
+ *   If wanting only specific fields back, specify either "date" or "time".
+ *   Leaving empty will return an array with both date and time keys, even if
+ *   some are empty. Returns an array with the following keys:
+ *   - year
+ *   - month
+ *   - day
+ *   - hour (in 24hr notation)
+ *   - minute
+ *   - second
+ */
+function webform_date_array($string, $type = NULL) {
+  $pattern = '/((\d{4}?)-(\d{2}?)-(\d{2}?))?(T?(\d{2}?):(\d{2}?):(\d{2}?))?/';
+  $matches = array();
+  preg_match($pattern, $string, $matches);
+  $matches += array_fill(0, 9, '');
+
+  $return = array();
+
+  // Check for a date string.
+  if ($type == 'date' || !isset($type)) {
+    $return['year'] = $matches[2] !== '' ? (int) $matches[2] : '';
+    $return['month'] = $matches[3] !== '' ? (int) $matches[3] : '';
+    $return['day'] = $matches[4] !== '' ? (int) $matches[4] : '';
+  }
+
+  // Check for a time string.
+  if ($type == 'time' || !isset($type)) {
+    $return['hour'] = $matches[6] !== '' ? (int) $matches[6] : '';
+    $return['minute'] = $matches[7] !== '' ? (int) $matches[7] : '';
+    $return['second'] = $matches[8] !== '' ? (int) $matches[8] : '';
+  }
+
+  return $return;
+}
+
+/**
+ * Convert an array of a date or time into an ISO 8601 compatible string.
+ *
+ * @param $array
+ *   The array to convert to a date or time string.
+ * @param $type
+ *   If wanting a specific string format back specify either "date" or "time".
+ *   Otherwise a full ISO 8601 date and time string will be returned.
  */
-function webform_strtotime($date) {
-  $current_tz = date_default_timezone_get();
-  date_default_timezone_set('UTC');
-  $timestamp = strtotime($date);
-  date_default_timezone_set($current_tz);
-  return $timestamp;
+function webform_date_string($array, $type = NULL) {
+  $string = '';
+
+  if ($type == 'date' || !isset($type)) {
+    $string .= empty($array['year']) ? '0000' : sprintf('%04d', $array['year']);
+    $string .= '-';
+    $string .= empty($array['month']) ? '00' : sprintf('%02d', $array['month']);
+    $string .= '-';
+    $string .= empty($array['day']) ? '00' : sprintf('%02d', $array['day']);
+  }
+
+  if (!isset($type)) {
+    $string .= 'T';
+  }
+
+  if ($type == 'time' || !isset($type)) {
+    $string .= empty($array['hour']) ? '00' :  sprintf('%02d', $array['hour']);
+    $string .= ':';
+    $string .= empty($array['minute']) ? '00' :  sprintf('%02d', $array['minute']);
+    $string .= ':';
+    $string .= empty($array['second']) ? '00' :  sprintf('%02d', $array['second']);
+  }
+
+  return $string;
 }
 
 /**
@@ -420,3 +468,14 @@
 
     return $date_format;
 }
+
+/**
+ * Get a timestamp in GMT time, ensuring timezone accuracy.
+ */
+function webform_strtotime($date) {
+  $current_tz = date_default_timezone_get();
+  date_default_timezone_set('UTC');
+  $timestamp = strtotime($date);
+  date_default_timezone_set($current_tz);
+  return $timestamp;
+}
Index: webform.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/webform.install,v
retrieving revision 1.40.2.4
diff -u -r1.40.2.4 webform.install
--- webform.install	19 Mar 2010 02:41:47 -0000	1.40.2.4
+++ webform.install	20 Mar 2010 01:37:17 -0000
@@ -1093,6 +1093,52 @@
 }
 
 /**
+ * Convert Dates and Times into using ISO 8601 strings instead of 3 rows.
+ */
+function webform_update_6314() {
+  $ret = array();
+
+  webform_component_include('date');
+  webform_component_include('time');
+
+  $result = db_query("SELECT c.type, c.extra, d.* FROM {webform_component} c INNER JOIN {webform_submitted_data} d ON c.nid = d.nid AND c.cid = d.cid WHERE c.type = 'time' OR c.type = 'date' ORDER BY d.sid, d.cid");
+  $current_cid = NULL;
+  $current_sid = NULL;
+  while ($row = db_fetch_object($result)) {
+    if ($current_cid != $row->cid || $current_sid != $row->sid) {
+      $current_cid = $row->cid;
+      $current_sid = $row->sid;
+      $extra = unserialize($row->extra);
+      $value = array();
+    }
+
+    $value[$row->no] = $row->data;
+
+    // Update a complete date.
+    if ($row->type == 'date' && array_key_exists('day', $value) && array_key_exists('month', $value) && array_key_exists('year', $value)) {
+      $value = ($value['day'] && $value['month'] && $value['year']) ? webform_date_string($value, 'date') : '';
+      db_query("UPDATE {webform_submitted_data} SET no = '0', data = '%s' WHERE sid = %d AND cid = %d AND no = 'day'", $value, $current_sid, $current_cid);
+    }
+
+    // Update a complete time.
+    if ($row->type == 'time' && array_key_exists('hour', $value) && array_key_exists('minute', $value) && ($extra['hourformat'] == '24-hour' || array_key_exists('ampm', $value))) {
+      if ($extra['hourformat'] == '12-hour') {
+        $value = webform_time_convert($value, '24-hour');
+      }
+      $value = ($value['hour']) ? webform_date_string($value, 'time') : '';
+      db_query("UPDATE {webform_submitted_data} SET no = '0', data = '%s' WHERE sid = %d AND cid = %d AND no = 'hour'", $value, $current_sid, $current_cid);
+    }
+  }
+
+  // Remove all extra rows (which should just be day, minute, and ampm values).
+  db_query("DELETE FROM {webform_submitted_data} WHERE no IN ('day', 'month', 'year', 'hour', 'minute', 'second', 'ampm')");
+
+  $ret[] = array('success' => TRUE, 'query' => t('Updated date and time components to use ISO 8601 formatted strings in the submitted values.'));
+
+  return $ret;
+}
+
+/**
  * Recursively delete all files and folders in the specified filepath, then
  * delete the containing folder.
  *
