[development] Patch for fixing date support in 4.7b4

Sammy Spets sammys-drupal at synerger.com
Thu Feb 2 07:03:38 UTC 2006


Hi ya'll,

Here is my first patch submission of this series. Hope you like it.

Problems solved:

* Date field data is now returned to the integer state before code 
reaches the submit stage (changed actually by the validation stage). The 
change is transparent to modules expecting the integer value to be in 
$edit.

* Dates were creeping backwards by a day each time a form was submitted. 
This is now fixed. Problem was with expand_date() using the timestamps 
if user timestamps are enabled. Problem was only evident when using 
positive timezone offsets. See discussion at the end of this mail for 
more details.

* Date field data is now automatically validated for correctness as a 
date. I.e whether the silly user entered in 31/Feb/2001. An error 
message is added with form_set_error().

* Select fields didn't flag errors found in their data. I.e the class
attribute of select fields was not set by theme_select.

* Date fields didn't display errors as the error cases didn't cascade 
into the child elements in the form array structures.

Lemme know if there is something wrong with this stuff.

Discussion
----------

IMO date fields can be classified into two different requirements. First 
is the kind of date which is the same regardless of your timezone. An 
example of this is a birthday. Second, the kind of date that changes 
depending on location such as the submission time of a comment.

Dates must to be placed into one of these categories before timezone 
support is feasible. The first category, dates without timezone, can be 
displayed by the current date implementation. Unfortunately, the second 
cannot and must be linked with the time part of the timestamp 
_at_all_times_. For this reason I proposed a time and datetime time to 
be placed in core.

I'm going to implement the two types and submit a patch. The datetime 
field will support timezones and the time field will not for the 
reasons above.

As for timezone support overall... there are two snags i ran into while 
doing some testing. 1) format_date() will break on Windows because it 
uses gmdate/date. 2) format_date() is broken anyway in the way it 
calculates the new timestamp. I.e the timestamp must be converted to 
UTC before adding the timezone to it. Here is an example:

say system timezone is +1100 (UNIX timestamp) and user timezone is -0300

timestamp = 2006-02-01 00:00:00
timestamp += timezone // == 2006-01-31 21:00  **wrong**

timestamp = 2006-02-01 00:00:00
timestamp -= system_timezone // == 2006-01-31 13:00
timestamp += timezone // == 2006-01-31 10:00 **right!**

I haven't fixed this yet as I need more information from developers and 
decision makers involved with drupal. I have the solution to this in my 
head. The decision to be made is whether the database stores localtime 
or UTC. localtime is better because it makes it possible to query the 
database without doing conversions from UTC. UTC is better because it 
avoids the common time hurdle with drupal - timezone support sucks.

Many of the developers on this list have responded to me with 
workarounds for various things. Thank you for those. Additionally, i've 
received something like, "we don't have enough time to put this stuff 
into drupal before releasing 4.7". The efforts you've all put in has 
been astounding and by far 4.7 is a major leap forward for drupal. 
Unfortunately, the celebration of 4.7 is marred for me, and no doubt 
many others, because fundamental things like date support are still 
broken since the days of 4.4. How can you even suggest releasing a 
product out to market that fails to meet the minimum standard of 
operability and call it a release? Would an OS be released if dates were 
broken? Even MS won't do that anymore! Drupal is like an OS and the 
applications are the modules. Don't forget that!

<take in a deep breath>

All better now. I'm off to add these types to the system. Happy 
patching!

-- 
Sammy Spets
Synerger Pty Ltd
http://www.synerger.com/

-------------- next part --------------
? includes/.common.inc.swp
? includes/fields.inc
? modules/calendar.module
? modules/committee.module
? modules/executive.module
? modules/member.module
? modules/organiser.module
? modules/payment.module
? modules/pes.module
? modules/quote.module
? modules/registration.module
? scripts/excludefiles
? scripts/maketags.sh
? sites/uwc.synerger.com
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.512
diff -u -p -r1.512 common.inc
--- includes/common.inc	29 Jan 2006 07:36:29 -0000	1.512
+++ includes/common.inc	2 Feb 2006 06:08:09 -0000
@@ -844,10 +844,10 @@ function format_date($timestamp, $type =
   for ($i = 0; $i < $max; $i++) {
     $c = $format[$i];
     if (strpos('AaDFlM', $c) !== false) {
-      $date .= t(gmdate($c, $timestamp));
+      $date .= t(date($c, $timestamp));
     }
     else if (strpos('BdgGhHiIjLmnsStTUwWYyz', $c) !== false) {
-      $date .= gmdate($c, $timestamp);
+      $date .= date($c, $timestamp);
     }
     else if ($c == 'r') {
       $date .= format_date($timestamp - $timezone, 'custom', 'D, d M Y H:i:s O', $timezone);
Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.61
diff -u -p -r1.61 form.inc
--- includes/form.inc	27 Jan 2006 15:57:43 -0000	1.61
+++ includes/form.inc	2 Feb 2006 06:08:11 -0000
@@ -482,7 +482,9 @@ function form_options_flatten($array, $r
 function theme_select($element) {
   $select = '';
   $size = $element['#size'] ? ' size="' . $element['#size'] . '"' : '';
-  return theme('form_element', $element['#title'], '<select name="'. $element['#name'] .''. ($element['#multiple'] ? '[]' : '') .'"'. ($element['#multiple'] ? ' multiple="multiple" ' : '') . drupal_attributes($element['#attributes']) .' id="' . $element['#id'] .'" '. $size .'>'. form_select_options($element) .'</select>', $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
+  $class = _form_get_class('form-select', $element['#required'], form_get_error($element));
+
+  return theme('form_element', $element['#title'], '<select name="'. $element['#name'] .''. ($element['#multiple'] ? '[]' : '') .'" class="' . $class .'"'. ($element['#multiple'] ? ' multiple="multiple" ' : '') . drupal_attributes($element['#attributes']) .' id="' . $element['#id'] .'" '. $size .'>'. form_select_options($element) .'</select>', $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
 }
 
 function form_select_options($element, $choices = NULL) {
@@ -630,13 +632,20 @@ function theme_date($element) {
  * Roll out a single date element.
  */
 function expand_date($element) {
-  // Default to current date
-  if (!isset($element['#value'])) {
-    $element['#value'] = array('day' => format_date(time(), 'custom', 'j'),
-                            'month' => format_date(time(), 'custom', 'n'),
-                            'year' => format_date(time(), 'custom', 'Y'));
+  // Default to current date or if the value is a timestamp set value accordingly
+  if (!is_array($element['#value'])) {
+    $t = (isset($element['#value']) && is_numeric($element['#value'])
+              ? $element['#value'] : time());
+    
+    // can't use a non-zero timezone here because date fields don't store time
+    // data to correctly deal with timezones. as a result the date fields will
+    // creep backwards by one day each time form is submitted when the timezone
+    // is positive (i.e east of UTC) <sammys>
+    $element['#value'] = array('day' => format_date($t, 'custom', 'j', 0),
+                            'month' => format_date($t, 'custom', 'n', 0),
+                            'year' => format_date($t, 'custom', 'Y', 0));
   }
-
+  
   $element['#tree'] = TRUE;
 
   // Determine the order of day, month, year in the site's chosen date format.
@@ -648,6 +657,7 @@ function expand_date($element) {
   asort($sort);
   $order = array_keys($sort);
 
+  $error = form_get_error($element);
   // Output multi-selector for date
   foreach ($order as $type) {
     switch ($type) {
@@ -674,6 +684,30 @@ function expand_date($element) {
   return $element;
 }
 
+function validate_date($elements, $args)
+{
+  global $form_values;
+
+  $fld = $elements['#parents'][0];
+  $f =& $elements['#value'];
+  
+  if (!is_array($f) || !checkdate($f['month'], $f['day'], $f['year']))
+    form_set_error($fld, 'Invalid date');
+}
+
+function shrink_date($elements)
+{
+  global $form_values, $user;
+
+  $fld = $elements['#parents'][0];
+
+  $f =& $form_values[$fld];
+  $val = strtotime("{$f['year']}-{$f['month']}-{$f['day']}");
+  $form_values[$fld] = $val;
+
+  return $elements;
+}
+
 /**
  * Helper function for usage with drupal_map_assoc to display month names.
  */
Index: modules/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system.module,v
retrieving revision 1.286
diff -u -p -r1.286 system.module
--- modules/system.module	1 Feb 2006 15:34:55 -0000	1.286
+++ modules/system.module	2 Feb 2006 06:08:48 -0000
@@ -76,7 +76,7 @@ function system_elements() {
   $type['checkboxes'] = array('#input' => TRUE, '#process' => array('expand_checkboxes' => array()), '#tree' => TRUE);
   $type['select'] = array('#input' => TRUE);
   $type['weight'] = array('#input' => TRUE, '#delta' => 10, '#default_value' => 0);
-  $type['date'] = array('#input' => TRUE, '#process' => array('expand_date' => array()));
+  $type['date'] = array('#input' => TRUE, '#process' => array('expand_date' => array()), '#after_build' => 'shrink_date', '#validate' => array('validate_date' => ''));
   $type['file'] = array('#input' => TRUE, '#size' => 60);
 
   // Form structure


More information about the development mailing list