You are here

public function ParserVcalendar::parseDateTimeProperty in Date iCal 7.3

Handler that parses DATE-TIME and DATE fields.

Return value

FeedsDateTime The parsed datetime object.

File

libraries/ParserVcalendar.inc, line 292
Defines a class that parses iCalcreator vcalendar objects into Feeds-compatible data arrays.

Class

ParserVcalendar
@file Defines a class that parses iCalcreator vcalendar objects into Feeds-compatible data arrays.

Code

public function parseDateTimeProperty($property_key, $vcalendar_component) {
  $property = $vcalendar_component
    ->getProperty($property_key, FALSE, TRUE);

  // Gather all the other date properties, so we can work with them later.
  $duration = $vcalendar_component
    ->getProperty('DURATION', FALSE, TRUE);
  $dtstart = $vcalendar_component
    ->getProperty('DTSTART', FALSE, TRUE);
  $uid = $vcalendar_component
    ->getProperty('UID');

  // DATE-type properties are treated as All Day events which can span over
  // multiple days.
  // The Date module's All Day event handling was never finalized
  // (http://drupal.org/node/874322), which requires us to do some some
  // special coddling later.
  $is_all_day = isset($property['params']['VALUE']) && $property['params']['VALUE'] == 'DATE';

  // Cover various conditions in which either DTSTART or DTEND are not set.
  if ($property === FALSE) {

    // When DTEND isn't defined, we may need to emulate it.
    if ($property_key == 'DTEND') {

      // Unset DTENDs need to emulate the DATE type from DTSTART.
      $is_all_day = isset($dtstart['params']['VALUE']) && $dtstart['params']['VALUE'] == 'DATE';
      if ($duration !== FALSE) {

        // If a DURATION is defined, emulate DTEND as DTSTART + DURATION.
        $property = array(
          'value' => iCalUtilityFunctions::_duration2date($dtstart['value'], $duration['value']),
          'params' => $dtstart['params'],
        );
      }
      elseif ($is_all_day) {

        // If this is an all-day event with no end or duration, treat this
        // as a single-day event by emulating DTEND as 1 day after DTSTART.
        $property = $dtstart;
        $property['value'] = iCalUtilityFunctions::_duration2date($property['value'], array(
          'day' => 1,
        ));
      }
      else {

        // This event has no end date.
        return NULL;
      }
    }
    elseif ($property_key == 'DTSTART') {

      // DTSTART can only be legally unset in non-VEVENT components.
      if ($vcalendar_component->objName == 'vevent') {
        throw new DateIcalParseException(t('Feed import failed! The VEVENT with UID %uid is invalid: it has no DTSTART.', array(
          '%uid' => $uid,
        )));
      }
      else {
        return NULL;
      }
    }
  }

  // When iCalcreator parses a UTC date (one that ends with Z) from an iCal
  // feed, it stores that 'Z' into the $property['value']['tz'] value.
  if (isset($property['value']['tz'])) {
    $property['params']['TZID'] = 'UTC';
  }
  if ($is_all_day) {
    if ($property_key == 'DTEND') {
      if ($dtstart === FALSE) {

        // This will almost certainly never happen, but the error message
        // would be incomprehensible without this check.
        throw new DateIcalParseException(t('Feed import failed! The event with UID %uid is invalid: it has a DTEND but no DTSTART!', array(
          '%uid' => $uid,
        )));
      }
      if (module_exists('date_all_day')) {

        // If the Date All Day module is installed, we need to rewind the
        // DTEND by one day, because of the problem with FeedsDateTime
        // mentioned below.
        $prev_day = iCalUtilityFunctions::_duration2date($property['value'], array(
          'day' => -1,
        ));
        $property['value'] = $prev_day;
      }
    }

    // FeedsDateTime->setTimezone() ignores timezone changes made to dates
    // with no time element, which means we can't compensate for the Date
    // module's automatic timezone conversion when it writes to the DB. To
    // get around that, we must add 00:00:00 explicitly, even though this
    // causes other problems (see above and below).
    $date_string = sprintf('%d-%d-%d 00:00:00', $property['value']['year'], $property['value']['month'], $property['value']['day']);

    // Use the server's timezone rather than letting it default to UTC.
    // This will help ensure that the date value doesn't get messed up when
    // Date converts its timezone as the value is read from the database.
    // This is *essential* for All Day events, because Date stores them as
    // '2013-10-03 00:00:00' in the database, rather than doing the sensible
    // thing and storing them as '2013-10-03'.
    // NOTE TO MAINTAINERS:
    // This will not work properly if the site is configured to allow users
    // to set their own timezone. Unfortunately, there isn't anything that
    // Date iCal can do about that, as far as I can tell.
    $datetimezone = new DateTimeZone(date_default_timezone_get());
  }
  else {

    // This is a DATE-TIME property.
    $date_string = iCalUtilityFunctions::_format_date_time($property['value']);

    // Allow modules to alter the timezone string. This also allows for
    // setting a TZID when one was not originally set for this property.
    $tzid = isset($property['params']['TZID']) ? $property['params']['TZID'] : NULL;
    $context = array(
      'property_key' => $property_key,
      'calendar_component' => $vcalendar_component,
      'calendar' => $this->calendar,
      'feeeds_source' => $this->source,
      'feeds_fetcher_result' => $this->fetcherResult,
    );
    drupal_alter('date_ical_import_timezone', $tzid, $context);
    if (isset($tzid)) {
      $datetimezone = $this
        ->_tzid_to_datetimezone($tzid);
    }
    elseif (isset($this->xtimezone)) {

      // No timezone was set on the parsed date property, so if a timezone
      // was detected for the entire iCal feed, use it.
      $datetimezone = $this->xtimezone;
    }
    else {
      $msg = t("No timezone was detected for one or more of the events in this feed, forcing Date iCal to use this server's timezone as a fallback.<br>\n            To make timezone-less events use a different timezone, implement hook_date_ical_import_timezone_alter() in a custom module.");
      drupal_set_message($msg, 'status', FALSE);
      $this->source
        ->log('parse', $msg, array(), WATCHDOG_NOTICE);
      $datetimezone = new DateTimeZone(date_default_timezone_get());
    }
  }
  $datetime = new FeedsDateTime($date_string, $datetimezone);
  return $datetime;
}