[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