[horde] Mapi/Timezone.php checks transitions of wrong year and fails to detect proper TZ / Re: ActiveSync -> CalDAV Timezone problems
Steffen
skhorde at smail.inf.fh-bonn-rhein-sieg.de
Thu Mar 17 10:01:12 UTC 2016
On Thu, 17 Mar 2016, Steffen wrote:
> On Wed, 16 Mar 2016, Michael J Rubinsky wrote:
>> Quoting Jan Schneider <jan at horde.org>:
>>
>>> Zitat von Jan Schneider <jan at horde.org>:
>>>
>>>> Zitat von Steffen <skhorde at smail.inf.fh-bonn-rhein-sieg.de>:
>>>>
>>>>> Hi,
>>>>>
>>>>> with
>>>>> Horde_ActiveSync 2.31.6 stable
>>>>> kronolith 4.2.15 stable
>>>>>
>>>>> when I create an event with Android ActiveSync, I get an entry with
>>>>> event_timezone = 'CET' in the database; the GUI and ActiveSync display
>>>>> the event correctly. But when downloaded by CalDAV I get this:
>>>>>
>>>>> BEGIN:VCALENDAR
>>>>> VERSION:2.0
>>>>> X-WR-CALNAME:Calendar of dvtest1
>>>>> PRODID:-//The Horde Project//Horde iCalendar Library//EN
>>>>> BEGIN:VEVENT
>>>>> DTSTART;TZID=CET:20160308T150000
>>>>> DTEND;TZID=CET:20160308T153000
>>>>> DTSTAMP:20160314T123714Z
>>>>> UID:20160314132443.4C_Xp8mUFWBF6GsNZi0nVRb at ...
>>>>> CREATED:20160314T122443Z
>>>>> LAST-MODIFIED:20160314T122443Z
>>>>> SUMMARY:B15:00
>>>>> CLASS:PUBLIC
>>>>> STATUS:CONFIRMED
>>>>> TRANSP:OPAQUE
>>>>> BEGIN:VALARM
>>>>> ACTION:DISPLAY
>>>>> DESCRIPTION:B15:00
>>>>> TRIGGER;VALUE=DURATION:-PT15M
>>>>> END:VALARM
>>>>> END:VEVENT
>>>>> BEGIN:VTIMEZONE
>>>>> TZID:CET
>>>>> END:VTIMEZONE
>>>>> END:VCALENDAR
>>>>>
>>>>> My client is totally confused by the timezone CET. If I replace the
>>>>> string CET by "Europe/Berlin" in the database, I get the correct date in
>>>>> the CalDAV client, too, and a lot more entries in VTIMEZONE.
>>>>>
>>>>> The user has Europe/Berlin as default timezone, as does PHP.
>>>>>
>>>>> Is this some configuration error?
>>>>> If I remember correctly, ActiveSync storred UTC as timezone, didn't it?
>>>>>
>>>>> --
>>>>> Steffen
>>>>
>>>> Looks like starting with PHP 5.5.10 DateTimeZone no longer converts
>>>> timezone abbreviations to full timezone names, which is of course a big
>>>> BC break. We would have to work around this.
>>>
>>> OTOH we don't import unknown timezones anyway, at least not via iCalendar
>>> import. But maybe we do this differently with ActiveSync. Michael does
>>> know better.
>>
>> Timezones from ActiveSync clients are presented in an MAPI encoded binary
>> format that describes the timezone instead of naming it. We use
>> DateTimeZone::listIdentifiers to retrieve the list of supported timezones
>> when parsing the MAPI data to determine the matching/supported timezone
>> name. So, if the timezone name comes from ActiveSync it means is MUST have
>> been returned by DateTimeZone.
>
> Horde/Mapi/Timezone.php
>
> public function getTimezone($offsets, $expectedTimezone = null)
> {
> $timezones = $this->getListOfTimezones($offsets, $expectedTimezone);
>
> ska_dumpvar('Mapi/getTimezone() = ', array("offsets" => $offsets, "expect" =>
> $expectedTimezone, "tz" => $timezones));
>
> if (isset($timezones[$expectedTimezone])) {
> return $expectedTimezone;
> } else {
> return current($timezones);
> }
> }
>
>
> has:
>
> Mapi/getTimezone() =
> array(3) {
> ["offsets"]=>
> string(232)
> "xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAEAAIAAAAAAAAAxP///w=="
> ["expect"]=>
> string(13) "Europe/Berlin"
> ["tz"]=>
> array(13) {
> ["Africa/Algiers"]=>
> string(3) "CET"
> ["Africa/Bangui"]=>
> string(3) "WAT"
> ["Africa/Brazzaville"]=>
> string(3) "WAT"
> ["Africa/Douala"]=>
> string(3) "WAT"
> ["Africa/Kinshasa"]=>
> string(3) "WAT"
> ["Africa/Lagos"]=>
> string(3) "WAT"
> ["Africa/Libreville"]=>
> string(3) "WAT"
> ["Africa/Luanda"]=>
> string(3) "WAT"
> ["Africa/Malabo"]=>
> string(3) "WAT"
> ["Africa/Ndjamena"]=>
> string(3) "WAT"
> ["Africa/Niamey"]=>
> string(3) "WAT"
> ["Africa/Porto-Novo"]=>
> string(3) "WAT"
> ["Africa/Tunis"]=>
> string(3) "CET"
> }
> }
>
> ===
>
> If "Europe/Berlin" would be part of the array, this function would return
> "Europe/Berlin"; because it is not, current() seems to return "CET".
Besides from the fact that above function either return "Europe/Berlin" or
the abbr[] "CET", there seems to be another problem:
Above function is called for:
2016-03-17T10:41:34+01:00 DEBUG: [20312] I <POOMCAL:Timezone>
2016-03-17T10:41:34+01:00 DEBUG: [20312] I
xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAEAAIAAAAAAAAAxP///w==
2016-03-17T10:41:34+01:00 DEBUG: [20312] I </POOMCAL:Timezone>
2016-03-17T10:41:34+01:00 DEBUG: [20312] I <POOMCAL:AllDayEvent>
2016-03-17T10:41:34+01:00 DEBUG: [20312] I 0
2016-03-17T10:41:34+01:00 DEBUG: [20312] I </POOMCAL:AllDayEvent>
2016-03-17T10:41:34+01:00 DEBUG: [20312] I <POOMCAL:StartTime>
2016-03-17T10:41:34+01:00 DEBUG: [20312] I 20160308T140000Z
2016-03-17T10:41:34+01:00 DEBUG: [20312] I </POOMCAL:StartTime>
2016-03-17T10:41:34+01:00 DEBUG: [20312] I <POOMCAL:EndTime>
2016-03-17T10:41:34+01:00 DEBUG: [20312] I 20160308T143000Z
2016-03-17T10:41:34+01:00 DEBUG: [20312] I </POOMCAL:EndTime>
2016-03-17T10:41:34+01:00 DEBUG: [20312] I <POOMCAL:DtStamp>
2016-03-17T10:41:34+01:00 DEBUG: [20312] I 20160317T094137Z
2016-03-17T10:41:34+01:00 DEBUG: [20312] I </POOMCAL:DtStamp>
fromASAppointment() gets:
["starttime"]=>
object(Horde_Date)#260 (9) {
["_year":protected]=>
int(2016)
["_month":protected]=>
int(3)
["_mday":protected]=>
int(8)
["_hour":protected]=>
int(14)
["_min":protected]=>
int(0)
["_sec":protected]=>
int(0)
["_timezone":protected]=>
string(3) "UTC"
["_defaultFormat":protected]=>
string(11) "Y-m-d H:i:s"
["_formatCache":protected]=>
array(0) {
}
Mapi/Timezone.php chkTimezone() is called with:
Mapi/chkTZ() =
array(3) {
["name"]=>
string(13) "Europe/Berlin" // my addition
["std"]=>
array(5) {
["ts"]=>
int(1445734800)
["time"]=>
string(24) "2015-10-25T01:00:00+0000"
["offset"]=>
int(3600)
["isdst"]=>
bool(false)
["abbr"]=>
string(3) "CET"
}
["dst"]=>
array(5) {
["ts"]=>
int(1427590800)
["time"]=>
string(24) "2015-03-29T01:00:00+0000"
["offset"]=>
int(7200)
["isdst"]=>
bool(true)
["abbr"]=>
string(4) "CEST"
}
}
This uses timestamps of year 2015, whereas the the event is in year 2016 .
Later in chkTransition():
Mapi/chkTrans()-4 =
array(6) {
["dst_ts"]=>
int(1427590800)
["[dstw]"]=>
int(4)
["dst?"]=>
bool(false)
["std_ts"]=>
int(1445734800)
["[stdw]"]=>
int(5)
["std?"]=>
bool(true)
}
[dstw] is $offsets['stdweek'] of the return expression:
return self::_isNthOcurrenceOfWeekdayInMonth($dst['ts'], $offsets['dstweek']) &&
self::_isNthOcurrenceOfWeekdayInMonth($std['ts'], $offsets['stdweek']);
self::_isNthOcurrenceOfWeekdayInMonth($dst['ts'], $offsets['dstweek'])
with a timestamp $dst[ts] of year 2015 fails, because in 2015 DST starts
in week #5 of March, but would succeed with a $dst[ts] of year 2016.
"Africa/Algiers" in above dump does not has no DST setting and therefore
returns with:
if ((empty($offsets['dstmonth']) && (empty($dst) || empty($dst['isdst']))) ||
(empty($dst) && !empty($offsets['dstmonth']))) {
// Offset contains DST, but no dst to compare
return true;
}
and is therefore the listed.
--
Steffen
More information about the horde
mailing list