diff --git a/str.go b/str.go index d25849e..f27b304 100644 --- a/str.go +++ b/str.go @@ -11,6 +11,10 @@ import ( const ( // DateTimeFormat is date-time format used in iCalendar (RFC 5545) DateTimeFormat = "20060102T150405Z" + // LocalDateTimeFormat is a date-time format without Z prefix + LocalDateTimeFormat = "20060102T150405" + // DateFormat is date format used in iCalendar (RFC 5545) + DateFormat = "20060102" ) func timeToStr(time time.Time) string { @@ -18,6 +22,17 @@ func timeToStr(time time.Time) string { } func strToTime(str string) (time.Time, error) { + return strToTimeInLoc(str, time.UTC) +} + +func strToTimeInLoc(str string, loc *time.Location) (time.Time, error) { + if len(str) == len(DateFormat) { + return time.ParseInLocation(DateFormat, str, loc) + } + if len(str) == len(LocalDateTimeFormat) { + return time.ParseInLocation(LocalDateTimeFormat, str, loc) + } + // date-time format carries zone info return time.Parse(DateTimeFormat, str) } @@ -143,6 +158,13 @@ func (option *ROption) String() string { // StrToROption converts string to ROption func StrToROption(rfcString string) (*ROption, error) { + return StrToROptionInLocation(rfcString, time.UTC) +} + +// StrToROptionInLocation is same as StrToROption but in case local +// time is supplied as date-time/date field (ex. UNTIL), it is parsed +// as a time in a given location (time zone) +func StrToROptionInLocation(rfcString string, loc *time.Location) (*ROption, error) { rfcString = strings.TrimSpace(rfcString) if len(rfcString) == 0 { return nil, errors.New("empty string") @@ -162,7 +184,7 @@ func StrToROption(rfcString string) (*ROption, error) { case "FREQ": result.Freq, e = strToFreq(value) case "DTSTART": - result.Dtstart, e = strToTime(value) + result.Dtstart, e = strToTimeInLoc(value, loc) case "INTERVAL": result.Interval, e = strconv.Atoi(value) case "WKST": @@ -170,7 +192,7 @@ func StrToROption(rfcString string) (*ROption, error) { case "COUNT": result.Count, e = strconv.Atoi(value) case "UNTIL": - result.Until, e = strToTime(value) + result.Until, e = strToTimeInLoc(value, loc) case "BYSETPOS": result.Bysetpos, e = strToInts(value) case "BYMONTH": diff --git a/str_test.go b/str_test.go index 8a1cb9c..451e30b 100644 --- a/str_test.go +++ b/str_test.go @@ -34,6 +34,7 @@ func TestInvalidString(t *testing.T) { func TestSetStr(t *testing.T) { setStr := "RRULE:FREQ=DAILY;UNTIL=20180517T235959Z\n" + "RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,TU\n" + + "RRULE:FREQ=MONTHLY;UNTIL=20180520;BYMONTHDAY=1,2,3\n" + "EXRULE:FREQ=WEEKLY;INTERVAL=4;BYDAY=MO\n" + "EXDATE;VALUE=DATE-TIME:20180525T070000Z,20180530T130000Z\n" + "RDATE;VALUE=DATE-TIME:20180801T131313Z,20180902T141414Z\n" @@ -45,8 +46,8 @@ func TestSetStr(t *testing.T) { // matching parsed RRules rRules := set.GetRRule() - if len(rRules) != 2 { - t.Errorf("Unexpected number of rrule parsed: %v != 2, rrules: %v", len(rRules), rRules) + if len(rRules) != 3 { + t.Errorf("Unexpected number of rrule parsed: %v != 3, rrules: %v", len(rRules), rRules) } if rRules[0].String() != "FREQ=DAILY;UNTIL=20180517T235959Z" { t.Errorf("Unexpected rrule: %s", rRules[0].String()) @@ -54,6 +55,9 @@ func TestSetStr(t *testing.T) { if rRules[1].String() != "FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,TU" { t.Errorf("Unexpected rrule: %s", rRules[0].String()) } + if rRules[2].String() != "FREQ=MONTHLY;UNTIL=20180520T000000Z;BYMONTHDAY=1,2,3" { + t.Errorf("Unexpected rrule: %s", rRules[2].String()) + } // matching parsed EXRules exRules := set.GetExRule()