Project

General

Profile

Paste
Download (7.45 KB) Statistics
| Branch: | Revision:

root / drupal7 / sites / all / modules / date_ical / date_ical.utils.inc @ 62e0cc08

1
<?php
2
/**
3
 * @file
4
 * Utility functions for Date iCal. Many of these are re-writes of buggy Date
5
 * module code.
6
 */
7

    
8
/**
9
 * Parse the repeat data into date values.
10
 *
11
 * This is a re-write of date_repeat_build_dates() which fixes it's bugs
12
 * regarding multi-property RDATEs and EXDATEs.
13
 */
14
function _date_ical_get_repeat_dates($field_name, $repeat_data, $item, $source) {
15
  module_load_include('inc', 'date_api', 'date_api_ical');
16
  $field_info = field_info_field($field_name);
17

    
18
  $rrule_values = _date_ical_parse_repeat_rule($repeat_data['RRULE']);
19
  //$exrule_values = _date_ical_parse_repeat_rule($repeat_data['EXRULE']);
20
  $rdates = _date_ical_parse_repeat_dates($repeat_data['RDATE']);
21
  $exdates = _date_ical_parse_repeat_dates($repeat_data['EXDATE']);
22

    
23
  // By the time we get here, the start and end dates have been
24
  // adjusted back to UTC, but we want localtime dates to do
25
  // things like '+1 Tuesday', so adjust back to localtime.
26
  $timezone = date_get_timezone($field_info['settings']['tz_handling'], $item['timezone']);
27
  $timezone_db = date_get_timezone_db($field_info['settings']['tz_handling']);
28
  $start = new DateObject($item['value'], $timezone_db, date_type_format($field_info['type']));
29
  $start->limitGranularity($field_info['settings']['granularity']);
30
  if ($timezone != $timezone_db) {
31
    date_timezone_set($start, timezone_open($timezone));
32
  }
33
  if (!empty($item['value2']) && $item['value2'] != $item['value']) {
34
    $end = new DateObject($item['value2'], date_get_timezone_db($field_info['settings']['tz_handling']), date_type_format($field_info['type']));
35
    $end->limitGranularity($field_info['settings']['granularity']);
36
    date_timezone_set($end, timezone_open($timezone));
37
  }
38
  else {
39
    $end = $start;
40
  }
41
  $duration = $start->difference($end);
42
  $start_datetime = date_format($start, DATE_FORMAT_DATETIME);
43

    
44
  if (!empty($rrule_values['UNTIL']['datetime'])) {
45
    // The spec says that UNTIL must be in UTC, but not all feed creators
46
    // follow that rule. If the user specified that he wanted to overcome this
47
    // problem, use $timezone for the $final_repeat, and then convert the UNTIL
48
    // in the unparsed RRULE to UTC.
49
    if (!empty($source->importer->config['parser']['config']['until_not_utc'])) {
50
      // Change the parsed UNTIL from UTC to $timezone, so that the
51
      // date_ical_date() won't convert it.
52
      $rrule_values['UNTIL']['tz'] = $timezone;
53

    
54
      // Convert the unparsed UNTIL to UTC, since the Date code will use it.
55
      // It may currently have a Z on it, but only because iCalcreator blindly
56
      // adds one to DATETIME-type UNTILs if it's not there.
57
      $matches = array();
58
      if (preg_match('/^(.*?)UNTIL=([\dT]+)Z?(.*?)$/', $repeat_data['RRULE'], $matches)) {
59
        // If the UNTIL value doesn't have a "T", it's a DATE, making timezone
60
        // fixes irrelvant.
61
        if (strpos($matches[2], 'T') !== FALSE) {
62
          $until_date = new DateObject($matches[2], $timezone);
63
          $until_date->setTimezone(new DateTimeZone('UTC'));
64
          $matches[2] = $until_date->format('Ymd\THis');
65
          $repeat_data['RRULE'] = "{$matches[1]}UNTIL={$matches[2]}Z{$matches[3]}";
66
        }
67
      }
68
      else {
69
        watchdog('date_ical', 'The RRULE string "%rrule" could not be parsed to fix the UNTIL value. Date repeats may not be calculated correctly.',
70
          array('%rrule' => $repeat_data['RRULE']), WATCHDOG_WARNING
71
        );
72
      }
73
    }
74
    $final_repeat = date_ical_date($rrule_values['UNTIL'], $timezone);
75
    $final_repeat_datetime = date_format($final_repeat, DATE_FORMAT_DATETIME);
76
  }
77
  elseif (!empty($rrule_values['COUNT'])) {
78
    $final_repeat_datetime = NULL;
79
  }
80
  else {
81
    // No UNTIL and no COUNT? This is an illegal RRULE.
82
    return array();
83
  }
84

    
85
  // Convert the EXDATE and RDATE values to datetime strings.
86
  // Even though exdates and rdates can be specified to the second, Date
87
  // Repeat's code checks them by comparing them to the date value only.
88
  $exceptions = array();
89
  foreach ($exdates as $exception) {
90
    $date = date_ical_date($exception, $timezone);
91
    $exceptions[] = date_format($date, 'Y-m-d');
92
  }
93
  $additions = array();
94
  foreach ($rdates as $rdate) {
95
    $date = date_ical_date($rdate, $timezone);
96
    $additions[] = date_format($date, 'Y-m-d');
97
  }
98

    
99
  // TODO: EXRULEs.
100

    
101
  $date_repeat_compatible_rrule = "{$repeat_data['RRULE']}\n{$repeat_data['RDATE']}\n{$repeat_data['EXDATE']}";
102
  $calculated_dates = date_repeat_calc($date_repeat_compatible_rrule, $start_datetime, $final_repeat_datetime, $exceptions, $timezone, $additions);
103
  $repeat_dates = array();
104
  foreach ($calculated_dates as $delta => $date) {
105
    // date_repeat_calc always returns DATE_DATETIME dates, which is
106
    // not necessarily $field_info['type'] dates.
107
    // Convert returned dates back to db timezone before storing.
108
    $date_start = new DateObject($date, $timezone, DATE_FORMAT_DATETIME);
109
    $date_start->limitGranularity($field_info['settings']['granularity']);
110
    date_timezone_set($date_start, timezone_open($timezone_db));
111
    $date_end = clone $date_start;
112
    date_modify($date_end, '+' . $duration . ' seconds');
113
    $repeat_dates[$delta] = array(
114
      'value' => date_format($date_start, date_type_format($field_info['type'])),
115
      'value2' => date_format($date_end, date_type_format($field_info['type'])),
116
      'offset' => date_offset_get($date_start),
117
      'offset2' => date_offset_get($date_end),
118
      'timezone' => $timezone,
119
      'rrule' => $date_repeat_compatible_rrule,
120
    );
121
  }
122
  return $repeat_dates;
123
}
124

    
125
/**
126
 * Parse an rrule or exrule string.
127
 *
128
 * @return array
129
 *   Array in the form of PROPERTY => array(VALUES)
130
 *   PROPERTIES include FREQ, INTERVAL, COUNT, BYDAY, BYMONTH, BYYEAR, UNTIL.
131
 */
132
function _date_ical_parse_repeat_rule($repeat_rule_string) {
133
  module_load_include('inc', 'date_api', 'date_api_ical');
134

    
135
  $repeat_rule_string = preg_replace('/(R|EX)RULE.*:/', '', $repeat_rule_string);
136
  $items = array('DATA' => $repeat_rule_string);
137
  foreach (explode(';', $repeat_rule_string) as $recur_val) {
138
    list($key, $value) = explode('=', $recur_val);
139
    // Must be some kind of invalid data.
140
    if (empty($key) || empty($value)) {
141
      continue;
142
    }
143

    
144
    // The following keys never have multiple values.
145
    if (in_array($key, array('UNTIL', 'FREQ', 'INTERVAL', 'COUNT', 'WKST'))) {
146
      if ($key == 'UNTIL') {
147
        // This is a function from the date_api module, not date_ical.
148
        $value = date_ical_parse_date('', $value);
149
      }
150
    }
151
    else {
152
      // The rest can be multi-value csv strings.
153
      $value = explode(',', $value);
154
    }
155

    
156
    $items[$key] = $value;
157
  }
158
  return $items;
159
}
160

    
161
/**
162
 * Parses an iCal RDATE or EXDATE, including multi-property rules.
163
 *
164
 * @param string $repeat_date_string
165
 *   An RDATE or EXDATE property string, e.g.
166
 *   "EXDATE;TZID=America/Los_Angeles:20130415T180000,20130422T180000"
167
 *   Can be multiple EXDATE or RDATE properties, separated by newlines.
168
 *
169
 * @return array
170
 *   An array of dates returned by date_ical_parse_date().
171
 */
172
function _date_ical_parse_repeat_dates($repeat_date_string) {
173
  module_load_include('inc', 'date_api', 'date_api_ical');
174

    
175
  $properties = explode("\n", str_replace("\r\n", "\n", $repeat_date_string));
176
  $parsed_dates = array();
177

    
178
  foreach ($properties as $property) {
179
    $matches = array();
180
    if (preg_match('/(R|EX)DATE([^:]*):(.*)/', $property, $matches)) {
181
      $params = $matches[2];
182
      foreach (explode(',', $matches[3]) as $date) {
183
        $parsed_dates[] = date_ical_parse_date($params, $date);
184
      }
185
    }
186
  }
187
  return $parsed_dates;
188
}