<?php
// $Id: date.module,v 1.13.2.13 2006/11/13 13:47:32 karens Exp $
/**
 * @file
 * Defines a date/time field type.
 */
// moved views functions into their own file for better readability
if (module_exist('views')) {
  include_once(drupal_get_path('module', 'date') .'/date_views.inc');
}

/**
 * Implementation of hook_help().
 */
function date_help($section) {
  switch ($section) {
    case 'admin/modules#description':
      return t('<strong>CCK:</strong> Defines a date/time field type for the content module and a date API. <em>Note: Requires content.module.</em>');
      break;
    case 'admin/help#date':
        $output = t('<h2>Date Module</h2>
          <p>The date module defines a highly configurable date/time field type for the content module and a date API that can be used by other applications. 
          Dates can use GMT, the site\'s timezone or a date-specific timezone and are converted to GMT and stored as either a Date (ISO 8601 YYYY-MM-DDTHH:MM:SS) or a Datestamp (a unix timestamp). When displayed, they are converted back to the desired timezone value and displayed using the desired display format. 
          Display formats include numerous combinations of date parts, sequences, and separators, to accomodate international date formatting needs.
          </p>
          <h3>Date Field Types</h3>
          <p>You have a choice of creating an ISO date or a unix timestamp. If you are porting information from another application you may want to create a field using a type that matches the source data. Some advantages and disadvantages of each include:</p>
          <dl>
          <dt>Unix Timestamp</dt>
          <dd>Stores the date as an integer.</dd>
          <dd>Takes up less room in the database because it\'s smaller.</dd>
          <dd>Often easier to use for date calculations because you can increase or decrease it just by adding or subtracting seconds.</dd>
          <dd>It is the format used by php date functions.</dd>
          <dd>It must be filled with a complete date -- year, month, day, hour, minute, second, so you sometimes have to arbitrarily set some of these values even if they are not applicable.</dd>
          <dt>ISO Date</dt>
          <dd>Stores the date in an ISO format (YYYY-MM-DDTHH:MM:SS).</dd>
          <dd>The data is in a human-readable form.</dd>
          <dd>You can use it for incomplete dates, i.e. only a year or only a year and a month, and pad the other values with zeros, so it does not appear to be any more precise than it really is.</dd>
          <dd>It is a format that is internationally-recognized, and it is used as-is on many web sites and in many applications.</dd>
          </dl>
          <h3>Date Widgets</h3>
          <p>There are several widgets to choose from to control how users can enter data for this field.</p>
          <dl>
          <dt>Text Field</dt><dd>The Text Field date widget uses the strtotime function to construct a date and will accept input allowed by the php <a href="http://www.php.net/manual/en/function.strtotime.php">strtotime function</a>. This allows the user to type a date in in many natural formats, like <strong>March 31, 1980</strong> or <strong>3/10/1980</strong> or in numeric formats like <strong>YYYY-MM-DD</strong>. 
          The strtotime function will assume date shortcuts are in American format (MM/DD/YY), and it will not work for dates prior to 1970. 
          </dd>
          <dt>Select List</dt><dd>The Select List date widget presents users with a drop-down list or textfield for each part of the date (year, month, day, hour, etc.). The whole selector is collapsed onto a single line using css (except for the timezone selector, if date-specific timezones are collected). The selector is highly configurable and will even handle years prior to 1900.</dd>
          <dt>Javascript Pop-up Calendar</dt><dd>The Javascript Pop-up Calendar is offered as an input alternative if the <a href="http://drupal.org/node/57285">jscalendar</a> module is installed. This widget can handle dates as early as 1900.
          </dl>');
        if (module_exist('views')) {
          $output .= date_views_help();
        }
        return $output;
      break;
  }
}

/**
 * Implementation of hook_field_info().
 */
function date_field_info() {
  return array(
    'date' => array('label' => 'Date'),
    'datestamp' => array('label' => 'Datestamp'),
  );
}

/**
 * Implementation of hook_field_settings().
 */
function date_field_settings($op, $field) {

  include_once(drupal_get_path('module', 'date') .'/date.inc');

  switch ($op) {
  case 'form':
      if (!module_exist('event')) {
        include_once(drupal_get_path('module', 'date') .'/date_timezones.inc');
      } else {
        include_once(drupal_get_path('module', 'event') .'/event_timezones.inc');
      }
      $form = array();
      $tz_handling = $field['tz_handling'] ? $field['tz_handling'] : 'gmt';
      $form['input'] = array(
        '#type' => 'fieldset',
        '#title' => t('Input options'),
      );
      $options = array(
        'Y' => t('Year'),
        'M' => t('Month'),
        'D' => t('Day'),
        'H' => t('Hour'),
        'N' => t('Minute'),
        'S' => t('Second'),
        //'T' => t('Timezone'),
        );
      $form['input']['granularity'] = array(
        '#type' => 'select',
        '#title' => t('Granularity'),
        '#default_value' => $field['granularity'] ? $field['granularity'] : array('Y', 'M', 'D'),
        '#options' => $options,
        '#multiple' => 1,
        '#description' => t('Set the date elements to be collected on input (at least a year is required).'),
        );
      $form['input']['advanced'] = array(
        '#type' => 'fieldset',
        '#title' => t('Advanced input options'),
        '#collapsible' => TRUE,
        '#collapsed' => TRUE,
        );
      $form['input']['advanced']['input_format'] = array(
        '#type' => 'select',
        '#title' => t('Input format'),
        '#default_value' => $field['input_format'] ? $field['input_format'] : 'site_format',
        '#options' => date_input_options(),
        '#description' => t('The specified format will be adapted to the chosen <b>granularity</b>. The \'default\' value uses the \'short date format\' defined in %settings', array('%settings' => l("admin/settings", "admin/settings"))),
      );
      $form['input']['advanced']['input_format_custom'] = array(
        '#type' => 'textfield',
        '#title' => t('Custom input format'),
        '#default_value' => $field['input_format_custom'] ? $field['input_format_custom'] : '',
        '#description' => t('The custom format, if provided, will override the input format selected above. See more about custom date formats below.'),
      );
      $form['output'] = array(
        '#type' => 'fieldset',
        '#title' => t('Display Options'),
        '#description' => t('Choose the way the date should be displayed.'),
        );
      
      $default_formatter = date_formatter_setup_form($field, 0);
      $form['output'][] = $default_formatter;

      $form['output']['advanced'] = array(
        '#type' => 'fieldset',
        '#title' => t('Advanced display options'),
        '#collapsible' => TRUE,
        '#collapsed' => TRUE,
        '#description' => t('Define alternate formatting for the date display. Options other than the default are made available to views and themes. Possible formats are default, long, medium, and short.'),
      );

      for ($i = 1; $i <= 3; $i++) {
        $form['output']['advanced'][$i] = date_formatter_setup_form($field, $i);
      }
      
      $form['timezone'] = array(
        '#type' => 'fieldset',
        '#title' => t('Timezone options'),
      );
      $form['timezone']['tz_handling'] = array(
        '#type' => 'select',
        '#title' => t('Time zone handling'),
        '#default_value' => $tz_handling,
        '#options' => date_timezone_handling_options(),
        '#description' => t('Select the timezone handling method to be used for this date field'),
      );
      $form['timezone']['advanced'] = array(
        '#type' => 'fieldset',
        '#title' => t('Advanced timezone options'),
        '#collapsible' => TRUE,
        '#collapsed' => (date_get_site_timezone() ? TRUE : FALSE),
        );
      // need a way to identify the correct system timezone from an array of timezones with the same offset
      // save it as a system variable so it will default to the correct value after the first time it is set
      // aligns with system setting 'date_default_timezone'
      $form['timezone']['advanced']['field_timezone'] = array(
        '#type' => 'select',
        '#title' => t('Site timezone'),
        '#default_value' => date_get_site_timezone(),
        '#options' => drupal_map_assoc(date_timezone_options(variable_get('date_default_timezone', 0))),  
        '#description' => t('Select the timezone to be used as the site\'s timezone for all date fields in every content type in which they appear. List includes GMT and all timezones with the same GMT offset as the site timezone setting.'),
      );
      $form['#suffix'] = t('<div class="form-item"><div class="description">* The custom format, if provided, will override the selected date display. Define a php date format like \'m-d-Y H:i\' (see <a href="http://php.net/date">http://php.net/date</a>). Do not add time zone formatting to the custom format, it will not work correctly. Use the zone display option instead.</div></div>');
  
      return $form;

    case 'validate':
      date_set_site_timezone($field['field_timezone']);
      if ($field['granularity']['Y'] != 'Y') {
        //form_set_error($field['field_name'], t('Input granularity must include a year.'));
      }
      break;

    case 'save':

      $options = array('granularity', 'field_timezone', 'tz_handling', 'input_format');
      for ($i = 0; $i <= 3; $i++) {
        switch ($i) {
          case (1):
            $name = 'long';
            break;
          case (2):
            $name = 'medium';
            break;
          case (3):
            $name = 'short';
            break;
          default:
            $name = 'default';
        }
        $append = ($i > 0 ? '_'. $name : '');
        $options[] = 'output_format_date'. $append;
        $options[] = 'output_format_zone'. $append;
        $options[] = 'output_format_custom'. $append;
      }
      return $options;

    case 'database columns':
      return date_columns($field);
      
    case 'filters':
      return date_views_filters($field);  
  }
}

/**
 *  A form to create a date formatter option
 */
function date_formatter_setup_form($field, $delta) {
  switch ($delta) {
  case (1):
    $name = 'long';
    $label = t('Long');
    $default = variable_get('date_format_long',  'l, F j, Y - H:i');
    break;
  case (2):
    $name = 'medium';
    $label = t('Medium');
    $default = variable_get('date_format_medium',  'D, m/d/Y - H:i');
    break;
  case (3):
    $name = 'short';
    $label = t('Short');
    $default = variable_get('date_format_short', 'm/d/Y - H:i');
    break;
  default:
    $name = 'default';
    $label = t('Default');
    $default = variable_get('date_format_short', 'm/d/Y - H:i');
  }
  $append = ($delta > 0 ? '_'. $name : '');
  $form = array(
    '#type' => 'fieldset',
    '#title' => $label,
    );
  $form['output_format_date' . $append] = array(
    '#type' => 'select',
    '#title' => t('Date display'),
    '#default_value' => $field['output_format_date'. $append] ? $field['output_format_date'. $append] : $default,
    '#options' => date_output_options($tz_handling),
    '#multiple' => false,
  );
  $form['output_format_zone' . $append] = array(
    '#type' => 'select',
    '#title' => t('Zone display'),
    '#default_value' => $field['format_zone'. $append] ? $field['format_zone'. $append] : '',
    '#options' => date_append_zone_options(),
  );
  $form['output_format_custom' . $append] = array(
    '#type' => 'textfield',
    '#title' => t('*Custom date format'),
    '#default_value' => $field['output_format_custom' . $append] ? $field['output_format_custom'. $append] : '',
  );
  return $form;
}

/**
 * Implementation of hook_field().
 */

function date_field($op, &$node, $field, &$items, $teaser, $page) {
  
  include_once(drupal_get_path('module', 'date') .'/date.inc');
  
  switch ($op) {
    case 'view':
      
      foreach ($items as $delta => $item) {
        $items[$delta]['view'] = content_format($field, $item, 'default', $node);
      }
      return theme('field', $node, $field, $items, $teaser, $page);
  }
}

/**
 * Implementation of hook_widget_info().
 */
function date_widget_info() {
  $info = array(
    'date_select' => array(
      'label' => t('Select List'),
      'field types' => array('date', 'datestamp'),
    ),
    'date_text' => array(
      'label' => t('Text Field with strtotime validation'),
      'field types' => array('date', 'datestamp'),
    ),
  );
  if (module_exist('jscalendar')) {
    $info['date_js'] = array(
       'label' => t('Text Field with javascript pop-up calendar'),
       'field types' => array('date', 'datestamp'),
     );
  }
  return $info;
}


/**
 * Implementation of hook_widget_settings().
 */
function date_widget_settings($op, $widget) {
  
  include_once(drupal_get_path('module', 'date') .'/date.inc');

  switch ($op) {
    case 'form':
        $form = array();
        if ($widget['type'] == 'date_select') {
          $form['input']['advanced'] = array(
            '#type' => 'fieldset',
            '#title' => t('Advanced select options'),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            );
          $form['input']['advanced']['select_day'] = array(
            '#type' => 'select',
            '#title' => t('Days'),
            '#default_value' => $widget['select_day'] ? $widget['select_day'] : 0,
            '#options' => array(0 => t('text field'), 1 => t('select list')),
            '#description' => t('Type of form to use for day selection.'),
            );
          $form['input']['advanced']['select_month'] = array(
            '#type' => 'select',
            '#title' => t('Months'),
            '#default_value' => $widget['select_month'] ? $widget['select_month'] : 0,
            '#options' => array(0 => t('text field'), 1 => t('select list')),
            '#description' => t('Type of form to use for month selection.'),
            );
          $form['input']['advanced']['select_year'] = array(
            '#type' => 'select',
            '#title' => t('Years'),
            '#default_value' => $widget['select_year'] ? $widget['select_year'] : 0,
            '#options' => array(0 => t('text field'), 1 => t('select list')),
            '#description' => t('Type of form to use for year selection.'),
            );
          $form['input']['advanced']['years_back'] = array(
            '#type' => 'textfield',
            '#title' => t('Years back'),
            '#default_value' => $widget['years_back'] ? $widget['years_back'] : 3,
            '#size' => 2,
            '#maxsize' => 2,
            '#description' => t('Number of years to go back when using a selection list.'),
            );
          $form['input']['advanced']['years_forward'] = array(
            '#type' => 'textfield',
            '#title' => t('Years forward'),
            '#default_value' => $widget['years_forward'] ? $widget['years_forward'] : 3,
            '#size' => 2,
            '#maxsize' => 2,
            '#description' => t('Number of years to go forward when using a selection list.'),
            );
          $form['input']['advanced']['increment'] = array(
            '#type' => 'select',
            '#title' => t('Time increment'),
            '#default_value' => $widget['increment'] ? $widget['increment'] : 1,
            '#options' => array(1 => 1, 5 => 5, 10 => 10, 15 => 15, 30 => 30),
            '#description' => t('Increment the minute and second fields by this amount.'),
          );
        }
        return $form;

    case 'save':
      cache_clear_all('date_formats:'. $widget['field_name'] .':'.$widget['type_name']);
      return array('increment', 'select_day', 'select_month', 'select_year', 'years_back', 'years_forward');
  }
}

/**
 * Implementation of hook_widget().
 */
function date_widget($op, &$node, $field, &$items) {
  
  include_once(drupal_get_path('module', 'date') .'/date.inc');

  switch ($op) {
    case 'form':
      $form = array();
      
      $form[$field['field_name']] = array('#tree' => TRUE);

      $tz_handling = $field['tz_handling'] ? $field['tz_handling'] : 'site';
      
      $function = 'date_widget_' . $field['widget']['type'];
      $max = $field['multiple'] ? 2 + sizeof($items) : 0;
      
      foreach (range(0, $max) as $delta) {
        
        $granularity = date_granularity_array($field);
        if ($tz_handling == 'date') array_push($granularity, 'T');
        
        $timezone = date_get_timezone($tz_handling, $items[$delta]['timezone']);

        $params = array(
          'label'        => $field['widget']['label'],
          'value'        => $items[$delta]['value'],
          'weight'       => $field['widget']['weight'],
          'delta'        => $delta,
          'granularity'  => $granularity,
          'format'       => $field['type'],
          'timezone_out' => ($items[$delta]['value'] && $params['required'] ? $timezone : ''),
          'timezone_in'  => ($items[$delta]['value'] && $params['required'] ? 'GMT' : ''),
          'description'  => $field['widget']['description'],
          'select_day'    => $field['widget']['select_day'],
          'select_month'  => $field['widget']['select_month'],
          'select_year'   => $field['widget']['select_year'],
          'years_back'    => $field['widget']['years_back'],
          'years_forward' => $field['widget']['years_forward'],
          );
        $params['required'] = ($field['required'] && $delta == 0) ? 1 : 0;
        $params['formats'] = date_get_formats($field);

        switch ($field['widget']['type']) {
         
          case ('date_select'):
          
            if ($delta > 0) $params['opt_fields'] = array('year', 'month', 'day');
            if ($delta > 0 || (!$params['required'] && $params['value'] == '')) $params['blank_default'] = 1;
            $params['increment'] = $field['widget']['increment'];

            // use the api date selector form from date.inc to create the date selector form
            $form[$field['field_name']][$delta]['value'] = date_select_input($params);
            break;

          default:

            // use the api text input form from date.inc
            if ($delta > 0) $params['blank_default'] = 1;
            $params['jscalendar'] = $field['widget']['type'] == 'date_js' ? 1 : 0;
            $form[$field['field_name']][$delta] = date_text_input($params);

        }
      }
      
      return $form;

    case 'process form values':
      /**
       *  Rebuild $items with converted dates and timezones
       * 
       *  input text field dates will hold an array like:
       *  [0] => Array (
       *    [value] => 2006-04-06T02:00:00
       *    [timezone] => US/Central

       *  input date selector dates will hold an array like:
       *  [0] => Array (
       *    [value] => Array (
       *      [month] => 4
       *      [day] => 05
       *      [year] => 2006
       *      [hour] => 1
       *      [minute] => 12
       *      [timezone] => US/Central
       */

      $formats = date_get_formats($field);
      $add = array();
      
      // Don't save empty fields.
      if ($field['multiple']) {
        foreach ($items as $delta => $item) {
          if (!$item['value'] && ($delta > 0 || !$field['required'])) {
            unset($items[$delta]);
          }
        }
      }
      
      foreach ($items as $delta => $item) {

        if ($items[$delta]) {
        
          $timezone = date_get_timezone($field['tz_handling'], $item['timezone']);

          switch ($field['widget']['type']) {
            case ('date_select'):
              $date = date_selector_make_dbdate($item['value'], $field['type'], $timezone, date_granularity_array($field));
              break;
            case ('date_js'):
              $date = date_jscalendar_make_dbdate(trim($item['value']), $field['type'], $formats['input']['text'], $timezone, date_granularity_array($field));
              break;
            default:
              $date = date_text_make_dbdate(trim($item['value']), $field['type'], $formats['input']['text'], $timezone, date_granularity_array($field));
              break;
          }

          // replace $items values with the converted date, timezone, and offset values
          $add[$delta]['value']    = date_show_value($date, 'db', $field['type']);
          if ($field['tz_handling'] == 'date') {
            $add[$delta]['timezone'] = ($add[$delta]['value'] || $field['required']) ? $timezone : NULL;
            $add[$delta]['offset']   = ($add[$delta]['value'] || $field['required']) ? $date->local->offset : NULL;
          }
        }
      }
        
      if ($add) $items = $add;
      return;

    case 'validate':
      $formats = date_get_formats($field);

      foreach ($items as $delta => $item) {
        
        // if multiple dates are allowed, need to adjust validation criteria on extra fields
        // must allow for way to omit unused date fields, so dates with an empty 'year' field will not be validated or saved
        $params['required'] = ($field['required'] && $delta == 0) ? 1 : 0;
        if ($delta > 0) $params['opt_fields'] = array('year', 'month', 'day');
        $params['granularity'] = date_granularity_array($field);

        switch ($field['widget']['type']) {
          case ('date_select'):
            $error = date_selector_validate($item['value'], $field['field_name'] .']['. $delta . '][value', $params);
            break;

          case ('date_js'):
            $error = date_jscalendar_validate(trim($item['value']), $field['field_name'] .']['. $delta . '][value', $field['type'], $formats['input']['text'], $params['required'], date_granularity_array($field));
            break;

          default:
            $error = date_text_validate(trim($item['value']), $field['field_name'] .']['. $delta . '][value', $field['type'], $formats['input']['text'], $params['required'], date_granularity_array($field));
            break;
        }
      }
      return;

  }
}

/**
 * Implementation of hook_field_formatter_info().
 */
function date_field_formatter_info() {
  return array(
    'default' => array(
      'label' => t('Default'),
      'field types' => array('date', 'datestamp'),
    ),
    'long' => array(
      'label' => t('Long'),
      'field types' => array('date', 'datestamp'),
    ),
    'medium' => array(
      'label' => t('Medium'),
      'field types' => array('date', 'datestamp'),
    ),
    'short' => array(
      'label' => t('Short'),
      'field types' => array('date', 'datestamp'),
    ),
    'iso' => array(
      'label' => t('ISO'),
      'field types' => array('date', 'datestamp'),
    ),
    'feed' => array(
      'label' => t('Feed'),
      'field types' => array('date', 'datestamp'),
    ),
    'ical' => array(
      'label' => t('iCal'),
      'field types' => array('date', 'datestamp'),
    ),
  );
}

/**
 * Implementation of hook_field_formatter().
 */
function date_field_formatter($field, $item, $formatter, $node) {
  
  if (empty($item['value'])) {
    return '';
  }
  
  include_once(drupal_get_path('module', 'date') .'/date.inc');

  $format_db_type = 'local';

  switch (strtolower($formatter)) {
    case ('long'):
      $format = $field['output_format_custom_long'] > '' ? $field['output_format_custom_long'] : ($field['output_format_date_long'] ? $field['output_format_date_long'] : variable_get('date_format_long',  'l, F j, Y - H:i'));
      $format_zone = $field['output_format_zone_long'];
      break;
    case ('medium'):
      $format = $field['output_format_custom_medium'] > '' ? $field['output_format_custom_medium'] : ($field['output_format_date_medium'] ? $field['output_format_date_medium'] : variable_get('date_format_medium',  'D, m/d/Y - H:i'));
      $format_zone = $field['output_format_zone_medium'];
      break;
    case ('short'):
      $format = $field['output_format_custom_short'] > '' ? $field['output_format_custom_short'] : ($field['output_format_date_short'] ? $field['output_format_date_short'] : variable_get('date_format_short', 'm/d/Y - H:i'));
      $format_zone = $field['output_format_zone_short'];
      break;
    case ('iso'):
      $format = DATE_STRING_ISO;
      $format_zone = '00:00';
      break;
    case ('feed'):
      $format = 'D, j M Y H:i:s';
      $format_zone = '0000';
      break;
    case ('ical'):
      // for ical, send the db value with Z appended to indicate it is the gmt value
      $format = 'Ymd\THis';
      $format_zone = '';
      $append = 'Z';
      $format_db_type = 'db';
      break;
    default:
      $format = $field['output_format_custom'] > '' ? $field['output_format_custom'] : ($field['output_format_date'] ? $field['output_format_date'] : variable_get('date_format_short', 'm/d/Y - H:i'));
      $format_zone = $field['output_format_zone'];
      break;
  }
  
  if ($field['tz_handling'] == 'none') {

    // if no timezone handling was elected, create a date object with the database value
    $date = date_make_date(trim($item['value']), 'none', 'local', $field['type']);

  } else {

    // create a date object with a gmt timezone from the database value
    $date = date_make_date(trim($item['value']), 'GMT', 'db', $field['type']);
    // convert the date object to the proper timezone, depending on the field's tz_handling value
    date_convert_timezone($date, 'GMT', date_get_timezone($field['tz_handling'], $item['timezone']), 'local');

  }

  // display the date using the selected format
  return date_show_date($date, $format, $format_db_type, $format_zone) . $append;

}

/**
 * $field['granularity'] will contain an array like ('H' => 'H', 'M' => 0) 
 * where the values turned on return their own names and the values turned off return a zero
 * need to reconfigure this into a simple array of the turned on values 
 */
function date_granularity_array($field) {
  if (!is_array($field) || !is_array($field['granularity'])) {
    $field['granularity'] = drupal_map_assoc(array('Y', 'M', 'D'));
  }
  return array_values(array_filter($field['granularity']));
}

function date_get_formats($field) {
  if ($cached = cache_get('date_formats:'. $field['field_name'] .':'.$field['type_name'])) {
    $formats = unserialize($cached->data);

    // are we up-to-date with current site-wide format ?
    if ($field['input_format'] != 'site-wide' || $formats['input']['site-wide'] == variable_get('date_format_short', 'm/d/Y - H:i')) {
      return $formats;
    }
  }

  // if we get there, it means we have to (re)generate the formats
  return date_set_formats($field);
}

function date_set_formats($field) {
  if (!empty($field['input_format_custom'])) {
    $format = $field['input_format_custom'];
  }
  else {
    $format = ($field['input_format'] == 'site-wide') ? variable_get('date_format_short', 'm/d/Y - H:i') : $field['input_format'];
  }
  $granularity = date_granularity_array($field);
  
  $formats = date_formats($format, $granularity);
  cache_set('date_formats:'. $field['field_name'] .':'.$field['type_name'], serialize($formats));

  return $formats;
}

/**
 *  A function that makes it possible to get column structure for various versions
 *  useful for updating and to make sure columns match user's version
 */
function date_columns($field, $version = '') {

  $version = $version ? $version : variable_get('date_version', 4);

  if ($version < 5) {
    if ($field['type'] == 'date') {
      $db_columns['value'] = array('type' => 'varchar', 'length' => 20, 'not null' => TRUE, 'default' => "'0001-01-01T00:00:00'", 'sortable' => TRUE);
    } elseif ($field['type'] == 'datestamp') {
      $db_columns['value'] = array('type' => 'integer', 'length' => 11, 'not null' => TRUE, 'default' => 0, 'sortable' => TRUE);
    }
    $db_columns['timezone'] = array('type' => 'varchar', 'length' => 50, 'not null' => TRUE, 'default' => "'GMT'", 'sortable' => TRUE);
  }
  else {
    // version 5 adds in timezone offsets and potential for empty (null) values for non-required fields
    if ($field['type'] == 'date') {
      if ($field['required']) {
        $db_columns['value'] = array('type' => 'varchar', 'length' => 20, 'not null' => TRUE, 'default' => "'0001-01-01T00:00:00'", 'sortable' => TRUE);
      }
      else {
        $db_columns['value'] = array('type' => 'varchar', 'length' => 20, 'not null' => FALSE, 'sortable' => TRUE);
      }
    } elseif ($field['type'] == 'datestamp') {
      if ($field['required']) {
        $db_columns['value'] = array('type' => 'integer', 'length' => 11, 'not null' => TRUE, 'default' => 0, 'sortable' => TRUE);
      }
      else {
        $db_columns['value'] = array('type' => 'integer', 'length' => 11, 'not null' => FALSE, 'sortable' => TRUE);
      }
    }
    if ($field['tz_handling'] == 'date' && ($field['type'] == 'date' || $field['type'] == 'datestamp')) {
      if ($field['required']) {
        $db_columns['timezone'] = array('type' => 'varchar', 'length' => 50, 'not null' => TRUE, 'default' => "'GMT'", 'sortable' => TRUE);
        $db_columns['offset'] = array('type' => 'integer', 'length' => 10, 'not null' => TRUE, 'default' => 0, 'sortable' => TRUE);
      }
      else {
        $db_columns['timezone'] = array('type' => 'varchar', 'length' => 50, 'not null' => FALSE, 'sortable' => TRUE);
        $db_columns['offset'] = array('type' => 'integer', 'length' => 10, 'not null' => FALSE, 'sortable' => TRUE);
      }
    }
  }
  return $db_columns;
}
