arran4 / golang-ical

A ICS / ICal parser and serialiser for Golang.
Apache License 2.0
279 stars 72 forks source link

IsInEvent functionality #70

Open Mereep opened 1 year ago

Mereep commented 1 year ago

Hi, I found this repo yesterday when brainstorming (yes, and GPT-Storming) for a general way to represent a allowance to unlock a door.

Given a valid iCal format, I now need a way to check if now falls into the calender event (true false). Is there an easy way to do so (I didn't see a command in the lib)? I do not have any prior experience with the format yet, so I cannot judge on the complexity and edgy cases for this.

idk if it helps, but this is what ChatGPT came up with (untested yet): Edit: code built into my program, tested for intervals and simple dates (UTC and local time)

func (cal *ICalCalendar) IsIn(unixTimestamp uint64)  (res Result[bool]) {
    defer func() {
        if r := recover(); r != nil {
            res = ResultError[bool](
                fmt.Errorf("panic when checking for date in ical calendar: %v", r),
                ERROR_ICAL_ISIN_CHECK)
        }
    }()
    givenTime := time.Unix(int64(unixTimestamp), 0)
    errors := []error{}
    found := false
    for _, event := range cal.inst.Events() {
        start, err := event.GetStartAt()
        if err != nil {
            errors = append(errors, err)
            continue
        }

        end, err := event.GetEndAt()
        if err != nil {
            errors = append(errors, err)
            continue
        }

        duration := end.Sub(start)

        // Check non-recurring event
        if (givenTime.After(start) || givenTime.Unix() == start.Unix()) && (givenTime.Before(end) || givenTime.Unix() == end.Unix()) {
            found = true
            break
        }

        // Check recurring event
        PropRRule := event.GetProperty("RRULE")
        if PropRRule != nil {
            startStr := event.GetProperty("DTSTART").Value
            rruleStr := PropRRule.Value
            r, err := rrule.StrToRRule(fmt.Sprintf("DTSTART:%s\nRRULE:%s", startStr, rruleStr))
            if err != nil {
                errors = append(errors, err)
                continue
            }

            instances := r.Between(start, start.Add(1*time.Hour*24*365), true) // Limit search to 1 year ahead
            for _, instance := range instances {
                instanceEnd := instance.Add(duration)
                if (givenTime.After(instance) || givenTime.Unix() == instance.Unix()) && (givenTime.Before(instanceEnd) || givenTime.Unix() == instanceEnd.Unix()) {
                    found = true
                    break
                }
            }
        }
    }

    if len(errors) > 0 {
        return ResultWarning[bool](
            found,
            errors,
            ERROR_ICAL_CHECK,
        )
    }

    return ResultOk(found)
}

it should be able to handle interval events also. Any feedback on that?