diff --git a/CHANGELOG.md b/CHANGELOG.md index 01f9cbaba..e94bb28c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Handle the case where an email or event cannot be retrieved from Exchange due to an `ErrorCorruptData` error. Corso will skip over the item but report it in the backup summary. - Emails attached within other emails are now correctly exported - Gracefully handle email and post attachments without name when exporting to eml +- Use correct timezone for event start and end times in Exchange exports (helps fix issues in relative recurrence patterns) ## [v0.19.0] (beta) - 2024-02-06 diff --git a/src/internal/converters/eml/eml_test.go b/src/internal/converters/eml/eml_test.go index f9ffbb9ca..a1c0fd5db 100644 --- a/src/internal/converters/eml/eml_test.go +++ b/src/internal/converters/eml/eml_test.go @@ -266,11 +266,11 @@ func (suite *EMLUnitSuite) TestConvert_eml_ics() { assert.Equal( t, - msg.GetCreatedDateTime().Format(ics.ICalDateTimeFormat), + msg.GetCreatedDateTime().Format(ics.ICalDateTimeFormatUTC), event.GetProperty(ical.ComponentPropertyCreated).Value) assert.Equal( t, - msg.GetLastModifiedDateTime().Format(ics.ICalDateTimeFormat), + msg.GetLastModifiedDateTime().Format(ics.ICalDateTimeFormatUTC), event.GetProperty(ical.ComponentPropertyLastModified).Value) st, err := ics.GetUTCTime( @@ -285,11 +285,11 @@ func (suite *EMLUnitSuite) TestConvert_eml_ics() { assert.Equal( t, - st.Format(ics.ICalDateTimeFormat), + st.Format(ics.ICalDateTimeFormatUTC), event.GetProperty(ical.ComponentPropertyDtStart).Value) assert.Equal( t, - et.Format(ics.ICalDateTimeFormat), + et.Format(ics.ICalDateTimeFormatUTC), event.GetProperty(ical.ComponentPropertyDtEnd).Value) tos := msg.GetToRecipients() diff --git a/src/internal/converters/ics/consts.go b/src/internal/converters/ics/consts.go index b8b25e3cb..4bf7e2be0 100644 --- a/src/internal/converters/ics/consts.go +++ b/src/internal/converters/ics/consts.go @@ -166,3 +166,20 @@ var GraphTimeZoneToTZ = map[string]string{ "Yukon Standard Time": "America/Whitehorse", "tzone://Microsoft/Utc": "Etc/UTC", } + +// Map from alternatives to the canonical time zone name +// There mapping are currently generated by manually going on the +// values in the GraphTimeZoneToTZ which is not available in the tzdb +var CanonicalTimeZoneMap = map[string]string{ + "Africa/Asmara": "Africa/Asmera", + "Asia/Calcutta": "Asia/Kolkata", + "Asia/Rangoon": "Asia/Yangon", + "Asia/Saigon": "Asia/Ho_Chi_Minh", + "Europe/Kiev": "Europe/Kyiv", + "Europe/Warsaw": "Europe/Warszawa", + "America/Buenos_Aires": "America/Argentina/Buenos_Aires", + "America/Godthab": "America/Nuuk", + // NOTE: "Atlantic/Raykjavik" missing in tzdb but is in MS list + + "Etc/UTC": "UTC", // simplifying the time zone name +} diff --git a/src/internal/converters/ics/ics.go b/src/internal/converters/ics/ics.go index af33ae1ac..dc9e1e2e9 100644 --- a/src/internal/converters/ics/ics.go +++ b/src/internal/converters/ics/ics.go @@ -17,6 +17,7 @@ import ( "github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/str" + "github.com/alcionai/corso/src/internal/converters/ics/tzdata" "github.com/alcionai/corso/src/pkg/dttm" "github.com/alcionai/corso/src/pkg/logger" "github.com/alcionai/corso/src/pkg/services/m365/api" @@ -32,8 +33,9 @@ import ( // TODO locations: https://github.com/alcionai/corso/issues/5003 const ( - ICalDateTimeFormat = "20060102T150405Z" - ICalDateFormat = "20060102" + ICalDateTimeFormat = "20060102T150405" + ICalDateTimeFormatUTC = "20060102T150405Z" + ICalDateFormat = "20060102" ) func keyValues(key, value string) *ics.KeyValues { @@ -173,6 +175,17 @@ func getRecurrencePattern( recurComponents = append(recurComponents, "BYDAY="+prefix+strings.Join(dowComponents, ",")) } + // This is necessary to compute when weekly events recur + fdow := pat.GetFirstDayOfWeek() + if fdow != nil { + icalday, ok := GraphToICalDOW[fdow.String()] + if !ok { + return "", clues.NewWC(ctx, "unknown first day of week").With("day", fdow) + } + + recurComponents = append(recurComponents, "WKST="+icalday) + } + rrange := recurrence.GetRangeEscaped() if rrange != nil { switch ptr.Val(rrange.GetTypeEscaped()) { @@ -196,7 +209,7 @@ func getRecurrencePattern( return "", clues.WrapWC(ctx, err, "parsing end time") } - recurComponents = append(recurComponents, "UNTIL="+endTime.Format(ICalDateTimeFormat)) + recurComponents = append(recurComponents, "UNTIL="+endTime.Format(ICalDateTimeFormatUTC)) } case models.NOEND_RECURRENCERANGETYPE: // Nothing to do @@ -225,10 +238,15 @@ func FromEventable(ctx context.Context, event models.Eventable) (string, error) cal := ics.NewCalendar() cal.SetProductId("-//Alcion//Corso") // Does this have to be customizable? + err := addTimeZoneComponents(ctx, cal, event) + if err != nil { + return "", clues.Wrap(err, "adding timezone components") + } + id := ptr.Val(event.GetId()) iCalEvent := cal.AddEvent(id) - err := updateEventProperties(ctx, event, iCalEvent) + err = updateEventProperties(ctx, event, iCalEvent) if err != nil { return "", clues.Wrap(err, "updating event properties") } @@ -259,7 +277,7 @@ func FromEventable(ctx context.Context, event models.Eventable) (string, error) exICalEvent := cal.AddEvent(id) start := exception.GetOriginalStart() // will always be in UTC - exICalEvent.AddProperty(ics.ComponentProperty(ics.PropertyRecurrenceId), start.Format(ICalDateTimeFormat)) + exICalEvent.AddProperty(ics.ComponentProperty(ics.PropertyRecurrenceId), start.Format(ICalDateTimeFormatUTC)) err = updateEventProperties(ctx, exception, exICalEvent) if err != nil { @@ -270,6 +288,91 @@ func FromEventable(ctx context.Context, event models.Eventable) (string, error) return cal.Serialize(), nil } +func getTZDataKeyValues(ctx context.Context, timezone string) (map[string]string, error) { + template, ok := tzdata.TZData[timezone] + if !ok { + return nil, clues.NewWC(ctx, "timezone not found in tz database"). + With("timezone", timezone) + } + + keyValues := map[string]string{} + + for _, line := range strings.Split(template, "\n") { + splits := strings.SplitN(line, ":", 2) + if len(splits) != 2 { + return nil, clues.NewWC(ctx, "invalid tzdata line"). + With("line", line). + With("timezone", timezone) + } + + keyValues[splits[0]] = splits[1] + } + + return keyValues, nil +} + +func addTimeZoneComponents(ctx context.Context, cal *ics.Calendar, event models.Eventable) error { + // Handling of timezone get a bit tricky when we have to deal with + // relative recurrence. The issue comes up when we set a recurrence + // to be something like "repeat every 3rd Tuesday". Tuesday in UTC + // and in IST will be different and so we cannot just always use UTC. + // + // The way this is solved is by using the timezone in the + // recurrence for start and end timezones as we have to use UTC + // for UNTIL(mostly). + // https://www.rfc-editor.org/rfc/rfc5545#section-3.3.10 + timezone, err := getRecurrenceTimezone(ctx, event) + if err != nil { + return clues.Stack(err) + } + + if timezone != time.UTC { + kvs, err := getTZDataKeyValues(ctx, timezone.String()) + if err != nil { + return clues.Stack(err) + } + + tz := cal.AddTimezone(timezone.String()) + + for k, v := range kvs { + tz.AddProperty(ics.ComponentProperty(k), v) + } + } + + return nil +} + +// getRecurrenceTimezone get the timezone specified by the recurrence +// in the calendar. It does a normalization pass where we always convert +// the timezone to the value in tzdb If we don't have a recurrence +// timezone, we don't have to use a specific timezone in the export and +// is safe to return UTC from this method. +func getRecurrenceTimezone(ctx context.Context, event models.Eventable) (*time.Location, error) { + if event.GetRecurrence() != nil { + timezone := ptr.Val(event.GetRecurrence().GetRangeEscaped().GetRecurrenceTimeZone()) + + ctz, ok := GraphTimeZoneToTZ[timezone] + if ok { + timezone = ctz + } + + cannon, ok := CanonicalTimeZoneMap[timezone] + if ok { + timezone = cannon + } + + loc, err := time.LoadLocation(timezone) + if err != nil { + return nil, clues.WrapWC(ctx, err, "unknown timezone"). + With("timezone", timezone) + } + + return loc, nil + } + + return time.UTC, nil +} + func isASCII(s string) bool { for _, c := range s { if c > unicode.MaxASCII { @@ -299,6 +402,11 @@ func updateEventProperties(ctx context.Context, event models.Eventable, iCalEven iCalEvent.SetModifiedAt(ptr.Val(modified)) } + timezone, err := getRecurrenceTimezone(ctx, event) + if err != nil { + return err + } + // DTSTART - https://www.rfc-editor.org/rfc/rfc5545#section-3.8.2.4 allDay := ptr.Val(event.GetIsAllDay()) startString := event.GetStart().GetDateTime() @@ -310,11 +418,7 @@ func updateEventProperties(ctx context.Context, event models.Eventable, iCalEven return clues.WrapWC(ctx, err, "parsing start time") } - if allDay { - iCalEvent.SetStartAt(start, ics.WithValue(string(ics.ValueDataTypeDate))) - } else { - iCalEvent.SetStartAt(start) - } + addTime(iCalEvent, ics.ComponentPropertyDtStart, start, allDay, timezone) } // DTEND - https://www.rfc-editor.org/rfc/rfc5545#section-3.8.2.2 @@ -327,11 +431,7 @@ func updateEventProperties(ctx context.Context, event models.Eventable, iCalEven return clues.WrapWC(ctx, err, "parsing end time") } - if allDay { - iCalEvent.SetEndAt(end, ics.WithValue(string(ics.ValueDataTypeDate))) - } else { - iCalEvent.SetEndAt(end) - } + addTime(iCalEvent, ics.ComponentPropertyDtEnd, end, allDay, timezone) } recurrence := event.GetRecurrence() @@ -630,6 +730,26 @@ func updateEventProperties(ctx context.Context, event models.Eventable, iCalEven return nil } +func addTime(iCalEvent *ics.VEvent, prop ics.ComponentProperty, tm time.Time, allDay bool, tzLoc *time.Location) { + if allDay { + if tzLoc == time.UTC { + iCalEvent.SetProperty(prop, tm.Format(ICalDateFormat), ics.WithValue(string(ics.ValueDataTypeDate))) + } else { + iCalEvent.SetProperty( + prop, + tm.In(tzLoc).Format(ICalDateFormat), + ics.WithValue(string(ics.ValueDataTypeDate)), + keyValues("TZID", tzLoc.String())) + } + } else { + if tzLoc == time.UTC { + iCalEvent.SetProperty(prop, tm.Format(ICalDateTimeFormatUTC)) + } else { + iCalEvent.SetProperty(prop, tm.In(tzLoc).Format(ICalDateTimeFormat), keyValues("TZID", tzLoc.String())) + } + } +} + func getCancelledDates(ctx context.Context, event models.Eventable) ([]time.Time, error) { dateStrings, err := api.GetCancelledEventDateStrings(event) if err != nil { diff --git a/src/internal/converters/ics/ics_test.go b/src/internal/converters/ics/ics_test.go index 0e2c24a78..937e8e1c9 100644 --- a/src/internal/converters/ics/ics_test.go +++ b/src/internal/converters/ics/ics_test.go @@ -13,6 +13,7 @@ import ( "testing" "time" + ics "github.com/arran4/golang-ical" "github.com/microsoft/kiota-abstractions-go/serialization" kjson "github.com/microsoft/kiota-serialization-json-go" "github.com/microsoftgraph/msgraph-sdk-go/models" @@ -21,6 +22,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/alcionai/corso/src/internal/common/ptr" + "github.com/alcionai/corso/src/internal/converters/ics/tzdata" "github.com/alcionai/corso/src/internal/tester" ) @@ -32,7 +34,7 @@ func TestICSUnitSuite(t *testing.T) { suite.Run(t, &ICSUnitSuite{Suite: tester.NewUnitSuite(t)}) } -func (suite *ICSUnitSuite) TestGetLocationString() { +func (s *ICSUnitSuite) TestGetLocationString() { table := []struct { name string loc func() models.Locationable @@ -110,13 +112,13 @@ func (suite *ICSUnitSuite) TestGetLocationString() { } for _, tt := range table { - suite.Run(tt.name, func() { - assert.Equal(suite.T(), tt.expect, getLocationString(tt.loc())) + s.Run(tt.name, func() { + assert.Equal(s.T(), tt.expect, getLocationString(tt.loc())) }) } } -func (suite *ICSUnitSuite) TestGetUTCTime() { +func (s *ICSUnitSuite) TestGetUTCTime() { table := []struct { name string timestamp string @@ -162,18 +164,18 @@ func (suite *ICSUnitSuite) TestGetUTCTime() { } for _, tt := range table { - suite.Run(tt.name, func() { + s.Run(tt.name, func() { t, err := GetUTCTime(tt.timestamp, tt.timezone) - tt.errCheck(suite.T(), err) + tt.errCheck(s.T(), err) if !tt.time.Equal(time.Time{}) { - assert.Equal(suite.T(), tt.time, t) + assert.Equal(s.T(), tt.time, t) } }) } } -func (suite *ICSUnitSuite) TestGetRecurrencePattern() { +func (s *ICSUnitSuite) TestGetRecurrencePattern() { table := []struct { name string recurrence func() models.PatternedRecurrenceable @@ -187,16 +189,37 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { pat := models.NewRecurrencePattern() typ, err := models.ParseRecurrencePatternType("daily") - require.NoError(suite.T(), err) + require.NoError(s.T(), err) pat.SetTypeEscaped(typ.(*models.RecurrencePatternType)) pat.SetInterval(ptr.To(int32(1))) + pat.SetFirstDayOfWeek(ptr.To(models.SUNDAY_DAYOFWEEK)) rec.SetPattern(pat) return rec }, - expect: "FREQ=DAILY;INTERVAL=1", + expect: "FREQ=DAILY;INTERVAL=1;WKST=SU", + errCheck: require.NoError, + }, + { + name: "daily different start of week", + recurrence: func() models.PatternedRecurrenceable { + rec := models.NewPatternedRecurrence() + pat := models.NewRecurrencePattern() + + typ, err := models.ParseRecurrencePatternType("daily") + require.NoError(s.T(), err) + + pat.SetTypeEscaped(typ.(*models.RecurrencePatternType)) + pat.SetInterval(ptr.To(int32(1))) + pat.SetFirstDayOfWeek(ptr.To(models.MONDAY_DAYOFWEEK)) + + rec.SetPattern(pat) + + return rec + }, + expect: "FREQ=DAILY;INTERVAL=1;WKST=MO", errCheck: require.NoError, }, { @@ -206,15 +229,16 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { pat := models.NewRecurrencePattern() typ, err := models.ParseRecurrencePatternType("daily") - require.NoError(suite.T(), err) + require.NoError(s.T(), err) pat.SetTypeEscaped(typ.(*models.RecurrencePatternType)) pat.SetInterval(ptr.To(int32(1))) + pat.SetFirstDayOfWeek(ptr.To(models.SUNDAY_DAYOFWEEK)) rng := models.NewRecurrenceRange() rrtype, err := models.ParseRecurrenceRangeType("endDate") - require.NoError(suite.T(), err) + require.NoError(s.T(), err) rng.SetTypeEscaped(rrtype.(*models.RecurrenceRangeType)) @@ -227,7 +251,7 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { return rec }, - expect: "FREQ=DAILY;INTERVAL=1;UNTIL=20210101T182959Z", + expect: "FREQ=DAILY;INTERVAL=1;WKST=SU;UNTIL=20210101T182959Z", errCheck: require.NoError, }, { @@ -237,16 +261,17 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { pat := models.NewRecurrencePattern() typ, err := models.ParseRecurrencePatternType("weekly") - require.NoError(suite.T(), err) + require.NoError(s.T(), err) pat.SetTypeEscaped(typ.(*models.RecurrencePatternType)) pat.SetInterval(ptr.To(int32(1))) + pat.SetFirstDayOfWeek(ptr.To(models.SUNDAY_DAYOFWEEK)) rec.SetPattern(pat) return rec }, - expect: "FREQ=WEEKLY;INTERVAL=1", + expect: "FREQ=WEEKLY;INTERVAL=1;WKST=SU", errCheck: require.NoError, }, { @@ -256,15 +281,16 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { pat := models.NewRecurrencePattern() typ, err := models.ParseRecurrencePatternType("weekly") - require.NoError(suite.T(), err) + require.NoError(s.T(), err) pat.SetTypeEscaped(typ.(*models.RecurrencePatternType)) pat.SetInterval(ptr.To(int32(1))) + pat.SetFirstDayOfWeek(ptr.To(models.SUNDAY_DAYOFWEEK)) rng := models.NewRecurrenceRange() rrtype, err := models.ParseRecurrenceRangeType("endDate") - require.NoError(suite.T(), err) + require.NoError(s.T(), err) rng.SetTypeEscaped(rrtype.(*models.RecurrenceRangeType)) @@ -277,7 +303,7 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { return rec }, - expect: "FREQ=WEEKLY;INTERVAL=1;UNTIL=20210101T235959Z", + expect: "FREQ=WEEKLY;INTERVAL=1;WKST=SU;UNTIL=20210101T235959Z", errCheck: require.NoError, }, { @@ -287,15 +313,16 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { pat := models.NewRecurrencePattern() typ, err := models.ParseRecurrencePatternType("weekly") - require.NoError(suite.T(), err) + require.NoError(s.T(), err) pat.SetTypeEscaped(typ.(*models.RecurrencePatternType)) pat.SetInterval(ptr.To(int32(1))) + pat.SetFirstDayOfWeek(ptr.To(models.SUNDAY_DAYOFWEEK)) rng := models.NewRecurrenceRange() rrtype, err := models.ParseRecurrenceRangeType("numbered") - require.NoError(suite.T(), err) + require.NoError(s.T(), err) rng.SetTypeEscaped(rrtype.(*models.RecurrenceRangeType)) @@ -307,7 +334,7 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { return rec }, - expect: "FREQ=WEEKLY;INTERVAL=1;COUNT=10", + expect: "FREQ=WEEKLY;INTERVAL=1;WKST=SU;COUNT=10", errCheck: require.NoError, }, { @@ -317,10 +344,11 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { pat := models.NewRecurrencePattern() typ, err := models.ParseRecurrencePatternType("weekly") - require.NoError(suite.T(), err) + require.NoError(s.T(), err) pat.SetTypeEscaped(typ.(*models.RecurrencePatternType)) pat.SetInterval(ptr.To(int32(1))) + pat.SetFirstDayOfWeek(ptr.To(models.SUNDAY_DAYOFWEEK)) days := []models.DayOfWeek{ models.MONDAY_DAYOFWEEK, @@ -334,7 +362,7 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { return rec }, - expect: "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,WE,TH", + expect: "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,WE,TH;WKST=SU", errCheck: require.NoError, }, { @@ -344,16 +372,17 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { pat := models.NewRecurrencePattern() typ, err := models.ParseRecurrencePatternType("daily") - require.NoError(suite.T(), err) + require.NoError(s.T(), err) pat.SetTypeEscaped(typ.(*models.RecurrencePatternType)) pat.SetInterval(ptr.To(int32(2))) + pat.SetFirstDayOfWeek(ptr.To(models.SUNDAY_DAYOFWEEK)) rec.SetPattern(pat) return rec }, - expect: "FREQ=DAILY;INTERVAL=2", + expect: "FREQ=DAILY;INTERVAL=2;WKST=SU", errCheck: require.NoError, }, { @@ -363,10 +392,11 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { pat := models.NewRecurrencePattern() typ, err := models.ParseRecurrencePatternType("absoluteMonthly") - require.NoError(suite.T(), err) + require.NoError(s.T(), err) pat.SetTypeEscaped(typ.(*models.RecurrencePatternType)) pat.SetInterval(ptr.To(int32(1))) + pat.SetFirstDayOfWeek(ptr.To(models.SUNDAY_DAYOFWEEK)) pat.SetDayOfMonth(ptr.To(int32(5))) @@ -374,7 +404,7 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { return rec }, - expect: "FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=5", + expect: "FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=5;WKST=SU", errCheck: require.NoError, }, { @@ -384,10 +414,11 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { pat := models.NewRecurrencePattern() typ, err := models.ParseRecurrencePatternType("absoluteYearly") - require.NoError(suite.T(), err) + require.NoError(s.T(), err) pat.SetTypeEscaped(typ.(*models.RecurrencePatternType)) pat.SetInterval(ptr.To(int32(3))) + pat.SetFirstDayOfWeek(ptr.To(models.SUNDAY_DAYOFWEEK)) pat.SetMonth(ptr.To(int32(8))) @@ -395,7 +426,7 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { return rec }, - expect: "FREQ=YEARLY;INTERVAL=3;BYMONTH=8", + expect: "FREQ=YEARLY;INTERVAL=3;BYMONTH=8;WKST=SU", errCheck: require.NoError, }, { @@ -405,37 +436,38 @@ func (suite *ICSUnitSuite) TestGetRecurrencePattern() { pat := models.NewRecurrencePattern() typ, err := models.ParseRecurrencePatternType("relativeYearly") - require.NoError(suite.T(), err) + require.NoError(s.T(), err) pat.SetTypeEscaped(typ.(*models.RecurrencePatternType)) pat.SetInterval(ptr.To(int32(1))) + pat.SetFirstDayOfWeek(ptr.To(models.SUNDAY_DAYOFWEEK)) pat.SetMonth(ptr.To(int32(8))) pat.SetDaysOfWeek([]models.DayOfWeek{models.FRIDAY_DAYOFWEEK}) wi, err := models.ParseWeekIndex("first") - require.NoError(suite.T(), err) + require.NoError(s.T(), err) pat.SetIndex(wi.(*models.WeekIndex)) rec.SetPattern(pat) return rec }, - expect: "FREQ=YEARLY;INTERVAL=1;BYMONTH=8;BYDAY=1FR", + expect: "FREQ=YEARLY;INTERVAL=1;BYMONTH=8;BYDAY=1FR;WKST=SU", errCheck: require.NoError, }, // TODO(meain): could still use more tests for edge cases of time } for _, tt := range table { - suite.Run(tt.name, func() { - ctx, flush := tester.NewContext(suite.T()) + s.Run(tt.name, func() { + ctx, flush := tester.NewContext(s.T()) defer flush() rec, err := getRecurrencePattern(ctx, tt.recurrence()) - tt.errCheck(suite.T(), err) + tt.errCheck(s.T(), err) - assert.Equal(suite.T(), tt.expect, rec) + assert.Equal(s.T(), tt.expect, rec) }) } } @@ -460,8 +492,8 @@ func baseEvent() *models.Event { return e } -func (suite *ICSUnitSuite) TestEventConversion() { - t := suite.T() +func (s *ICSUnitSuite) TestEventConversion() { + t := s.T() table := []struct { name string @@ -546,14 +578,19 @@ func (suite *ICSUnitSuite) TestEventConversion() { rec := models.NewPatternedRecurrence() pat := models.NewRecurrencePattern() + rng := models.NewRecurrenceRange() typ, err := models.ParseRecurrencePatternType("daily") require.NoError(t, err) pat.SetTypeEscaped(typ.(*models.RecurrencePatternType)) pat.SetInterval(ptr.To(int32(1))) + pat.SetFirstDayOfWeek(ptr.To(models.SUNDAY_DAYOFWEEK)) + + rng.SetRecurrenceTimeZone(ptr.To("UTC")) rec.SetPattern(pat) + rec.SetRangeEscaped(rng) e.SetRecurrence(rec) @@ -830,8 +867,8 @@ func (suite *ICSUnitSuite) TestEventConversion() { } for _, tt := range table { - suite.Run(tt.name, func() { - t := suite.T() + s.Run(tt.name, func() { + t := s.T() ctx, flush := tester.NewContext(t) defer flush() @@ -881,8 +918,8 @@ func checkAttendee(t *testing.T, out, check, msg string) { assert.ElementsMatch(t, as, bs, fmt.Sprintf("fields %s", msg)) } -func (suite *ICSUnitSuite) TestAttendees() { - t := suite.T() +func (s *ICSUnitSuite) TestAttendees() { + t := s.T() table := []struct { name string @@ -949,8 +986,8 @@ func (suite *ICSUnitSuite) TestAttendees() { } for _, tt := range table { - suite.Run(tt.name, func() { - t := suite.T() + s.Run(tt.name, func() { + t := s.T() ctx, flush := tester.NewContext(t) defer flush() @@ -1071,8 +1108,8 @@ func checkAttachment(t *testing.T, out, check, msg string) { assert.ElementsMatch(t, as, bs, fmt.Sprintf("fields %s", msg)) } -func (suite *ICSUnitSuite) TestAttachments() { - t := suite.T() +func (s *ICSUnitSuite) TestAttachments() { + t := s.T() type attachment struct { cid string // contentid @@ -1128,8 +1165,8 @@ func (suite *ICSUnitSuite) TestAttachments() { } for _, tt := range table { - suite.Run(tt.name, func() { - t := suite.T() + s.Run(tt.name, func() { + t := s.T() ctx, flush := tester.NewContext(t) defer flush() @@ -1172,7 +1209,7 @@ func (suite *ICSUnitSuite) TestAttachments() { } } -func (suite *ICSUnitSuite) TestCancellations() { +func (s *ICSUnitSuite) TestCancellations() { table := []struct { name string cancelledIds []string @@ -1196,8 +1233,8 @@ func (suite *ICSUnitSuite) TestCancellations() { } for _, tt := range table { - suite.Run(tt.name, func() { - t := suite.T() + s.Run(tt.name, func() { + t := s.T() ctx, flush := tester.NewContext(t) defer flush() @@ -1260,7 +1297,7 @@ func eventToJSON(e *models.Event) ([]byte, error) { return bts, err } -func (suite *ICSUnitSuite) TestEventExceptions() { +func (s *ICSUnitSuite) TestEventExceptions() { table := []struct { name string event func() *models.Event @@ -1282,7 +1319,7 @@ func (suite *ICSUnitSuite) TestEventExceptions() { exception.SetEnd(newEnd) parsed, err := eventToMap(exception) - require.NoError(suite.T(), err, "parsing exception") + require.NoError(s.T(), err, "parsing exception") // add exception event to additional data e.SetAdditionalData(map[string]any{ @@ -1301,15 +1338,15 @@ func (suite *ICSUnitSuite) TestEventExceptions() { } } - assert.Equal(suite.T(), 2, events, "number of events") + assert.Equal(s.T(), 2, events, "number of events") - assert.Contains(suite.T(), out, "RECURRENCE-ID:20210101T120000Z", "recurrence id") + assert.Contains(s.T(), out, "RECURRENCE-ID:20210101T120000Z", "recurrence id") - assert.Contains(suite.T(), out, "SUMMARY:Subject", "original event") - assert.Contains(suite.T(), out, "SUMMARY:Exception", "exception event") + assert.Contains(s.T(), out, "SUMMARY:Subject", "original event") + assert.Contains(s.T(), out, "SUMMARY:Exception", "exception event") - assert.Contains(suite.T(), out, "DTSTART:20210101T130000Z", "new start time") - assert.Contains(suite.T(), out, "DTEND:20210101T140000Z", "new end time") + assert.Contains(s.T(), out, "DTSTART:20210101T130000Z", "new start time") + assert.Contains(s.T(), out, "DTEND:20210101T140000Z", "new end time") }, }, { @@ -1338,10 +1375,10 @@ func (suite *ICSUnitSuite) TestEventExceptions() { exception2.SetEnd(newEnd) parsed1, err := eventToMap(exception1) - require.NoError(suite.T(), err, "parsing exception 1") + require.NoError(s.T(), err, "parsing exception 1") parsed2, err := eventToMap(exception2) - require.NoError(suite.T(), err, "parsing exception 2") + require.NoError(s.T(), err, "parsing exception 2") // add exception event to additional data e.SetAdditionalData(map[string]any{ @@ -1360,36 +1397,230 @@ func (suite *ICSUnitSuite) TestEventExceptions() { } } - assert.Equal(suite.T(), 3, events, "number of events") + assert.Equal(s.T(), 3, events, "number of events") - assert.Contains(suite.T(), out, "RECURRENCE-ID:20210101T120000Z", "recurrence id 1") - assert.Contains(suite.T(), out, "RECURRENCE-ID:20210102T120000Z", "recurrence id 2") + assert.Contains(s.T(), out, "RECURRENCE-ID:20210101T120000Z", "recurrence id 1") + assert.Contains(s.T(), out, "RECURRENCE-ID:20210102T120000Z", "recurrence id 2") - assert.Contains(suite.T(), out, "SUMMARY:Subject", "original event") - assert.Contains(suite.T(), out, "SUMMARY:Exception 1", "exception event 1") - assert.Contains(suite.T(), out, "SUMMARY:Exception 2", "exception event 2") + assert.Contains(s.T(), out, "SUMMARY:Subject", "original event") + assert.Contains(s.T(), out, "SUMMARY:Exception 1", "exception event 1") + assert.Contains(s.T(), out, "SUMMARY:Exception 2", "exception event 2") - assert.Contains(suite.T(), out, "DTSTART:20210101T130000Z", "new start time 1") - assert.Contains(suite.T(), out, "DTEND:20210101T140000Z", "new end time 1") + assert.Contains(s.T(), out, "DTSTART:20210101T130000Z", "new start time 1") + assert.Contains(s.T(), out, "DTEND:20210101T140000Z", "new end time 1") - assert.Contains(suite.T(), out, "DTSTART:20210102T130000Z", "new start time 2") - assert.Contains(suite.T(), out, "DTEND:20210102T140000Z", "new end time 2") + assert.Contains(s.T(), out, "DTSTART:20210102T130000Z", "new start time 2") + assert.Contains(s.T(), out, "DTEND:20210102T140000Z", "new end time 2") }, }, } for _, tt := range table { - suite.Run(tt.name, func() { - ctx, flush := tester.NewContext(suite.T()) + s.Run(tt.name, func() { + ctx, flush := tester.NewContext(s.T()) defer flush() bts, err := eventToJSON(tt.event()) - require.NoError(suite.T(), err, "getting serialized content") + require.NoError(s.T(), err, "getting serialized content") out, err := FromJSON(ctx, bts) - require.NoError(suite.T(), err, "converting to ics") + require.NoError(s.T(), err, "converting to ics") tt.check(out) }) } } + +func (s *ICSUnitSuite) TestGetRecurrenceTimezone() { + table := []struct { + name string + intz string + outtz string + }{ + { + name: "empty", + intz: "", + outtz: "UTC", + }, + { + name: "utc", + intz: "UTC", + outtz: "UTC", + }, + { + name: "simple", + intz: "Asia/Kolkata", + outtz: "Asia/Kolkata", + }, + { + name: "windows tz", + intz: "India Standard Time", + outtz: "Asia/Kolkata", + }, + { + name: "non canonical", + intz: "Asia/Calcutta", + outtz: "Asia/Kolkata", + }, + } + + for _, tt := range table { + s.Run(tt.name, func() { + ctx, flush := tester.NewContext(s.T()) + defer flush() + + event := baseEvent() + if len(tt.intz) > 0 { + recur := models.NewPatternedRecurrence() + rp := models.NewRecurrenceRange() + rp.SetRecurrenceTimeZone(ptr.To(tt.intz)) + + recur.SetRangeEscaped(rp) + event.SetRecurrence(recur) + } + + timezone, err := getRecurrenceTimezone(ctx, event) + require.NoError(s.T(), err) + assert.Equal(s.T(), tt.outtz, timezone.String()) + }) + } +} + +func (s *ICSUnitSuite) TestAddTimezoneComponents() { + event := baseEvent() + recur := models.NewPatternedRecurrence() + rp := models.NewRecurrenceRange() + rp.SetRecurrenceTimeZone(ptr.To("Asia/Kolkata")) + + recur.SetRangeEscaped(rp) + event.SetRecurrence(recur) + + ctx, flush := tester.NewContext(s.T()) + defer flush() + + cal := ics.NewCalendar() + + err := addTimeZoneComponents(ctx, cal, event) + require.NoError(s.T(), err) + + text := cal.Serialize() + assert.Contains(s.T(), text, "BEGIN:VTIMEZONE", "beginning of timezone") + assert.Contains(s.T(), text, "TZID:Asia/Kolkata", "timezone id") + assert.Contains(s.T(), text, "END:VTIMEZONE", "end of timezone") +} + +func (s *ICSUnitSuite) TestAddTime() { + locak, err := time.LoadLocation("Asia/Kolkata") + require.NoError(s.T(), err) + + table := []struct { + name string + prop ics.ComponentProperty + time time.Time + allDay bool + loc *time.Location + exp string + }{ + { + name: "utc", + prop: ics.ComponentPropertyDtStart, + time: time.Date(2021, 1, 2, 3, 4, 5, 0, time.UTC), + allDay: false, + loc: time.UTC, + exp: "DTSTART:20210102T030405Z", + }, + { + name: "local", + prop: ics.ComponentPropertyDtStart, + time: time.Date(2021, 1, 2, 3, 4, 5, 0, time.UTC), + allDay: false, + loc: locak, + exp: "DTSTART;TZID=Asia/Kolkata:20210102T083405", + }, + { + name: "all day", + prop: ics.ComponentPropertyDtStart, + time: time.Date(2021, 1, 2, 0, 0, 0, 0, time.UTC), + allDay: true, + loc: time.UTC, + exp: "DTSTART;VALUE=DATE:20210102", + }, + { + name: "all day local", + prop: ics.ComponentPropertyDtStart, + time: time.Date(2021, 1, 2, 0, 0, 0, 0, time.UTC), + allDay: true, + loc: locak, + exp: "DTSTART;VALUE=DATE;TZID=Asia/Kolkata:20210102", + }, + { + name: "end", + prop: ics.ComponentPropertyDtEnd, + time: time.Date(2021, 1, 2, 3, 4, 5, 0, time.UTC), + allDay: false, + loc: time.UTC, + exp: "DTEND:20210102T030405Z", + }, + { + // This won't happen, but a good test to have to test loc handling + name: "windows tz", + prop: ics.ComponentPropertyDtStart, + time: time.Date(2021, 1, 2, 3, 4, 5, 0, time.UTC), + allDay: false, + loc: time.FixedZone("India Standard Time", 5*60*60+30*60), + exp: "DTSTART;TZID=India Standard Time:20210102T083405", + }, + } + + for _, tt := range table { + s.Run(tt.name, func() { + cal := ics.NewCalendar() + evt := cal.AddEvent("id") + + addTime(evt, tt.prop, tt.time, tt.allDay, tt.loc) + + expSplits := strings.FieldsFunc(tt.exp, func(c rune) bool { + return c == ':' || c == ';' + }) + + text := cal.Serialize() + checkLine := "" + + for _, l := range strings.Split(text, "\r\n") { + if strings.HasPrefix(l, string(tt.prop)) { + checkLine = l + break + } + } + + actSplits := strings.FieldsFunc(checkLine, func(c rune) bool { + return c == ':' || c == ';' + }) + + assert.Greater(s.T(), len(checkLine), 0, "line not found") + assert.Equal(s.T(), len(expSplits), len(actSplits), "length of fields") + assert.ElementsMatch(s.T(), expSplits, actSplits, "fields") + }) + } +} + +// This tests and ensures that the generated data is int he format +// that we expect +func (s *ICSUnitSuite) TestGetTZDataKeyValues() { + for key := range tzdata.TZData { + s.Run(key, func() { + ctx, flush := tester.NewContext(s.T()) + defer flush() + + data, err := getTZDataKeyValues(ctx, key) + require.NoError(s.T(), err) + + assert.NotEmpty(s.T(), data, "data") + assert.NotContains(s.T(), data, "BEGIN", "beginning of timezone") // should be stripped + assert.NotContains(s.T(), data, "END", "end of timezone") // should be stripped + assert.NotContains(s.T(), data, "TZID", "timezone id") // should be stripped + assert.Contains(s.T(), data, "DTSTART", "start time") + assert.Contains(s.T(), data, "TZOFFSETFROM", "offset from") + }) + } +} diff --git a/src/internal/converters/ics/tzdata/data.go b/src/internal/converters/ics/tzdata/data.go new file mode 100644 index 000000000..b586366e8 --- /dev/null +++ b/src/internal/converters/ics/tzdata/data.go @@ -0,0 +1,2796 @@ +package tzdata + +var TZData = map[string]string{ + "Europe/Ulyanovsk": `X-LIC-LOCATION:Europe/Ulyanovsk +LAST-MODIFIED:20240205T192834Z +TZNAME:+04 +TZOFFSETFROM:+0400 +TZOFFSETTO:+0400 +DTSTART:19700101T000000`, + "Europe/Sofia": `X-LIC-LOCATION:Europe/Sofia +LAST-MODIFIED:20240205T192834Z +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700329T030000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701025T040000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Simferopol": `X-LIC-LOCATION:Europe/Simferopol +LAST-MODIFIED:20240205T192834Z +TZNAME:MSK +TZOFFSETFROM:+0300 +TZOFFSETTO:+0300 +DTSTART:19700101T000000`, + "Europe/Budapest": `X-LIC-LOCATION:Europe/Budapest +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Prague": `X-LIC-LOCATION:Europe/Prague +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Minsk": `X-LIC-LOCATION:Europe/Minsk +LAST-MODIFIED:20240205T192834Z +TZNAME:+03 +TZOFFSETFROM:+0300 +TZOFFSETTO:+0300 +DTSTART:19700101T000000`, + "Europe/Gibraltar": `X-LIC-LOCATION:Europe/Gibraltar +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Belgrade": `X-LIC-LOCATION:Europe/Belgrade +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Berlin": `X-LIC-LOCATION:Europe/Berlin +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Kaliningrad": `X-LIC-LOCATION:Europe/Kaliningrad +LAST-MODIFIED:20240205T192834Z +TZNAME:EET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0200 +DTSTART:19700101T000000`, + "Europe/Lisbon": `X-LIC-LOCATION:Europe/Lisbon +LAST-MODIFIED:20240205T192834Z +TZNAME:WET +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +DTSTART:19701025T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +TZNAME:WEST +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +DTSTART:19700329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU`, + "Europe/London": `X-LIC-LOCATION:Europe/London +LAST-MODIFIED:20240205T192834Z +TZNAME:BST +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +DTSTART:19700329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:GMT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +DTSTART:19701025T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Dublin": `X-LIC-LOCATION:Europe/Dublin +LAST-MODIFIED:20240205T192834Z +TZNAME:IST +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +DTSTART:19700329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:GMT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +DTSTART:19701025T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Bucharest": `X-LIC-LOCATION:Europe/Bucharest +LAST-MODIFIED:20240205T192834Z +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700329T030000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701025T040000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Moscow": `X-LIC-LOCATION:Europe/Moscow +LAST-MODIFIED:20240205T192834Z +TZNAME:MSK +TZOFFSETFROM:+0300 +TZOFFSETTO:+0300 +DTSTART:19700101T000000`, + "Europe/Saratov": `X-LIC-LOCATION:Europe/Saratov +LAST-MODIFIED:20240205T192834Z +TZNAME:+04 +TZOFFSETFROM:+0400 +TZOFFSETTO:+0400 +DTSTART:19700101T000000`, + "Europe/Istanbul": `X-LIC-LOCATION:Europe/Istanbul +LAST-MODIFIED:20240205T192834Z +TZNAME:+03 +TZOFFSETFROM:+0300 +TZOFFSETTO:+0300 +DTSTART:19700101T000000`, + "Europe/Brussels": `X-LIC-LOCATION:Europe/Brussels +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Helsinki": `X-LIC-LOCATION:Europe/Helsinki +LAST-MODIFIED:20240205T192834Z +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700329T030000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701025T040000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Kyiv": `X-LIC-LOCATION:Europe/Kyiv +LAST-MODIFIED:20240205T192834Z +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701025T040000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700329T030000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU`, + "Europe/Tallinn": `X-LIC-LOCATION:Europe/Tallinn +LAST-MODIFIED:20240205T192834Z +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700329T030000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701025T040000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Astrakhan": `X-LIC-LOCATION:Europe/Astrakhan +LAST-MODIFIED:20240205T192834Z +TZNAME:+04 +TZOFFSETFROM:+0400 +TZOFFSETTO:+0400 +DTSTART:19700101T000000`, + "Europe/Athens": `X-LIC-LOCATION:Europe/Athens +LAST-MODIFIED:20240205T192834Z +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700329T030000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701025T040000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Volgograd": `X-LIC-LOCATION:Europe/Volgograd +LAST-MODIFIED:20240205T192834Z +TZNAME:MSK +TZOFFSETFROM:+0300 +TZOFFSETTO:+0300 +DTSTART:19700101T000000`, + "Europe/Andorra": `X-LIC-LOCATION:Europe/Andorra +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Madrid": `X-LIC-LOCATION:Europe/Madrid +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Kirov": `X-LIC-LOCATION:Europe/Kirov +LAST-MODIFIED:20240205T192834Z +TZNAME:MSK +TZOFFSETFROM:+0300 +TZOFFSETTO:+0300 +DTSTART:19700101T000000`, + "Europe/Warsaw": `X-LIC-LOCATION:Europe/Warsaw +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Riga": `X-LIC-LOCATION:Europe/Riga +LAST-MODIFIED:20240205T192834Z +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700329T030000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701025T040000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Vienna": `X-LIC-LOCATION:Europe/Vienna +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Samara": `X-LIC-LOCATION:Europe/Samara +LAST-MODIFIED:20240205T192834Z +TZNAME:+04 +TZOFFSETFROM:+0400 +TZOFFSETTO:+0400 +DTSTART:19700101T000000`, + "Europe/Paris": `X-LIC-LOCATION:Europe/Paris +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Tirane": `X-LIC-LOCATION:Europe/Tirane +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Malta": `X-LIC-LOCATION:Europe/Malta +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Chisinau": `X-LIC-LOCATION:Europe/Chisinau +LAST-MODIFIED:20240205T192834Z +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Zurich": `X-LIC-LOCATION:Europe/Zurich +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Vilnius": `X-LIC-LOCATION:Europe/Vilnius +LAST-MODIFIED:20240205T192834Z +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700329T030000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701025T040000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Europe/Rome": `X-LIC-LOCATION:Europe/Rome +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "MST7MDT": `X-LIC-LOCATION:MST7MDT +LAST-MODIFIED:20240205T192834Z +TZNAME:MDT +TZOFFSETFROM:-0700 +TZOFFSETTO:-0600 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:MST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0700 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "PST8PDT": `X-LIC-LOCATION:PST8PDT +LAST-MODIFIED:20240205T192834Z +TZNAME:PDT +TZOFFSETFROM:-0800 +TZOFFSETTO:-0700 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:PST +TZOFFSETFROM:-0700 +TZOFFSETTO:-0800 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "EST5EDT": `X-LIC-LOCATION:EST5EDT +LAST-MODIFIED:20240205T192834Z +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "Antarctica/Troll": `X-LIC-LOCATION:Antarctica/Troll +LAST-MODIFIED:20240205T192834Z +TZNAME:+02 +TZOFFSETFROM:+0000 +TZOFFSETTO:+0200 +DTSTART:19700329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:+00 +TZOFFSETFROM:+0200 +TZOFFSETTO:+0000 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Antarctica/Mawson": `X-LIC-LOCATION:Antarctica/Mawson +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Antarctica/Palmer": `X-LIC-LOCATION:Antarctica/Palmer +LAST-MODIFIED:20240205T192835Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "Antarctica/Macquarie": `X-LIC-LOCATION:Antarctica/Macquarie +LAST-MODIFIED:20240205T192834Z +TZNAME:AEST +TZOFFSETFROM:+1100 +TZOFFSETTO:+1000 +DTSTART:19700405T030000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU +TZNAME:AEDT +TZOFFSETFROM:+1000 +TZOFFSETTO:+1100 +DTSTART:19701004T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU`, + "Antarctica/Casey": `X-LIC-LOCATION:Antarctica/Casey +LAST-MODIFIED:20240205T192834Z +TZNAME:+08 +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +DTSTART:19700101T000000`, + "Antarctica/Davis": `X-LIC-LOCATION:Antarctica/Davis +LAST-MODIFIED:20240205T192834Z +TZNAME:+07 +TZOFFSETFROM:+0700 +TZOFFSETTO:+0700 +DTSTART:19700101T000000`, + "Antarctica/Vostok": `X-LIC-LOCATION:Antarctica/Vostok +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Antarctica/Rothera": `X-LIC-LOCATION:Antarctica/Rothera +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "MST": `X-LIC-LOCATION:MST +LAST-MODIFIED:20240205T192834Z +TZNAME:MST +TZOFFSETFROM:-0700 +TZOFFSETTO:-0700 +DTSTART:19700101T000000`, + "EST": `X-LIC-LOCATION:EST +LAST-MODIFIED:20240205T192834Z +TZNAME:EST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0500 +DTSTART:19700101T000000`, + "WET": `X-LIC-LOCATION:WET +LAST-MODIFIED:20240205T192834Z +TZNAME:WEST +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +DTSTART:19700329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:WET +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +DTSTART:19701025T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "CST6CDT": `X-LIC-LOCATION:CST6CDT +LAST-MODIFIED:20240205T192834Z +TZNAME:CDT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:CST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/New_York": `X-LIC-LOCATION:America/New_York +LAST-MODIFIED:20240205T192834Z +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Dawson_Creek": `X-LIC-LOCATION:America/Dawson_Creek +LAST-MODIFIED:20240205T192834Z +TZNAME:MST +TZOFFSETFROM:-0700 +TZOFFSETTO:-0700 +DTSTART:19700101T000000`, + "America/Monterrey": `X-LIC-LOCATION:America/Monterrey +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "America/Ciudad_Juarez": `X-LIC-LOCATION:America/Ciudad_Juarez +LAST-MODIFIED:20240205T192834Z +TZNAME:MDT +TZOFFSETFROM:-0700 +TZOFFSETTO:-0600 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:MST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0700 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Panama": `X-LIC-LOCATION:America/Panama +LAST-MODIFIED:20240205T192834Z +TZNAME:EST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0500 +DTSTART:19700101T000000`, + "America/Bahia_Banderas": `X-LIC-LOCATION:America/Bahia_Banderas +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "America/Belize": `X-LIC-LOCATION:America/Belize +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "America/Noronha": `X-LIC-LOCATION:America/Noronha +LAST-MODIFIED:20240205T192834Z +TZNAME:-02 +TZOFFSETFROM:-0200 +TZOFFSETTO:-0200 +DTSTART:19700101T000000`, + "America/Winnipeg": `X-LIC-LOCATION:America/Winnipeg +LAST-MODIFIED:20240205T192834Z +TZNAME:CDT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:CST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Menominee": `X-LIC-LOCATION:America/Menominee +LAST-MODIFIED:20240205T192834Z +TZNAME:CDT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:CST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Regina": `X-LIC-LOCATION:America/Regina +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "America/Port-au-Prince": `X-LIC-LOCATION:America/Port-au-Prince +LAST-MODIFIED:20240205T192834Z +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Tegucigalpa": `X-LIC-LOCATION:America/Tegucigalpa +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "America/Fort_Nelson": `X-LIC-LOCATION:America/Fort_Nelson +LAST-MODIFIED:20240205T192834Z +TZNAME:MST +TZOFFSETFROM:-0700 +TZOFFSETTO:-0700 +DTSTART:19700101T000000`, + "America/Whitehorse": `X-LIC-LOCATION:America/Whitehorse +LAST-MODIFIED:20240205T192834Z +TZNAME:MST +TZOFFSETFROM:-0700 +TZOFFSETTO:-0700 +DTSTART:19700101T000000`, + "America/Fortaleza": `X-LIC-LOCATION:America/Fortaleza +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Havana": `X-LIC-LOCATION:America/Havana +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T010000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +TZNAME:CDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T000000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU`, + "America/Edmonton": `X-LIC-LOCATION:America/Edmonton +LAST-MODIFIED:20240205T192834Z +TZNAME:MDT +TZOFFSETFROM:-0700 +TZOFFSETTO:-0600 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:MST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0700 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Cayenne": `X-LIC-LOCATION:America/Cayenne +LAST-MODIFIED:20240205T192835Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Mexico_City": `X-LIC-LOCATION:America/Mexico_City +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "America/Rankin_Inlet": `X-LIC-LOCATION:America/Rankin_Inlet +LAST-MODIFIED:20240205T192834Z +TZNAME:CDT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:CST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Jamaica": `X-LIC-LOCATION:America/Jamaica +LAST-MODIFIED:20240205T192834Z +TZNAME:EST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0500 +DTSTART:19700101T000000`, + "America/Boa_Vista": `X-LIC-LOCATION:America/Boa_Vista +LAST-MODIFIED:20240205T192834Z +TZNAME:-04 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0400 +DTSTART:19700101T000000`, + "America/Santiago": `X-LIC-LOCATION:America/Santiago +LAST-MODIFIED:20240205T192834Z +TZNAME:-04 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0400 +DTSTART:19700405T000000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU +TZNAME:-03 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0300 +DTSTART:19700906T000000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=1SU`, + "America/Resolute": `X-LIC-LOCATION:America/Resolute +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +TZNAME:CDT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU`, + "America/Hermosillo": `X-LIC-LOCATION:America/Hermosillo +LAST-MODIFIED:20240205T192834Z +TZNAME:MST +TZOFFSETFROM:-0700 +TZOFFSETTO:-0700 +DTSTART:19700101T000000`, + "America/Glace_Bay": `X-LIC-LOCATION:America/Glace_Bay +LAST-MODIFIED:20240205T192834Z +TZNAME:ADT +TZOFFSETFROM:-0400 +TZOFFSETTO:-0300 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:AST +TZOFFSETFROM:-0300 +TZOFFSETTO:-0400 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Swift_Current": `X-LIC-LOCATION:America/Swift_Current +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "America/Miquelon": `X-LIC-LOCATION:America/Miquelon +LAST-MODIFIED:20240205T192834Z +TZNAME:-02 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0200 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:-03 +TZOFFSETFROM:-0200 +TZOFFSETTO:-0300 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Porto_Velho": `X-LIC-LOCATION:America/Porto_Velho +LAST-MODIFIED:20240205T192834Z +TZNAME:-04 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0400 +DTSTART:19700101T000000`, + "America/Grand_Turk": `X-LIC-LOCATION:America/Grand_Turk +LAST-MODIFIED:20240205T192834Z +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU`, + "America/Merida": `X-LIC-LOCATION:America/Merida +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "America/Halifax": `X-LIC-LOCATION:America/Halifax +LAST-MODIFIED:20240205T192834Z +TZNAME:ADT +TZOFFSETFROM:-0400 +TZOFFSETTO:-0300 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:AST +TZOFFSETFROM:-0300 +TZOFFSETTO:-0400 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Vancouver": `X-LIC-LOCATION:America/Vancouver +LAST-MODIFIED:20240205T192834Z +TZNAME:PDT +TZOFFSETFROM:-0800 +TZOFFSETTO:-0700 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:PST +TZOFFSETFROM:-0700 +TZOFFSETTO:-0800 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Costa_Rica": `X-LIC-LOCATION:America/Costa_Rica +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "America/Los_Angeles": `X-LIC-LOCATION:America/Los_Angeles +LAST-MODIFIED:20240205T192834Z +TZNAME:PDT +TZOFFSETFROM:-0800 +TZOFFSETTO:-0700 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:PST +TZOFFSETFROM:-0700 +TZOFFSETTO:-0800 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Nome": `X-LIC-LOCATION:America/Nome +LAST-MODIFIED:20240205T192834Z +TZNAME:AKDT +TZOFFSETFROM:-0900 +TZOFFSETTO:-0800 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:AKST +TZOFFSETFROM:-0800 +TZOFFSETTO:-0900 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/La_Paz": `X-LIC-LOCATION:America/La_Paz +LAST-MODIFIED:20240205T192834Z +TZNAME:-04 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0400 +DTSTART:19700101T000000`, + "America/Argentina/Tucuman": `X-LIC-LOCATION:America/Argentina/Tucuman +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Argentina/San_Luis": `X-LIC-LOCATION:America/Argentina/San_Luis +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Argentina/Mendoza": `X-LIC-LOCATION:America/Argentina/Mendoza +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Argentina/Catamarca": `X-LIC-LOCATION:America/Argentina/Catamarca +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Argentina/La_Rioja": `X-LIC-LOCATION:America/Argentina/La_Rioja +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Argentina/Salta": `X-LIC-LOCATION:America/Argentina/Salta +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Argentina/Jujuy": `X-LIC-LOCATION:America/Argentina/Jujuy +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Argentina/Buenos_Aires": `X-LIC-LOCATION:America/Argentina/Buenos_Aires +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Argentina/Rio_Gallegos": `X-LIC-LOCATION:America/Argentina/Rio_Gallegos +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Argentina/Ushuaia": `X-LIC-LOCATION:America/Argentina/Ushuaia +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Argentina/Cordoba": `X-LIC-LOCATION:America/Argentina/Cordoba +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Argentina/San_Juan": `X-LIC-LOCATION:America/Argentina/San_Juan +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Toronto": `X-LIC-LOCATION:America/Toronto +LAST-MODIFIED:20240205T192834Z +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Asuncion": `X-LIC-LOCATION:America/Asuncion +LAST-MODIFIED:20240205T192835Z +TZNAME:-03 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0300 +DTSTART:19701004T000000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU +TZNAME:-04 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0400 +DTSTART:19700322T000000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=4SU`, + "America/Thule": `X-LIC-LOCATION:America/Thule +LAST-MODIFIED:20240205T192834Z +TZNAME:ADT +TZOFFSETFROM:-0400 +TZOFFSETTO:-0300 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:AST +TZOFFSETFROM:-0300 +TZOFFSETTO:-0400 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Anchorage": `X-LIC-LOCATION:America/Anchorage +LAST-MODIFIED:20240205T192834Z +TZNAME:AKDT +TZOFFSETFROM:-0900 +TZOFFSETTO:-0800 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:AKST +TZOFFSETFROM:-0800 +TZOFFSETTO:-0900 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Belem": `X-LIC-LOCATION:America/Belem +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Martinique": `X-LIC-LOCATION:America/Martinique +LAST-MODIFIED:20240205T192834Z +TZNAME:AST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0400 +DTSTART:19700101T000000`, + "America/Cuiaba": `X-LIC-LOCATION:America/Cuiaba +LAST-MODIFIED:20240205T192834Z +TZNAME:-04 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0400 +DTSTART:19700101T000000`, + "America/Ojinaga": `X-LIC-LOCATION:America/Ojinaga +LAST-MODIFIED:20240205T192834Z +TZNAME:CDT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:CST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Matamoros": `X-LIC-LOCATION:America/Matamoros +LAST-MODIFIED:20240205T192834Z +TZNAME:CDT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:CST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Bogota": `X-LIC-LOCATION:America/Bogota +LAST-MODIFIED:20240205T192835Z +TZNAME:-05 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0500 +DTSTART:19700101T000000`, + "America/Inuvik": `X-LIC-LOCATION:America/Inuvik +LAST-MODIFIED:20240205T192834Z +TZNAME:MDT +TZOFFSETFROM:-0700 +TZOFFSETTO:-0600 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:MST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0700 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Dawson": `X-LIC-LOCATION:America/Dawson +LAST-MODIFIED:20240205T192834Z +TZNAME:MST +TZOFFSETFROM:-0700 +TZOFFSETTO:-0700 +DTSTART:19700101T000000`, + "America/Iqaluit": `X-LIC-LOCATION:America/Iqaluit +LAST-MODIFIED:20240205T192834Z +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Boise": `X-LIC-LOCATION:America/Boise +LAST-MODIFIED:20240205T192834Z +TZNAME:MDT +TZOFFSETFROM:-0700 +TZOFFSETTO:-0600 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:MST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0700 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Sao_Paulo": `X-LIC-LOCATION:America/Sao_Paulo +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Bahia": `X-LIC-LOCATION:America/Bahia +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Caracas": `X-LIC-LOCATION:America/Caracas +LAST-MODIFIED:20240205T192835Z +TZNAME:-04 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0400 +DTSTART:19700101T000000`, + "America/Montevideo": `X-LIC-LOCATION:America/Montevideo +LAST-MODIFIED:20240205T192835Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Rio_Branco": `X-LIC-LOCATION:America/Rio_Branco +LAST-MODIFIED:20240205T192834Z +TZNAME:-05 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0500 +DTSTART:19700101T000000`, + "America/Denver": `X-LIC-LOCATION:America/Denver +LAST-MODIFIED:20240205T192834Z +TZNAME:MDT +TZOFFSETFROM:-0700 +TZOFFSETTO:-0600 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:MST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0700 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/North_Dakota/New_Salem": `X-LIC-LOCATION:America/North_Dakota/New_Salem +LAST-MODIFIED:20240205T192834Z +TZNAME:CDT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:CST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/North_Dakota/Beulah": `X-LIC-LOCATION:America/North_Dakota/Beulah +LAST-MODIFIED:20240205T192834Z +TZNAME:CDT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:CST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/North_Dakota/Center": `X-LIC-LOCATION:America/North_Dakota/Center +LAST-MODIFIED:20240205T192834Z +TZNAME:CDT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:CST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Adak": `X-LIC-LOCATION:America/Adak +LAST-MODIFIED:20240205T192834Z +TZNAME:HDT +TZOFFSETFROM:-1000 +TZOFFSETTO:-0900 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:HST +TZOFFSETFROM:-0900 +TZOFFSETTO:-1000 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Manaus": `X-LIC-LOCATION:America/Manaus +LAST-MODIFIED:20240205T192834Z +TZNAME:-04 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0400 +DTSTART:19700101T000000`, + "America/Guyana": `X-LIC-LOCATION:America/Guyana +LAST-MODIFIED:20240205T192835Z +TZNAME:-04 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0400 +DTSTART:19700101T000000`, + "America/Juneau": `X-LIC-LOCATION:America/Juneau +LAST-MODIFIED:20240205T192834Z +TZNAME:AKDT +TZOFFSETFROM:-0900 +TZOFFSETTO:-0800 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:AKST +TZOFFSETFROM:-0800 +TZOFFSETTO:-0900 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Tijuana": `X-LIC-LOCATION:America/Tijuana +LAST-MODIFIED:20240205T192834Z +TZNAME:PDT +TZOFFSETFROM:-0800 +TZOFFSETTO:-0700 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:PST +TZOFFSETFROM:-0700 +TZOFFSETTO:-0800 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Managua": `X-LIC-LOCATION:America/Managua +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "America/Barbados": `X-LIC-LOCATION:America/Barbados +LAST-MODIFIED:20240205T192834Z +TZNAME:AST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0400 +DTSTART:19700101T000000`, + "America/Paramaribo": `X-LIC-LOCATION:America/Paramaribo +LAST-MODIFIED:20240205T192835Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Araguaina": `X-LIC-LOCATION:America/Araguaina +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Moncton": `X-LIC-LOCATION:America/Moncton +LAST-MODIFIED:20240205T192834Z +TZNAME:ADT +TZOFFSETFROM:-0400 +TZOFFSETTO:-0300 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:AST +TZOFFSETFROM:-0300 +TZOFFSETTO:-0400 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Goose_Bay": `X-LIC-LOCATION:America/Goose_Bay +LAST-MODIFIED:20240205T192834Z +TZNAME:AST +TZOFFSETFROM:-0300 +TZOFFSETTO:-0400 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +TZNAME:ADT +TZOFFSETFROM:-0400 +TZOFFSETTO:-0300 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU`, + "America/St_Johns": `X-LIC-LOCATION:America/St_Johns +LAST-MODIFIED:20240205T192834Z +TZNAME:NST +TZOFFSETFROM:-0230 +TZOFFSETTO:-0330 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +TZNAME:NDT +TZOFFSETFROM:-0330 +TZOFFSETTO:-0230 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU`, + "America/Guatemala": `X-LIC-LOCATION:America/Guatemala +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "America/Santarem": `X-LIC-LOCATION:America/Santarem +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Chicago": `X-LIC-LOCATION:America/Chicago +LAST-MODIFIED:20240205T192834Z +TZNAME:CDT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:CST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Maceio": `X-LIC-LOCATION:America/Maceio +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Nuuk": `X-LIC-LOCATION:America/Nuuk +LAST-MODIFIED:20240205T192834Z +TZNAME:-01 +TZOFFSETFROM:-0200 +TZOFFSETTO:-0100 +DTSTART:19700328T230000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SA +TZNAME:-02 +TZOFFSETFROM:-0100 +TZOFFSETTO:-0200 +DTSTART:19701025T000000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "America/Detroit": `X-LIC-LOCATION:America/Detroit +LAST-MODIFIED:20240205T192834Z +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Metlakatla": `X-LIC-LOCATION:America/Metlakatla +LAST-MODIFIED:20240205T192834Z +TZNAME:AKDT +TZOFFSETFROM:-0900 +TZOFFSETTO:-0800 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:AKST +TZOFFSETFROM:-0800 +TZOFFSETTO:-0900 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Recife": `X-LIC-LOCATION:America/Recife +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Sitka": `X-LIC-LOCATION:America/Sitka +LAST-MODIFIED:20240205T192834Z +TZNAME:AKDT +TZOFFSETFROM:-0900 +TZOFFSETTO:-0800 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:AKST +TZOFFSETFROM:-0800 +TZOFFSETTO:-0900 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Indiana/Marengo": `X-LIC-LOCATION:America/Indiana/Marengo +LAST-MODIFIED:20240205T192834Z +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Indiana/Winamac": `X-LIC-LOCATION:America/Indiana/Winamac +LAST-MODIFIED:20240205T192834Z +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU`, + "America/Indiana/Vevay": `X-LIC-LOCATION:America/Indiana/Vevay +LAST-MODIFIED:20240205T192834Z +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Indiana/Indianapolis": `X-LIC-LOCATION:America/Indiana/Indianapolis +LAST-MODIFIED:20240205T192834Z +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Indiana/Knox": `X-LIC-LOCATION:America/Indiana/Knox +LAST-MODIFIED:20240205T192834Z +TZNAME:CDT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:CST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Indiana/Tell_City": `X-LIC-LOCATION:America/Indiana/Tell_City +LAST-MODIFIED:20240205T192834Z +TZNAME:CDT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:CST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Indiana/Vincennes": `X-LIC-LOCATION:America/Indiana/Vincennes +LAST-MODIFIED:20240205T192834Z +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Indiana/Petersburg": `X-LIC-LOCATION:America/Indiana/Petersburg +LAST-MODIFIED:20240205T192834Z +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Punta_Arenas": `X-LIC-LOCATION:America/Punta_Arenas +LAST-MODIFIED:20240205T192834Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "America/Yakutat": `X-LIC-LOCATION:America/Yakutat +LAST-MODIFIED:20240205T192834Z +TZNAME:AKDT +TZOFFSETFROM:-0900 +TZOFFSETTO:-0800 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:AKST +TZOFFSETFROM:-0800 +TZOFFSETTO:-0900 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Campo_Grande": `X-LIC-LOCATION:America/Campo_Grande +LAST-MODIFIED:20240205T192834Z +TZNAME:-04 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0400 +DTSTART:19700101T000000`, + "America/Scoresbysund": `X-LIC-LOCATION:America/Scoresbysund +LAST-MODIFIED:20240205T192834Z +TZNAME:-02 +TZOFFSETFROM:-0100 +TZOFFSETTO:-0200 +DTSTART:19701025T000000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +TZNAME:-01 +TZOFFSETFROM:-0200 +TZOFFSETTO:-0100 +DTSTART:19700328T230000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SA`, + "America/El_Salvador": `X-LIC-LOCATION:America/El_Salvador +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "America/Mazatlan": `X-LIC-LOCATION:America/Mazatlan +LAST-MODIFIED:20240205T192834Z +TZNAME:MST +TZOFFSETFROM:-0700 +TZOFFSETTO:-0700 +DTSTART:19700101T000000`, + "America/Cancun": `X-LIC-LOCATION:America/Cancun +LAST-MODIFIED:20240205T192834Z +TZNAME:EST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0500 +DTSTART:19700101T000000`, + "America/Cambridge_Bay": `X-LIC-LOCATION:America/Cambridge_Bay +LAST-MODIFIED:20240205T192834Z +TZNAME:MDT +TZOFFSETFROM:-0700 +TZOFFSETTO:-0600 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:MST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0700 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Santo_Domingo": `X-LIC-LOCATION:America/Santo_Domingo +LAST-MODIFIED:20240205T192834Z +TZNAME:AST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0400 +DTSTART:19700101T000000`, + "America/Lima": `X-LIC-LOCATION:America/Lima +LAST-MODIFIED:20240205T192835Z +TZNAME:-05 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0500 +DTSTART:19700101T000000`, + "America/Eirunepe": `X-LIC-LOCATION:America/Eirunepe +LAST-MODIFIED:20240205T192834Z +TZNAME:-05 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0500 +DTSTART:19700101T000000`, + "America/Chihuahua": `X-LIC-LOCATION:America/Chihuahua +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "America/Guayaquil": `X-LIC-LOCATION:America/Guayaquil +LAST-MODIFIED:20240205T192835Z +TZNAME:-05 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0500 +DTSTART:19700101T000000`, + "America/Danmarkshavn": `X-LIC-LOCATION:America/Danmarkshavn +LAST-MODIFIED:20240205T192834Z +TZNAME:GMT +TZOFFSETFROM:+0000 +TZOFFSETTO:+0000 +DTSTART:19700101T000000`, + "America/Puerto_Rico": `X-LIC-LOCATION:America/Puerto_Rico +LAST-MODIFIED:20240205T192834Z +TZNAME:AST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0400 +DTSTART:19700101T000000`, + "America/Kentucky/Louisville": `X-LIC-LOCATION:America/Kentucky/Louisville +LAST-MODIFIED:20240205T192834Z +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Kentucky/Monticello": `X-LIC-LOCATION:America/Kentucky/Monticello +LAST-MODIFIED:20240205T192834Z +TZNAME:EDT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:EST +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "America/Phoenix": `X-LIC-LOCATION:America/Phoenix +LAST-MODIFIED:20240205T192834Z +TZNAME:MST +TZOFFSETFROM:-0700 +TZOFFSETTO:-0700 +DTSTART:19700101T000000`, + "Asia/Kathmandu": `X-LIC-LOCATION:Asia/Kathmandu +LAST-MODIFIED:20240205T192834Z +TZNAME:+0545 +TZOFFSETFROM:+0545 +TZOFFSETTO:+0545 +DTSTART:19700101T000000`, + "Asia/Baghdad": `X-LIC-LOCATION:Asia/Baghdad +LAST-MODIFIED:20240205T192834Z +TZNAME:+03 +TZOFFSETFROM:+0300 +TZOFFSETTO:+0300 +DTSTART:19700101T000000`, + "Asia/Dubai": `X-LIC-LOCATION:Asia/Dubai +LAST-MODIFIED:20240205T192834Z +TZNAME:+04 +TZOFFSETFROM:+0400 +TZOFFSETTO:+0400 +DTSTART:19700101T000000`, + "Asia/Makassar": `X-LIC-LOCATION:Asia/Makassar +LAST-MODIFIED:20240205T192834Z +TZNAME:WITA +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +DTSTART:19700101T000000`, + "Asia/Omsk": `X-LIC-LOCATION:Asia/Omsk +LAST-MODIFIED:20240205T192834Z +TZNAME:+06 +TZOFFSETFROM:+0600 +TZOFFSETTO:+0600 +DTSTART:19700101T000000`, + "Asia/Aqtobe": `X-LIC-LOCATION:Asia/Aqtobe +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Asia/Krasnoyarsk": `X-LIC-LOCATION:Asia/Krasnoyarsk +LAST-MODIFIED:20240205T192834Z +TZNAME:+07 +TZOFFSETFROM:+0700 +TZOFFSETTO:+0700 +DTSTART:19700101T000000`, + "Asia/Singapore": `X-LIC-LOCATION:Asia/Singapore +LAST-MODIFIED:20240205T192834Z +TZNAME:+08 +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +DTSTART:19700101T000000`, + "Asia/Tbilisi": `X-LIC-LOCATION:Asia/Tbilisi +LAST-MODIFIED:20240205T192834Z +TZNAME:+04 +TZOFFSETFROM:+0400 +TZOFFSETTO:+0400 +DTSTART:19700101T000000`, + "Asia/Tehran": `X-LIC-LOCATION:Asia/Tehran +LAST-MODIFIED:20240205T192834Z +TZNAME:+0330 +TZOFFSETFROM:+0330 +TZOFFSETTO:+0330 +DTSTART:19700101T000000`, + "Asia/Dhaka": `X-LIC-LOCATION:Asia/Dhaka +LAST-MODIFIED:20240205T192834Z +TZNAME:+06 +TZOFFSETFROM:+0600 +TZOFFSETTO:+0600 +DTSTART:19700101T000000`, + "Asia/Colombo": `X-LIC-LOCATION:Asia/Colombo +LAST-MODIFIED:20240205T192834Z +TZNAME:+0530 +TZOFFSETFROM:+0530 +TZOFFSETTO:+0530 +DTSTART:19700101T000000`, + "Asia/Barnaul": `X-LIC-LOCATION:Asia/Barnaul +LAST-MODIFIED:20240205T192834Z +TZNAME:+07 +TZOFFSETFROM:+0700 +TZOFFSETTO:+0700 +DTSTART:19700101T000000`, + "Asia/Atyrau": `X-LIC-LOCATION:Asia/Atyrau +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Asia/Qyzylorda": `X-LIC-LOCATION:Asia/Qyzylorda +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Asia/Thimphu": `X-LIC-LOCATION:Asia/Thimphu +LAST-MODIFIED:20240205T192834Z +TZNAME:+06 +TZOFFSETFROM:+0600 +TZOFFSETTO:+0600 +DTSTART:19700101T000000`, + "Asia/Manila": `X-LIC-LOCATION:Asia/Manila +LAST-MODIFIED:20240205T192834Z +TZNAME:PST +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +DTSTART:19700101T000000`, + "Asia/Tashkent": `X-LIC-LOCATION:Asia/Tashkent +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Asia/Qatar": `X-LIC-LOCATION:Asia/Qatar +LAST-MODIFIED:20240205T192834Z +TZNAME:+03 +TZOFFSETFROM:+0300 +TZOFFSETTO:+0300 +DTSTART:19700101T000000`, + "Asia/Samarkand": `X-LIC-LOCATION:Asia/Samarkand +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Asia/Dushanbe": `X-LIC-LOCATION:Asia/Dushanbe +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Asia/Famagusta": `X-LIC-LOCATION:Asia/Famagusta +LAST-MODIFIED:20240205T192834Z +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700329T030000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701025T040000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Asia/Hebron": `X-LIC-LOCATION:Asia/Hebron +LAST-MODIFIED:20240205T192834Z +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700328T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SA +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SA`, + "Asia/Magadan": `X-LIC-LOCATION:Asia/Magadan +LAST-MODIFIED:20240205T192834Z +TZNAME:+11 +TZOFFSETFROM:+1100 +TZOFFSETTO:+1100 +DTSTART:19700101T000000`, + "Asia/Kamchatka": `X-LIC-LOCATION:Asia/Kamchatka +LAST-MODIFIED:20240205T192834Z +TZNAME:+12 +TZOFFSETFROM:+1200 +TZOFFSETTO:+1200 +DTSTART:19700101T000000`, + "Asia/Aqtau": `X-LIC-LOCATION:Asia/Aqtau +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Asia/Choibalsan": `X-LIC-LOCATION:Asia/Choibalsan +LAST-MODIFIED:20240205T192834Z +TZNAME:+08 +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +DTSTART:19700101T000000`, + "Asia/Kabul": `X-LIC-LOCATION:Asia/Kabul +LAST-MODIFIED:20240205T192834Z +TZNAME:+0430 +TZOFFSETFROM:+0430 +TZOFFSETTO:+0430 +DTSTART:19700101T000000`, + "Asia/Dili": `X-LIC-LOCATION:Asia/Dili +LAST-MODIFIED:20240205T192834Z +TZNAME:+09 +TZOFFSETFROM:+0900 +TZOFFSETTO:+0900 +DTSTART:19700101T000000`, + "Asia/Urumqi": `X-LIC-LOCATION:Asia/Urumqi +LAST-MODIFIED:20240205T192834Z +TZNAME:+06 +TZOFFSETFROM:+0600 +TZOFFSETTO:+0600 +DTSTART:19700101T000000`, + "Asia/Almaty": `X-LIC-LOCATION:Asia/Almaty +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Asia/Kuching": `X-LIC-LOCATION:Asia/Kuching +LAST-MODIFIED:20240205T192834Z +TZNAME:+08 +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +DTSTART:19700101T000000`, + "Asia/Riyadh": `X-LIC-LOCATION:Asia/Riyadh +LAST-MODIFIED:20240205T192834Z +TZNAME:+03 +TZOFFSETFROM:+0300 +TZOFFSETTO:+0300 +DTSTART:19700101T000000`, + "Asia/Irkutsk": `X-LIC-LOCATION:Asia/Irkutsk +LAST-MODIFIED:20240205T192834Z +TZNAME:+08 +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +DTSTART:19700101T000000`, + "Asia/Yekaterinburg": `X-LIC-LOCATION:Asia/Yekaterinburg +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Asia/Seoul": `X-LIC-LOCATION:Asia/Seoul +LAST-MODIFIED:20240205T192834Z +TZNAME:KST +TZOFFSETFROM:+0900 +TZOFFSETTO:+0900 +DTSTART:19700101T000000`, + "Asia/Novosibirsk": `X-LIC-LOCATION:Asia/Novosibirsk +LAST-MODIFIED:20240205T192834Z +TZNAME:+07 +TZOFFSETFROM:+0700 +TZOFFSETTO:+0700 +DTSTART:19700101T000000`, + "Asia/Jerusalem": `X-LIC-LOCATION:Asia/Jerusalem +LAST-MODIFIED:20240205T192834Z +TZNAME:IDT +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700327T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1FR +TZNAME:IST +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701025T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Asia/Yangon": `X-LIC-LOCATION:Asia/Yangon +LAST-MODIFIED:20240205T192834Z +TZNAME:+0630 +TZOFFSETFROM:+0630 +TZOFFSETTO:+0630 +DTSTART:19700101T000000`, + "Asia/Pontianak": `X-LIC-LOCATION:Asia/Pontianak +LAST-MODIFIED:20240205T192834Z +TZNAME:WIB +TZOFFSETFROM:+0700 +TZOFFSETTO:+0700 +DTSTART:19700101T000000`, + "Asia/Karachi": `X-LIC-LOCATION:Asia/Karachi +LAST-MODIFIED:20240205T192834Z +TZNAME:PKT +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Asia/Khandyga": `X-LIC-LOCATION:Asia/Khandyga +LAST-MODIFIED:20240205T192834Z +TZNAME:+09 +TZOFFSETFROM:+0900 +TZOFFSETTO:+0900 +DTSTART:19700101T000000`, + "Asia/Tomsk": `X-LIC-LOCATION:Asia/Tomsk +LAST-MODIFIED:20240205T192834Z +TZNAME:+07 +TZOFFSETFROM:+0700 +TZOFFSETTO:+0700 +DTSTART:19700101T000000`, + "Asia/Damascus": `X-LIC-LOCATION:Asia/Damascus +LAST-MODIFIED:20240205T192834Z +TZNAME:+03 +TZOFFSETFROM:+0300 +TZOFFSETTO:+0300 +DTSTART:19700101T000000`, + "Asia/Ho_Chi_Minh": `X-LIC-LOCATION:Asia/Ho_Chi_Minh +LAST-MODIFIED:20240205T192834Z +TZNAME:+07 +TZOFFSETFROM:+0700 +TZOFFSETTO:+0700 +DTSTART:19700101T000000`, + "Asia/Jayapura": `X-LIC-LOCATION:Asia/Jayapura +LAST-MODIFIED:20240205T192834Z +TZNAME:WIT +TZOFFSETFROM:+0900 +TZOFFSETTO:+0900 +DTSTART:19700101T000000`, + "Asia/Yerevan": `X-LIC-LOCATION:Asia/Yerevan +LAST-MODIFIED:20240205T192834Z +TZNAME:+04 +TZOFFSETFROM:+0400 +TZOFFSETTO:+0400 +DTSTART:19700101T000000`, + "Asia/Anadyr": `X-LIC-LOCATION:Asia/Anadyr +LAST-MODIFIED:20240205T192834Z +TZNAME:+12 +TZOFFSETFROM:+1200 +TZOFFSETTO:+1200 +DTSTART:19700101T000000`, + "Asia/Pyongyang": `X-LIC-LOCATION:Asia/Pyongyang +LAST-MODIFIED:20240205T192834Z +TZNAME:KST +TZOFFSETFROM:+0900 +TZOFFSETTO:+0900 +DTSTART:19700101T000000`, + "Asia/Macau": `X-LIC-LOCATION:Asia/Macau +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +DTSTART:19700101T000000`, + "Asia/Oral": `X-LIC-LOCATION:Asia/Oral +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Asia/Novokuznetsk": `X-LIC-LOCATION:Asia/Novokuznetsk +LAST-MODIFIED:20240205T192834Z +TZNAME:+07 +TZOFFSETFROM:+0700 +TZOFFSETTO:+0700 +DTSTART:19700101T000000`, + "Asia/Ashgabat": `X-LIC-LOCATION:Asia/Ashgabat +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Asia/Gaza": `X-LIC-LOCATION:Asia/Gaza +LAST-MODIFIED:20240205T192834Z +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700328T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SA +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701024T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SA`, + "Asia/Vladivostok": `X-LIC-LOCATION:Asia/Vladivostok +LAST-MODIFIED:20240205T192834Z +TZNAME:+10 +TZOFFSETFROM:+1000 +TZOFFSETTO:+1000 +DTSTART:19700101T000000`, + "Asia/Taipei": `X-LIC-LOCATION:Asia/Taipei +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +DTSTART:19700101T000000`, + "Asia/Amman": `X-LIC-LOCATION:Asia/Amman +LAST-MODIFIED:20240205T192834Z +TZNAME:+03 +TZOFFSETFROM:+0300 +TZOFFSETTO:+0300 +DTSTART:19700101T000000`, + "Asia/Nicosia": `X-LIC-LOCATION:Asia/Nicosia +LAST-MODIFIED:20240205T192834Z +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701025T040000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700329T030000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU`, + "Asia/Chita": `X-LIC-LOCATION:Asia/Chita +LAST-MODIFIED:20240205T192834Z +TZNAME:+09 +TZOFFSETFROM:+0900 +TZOFFSETTO:+0900 +DTSTART:19700101T000000`, + "Asia/Bangkok": `X-LIC-LOCATION:Asia/Bangkok +LAST-MODIFIED:20240205T192834Z +TZNAME:+07 +TZOFFSETFROM:+0700 +TZOFFSETTO:+0700 +DTSTART:19700101T000000`, + "Asia/Jakarta": `X-LIC-LOCATION:Asia/Jakarta +LAST-MODIFIED:20240205T192834Z +TZNAME:WIB +TZOFFSETFROM:+0700 +TZOFFSETTO:+0700 +DTSTART:19700101T000000`, + "Asia/Qostanay": `X-LIC-LOCATION:Asia/Qostanay +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Asia/Yakutsk": `X-LIC-LOCATION:Asia/Yakutsk +LAST-MODIFIED:20240205T192834Z +TZNAME:+09 +TZOFFSETFROM:+0900 +TZOFFSETTO:+0900 +DTSTART:19700101T000000`, + "Asia/Bishkek": `X-LIC-LOCATION:Asia/Bishkek +LAST-MODIFIED:20240205T192834Z +TZNAME:+06 +TZOFFSETFROM:+0600 +TZOFFSETTO:+0600 +DTSTART:19700101T000000`, + "Asia/Hong_Kong": `X-LIC-LOCATION:Asia/Hong_Kong +LAST-MODIFIED:20240205T192834Z +TZNAME:HKT +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +DTSTART:19700101T000000`, + "Asia/Shanghai": `X-LIC-LOCATION:Asia/Shanghai +LAST-MODIFIED:20240205T192834Z +TZNAME:CST +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +DTSTART:19700101T000000`, + "Asia/Ust-Nera": `X-LIC-LOCATION:Asia/Ust-Nera +LAST-MODIFIED:20240205T192834Z +TZNAME:+10 +TZOFFSETFROM:+1000 +TZOFFSETTO:+1000 +DTSTART:19700101T000000`, + "Asia/Tokyo": `X-LIC-LOCATION:Asia/Tokyo +LAST-MODIFIED:20240205T192834Z +TZNAME:JST +TZOFFSETFROM:+0900 +TZOFFSETTO:+0900 +DTSTART:19700101T000000`, + "Asia/Ulaanbaatar": `X-LIC-LOCATION:Asia/Ulaanbaatar +LAST-MODIFIED:20240205T192834Z +TZNAME:+08 +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +DTSTART:19700101T000000`, + "Asia/Sakhalin": `X-LIC-LOCATION:Asia/Sakhalin +LAST-MODIFIED:20240205T192834Z +TZNAME:+11 +TZOFFSETFROM:+1100 +TZOFFSETTO:+1100 +DTSTART:19700101T000000`, + "Asia/Hovd": `X-LIC-LOCATION:Asia/Hovd +LAST-MODIFIED:20240205T192834Z +TZNAME:+07 +TZOFFSETFROM:+0700 +TZOFFSETTO:+0700 +DTSTART:19700101T000000`, + "Asia/Srednekolymsk": `X-LIC-LOCATION:Asia/Srednekolymsk +LAST-MODIFIED:20240205T192834Z +TZNAME:+11 +TZOFFSETFROM:+1100 +TZOFFSETTO:+1100 +DTSTART:19700101T000000`, + "Asia/Baku": `X-LIC-LOCATION:Asia/Baku +LAST-MODIFIED:20240205T192834Z +TZNAME:+04 +TZOFFSETFROM:+0400 +TZOFFSETTO:+0400 +DTSTART:19700101T000000`, + "Asia/Kolkata": `X-LIC-LOCATION:Asia/Kolkata +LAST-MODIFIED:20240205T192834Z +TZNAME:IST +TZOFFSETFROM:+0530 +TZOFFSETTO:+0530 +DTSTART:19700101T000000`, + "Asia/Beirut": `X-LIC-LOCATION:Asia/Beirut +LAST-MODIFIED:20240205T192834Z +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700329T000000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701025T000000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "MET": `X-LIC-LOCATION:MET +LAST-MODIFIED:20240205T192834Z +TZNAME:MEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:MET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Australia/Brisbane": `X-LIC-LOCATION:Australia/Brisbane +LAST-MODIFIED:20240205T192834Z +TZNAME:AEST +TZOFFSETFROM:+1000 +TZOFFSETTO:+1000 +DTSTART:19700101T000000`, + "Australia/Melbourne": `X-LIC-LOCATION:Australia/Melbourne +LAST-MODIFIED:20240205T192834Z +TZNAME:AEST +TZOFFSETFROM:+1100 +TZOFFSETTO:+1000 +DTSTART:19700405T030000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU +TZNAME:AEDT +TZOFFSETFROM:+1000 +TZOFFSETTO:+1100 +DTSTART:19701004T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU`, + "Australia/Hobart": `X-LIC-LOCATION:Australia/Hobart +LAST-MODIFIED:20240205T192834Z +TZNAME:AEDT +TZOFFSETFROM:+1000 +TZOFFSETTO:+1100 +DTSTART:19701004T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU +TZNAME:AEST +TZOFFSETFROM:+1100 +TZOFFSETTO:+1000 +DTSTART:19700405T030000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU`, + "Australia/Eucla": `X-LIC-LOCATION:Australia/Eucla +LAST-MODIFIED:20240205T192834Z +TZNAME:+0845 +TZOFFSETFROM:+0845 +TZOFFSETTO:+0845 +DTSTART:19700101T000000`, + "Australia/Adelaide": `X-LIC-LOCATION:Australia/Adelaide +LAST-MODIFIED:20240205T192834Z +TZNAME:ACST +TZOFFSETFROM:+1030 +TZOFFSETTO:+0930 +DTSTART:19700405T030000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU +TZNAME:ACDT +TZOFFSETFROM:+0930 +TZOFFSETTO:+1030 +DTSTART:19701004T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU`, + "Australia/Sydney": `X-LIC-LOCATION:Australia/Sydney +LAST-MODIFIED:20240205T192834Z +TZNAME:AEST +TZOFFSETFROM:+1100 +TZOFFSETTO:+1000 +DTSTART:19700405T030000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU +TZNAME:AEDT +TZOFFSETFROM:+1000 +TZOFFSETTO:+1100 +DTSTART:19701004T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU`, + "Australia/Perth": `X-LIC-LOCATION:Australia/Perth +LAST-MODIFIED:20240205T192834Z +TZNAME:AWST +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +DTSTART:19700101T000000`, + "Australia/Lord_Howe": `X-LIC-LOCATION:Australia/Lord_Howe +LAST-MODIFIED:20240205T192834Z +TZNAME:+1030 +TZOFFSETFROM:+1100 +TZOFFSETTO:+1030 +DTSTART:19700405T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU +TZNAME:+11 +TZOFFSETFROM:+1030 +TZOFFSETTO:+1100 +DTSTART:19701004T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU`, + "Australia/Broken_Hill": `X-LIC-LOCATION:Australia/Broken_Hill +LAST-MODIFIED:20240205T192834Z +TZNAME:ACST +TZOFFSETFROM:+1030 +TZOFFSETTO:+0930 +DTSTART:19700405T030000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU +TZNAME:ACDT +TZOFFSETFROM:+0930 +TZOFFSETTO:+1030 +DTSTART:19701004T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU`, + "Australia/Lindeman": `X-LIC-LOCATION:Australia/Lindeman +LAST-MODIFIED:20240205T192834Z +TZNAME:AEST +TZOFFSETFROM:+1000 +TZOFFSETTO:+1000 +DTSTART:19700101T000000`, + "Australia/Darwin": `X-LIC-LOCATION:Australia/Darwin +LAST-MODIFIED:20240205T192834Z +TZNAME:ACST +TZOFFSETFROM:+0930 +TZOFFSETTO:+0930 +DTSTART:19700101T000000`, + "Indian/Maldives": `X-LIC-LOCATION:Indian/Maldives +LAST-MODIFIED:20240205T192834Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Indian/Mauritius": `X-LIC-LOCATION:Indian/Mauritius +LAST-MODIFIED:20240205T192834Z +TZNAME:+04 +TZOFFSETFROM:+0400 +TZOFFSETTO:+0400 +DTSTART:19700101T000000`, + "Indian/Chagos": `X-LIC-LOCATION:Indian/Chagos +LAST-MODIFIED:20240205T192834Z +TZNAME:+06 +TZOFFSETFROM:+0600 +TZOFFSETTO:+0600 +DTSTART:19700101T000000`, + "CET": `X-LIC-LOCATION:CET +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Africa/Windhoek": `X-LIC-LOCATION:Africa/Windhoek +LAST-MODIFIED:20240205T192834Z +TZNAME:CAT +TZOFFSETFROM:+0200 +TZOFFSETTO:+0200 +DTSTART:19700101T000000`, + "Africa/Algiers": `X-LIC-LOCATION:Africa/Algiers +LAST-MODIFIED:20240205T192834Z +TZNAME:CET +TZOFFSETFROM:+0100 +TZOFFSETTO:+0100 +DTSTART:19700101T000000`, + "Africa/Maputo": `X-LIC-LOCATION:Africa/Maputo +LAST-MODIFIED:20240205T192834Z +TZNAME:CAT +TZOFFSETFROM:+0200 +TZOFFSETTO:+0200 +DTSTART:19700101T000000`, + "Africa/Casablanca": `TZUNTIL:20870511T020001Z +X-LIC-LOCATION:Africa/Casablanca +LAST-MODIFIED:20240205T192834Z +TZNAME:+01 +TZOFFSETFROM:+0100 +TZOFFSETTO:+0100 +DTSTART:19700101T000000`, + "Africa/Lagos": `X-LIC-LOCATION:Africa/Lagos +LAST-MODIFIED:20240205T192834Z +TZNAME:WAT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0100 +DTSTART:19700101T000000`, + "Africa/Khartoum": `X-LIC-LOCATION:Africa/Khartoum +LAST-MODIFIED:20240205T192834Z +TZNAME:CAT +TZOFFSETFROM:+0200 +TZOFFSETTO:+0200 +DTSTART:19700101T000000`, + "Africa/Nairobi": `X-LIC-LOCATION:Africa/Nairobi +LAST-MODIFIED:20240205T192834Z +TZNAME:EAT +TZOFFSETFROM:+0300 +TZOFFSETTO:+0300 +DTSTART:19700101T000000`, + "Africa/Tunis": `X-LIC-LOCATION:Africa/Tunis +LAST-MODIFIED:20240205T192834Z +TZNAME:CET +TZOFFSETFROM:+0100 +TZOFFSETTO:+0100 +DTSTART:19700101T000000`, + "Africa/Tripoli": `X-LIC-LOCATION:Africa/Tripoli +LAST-MODIFIED:20240205T192834Z +TZNAME:EET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0200 +DTSTART:19700101T000000`, + "Africa/Abidjan": `X-LIC-LOCATION:Africa/Abidjan +LAST-MODIFIED:20240205T192834Z +TZNAME:GMT +TZOFFSETFROM:+0000 +TZOFFSETTO:+0000 +DTSTART:19700101T000000`, + "Africa/Monrovia": `X-LIC-LOCATION:Africa/Monrovia +LAST-MODIFIED:20240205T192834Z +TZNAME:GMT +TZOFFSETFROM:+0000 +TZOFFSETTO:+0000 +DTSTART:19700101T000000`, + "Africa/Bissau": `X-LIC-LOCATION:Africa/Bissau +LAST-MODIFIED:20240205T192834Z +TZNAME:GMT +TZOFFSETFROM:+0000 +TZOFFSETTO:+0000 +DTSTART:19700101T000000`, + "Africa/Juba": `X-LIC-LOCATION:Africa/Juba +LAST-MODIFIED:20240205T192834Z +TZNAME:CAT +TZOFFSETFROM:+0200 +TZOFFSETTO:+0200 +DTSTART:19700101T000000`, + "Africa/Cairo": `X-LIC-LOCATION:Africa/Cairo +LAST-MODIFIED:20240205T192834Z +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700424T000000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1FR +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701030T000000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1FR`, + "Africa/Sao_Tome": `X-LIC-LOCATION:Africa/Sao_Tome +LAST-MODIFIED:20240205T192834Z +TZNAME:GMT +TZOFFSETFROM:+0000 +TZOFFSETTO:+0000 +DTSTART:19700101T000000`, + "Africa/Ndjamena": `X-LIC-LOCATION:Africa/Ndjamena +LAST-MODIFIED:20240205T192834Z +TZNAME:WAT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0100 +DTSTART:19700101T000000`, + "Africa/El_Aaiun": `TZUNTIL:20870511T020001Z +X-LIC-LOCATION:Africa/El_Aaiun +LAST-MODIFIED:20240205T192834Z +TZNAME:+01 +TZOFFSETFROM:+0100 +TZOFFSETTO:+0100 +DTSTART:19700101T000000`, + "Africa/Ceuta": `X-LIC-LOCATION:Africa/Ceuta +LAST-MODIFIED:20240205T192834Z +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Africa/Johannesburg": `X-LIC-LOCATION:Africa/Johannesburg +LAST-MODIFIED:20240205T192834Z +TZNAME:SAST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0200 +DTSTART:19700101T000000`, + "EET": `X-LIC-LOCATION:EET +LAST-MODIFIED:20240205T192834Z +TZNAME:EEST +TZOFFSETFROM:+0200 +TZOFFSETTO:+0300 +DTSTART:19700329T030000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:EET +TZOFFSETFROM:+0300 +TZOFFSETTO:+0200 +DTSTART:19701025T040000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Atlantic/South_Georgia": `X-LIC-LOCATION:Atlantic/South_Georgia +LAST-MODIFIED:20240205T192835Z +TZNAME:-02 +TZOFFSETFROM:-0200 +TZOFFSETTO:-0200 +DTSTART:19700101T000000`, + "Atlantic/Faroe": `X-LIC-LOCATION:Atlantic/Faroe +LAST-MODIFIED:20240205T192834Z +TZNAME:WEST +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +DTSTART:19700329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:WET +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +DTSTART:19701025T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Atlantic/Stanley": `X-LIC-LOCATION:Atlantic/Stanley +LAST-MODIFIED:20240205T192835Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "Atlantic/Azores": `X-LIC-LOCATION:Atlantic/Azores +LAST-MODIFIED:20240205T192834Z +TZNAME:+00 +TZOFFSETFROM:-0100 +TZOFFSETTO:+0000 +DTSTART:19700329T000000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:-01 +TZOFFSETFROM:+0000 +TZOFFSETTO:-0100 +DTSTART:19701025T010000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Atlantic/Madeira": `X-LIC-LOCATION:Atlantic/Madeira +LAST-MODIFIED:20240205T192834Z +TZNAME:WEST +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +DTSTART:19700329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:WET +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +DTSTART:19701025T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Atlantic/Bermuda": `X-LIC-LOCATION:Atlantic/Bermuda +LAST-MODIFIED:20240205T192834Z +TZNAME:ADT +TZOFFSETFROM:-0400 +TZOFFSETTO:-0300 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:AST +TZOFFSETFROM:-0300 +TZOFFSETTO:-0400 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU`, + "Atlantic/Canary": `X-LIC-LOCATION:Atlantic/Canary +LAST-MODIFIED:20240205T192834Z +TZNAME:WEST +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +DTSTART:19700329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:WET +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +DTSTART:19701025T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU`, + "Atlantic/Cape_Verde": `X-LIC-LOCATION:Atlantic/Cape_Verde +LAST-MODIFIED:20240205T192834Z +TZNAME:-01 +TZOFFSETFROM:-0100 +TZOFFSETTO:-0100 +DTSTART:19700101T000000`, + "Etc/GMT-4": `X-LIC-LOCATION:Etc/GMT-4 +LAST-MODIFIED:20240205T192835Z +TZNAME:+04 +TZOFFSETFROM:+0400 +TZOFFSETTO:+0400 +DTSTART:19700101T000000`, + "Etc/GMT-8": `X-LIC-LOCATION:Etc/GMT-8 +LAST-MODIFIED:20240205T192835Z +TZNAME:+08 +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +DTSTART:19700101T000000`, + "Etc/GMT+12": `X-LIC-LOCATION:Etc/GMT+12 +LAST-MODIFIED:20240205T192835Z +TZNAME:-12 +TZOFFSETFROM:-1200 +TZOFFSETTO:-1200 +DTSTART:19700101T000000`, + "Etc/GMT+2": `X-LIC-LOCATION:Etc/GMT+2 +LAST-MODIFIED:20240205T192835Z +TZNAME:-02 +TZOFFSETFROM:-0200 +TZOFFSETTO:-0200 +DTSTART:19700101T000000`, + "Etc/GMT+7": `X-LIC-LOCATION:Etc/GMT+7 +LAST-MODIFIED:20240205T192835Z +TZNAME:-07 +TZOFFSETFROM:-0700 +TZOFFSETTO:-0700 +DTSTART:19700101T000000`, + "Etc/GMT-7": `X-LIC-LOCATION:Etc/GMT-7 +LAST-MODIFIED:20240205T192835Z +TZNAME:+07 +TZOFFSETFROM:+0700 +TZOFFSETTO:+0700 +DTSTART:19700101T000000`, + "Etc/GMT+10": `X-LIC-LOCATION:Etc/GMT+10 +LAST-MODIFIED:20240205T192835Z +TZNAME:-10 +TZOFFSETFROM:-1000 +TZOFFSETTO:-1000 +DTSTART:19700101T000000`, + "Etc/GMT-13": `X-LIC-LOCATION:Etc/GMT-13 +LAST-MODIFIED:20240205T192835Z +TZNAME:+13 +TZOFFSETFROM:+1300 +TZOFFSETTO:+1300 +DTSTART:19700101T000000`, + "Etc/UTC": `X-LIC-LOCATION:Etc/UTC +LAST-MODIFIED:20240205T192835Z +TZNAME:UTC +TZOFFSETFROM:+0000 +TZOFFSETTO:+0000 +DTSTART:19700101T000000`, + "Etc/GMT-5": `X-LIC-LOCATION:Etc/GMT-5 +LAST-MODIFIED:20240205T192835Z +TZNAME:+05 +TZOFFSETFROM:+0500 +TZOFFSETTO:+0500 +DTSTART:19700101T000000`, + "Etc/GMT-6": `X-LIC-LOCATION:Etc/GMT-6 +LAST-MODIFIED:20240205T192835Z +TZNAME:+06 +TZOFFSETFROM:+0600 +TZOFFSETTO:+0600 +DTSTART:19700101T000000`, + "Etc/GMT+8": `X-LIC-LOCATION:Etc/GMT+8 +LAST-MODIFIED:20240205T192835Z +TZNAME:-08 +TZOFFSETFROM:-0800 +TZOFFSETTO:-0800 +DTSTART:19700101T000000`, + "Etc/GMT+11": `X-LIC-LOCATION:Etc/GMT+11 +LAST-MODIFIED:20240205T192835Z +TZNAME:-11 +TZOFFSETFROM:-1100 +TZOFFSETTO:-1100 +DTSTART:19700101T000000`, + "Etc/GMT+9": `X-LIC-LOCATION:Etc/GMT+9 +LAST-MODIFIED:20240205T192835Z +TZNAME:-09 +TZOFFSETFROM:-0900 +TZOFFSETTO:-0900 +DTSTART:19700101T000000`, + "Etc/GMT-1": `X-LIC-LOCATION:Etc/GMT-1 +LAST-MODIFIED:20240205T192835Z +TZNAME:+01 +TZOFFSETFROM:+0100 +TZOFFSETTO:+0100 +DTSTART:19700101T000000`, + "Etc/GMT+5": `X-LIC-LOCATION:Etc/GMT+5 +LAST-MODIFIED:20240205T192835Z +TZNAME:-05 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0500 +DTSTART:19700101T000000`, + "Etc/GMT-3": `X-LIC-LOCATION:Etc/GMT-3 +LAST-MODIFIED:20240205T192835Z +TZNAME:+03 +TZOFFSETFROM:+0300 +TZOFFSETTO:+0300 +DTSTART:19700101T000000`, + "Etc/GMT-14": `X-LIC-LOCATION:Etc/GMT-14 +LAST-MODIFIED:20240205T192835Z +TZNAME:+14 +TZOFFSETFROM:+1400 +TZOFFSETTO:+1400 +DTSTART:19700101T000000`, + "Etc/GMT-9": `X-LIC-LOCATION:Etc/GMT-9 +LAST-MODIFIED:20240205T192835Z +TZNAME:+09 +TZOFFSETFROM:+0900 +TZOFFSETTO:+0900 +DTSTART:19700101T000000`, + "Etc/GMT-12": `X-LIC-LOCATION:Etc/GMT-12 +LAST-MODIFIED:20240205T192835Z +TZNAME:+12 +TZOFFSETFROM:+1200 +TZOFFSETTO:+1200 +DTSTART:19700101T000000`, + "Etc/GMT+3": `X-LIC-LOCATION:Etc/GMT+3 +LAST-MODIFIED:20240205T192835Z +TZNAME:-03 +TZOFFSETFROM:-0300 +TZOFFSETTO:-0300 +DTSTART:19700101T000000`, + "Etc/GMT-10": `X-LIC-LOCATION:Etc/GMT-10 +LAST-MODIFIED:20240205T192835Z +TZNAME:+10 +TZOFFSETFROM:+1000 +TZOFFSETTO:+1000 +DTSTART:19700101T000000`, + "Etc/GMT-2": `X-LIC-LOCATION:Etc/GMT-2 +LAST-MODIFIED:20240205T192835Z +TZNAME:+02 +TZOFFSETFROM:+0200 +TZOFFSETTO:+0200 +DTSTART:19700101T000000`, + "Etc/GMT+6": `X-LIC-LOCATION:Etc/GMT+6 +LAST-MODIFIED:20240205T192835Z +TZNAME:-06 +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "Etc/GMT": `X-LIC-LOCATION:Etc/GMT +LAST-MODIFIED:20240205T192835Z +TZNAME:GMT +TZOFFSETFROM:+0000 +TZOFFSETTO:+0000 +DTSTART:19700101T000000`, + "Etc/GMT+1": `X-LIC-LOCATION:Etc/GMT+1 +LAST-MODIFIED:20240205T192835Z +TZNAME:-01 +TZOFFSETFROM:-0100 +TZOFFSETTO:-0100 +DTSTART:19700101T000000`, + "Etc/GMT+4": `X-LIC-LOCATION:Etc/GMT+4 +LAST-MODIFIED:20240205T192835Z +TZNAME:-04 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0400 +DTSTART:19700101T000000`, + "Etc/GMT-11": `X-LIC-LOCATION:Etc/GMT-11 +LAST-MODIFIED:20240205T192835Z +TZNAME:+11 +TZOFFSETFROM:+1100 +TZOFFSETTO:+1100 +DTSTART:19700101T000000`, + "Pacific/Kanton": `X-LIC-LOCATION:Pacific/Kanton +LAST-MODIFIED:20240205T192834Z +TZNAME:+13 +TZOFFSETFROM:+1300 +TZOFFSETTO:+1300 +DTSTART:19700101T000000`, + "Pacific/Auckland": `X-LIC-LOCATION:Pacific/Auckland +LAST-MODIFIED:20240205T192834Z +TZNAME:NZDT +TZOFFSETFROM:+1200 +TZOFFSETTO:+1300 +DTSTART:19700927T020000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1SU +TZNAME:NZST +TZOFFSETFROM:+1300 +TZOFFSETTO:+1200 +DTSTART:19700405T030000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU`, + "Pacific/Norfolk": `X-LIC-LOCATION:Pacific/Norfolk +LAST-MODIFIED:20240205T192834Z +TZNAME:+12 +TZOFFSETFROM:+1100 +TZOFFSETTO:+1200 +DTSTART:19701004T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU +TZNAME:+11 +TZOFFSETFROM:+1200 +TZOFFSETTO:+1100 +DTSTART:19700405T030000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU`, + "Pacific/Easter": `X-LIC-LOCATION:Pacific/Easter +LAST-MODIFIED:20240205T192835Z +TZNAME:-06 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19700404T220000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SA +TZNAME:-05 +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700905T220000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=1SA`, + "Pacific/Kwajalein": `X-LIC-LOCATION:Pacific/Kwajalein +LAST-MODIFIED:20240205T192834Z +TZNAME:+12 +TZOFFSETFROM:+1200 +TZOFFSETTO:+1200 +DTSTART:19700101T000000`, + "Pacific/Pitcairn": `X-LIC-LOCATION:Pacific/Pitcairn +LAST-MODIFIED:20240205T192834Z +TZNAME:-08 +TZOFFSETFROM:-0800 +TZOFFSETTO:-0800 +DTSTART:19700101T000000`, + "Pacific/Honolulu": `X-LIC-LOCATION:Pacific/Honolulu +LAST-MODIFIED:20240205T192834Z +TZNAME:HST +TZOFFSETFROM:-1000 +TZOFFSETTO:-1000 +DTSTART:19700101T000000`, + "Pacific/Niue": `X-LIC-LOCATION:Pacific/Niue +LAST-MODIFIED:20240205T192834Z +TZNAME:-11 +TZOFFSETFROM:-1100 +TZOFFSETTO:-1100 +DTSTART:19700101T000000`, + "Pacific/Nauru": `X-LIC-LOCATION:Pacific/Nauru +LAST-MODIFIED:20240205T192834Z +TZNAME:+12 +TZOFFSETFROM:+1200 +TZOFFSETTO:+1200 +DTSTART:19700101T000000`, + "Pacific/Efate": `X-LIC-LOCATION:Pacific/Efate +LAST-MODIFIED:20240205T192834Z +TZNAME:+11 +TZOFFSETFROM:+1100 +TZOFFSETTO:+1100 +DTSTART:19700101T000000`, + "Pacific/Bougainville": `X-LIC-LOCATION:Pacific/Bougainville +LAST-MODIFIED:20240205T192834Z +TZNAME:+11 +TZOFFSETFROM:+1100 +TZOFFSETTO:+1100 +DTSTART:19700101T000000`, + "Pacific/Gambier": `X-LIC-LOCATION:Pacific/Gambier +LAST-MODIFIED:20240205T192834Z +TZNAME:-09 +TZOFFSETFROM:-0900 +TZOFFSETTO:-0900 +DTSTART:19700101T000000`, + "Pacific/Tahiti": `X-LIC-LOCATION:Pacific/Tahiti +LAST-MODIFIED:20240205T192834Z +TZNAME:-10 +TZOFFSETFROM:-1000 +TZOFFSETTO:-1000 +DTSTART:19700101T000000`, + "Pacific/Fiji": `X-LIC-LOCATION:Pacific/Fiji +LAST-MODIFIED:20240205T192834Z +TZNAME:+12 +TZOFFSETFROM:+1200 +TZOFFSETTO:+1200 +DTSTART:19700101T000000`, + "Pacific/Apia": `X-LIC-LOCATION:Pacific/Apia +LAST-MODIFIED:20240205T192834Z +TZNAME:+13 +TZOFFSETFROM:+1300 +TZOFFSETTO:+1300 +DTSTART:19700101T000000`, + "Pacific/Fakaofo": `X-LIC-LOCATION:Pacific/Fakaofo +LAST-MODIFIED:20240205T192834Z +TZNAME:+13 +TZOFFSETFROM:+1300 +TZOFFSETTO:+1300 +DTSTART:19700101T000000`, + "Pacific/Tarawa": `X-LIC-LOCATION:Pacific/Tarawa +LAST-MODIFIED:20240205T192834Z +TZNAME:+12 +TZOFFSETFROM:+1200 +TZOFFSETTO:+1200 +DTSTART:19700101T000000`, + "Pacific/Guam": `X-LIC-LOCATION:Pacific/Guam +LAST-MODIFIED:20240205T192834Z +TZNAME:ChST +TZOFFSETFROM:+1000 +TZOFFSETTO:+1000 +DTSTART:19700101T000000`, + "Pacific/Port_Moresby": `X-LIC-LOCATION:Pacific/Port_Moresby +LAST-MODIFIED:20240205T192834Z +TZNAME:+10 +TZOFFSETFROM:+1000 +TZOFFSETTO:+1000 +DTSTART:19700101T000000`, + "Pacific/Kiritimati": `X-LIC-LOCATION:Pacific/Kiritimati +LAST-MODIFIED:20240205T192834Z +TZNAME:+14 +TZOFFSETFROM:+1400 +TZOFFSETTO:+1400 +DTSTART:19700101T000000`, + "Pacific/Tongatapu": `X-LIC-LOCATION:Pacific/Tongatapu +LAST-MODIFIED:20240205T192834Z +TZNAME:+13 +TZOFFSETFROM:+1300 +TZOFFSETTO:+1300 +DTSTART:19700101T000000`, + "Pacific/Marquesas": `X-LIC-LOCATION:Pacific/Marquesas +LAST-MODIFIED:20240205T192834Z +TZNAME:-0930 +TZOFFSETFROM:-0930 +TZOFFSETTO:-0930 +DTSTART:19700101T000000`, + "Pacific/Guadalcanal": `X-LIC-LOCATION:Pacific/Guadalcanal +LAST-MODIFIED:20240205T192834Z +TZNAME:+11 +TZOFFSETFROM:+1100 +TZOFFSETTO:+1100 +DTSTART:19700101T000000`, + "Pacific/Palau": `X-LIC-LOCATION:Pacific/Palau +LAST-MODIFIED:20240205T192834Z +TZNAME:+09 +TZOFFSETFROM:+0900 +TZOFFSETTO:+0900 +DTSTART:19700101T000000`, + "Pacific/Rarotonga": `X-LIC-LOCATION:Pacific/Rarotonga +LAST-MODIFIED:20240205T192834Z +TZNAME:-10 +TZOFFSETFROM:-1000 +TZOFFSETTO:-1000 +DTSTART:19700101T000000`, + "Pacific/Chatham": `X-LIC-LOCATION:Pacific/Chatham +LAST-MODIFIED:20240205T192834Z +TZNAME:+1345 +TZOFFSETFROM:+1245 +TZOFFSETTO:+1345 +DTSTART:19700927T024500 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1SU +TZNAME:+1245 +TZOFFSETFROM:+1345 +TZOFFSETTO:+1245 +DTSTART:19700405T034500 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU`, + "Pacific/Galapagos": `X-LIC-LOCATION:Pacific/Galapagos +LAST-MODIFIED:20240205T192835Z +TZNAME:-06 +TZOFFSETFROM:-0600 +TZOFFSETTO:-0600 +DTSTART:19700101T000000`, + "Pacific/Kosrae": `X-LIC-LOCATION:Pacific/Kosrae +LAST-MODIFIED:20240205T192834Z +TZNAME:+11 +TZOFFSETFROM:+1100 +TZOFFSETTO:+1100 +DTSTART:19700101T000000`, + "Pacific/Pago_Pago": `X-LIC-LOCATION:Pacific/Pago_Pago +LAST-MODIFIED:20240205T192834Z +TZNAME:SST +TZOFFSETFROM:-1100 +TZOFFSETTO:-1100 +DTSTART:19700101T000000`, + "Pacific/Noumea": `X-LIC-LOCATION:Pacific/Noumea +LAST-MODIFIED:20240205T192834Z +TZNAME:+11 +TZOFFSETFROM:+1100 +TZOFFSETTO:+1100 +DTSTART:19700101T000000`, + "HST": `X-LIC-LOCATION:HST +LAST-MODIFIED:20240205T192834Z +TZNAME:HST +TZOFFSETFROM:-1000 +TZOFFSETTO:-1000 +DTSTART:19700101T000000`, +} diff --git a/src/internal/converters/ics/tzdata/fetch.sh b/src/internal/converters/ics/tzdata/fetch.sh new file mode 100755 index 000000000..2b2285cc1 --- /dev/null +++ b/src/internal/converters/ics/tzdata/fetch.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +set -eo pipefail + +if ! echo "$PWD" | grep -q '/tzdata$'; then + echo "Please run this script from the tzdata dir" + exit 1 +fi + +# TODO: Generate from https://www.iana.org/time-zones +if [ ! -d /tmp/corso-tzdata ]; then + git clone --depth 1 https://github.com/add2cal/timezones-ical-library.git /tmp/corso-tzdata +else + cd /tmp/corso-tzdata + git pull + cd - +fi + +# Generate a huge go file with all the timezones +echo "package tzdata" >data.go +echo "" >>data.go + +echo "var TZData = map[string]string{" >>data.go + +find /tmp/corso-tzdata/ -name '*.ics' | while read -r f; do + tz=$(echo "$f" | sed 's|/tmp/corso-tzdata/api/||;s|\.ics$||') + echo "Processing $tz" + printf "\t\"%s\": \`" "$tz" >>data.go + cat "$f" | grep -Ev "(BEGIN:|END:|TZID:)" | + sed 's|`|\\`|g;s|\r||;s|TZID:/timezones-ical-library/|TZID:|' | + perl -pe 'chomp if eof' >>data.go + echo "\`," >>data.go +done + +echo "}" >>data.go