Open WhyNotHugo opened 2 years ago
It seems like it's incorrect to assume that TZID
is a time zone name. It's just an identifier used to refer to the timezone in the rest of the file. Here "local" is used, but "foo" or "bar" could be used as well.
The TZOFFSETFROM
and TZOFFSETTO
need to be used to parse the timezone instead.
I've been investigating how to address this a bit more. We need to parse the
VTIMEZONE
and create a Location
object.
There's no simple constructor for these, only LoadLocationFromTZData. It takes binary tzdata, from what I understand in tzfile(5) format.
Honestly, I think converting VTIMEZONE
into tzfile
into Location
is a bit
of too much dancing. Serialising to a binary format as an intermediate is a lot
of ridiculous complexity for no real value. But I'm not sure there's any other
way to create a Location
instance if there's no generic constructor, and most
of the API around Location (and zone, etc) is private.
I considered creating a location using FixedZone
. This would work great for
de-serialising, printing, etc, but sounds like it might result in more issues
further down the line:
Location
instance, since the
new time might have a different offset.VTIMEZONE
around.I can imagine the recurrence issues being the biggest dealbreaker.
Any suggestions?
I agree with all you've written, and I don't have good ideas to solve this.
I've proposed adding a new public constructor for Location
upstream (since the same issue is blocking other related projects too): https://github.com/golang/go/issues/49951
DTSTART;TZID="local;VALUE=DATE-TIME":20150315T123000
Aren’t the quotes wrong here? I mean, the only property parameter name is TZID and its value is local;VALUE=DATE-TIME
. For this value there is no definition in the VTIMEZONE component.
Looks like https://github.com/martin-sucha/timezones/blob/main/timezones.go can be used as a reference to encode system TZDATA into VTIMEZONE.
Also, https://github.com/martin-sucha/vtimezone2tzif can be used to convert VTIMEZONE into TZDATA into time.Location
.
The process isn't ideal; there's lots of pointless conversions to intermediate states, but it's the best that can be done with go's existing API, and it seems that changing isn't desirable right now.
This sadly is a feature required to parse Office365 calendars too. The Exchange Server always embeds a timezone into the calendar with kinda random names (Romance Standard Time
in the test calendar below, W. Europe Standard Time
in my personal one even though both of them are just Europe/Berlin time calendars) causing a parse using this library to fail as of every event having a TZID
reference to the embedded (IMHO useless and kinda broken) timezone.
I had the same issue. I ended up pre-processing the calendar events, deleting TZID parameters which can't be loaded with LoadLocation
. This is really hacky, it will produce wrong results, and I should probably cache LoadLocation results, but might still be helpful for someone:
for _, propid := range []string{ical.PropDateTimeStart, ical.PropDateTimeEnd} {
prop := event.Props.Get(propid)
if prop != nil {
tzid := prop.Params.Get(ical.PropTimezoneID)
if tzid != "" {
_, err := time.LoadLocation(tzid)
if err != nil {
prop.Params.Del(ical.PropTimezoneID)
}
}
}
}
start, err := event.DateTimeStart(location) // works now
// ...
Looks like https://github.com/martin-sucha/timezones/blob/main/timezones.go can be used as a reference to encode system TZDATA into VTIMEZONE.
Also, https://github.com/martin-sucha/vtimezone2tzif can be used to convert VTIMEZONE into TZDATA into
time.Location
.The process isn't ideal; there's lots of pointless conversions to intermediate states, but it's the best that can be done with go's existing API, and it seems that changing isn't desirable right now.
Did you find how to generate the VTIMEZONE from a time.Location, if you happened to look deeper into it?
I'm stuck on how to split the timezones.Zone.Offset
into TZOFFSETFROM
and TZOFFSETTO
.
And also how to get a timezones.Template
from a timezone name (e.g. America/New_York
). What i'm thinking is looking into a tzif binary and finding the timezone's data that way, and then passing it to timezones.LoadTZData()
.
Sorry, I didn't look into this any further and don't have anything new to add.
Looking at the timezones
package that you linked, it sounds like you're going to have to parse the VTIMEZONE
yourself and create the new instance.
I've a lot of events that look like this:
This doesn't seem to be invalid; the timezone is specific in the file. However, parsing these returns error
unknown time zone local
.