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
|
// TODO: The spec says that UNTIL must be specified in UTC, but I think
|
46
|
// this may not be followd by all feed creators. It may be a good idea to
|
47
|
// support optionally assuming the date has the same TZID as DTSTART.
|
48
|
$end = date_ical_date($rrule_values['UNTIL'], $timezone);
|
49
|
$end_datetime = date_format($end, DATE_FORMAT_DATETIME);
|
50
|
}
|
51
|
elseif (!empty($rrule_values['COUNT'])) {
|
52
|
$end_datetime = NULL;
|
53
|
}
|
54
|
else {
|
55
|
// No UNTIL and no COUNT?
|
56
|
return array();
|
57
|
}
|
58
|
|
59
|
// Convert the EXDATE and RDATE values to datetime strings.
|
60
|
// Even though exdates and rdates can be specified to the second, Date
|
61
|
// Repeat's code checks them by comparing them to the date value only.
|
62
|
$exceptions = array();
|
63
|
foreach ($exdates as $exception) {
|
64
|
$date = date_ical_date($exception, $timezone);
|
65
|
$exceptions[] = date_format($date, 'Y-m-d');
|
66
|
}
|
67
|
$additions = array();
|
68
|
foreach ($rdates as $rdate) {
|
69
|
$date = date_ical_date($rdate, $timezone);
|
70
|
$additions[] = date_format($date, 'Y-m-d');
|
71
|
}
|
72
|
// TODO: EXRULE.
|
73
|
|
74
|
$date_repeat_compatible_rrule = "{$repeat_data['RRULE']}\n{$repeat_data['RDATE']}\n{$repeat_data['EXDATE']}";
|
75
|
$calculated_dates = date_repeat_calc($date_repeat_compatible_rrule, $start_datetime, $end_datetime, $exceptions, $timezone, $additions);
|
76
|
$repeat_dates = array();
|
77
|
foreach ($calculated_dates as $delta => $date) {
|
78
|
// date_repeat_calc always returns DATE_DATETIME dates, which is
|
79
|
// not necessarily $field_info['type'] dates.
|
80
|
// Convert returned dates back to db timezone before storing.
|
81
|
$date_start = new DateObject($date, $timezone, DATE_FORMAT_DATETIME);
|
82
|
$date_start->limitGranularity($field_info['settings']['granularity']);
|
83
|
date_timezone_set($date_start, timezone_open($timezone_db));
|
84
|
$date_end = clone($date_start);
|
85
|
date_modify($date_end, '+' . $duration . ' seconds');
|
86
|
$repeat_dates[$delta] = array(
|
87
|
'value' => date_format($date_start, date_type_format($field_info['type'])),
|
88
|
'value2' => date_format($date_end, date_type_format($field_info['type'])),
|
89
|
'offset' => date_offset_get($date_start),
|
90
|
'offset2' => date_offset_get($date_end),
|
91
|
'timezone' => $timezone,
|
92
|
'rrule' => $date_repeat_compatible_rrule,
|
93
|
);
|
94
|
}
|
95
|
return $repeat_dates;
|
96
|
}
|
97
|
|
98
|
/**
|
99
|
* Parse an rrule or exrule string.
|
100
|
*
|
101
|
* @return array
|
102
|
* Array in the form of PROPERTY => array(VALUES)
|
103
|
* PROPERTIES include FREQ, INTERVAL, COUNT, BYDAY, BYMONTH, BYYEAR, UNTIL.
|
104
|
*/
|
105
|
function _date_ical_parse_repeat_rule($repeat_rule_string) {
|
106
|
module_load_include('inc', 'date_api', 'date_api_ical');
|
107
|
|
108
|
$repeat_rule_string = preg_replace('/(R|EX)RULE.*:/', '', $repeat_rule_string);
|
109
|
$items = array('DATA' => $repeat_rule_string);
|
110
|
foreach (explode(';', $repeat_rule_string) as $recur_val) {
|
111
|
list($key, $value) = explode('=', $recur_val);
|
112
|
// Must be some kind of invalid data.
|
113
|
if (empty($key) || empty($value)) {
|
114
|
continue;
|
115
|
}
|
116
|
|
117
|
// These keys never have multiple values.
|
118
|
if (in_array($key, array('UNTIL', 'FREQ', 'INTERVAL', 'COUNT', 'WKST'))) {
|
119
|
if ($key == 'UNTIL') {
|
120
|
// This is a function from the date_api module, not date_ical.
|
121
|
$value = date_ical_parse_date('', $value);
|
122
|
}
|
123
|
}
|
124
|
else {
|
125
|
// The rest can be multi-value csv strings.
|
126
|
$value = explode(',', $value);
|
127
|
}
|
128
|
|
129
|
$items[$key] = $value;
|
130
|
}
|
131
|
return $items;
|
132
|
}
|
133
|
|
134
|
/**
|
135
|
* Parses an iCal RDATE or EXDATE, including multi-property rules.
|
136
|
*
|
137
|
* @param string $repeat_date_string
|
138
|
* An RDATE or EXDATE property string, e.g.
|
139
|
* "EXDATE;TZID=America/Los_Angeles:20130415T180000,20130422T180000"
|
140
|
* Can be multiple EXDATE or RDATE properties, separated by newlines.
|
141
|
*
|
142
|
* @return array
|
143
|
* An array of dates returned by date_ical_parse_date().
|
144
|
*/
|
145
|
function _date_ical_parse_repeat_dates($repeat_date_string) {
|
146
|
module_load_include('inc', 'date_api', 'date_api_ical');
|
147
|
|
148
|
$properties = explode("\n", str_replace("\r\n", "\n", $repeat_date_string));
|
149
|
$parsed_dates = array();
|
150
|
|
151
|
foreach ($properties as $property) {
|
152
|
$matches = array();
|
153
|
if (preg_match('/(R|EX)DATE([^:]*):(.*)/', $property, $matches)) {
|
154
|
$params = $matches[2];
|
155
|
foreach (explode(',', $matches[3]) as $date) {
|
156
|
$parsed_dates[] = date_ical_parse_date($params, $date);
|
157
|
}
|
158
|
}
|
159
|
}
|
160
|
return $parsed_dates;
|
161
|
}
|