vobject
vobject copied to clipboard
Detecting timezone based on VTIMEZONE values
Since forever we've been mapping VTIMEZONE objects to PHP timezones based on the TZID, X-LIC-*
properties and others.
This system works pretty well, but every now and then we run across a VTIMEZONE
object that we don't support. For those we should really fall back on parsing VTIMEZONE objects and figuring which PHP timezone they map to based on their values.
See #247
There is a $15 open bounty on this issue. Add to the bounty at Bountysource.
+1 from me
Related to #44. Also some previous research here: https://gist.github.com/SimonSimCity/9950755
:+1:
We are experiencing a related issue with iCalendar attachments received from Microsoft Exchange Server 2010, where the sender is in the UK (Europe/London timezone). For some reason, Exchange sometimes uses a TZID
of 'GMT' for these users, but include STANDARD
and DAYLIGHT
DST rules and specifies Europe/London times.
I'm mentioning this because even though the VTIMEZONE
's TZID
is supported, it is incorrect. In this case at least, a PHP timezone found by examining the DST rules should be used in preference to that indicated by TZID
, and not as a fallback.
This example illustrates the problem:
// Event with 'GMT' timezone as received from Microsoft Exchange Server 2010
$eventGmt = <<<EVENT
BEGIN:VCALENDAR
METHOD:REQUEST
PRODID:Microsoft Exchange Server 2010
VERSION:2.0
BEGIN:VTIMEZONE
TZID:GMT Standard Time
BEGIN:STANDARD
DTSTART:16010101T020000
TZOFFSETFROM:+0100
TZOFFSETTO:+0000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010101T010000
TZOFFSETFROM:+0000
TZOFFSETTO:+0100
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
ORGANIZER;CN=Mr Person;SENT-BY="MAILTO:[email protected]":MAILTO
:[email protected]
ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=Mrs Colleague
s:MAILTO:[email protected]
DESCRIPTION;LANGUAGE=en-US:\n\n
SUMMARY;LANGUAGE=en-US:GMT Meeting
DTSTART;TZID=GMT Standard Time:20160807T080000
DTEND;TZID=GMT Standard Time:20160807T090000
UID:04000B16B00B1E50008200E00074C5B7101A82E0080005007B0F59FC1D101000000000000000
0100000002DA3DB6FC7061B4680DAEF792F5776F8
CLASS:PUBLIC
PRIORITY:5
DTSTAMP:20160608T150813Z
TRANSP:OPAQUE
STATUS:CONFIRMED
SEQUENCE:0
LOCATION;LANGUAGE=en-US:Meeting Venue
X-MICROSOFT-CDO-APPT-SEQUENCE:0
X-MICROSOFT-CDO-OWNERAPPTID:1896376288
X-MICROSOFT-CDO-BUSYSTATUS:TENTATIVE
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
X-MICROSOFT-CDO-IMPORTANCE:1
X-MICROSOFT-CDO-INSTTYPE:0
X-MICROSOFT-DISALLOW-COUNTER:FALSE
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:REMINDER
TRIGGER;RELATED=START:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
EVENT;
// Read the 'GMT' event
$cal = Sabre\VObject\Reader::read($eventGmt);
$vevent = current($cal->select('VEVENT'));
echo $vevent->DTSTART->getDateTime()->format('r').' '.$vevent->DTSTART->getDateTime()->getTimezone()->getName(). "<br>\n";
echo $vevent->DTEND->getDateTime()->format('r').' '.$vevent->DTEND->getDateTime()->getTimezone()->getName()."<br><br>\n\n";
// Read the same event but with Europe/London TZID
$cal = Sabre\VObject\Reader::read(str_replace('GMT Standard Time', 'Europe/London', $eventGmt));
$vevent = current($cal->select('VEVENT'));
echo $vevent->DTSTART->getDateTime()->format('r').' '.$vevent->DTSTART->getDateTime()->getTimezone()->getName()."<br>\n";
echo $vevent->DTEND->getDateTime()->format('r').' '.$vevent->DTEND->getDateTime()->getTimezone()->getName()."\n";
Makes sense! Having this in place would be hugely beneficial
@evert how would you suggest that we handle the getDateTime
method call on something like DTSTART
since we need a Timezone for that no? Seems like we would have to map the custom timezone definition to something that the php "knows" since it doesn't look like you can set transitions or anything on the DateTimeZone
standard php class without creating a custom class to extend it.
The goal of this enhancement would be to read the information from VTIMEZONE, and based on the rules specified in DAYLIGHT and STANDARD, we would try to find the closest match to an olson identifer.
So I would still want to map to PHP's DateTimeZone, but instead of making an educated guess based on some properties, getTransitions
and match it up with the data contained in VTIMEZONE.
But even for those cases I would only want to this if we don't have an exact match for TZID, because this system would definitely be a lot slower, and possibly less accurate. If an iCalendar object states TZID=Europe/Amsterdam
, I still want to assume that that's correct and ignore the information from VTIMEZONE
.
@evert if that is the case, wouldn't it be just much easier (although more hacky) to add a mapping table from the Exchange Identifiers (since Exchange seems to be the biggest violator) to the php ones as a simple workaround with a lot less work for now?
Exchange timezones are mapped here: https://github.com/fruux/sabre-vobject/blob/master/lib/timezonedata/exchangezones.php
@DominikTo @evert then I guess it is missing a few since I received one not mentioned there (I guess that was the point of this issue ... to automatically figure that out without needing to maintain the mapping). Want me to create a pull request for these additional mapping names or wait until it is solved via parsing the VTIMEZONE
records?
BEGIN:VCALENDAR
METHOD:PUBLISH
PRODID:Microsoft Exchange Server 2010
VERSION:2.0
X-WR-CALNAME:Calendar
BEGIN:VTIMEZONE
TZID:(UTC-05:00) Eastern Time (US & Canada)
BEGIN:STANDARD
DTSTART:16010101T020000
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010101T020000
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
DESCRIPTION:3 Homes for Builder requiring Photography & Walk through Video
of each home.
UID:040000003232E00074C5B7876A82E00800000000FD0CCC288FF8D101000000000000000
01000000006F59CB9230499438F3AB6F603BBFA69
SUMMARY:PHOTO & VIDEOS for Joe Smith
DTSTART;TZID="(UTC-05:00) Eastern Time (US & Canada)":20160823T100000
DTEND;TZID="(UTC-05:00) Eastern Time (US & Canada)":20160823T170000
CLASS:PUBLIC
PRIORITY:5
DTSTAMP:20160822T184151Z
TRANSP:OPAQUE
STATUS:CONFIRMED
SEQUENCE:0
LOCATION:
X-MICROSOFT-CDO-APPT-SEQUENCE:0
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
X-MICROSOFT-CDO-IMPORTANCE:1
X-MICROSOFT-CDO-INSTTYPE:0
X-MICROSOFT-DISALLOW-COUNTER:FALSE
END:VEVENT
END:VCALENDAR
The goal of this issue is to solve situations where the 'automatic mapping' fails. However, until then, I would be happy to receive patches or bug reports for obvious missing entries.
However, I'm doubting a little bit if the sample file you submitted here is correct. Exchange-generated iCalendar files usually have the X-MICROSOFT-CDO-TZID
property set, which appears to be stripped from your sample file.
So my answer to situations like this is always, please find out what software generates this file, so we can determine how widespread the issue is and based on that figure out if we need a workaround. Is this a reproducible case?
The reason this is important for me, is because I want to make absolutely sure we're not adding a workaround that we'll have to maintain for years to come for some persons script that does a poor job handing iCalendar. At the very least I must know what produces this.
Anyway, this is off-topic for this github issue, so please open a new one if you want to continue the conversation.
@evert The links that you and @jpirkey have posted above seem to indicate that the case I reported above (invites from Exchange in Europe/London timezone with TZID:GMT Standard Time
) should already be correctly handled, or am I misunderstanding?
Microsoft's Exchange Identifiers map "GMT Standard Time" to "Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London", and the Sabre mappings map "Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London" to "Europe/Lisbon" ie the correct timezone.
If this is correct, I can do some further investigations into why it's not working as expected and open a new issue if appropriate.
Thank you.
@lightstream I believe it works fine (at least for the scenarios I have encountered). The issue I reported was due to the timezone offset being prepended from some exchange installs: https://github.com/fruux/sabre-vobject/issues/344 .. so now we strip that off and try to find the identifier without it.
@jpirkey I read your linked issue. The problem we have is that invites received from Exchange with TZID:GMT Standard Time
are parsed into DateTime
objects with timezone of GMT, when they should have a timezone of Europe/London.
What I wasn't sure of was whether Sabre is applying the Exchange mappings from the link you posted, or just the ones in the file linked by @evert if that makes sense!
Just the ones in the the mapping files. Not only the file @evert linked to, but also the ones in the other mapping files under lib/timezonedata .. they are merged together to create one mapping in TimeZoneUtil::loadTzMaps
Thanks for that @jpirkey, gives me a starting point to investigate my issue further.
@lightstream it looks like the mapping is there in the windowszones.php file:
'GMT Standard Time' => 'Europe/London',
Thank you very much for all your help on this @jpirkey, it does look like it should be handling these timezones correctly.
I think my issue isn't relevant here after all. I will report it to the Kolab Calendar project (a roundcube plugin that uses Sabre).
Apologies to all for polluting this thread...
For future commenters, if you have a timezone related problem, and you're not sure how it pertains to this ticket, open a new one!
2.2.54 [RFC2445] Section 4.6.5, Time Zone Component V0058: The specification describes the VTIMEZONE component. Microsoft® Office Outlook® 2007, Microsoft® Outlook® 2010, Microsoft® Outlook® 15 Technical Preview On import, Outlook attempts to approximate VTIMEZONE components to a VTIMEZONE with one annually-recurring standard-to-Daylight Saving transition date, and one annually-recurring Daylight Saving-to-standard transition date. The approximation process is specified in [MS-OXCICAL] section 2.1.3.1.1.19. Only the following properties are used to approximate a VTIMEZONE, all other properties are ignored: TZID (in VTIMEZONE components), DTSTART (in DAYLIGHT or STANDARD components), RRULE (in DAYLIGHT or STANDARD components), TZOFFSETFROM (in DAYLIGHT or STANDARD components), and TZOFFSETTO (in DAYLIGHT or STANDARD components). If a time zone cannot be approximated or parsed, it is ignored.
I think this problem not just to add mapping, the reason is the Microsoft handle the timezone different others. Above section contents on the pdf of [MS-STANOICAL].