Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add absent RRuleSet getters (GetRDate(), ...), extract StrSliceToRRuleSet() and StrToDates() functions #10

Merged
merged 5 commits into from
May 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions rruleset.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,35 @@ func (set *Set) RDate(rdate time.Time) {
set.rdate = append(set.rdate, rdate)
}

// GetRDate returns explicitly added dates (rdates) in the set
func (set *Set) GetRDate() []time.Time {
return set.rdate
}

// ExRule include the given rrule instance in the recurrence set exclusion list.
// Dates which are part of the given recurrence rules will not be generated,
// even if some inclusive rrule or rdate matches them.
func (set *Set) ExRule(exrule *RRule) {
set.exrule = append(set.exrule, exrule)
}

// GetExRule returns exclusion rrules list from in the set
func (set *Set) GetExRule() []*RRule {
return set.exrule
}

// ExDate include the given datetime instance in the recurrence set exclusion list.
// Dates included that way will not be generated,
// even if some inclusive rrule or rdate matches them.
func (set *Set) ExDate(exdate time.Time) {
set.exdate = append(set.exdate, exdate)
}

// GetExDate returns explicitly excluded dates (exdates) in the set
func (set *Set) GetExDate() []time.Time {
return set.exdate
}

type genItem struct {
dt time.Time
gen Next
Expand Down
75 changes: 50 additions & 25 deletions str.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ import (
)

const (
strformat = "20060102T150405Z"
// DateTimeFormat is date-time format used in iCalendar (RFC 5545)
DateTimeFormat = "20060102T150405Z"
)

func timeToStr(time time.Time) string {
return time.UTC().Format(strformat)
return time.UTC().Format(DateTimeFormat)
}

func strToTime(str string) (time.Time, error) {
return time.Parse(strformat, str)
return time.Parse(DateTimeFormat, str)
}

func (f Frequency) String() string {
Expand Down Expand Up @@ -220,30 +221,31 @@ func StrToRRule(rfcString string) (*RRule, error) {

// StrToRRuleSet converts string to RRuleSet
func StrToRRuleSet(s string) (*Set, error) {
s = strings.TrimSpace(strings.ToUpper(s))
s = strings.TrimSpace(s)
if s == "" {
return nil, errors.New("empty string")
}
ss := strings.Split(s, "\n")
return StrSliceToRRuleSet(ss)
}

// StrSliceToRRuleSet converts given str slice to RRuleSet
func StrSliceToRRuleSet(ss []string) (*Set, error) {
set := Set{}
for _, line := range strings.Split(s, "\n") {
line = strings.TrimSpace(line)
for _, line := range ss {
line = strings.ToUpper(strings.TrimSpace(line))
if line == "" {
continue
}
temp := strings.SplitN(line, ":", 2)
if len(temp) != 2 {
nameLen := strings.IndexAny(line, ";:")
if nameLen < 0 {
return nil, errors.New("bad format")
}
name, value := temp[0], temp[1]
parms := strings.Split(name, ";")
name = parms[0]
parms = parms[1:]
name := line[:nameLen]

switch name {
case "RRULE", "EXRULE":
for _, parm := range parms {
return nil, fmt.Errorf("unsupported RRULE/EXRULE parm: %v", parm)
}
r, err := StrToRRule(value)
r, err := StrToRRule(line[nameLen+1:])
if err != nil {
return nil, fmt.Errorf("strToRRule failed: %v", err)
}
Expand All @@ -253,16 +255,11 @@ func StrToRRuleSet(s string) (*Set, error) {
set.ExRule(r)
}
case "RDATE", "EXDATE":
for _, parm := range parms {
if parm != "VALUE=DATE-TIME" {
return nil, fmt.Errorf("unsupported RDATE/EXDATE parm: %v", parm)
}
ts, err := StrToDates(line[nameLen+1:])
if err != nil {
return nil, fmt.Errorf("strToDates failed: %v", err)
}
for _, datestr := range strings.Split(value, ",") {
t, err := strToTime(datestr)
if err != nil {
return nil, fmt.Errorf("strToTime failed: %v", err)
}
for _, t := range ts {
if name == "RDATE" {
set.RDate(t)
} else {
Expand All @@ -273,5 +270,33 @@ func StrToRRuleSet(s string) (*Set, error) {
return nil, fmt.Errorf("unsupported property: %v", name)
}
}

return &set, nil
}

// StrToDates accepts string with format: "VALUE=DATE-TIME:{time},{time},...,{time}"
// or simply "{time},{time},...{time}" and parses it to array of dates
// may be used to parse RDATE/EXDATE rules
func StrToDates(str string) (ts []time.Time, err error) {
tmp := strings.Split(str, ":")
if len(tmp) > 2 {
return nil, fmt.Errorf("bad format")
}
if len(tmp) == 2 {
params := strings.Split(tmp[0], ";")
for _, param := range params {
if param != "VALUE=DATE-TIME" {
return nil, fmt.Errorf("unsupported RDATE/EXDATE parm: %v", param)
}
}
tmp = tmp[1:]
}
for _, datestr := range strings.Split(tmp[0], ",") {
t, err := strToTime(datestr)
if err != nil {
return nil, fmt.Errorf("strToTime failed: %v", err)
}
ts = append(ts, t)
}
return
}
56 changes: 55 additions & 1 deletion str_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package rrule

import "testing"
import (
"testing"
)

func TestStr(t *testing.T) {
str := "FREQ=WEEKLY;DTSTART=20120201T093000Z;INTERVAL=5;WKST=TU;COUNT=2;UNTIL=20130130T230000Z;BYSETPOS=2;BYMONTH=3;BYYEARDAY=95;BYWEEKNO=1;BYDAY=MO,+2FR;BYHOUR=9;BYMINUTE=30;BYSECOND=0;BYEASTER=-1"
Expand Down Expand Up @@ -28,3 +30,55 @@ 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" +
"EXRULE:FREQ=WEEKLY;INTERVAL=4;BYDAY=MO\n" +
"EXDATE;VALUE=DATE-TIME:20180525T070000Z,20180530T130000Z\n" +
"RDATE;VALUE=DATE-TIME:20180801T131313Z,20180902T141414Z\n"

set, err := StrToRRuleSet(setStr)
if err != nil {
t.Fatalf("StrToRRuleSet(%s) returned error: %v", setStr, err)
}

// 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 rRules[0].String() != "FREQ=DAILY;UNTIL=20180517T235959Z" {
t.Errorf("Unexpected rrule: %s", rRules[0].String())
}
if rRules[1].String() != "FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,TU" {
t.Errorf("Unexpected rrule: %s", rRules[0].String())
}

// matching parsed EXRules
exRules := set.GetExRule()
if len(exRules) != 1 {
t.Errorf("Unexpected number of exrule parsed: %v != 1, exrules: %v", len(exRules), exRules)
}
if exRules[0].String() != "FREQ=WEEKLY;INTERVAL=4;BYDAY=MO" {
t.Errorf("Unexpected exrule: %s", exRules[0].String())
}

// matching parsed EXDates
exDates := set.GetExDate()
if len(exDates) != 2 {
t.Errorf("Unexpected number of exDates: %v != 2, %v", len(exDates), exDates)
}
if [2]string{timeToStr(exDates[0]), timeToStr(exDates[1])} != [2]string{"20180525T070000Z", "20180530T130000Z"} {
t.Errorf("Unexpected exDates: %v", exDates)
}

// matching parsed RDates
rDates := set.GetRDate()
if len(rDates) != 2 {
t.Errorf("Unexpected number of rDates: %v != 2, %v", len(rDates), rDates)
}
if [2]string{timeToStr(rDates[0]), timeToStr(rDates[1])} != [2]string{"20180801T131313Z", "20180902T141414Z"} {
t.Errorf("Unexpected exDates: %v", exDates)
}
}