You are here

function _date_ical_get_repeat_dates in Date iCal 7.3

Parse the repeat data into date values.

This is a re-write of date_repeat_build_dates() which fixes it's bugs regarding multi-property RDATEs and EXDATEs.

1 call to _date_ical_get_repeat_dates()
date_ical_feeds_set_rrule in ./date_ical.module
Callback specified in date_ical_feeds_processor_targets_alter() for RRULEs.

File

./date_ical.utils.inc, line 14
Utility functions for Date iCal. Many of these are re-writes of buggy Date module code.

Code

function _date_ical_get_repeat_dates($field_name, $repeat_data, $item, $source) {
  module_load_include('inc', 'date_api', 'date_api_ical');
  $field_info = field_info_field($field_name);
  $rrule_values = _date_ical_parse_repeat_rule($repeat_data['RRULE']);

  //$exrule_values = _date_ical_parse_repeat_rule($repeat_data['EXRULE']);
  $rdates = _date_ical_parse_repeat_dates($repeat_data['RDATE']);
  $exdates = _date_ical_parse_repeat_dates($repeat_data['EXDATE']);

  // By the time we get here, the start and end dates have been
  // adjusted back to UTC, but we want localtime dates to do
  // things like '+1 Tuesday', so adjust back to localtime.
  $timezone = date_get_timezone($field_info['settings']['tz_handling'], $item['timezone']);
  $timezone_db = date_get_timezone_db($field_info['settings']['tz_handling']);
  $start = new DateObject($item['value'], $timezone_db, date_type_format($field_info['type']));
  $start
    ->limitGranularity($field_info['settings']['granularity']);
  if ($timezone != $timezone_db) {
    date_timezone_set($start, timezone_open($timezone));
  }
  if (!empty($item['value2']) && $item['value2'] != $item['value']) {
    $end = new DateObject($item['value2'], date_get_timezone_db($field_info['settings']['tz_handling']), date_type_format($field_info['type']));
    $end
      ->limitGranularity($field_info['settings']['granularity']);
    date_timezone_set($end, timezone_open($timezone));
  }
  else {
    $end = $start;
  }
  $duration = $start
    ->difference($end);
  $start_datetime = date_format($start, DATE_FORMAT_DATETIME);
  if (!empty($rrule_values['UNTIL']['datetime'])) {

    // The spec says that UNTIL must be in UTC, but not all feed creators
    // follow that rule. If the user specified that he wanted to overcome this
    // problem, use $timezone for the $final_repeat, and then convert the UNTIL
    // in the unparsed RRULE to UTC.
    if (!empty($source->importer->config['parser']['config']['until_not_utc'])) {

      // Change the parsed UNTIL from UTC to $timezone, so that the
      // date_ical_date() won't convert it.
      $rrule_values['UNTIL']['tz'] = $timezone;

      // Convert the unparsed UNTIL to UTC, since the Date code will use it.
      // It may currently have a Z on it, but only because iCalcreator blindly
      // adds one to DATETIME-type UNTILs if it's not there.
      $matches = array();
      if (preg_match('/^(.*?)UNTIL=([\\dT]+)Z?(.*?)$/', $repeat_data['RRULE'], $matches)) {

        // If the UNTIL value doesn't have a "T", it's a DATE, making timezone
        // fixes irrelvant.
        if (strpos($matches[2], 'T') !== FALSE) {
          $until_date = new DateObject($matches[2], $timezone);
          $until_date
            ->setTimezone(new DateTimeZone('UTC'));
          $matches[2] = $until_date
            ->format('Ymd\\THis');
          $repeat_data['RRULE'] = "{$matches[1]}UNTIL={$matches[2]}Z{$matches[3]}";
        }
      }
      else {
        watchdog('date_ical', 'The RRULE string "%rrule" could not be parsed to fix the UNTIL value. Date repeats may not be calculated correctly.', array(
          '%rrule' => $repeat_data['RRULE'],
        ), WATCHDOG_WARNING);
      }
    }
    $final_repeat = date_ical_date($rrule_values['UNTIL'], $timezone);
    $final_repeat_datetime = date_format($final_repeat, DATE_FORMAT_DATETIME);
  }
  elseif (!empty($rrule_values['COUNT'])) {
    $final_repeat_datetime = NULL;
  }
  else {

    // No UNTIL and no COUNT? This is an illegal RRULE.
    return array();
  }

  // Convert the EXDATE and RDATE values to datetime strings.
  // Even though exdates and rdates can be specified to the second, Date
  // Repeat's code checks them by comparing them to the date value only.
  $exceptions = array();
  foreach ($exdates as $exception) {
    $date = date_ical_date($exception, $timezone);
    $exceptions[] = date_format($date, 'Y-m-d');
  }
  $additions = array();
  foreach ($rdates as $rdate) {
    $date = date_ical_date($rdate, $timezone);
    $additions[] = date_format($date, 'Y-m-d');
  }

  // TODO: EXRULEs.
  $date_repeat_compatible_rrule = "{$repeat_data['RRULE']}\n{$repeat_data['RDATE']}\n{$repeat_data['EXDATE']}";
  $calculated_dates = date_repeat_calc($date_repeat_compatible_rrule, $start_datetime, $final_repeat_datetime, $exceptions, $timezone, $additions);
  $repeat_dates = array();
  foreach ($calculated_dates as $delta => $date) {

    // date_repeat_calc always returns DATE_DATETIME dates, which is
    // not necessarily $field_info['type'] dates.
    // Convert returned dates back to db timezone before storing.
    $date_start = new DateObject($date, $timezone, DATE_FORMAT_DATETIME);
    $date_start
      ->limitGranularity($field_info['settings']['granularity']);
    date_timezone_set($date_start, timezone_open($timezone_db));
    $date_end = clone $date_start;
    date_modify($date_end, '+' . $duration . ' seconds');
    $repeat_dates[$delta] = array(
      'value' => date_format($date_start, date_type_format($field_info['type'])),
      'value2' => date_format($date_end, date_type_format($field_info['type'])),
      'offset' => date_offset_get($date_start),
      'offset2' => date_offset_get($date_end),
      'timezone' => $timezone,
      'rrule' => $date_repeat_compatible_rrule,
    );
  }
  return $repeat_dates;
}