Index: includes/date.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/signup/includes/date.inc,v
retrieving revision 1.13
diff -u -p -r1.13 date.inc
--- includes/date.inc	22 Nov 2008 01:44:19 -0000	1.13
+++ includes/date.inc	28 Nov 2008 22:40:26 -0000
@@ -12,10 +12,17 @@
  * @return Array of SQL clauses for admin overview page query builder.
  */
 function _signup_date_admin_sql($content_type) {
+  // Get the date field information for this content type.
   $field = signup_date_field($content_type);
+  // See what fields to SELECT.
+  $fields[] = $field['database']['columns']['value']['column'];
+  if (isset($field['database']['columns']['timezone']['column'])) {
+    $fields[] = $field['database']['columns']['timezone']['column'];
+  }
+  $table = '{'. $field['database']['table'] .'}';
   return array(
-    'fields' => array($field['database']['columns']['value']['column']),
-    'joins' => array('LEFT JOIN {'. $field['database']['table'] .'} ON {'. $field['database']['table'] .'}.vid = n.vid'),
+    'fields' => $fields,
+    'joins' => array("LEFT JOIN $table ON $table.vid = n.vid"),
   );
 }
 
@@ -279,29 +286,45 @@ function _signup_get_date_field_options(
  * @return Array of SQL clauses for cron reminder email query builder.
  */
 function _signup_date_reminder_sql($content_type) {
+  // Get the date field information for this content type.
   $field = signup_date_field($content_type);
-  global $db_type;
-  include_once drupal_get_path('module', 'date_api') .'/date_api_sql.inc';
-  $handler = new date_sql_handler();
-  switch ($db_type) {
-    case 'mysql':
-    case 'mysqli':
-      $where = array(
-        "NOW() + INTERVAL s.reminder_days_before DAY > ". $handler->sql_extract('DATE', $field['database']['columns']['value']['column']),
-        "NOW() <= ". $handler->sql_extract('DATE', $field['database']['columns']['value']['column']),
-      );
-      break;
+  $start_field = $field['database']['columns']['value']['column'];
 
-    case 'pgsql':
-      $where = array(
-        "NOW() + INTERVAL s.reminder_days_before days > ". $handler->sql_extract('DATE', $field['database']['columns']['value']['column']),
-        "NOW() <= ". $handler->sql_extract('DATE', $field['database']['columns']['value']['column']),
-      );
-      break;
+  // Figure out what TZ we want to do the date comparisons in.
+  $compare_tz = $field['tz_handling'] == 'none' ? date_default_timezone_name() : 'UTC';
+  // Get a DateAPI SQL handler class for this field.
+  $handler = _signup_date_sql_handler($field, $compare_tz);
+
+  // Find the current time in the appropriate TZ for this field.
+  $now_date = date_now($compare_tz);
+  // Need to enclose this in ' marks to use directly in the SQL.
+  $now = "'". date_format($now_date, DATE_FORMAT_DATETIME) ."'";
+
+  // Extract the correct SQL to represent the start time.
+  $start_time = $handler->sql_field($start_field);
+
+  // Create SQL to represent the time we should start sending reminders, based
+  // on the SQL for the start time and the reminder_days_before field.
+  $reminder_start = _signup_date_sql_math($start_time, 'SUB', 's.reminder_days_before', 'DAY');
+  $reminder_stop = _signup_date_sql_math($start_time, 'ADD', 1, 'HOUR');
+
+  // The WHERE clauses are now trivial: We want to make sure a) the current
+  // time is after the time we should start sending reminders, but before the
+  // actual start time itself.
+  $where = array(
+    "$now >= $reminder_start",
+    "$now <= $reminder_stop",
+  );
+
+  // See what fields to SELECT.
+  $fields[] = $start_field;
+  if (isset($field['database']['columns']['timezone']['column'])) {
+    $fields[] = $field['database']['columns']['timezone']['column'];
   }
+  $table = '{'. $field['database']['table'] .'}';
   return array(
-    'fields' => array($field['database']['columns']['value']['column']),
-    'joins' => array('LEFT JOIN {'. $field['database']['table'] .'} ON {'. $field['database']['table'] .'}.vid = n.vid'),
+    'fields' => $fields,
+    'joins' => array("LEFT JOIN $table ON $table.vid = n.vid"),
     'where' => $where,
   );
 }
@@ -311,27 +334,144 @@ function _signup_date_reminder_sql($cont
  * @return Array of SQL clauses for cron auto-close query builder.
  */
 function _signup_date_autoclose_sql($content_type) {
+  // Get the date field information for this content type.
   $field = signup_date_field($content_type);
-  module_load_include('inc', 'date_api', 'date_api_sql');
-  $handler = new date_sql_handler();
+  $start_field = $field['database']['columns']['value']['column'];
+
+  // Figure out what TZ we want to do the date comparisons in.
+  $compare_tz = $field['tz_handling'] == 'none' ? date_default_timezone_name() : 'UTC';
+  // Get a DateAPI SQL handler class for this field.
+  $handler = _signup_date_sql_handler($field, $compare_tz);
+
+  // Compute a string representing the moment when signups should start
+  // auto-closing.  If the field has no TZ handling, we just want to grab the
+  // current local time.  If the field has any TZ handling, the date will be
+  // stored in the DB in UTC time, so start from current UTC time.  Once we
+  // have the right current time, we need to add our close-in-advance offset.
+  $close_early_hours = variable_get('signup_close_early', 1);
+  $close_date = date_now($compare_tz);
+  date_modify($close_date, "+$close_early_hours hours");
+  $close_date_str = date_format($close_date, DATE_FORMAT_DATETIME);
+
+  // Use the DateAPI SQL handler to construct an appropriate WHERE clause.
+  // Make sure that the start time is <= NOW plus the auto-close window.
+  $where = $handler->sql_where_date('DATE', $start_field, '<=', $close_date_str);
+
+  // See what fields to SELECT.
+  $fields[] = $start_field;
+  if (isset($field['database']['columns']['timezone']['column'])) {
+    $fields[] = $field['database']['columns']['timezone']['column'];
+  }
+  $table = '{'. $field['database']['table'] .'}';
   return array(
-    'fields' => array($field['database']['columns']['value']['column']),
-    'joins' => array(
-      'LEFT JOIN {'. $field['database']['table'] .'} ON {'. $field['database']['table'] .'}.vid = n.vid',
-    ),
-    'where' => array($handler->sql_extract('DATE', $field['database']['columns']['value']['column']) ." < '". date('Y-m-d\TH:i:s', time() + variable_get('signup_close_early', 1) * 3600) ."'"),
+    'fields' => $fields,
+    'joins' => array("LEFT JOIN $table ON $table.vid = n.vid"),
+    'where' => $where,
   );
 }
 
 /**
+ * Generate a DateAPI SQL handler for the given CCK date field.
+ *
+ * This can be removed once revision 1.61.2.4.2.32 is widely available in an
+ * official release.
+ */
+function _signup_date_sql_handler($field, $compare_tz = NULL) {
+  module_load_include('inc', 'date_api', 'date_api_sql');
+
+  // Create a DateAPI SQL handler class for this field type.
+  $handler = new date_sql_handler();
+  $handler->construct($field['type']);
+
+  // If this date field stores a timezone in the DB, tell the handler about it.
+  if ($field['tz_handling'] == 'date') {
+    // The field has a date column
+    $handler->db_timezone_field = $field['database']['columns']['timezone']['column'];
+  }
+  else {
+    $handler->db_timezone = date_get_timezone_db($field);
+  }
+
+  if (empty($compare_tz)) {
+    $compare_tz = date_get_timezone($field);
+  }
+  $handler->local_timezone = $compare_tz;
+
+  // Now that the handler is properly initialized, tell the DB what TZ to use.
+  $handler->set_db_timezone();
+
+  return $handler;
+}
+
+/**
+ * Helper function to handle date math across DB types.
+ *
+ * This can be removed once date_api_sql.inc revision 1.9.2.3.2.28 is widely
+ * available in an official release.
+ *
+ * @param $field
+ *   The field to be adjusted.
+ * @param $direction
+ *   Either ADD or SUB.
+ * @param $count
+ *   The number of values to adjust.
+ * @param $granularity
+ *   The granularity of the adjustment, should be singular,
+ *   like SECOND, MINUTE, DAY, HOUR.
+ */
+function _signup_date_sql_math($field, $direction, $count, $granularity) {
+  $granularity = strtoupper($granularity);
+  switch ($GLOBALS['db_type']) {
+    case 'mysql':
+    case 'mysqli':
+      if ($direction == 'ADD') {
+        return "DATE_ADD($field, INTERVAL $count $granularity)";
+      }
+      else {
+        return "DATE_SUB($field, INTERVAL $count $granularity)";
+      }
+      
+    case 'pgsql':
+      $granularity .= 'S';
+      if ($direction == 'ADD') {
+        return "($field + INTERVAL '$count $granularity')";
+      }
+      else {
+        return "($field - INTERVAL '$count $granularity')";
+      }
+  }
+  return $field;
+}
+
+/**
  * Returns TRUE if the given node is event-enabled, and the start time
  * has already passed the "Close x hours before" setting.
  */
 function _signup_date_node_completed($node) {
   $field = signup_date_field($node->type);
   if ($field && $field != 'none' && isset($node->{$field['field_name']})) {
-    $closing_time = time() + (variable_get('signup_close_early', 1) * 3600);
-    if (strtotime($node->{$field['field_name']}[0]['value']) < $closing_time) {
+    // Grab whatever date value we actually have, regardless of format.
+    $date_value = $node->{$field['field_name']}[0]['value'];
+    // Figure out the timezone handling for this date.
+    if ($field['tz_handling'] == 'date') {
+      $tz = $node->{$field['field_name']}[0]['timezone'];
+    }
+    else {
+      $tz = date_default_timezone_name();
+    }
+    $db_tz = date_get_timezone_db($field['tz_handling'], $tz);
+    // Create a date object
+    $date = date_make_date($date_value, $db_tz, $field['type']);
+    // Make sure the date object is going to print UTC values.
+    date_timezone_set($date, timezone_open('UTC'));
+    // Find out how early signups should be automatically closed.
+    $close_early_hours = variable_get('signup_close_early', 1);
+    date_modify($date, "-$close_early_hours hours");
+    $close_time = date_format($date, 'U');
+    // Find the current UTC time.
+    $now = date_now('UTC');
+    if (date_format($now, 'U') >= $close_time) {
+      // It's now later than when this node would automatically close signups.
       return TRUE;
     }
   }
@@ -344,7 +484,12 @@ function _signup_date_format_date($node,
     return '';
   }
   if ($field['tz_handling'] == 'date') {
-    $tz = $node->{$field['field_name']}[0]['timezone'];
+    if (isset($node->{$field['field_name']})) {
+      $tz = $node->{$field['field_name']}[0]['timezone'];
+    }
+    else {
+      $tz = $node->{$field['database']['columns']['timezone']['column']};
+    }
   }
   else {
     $tz = date_default_timezone_name();
