feat: implement caldav search
This commit is contained in:
+265
@@ -0,0 +1,265 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dolanor/caldav-go/icalendar/properties"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
const DateFormatString = "20060102"
|
||||
const DateTimeFormatString = "20060102T150405"
|
||||
const UTCDateTimeFormatString = "20060102T150405Z"
|
||||
|
||||
// a representation of a date and time for iCalendar
|
||||
type DateTime struct {
|
||||
t time.Time
|
||||
}
|
||||
|
||||
type DateTimes []*DateTime
|
||||
|
||||
// The exception dates, if specified, are used in computing the recurrence set. The recurrence set is the complete set
|
||||
// of recurrence instances for a calendar component. The recurrence set is generated by considering the initial
|
||||
// "DTSTART" property along with the "RRULE", "RDATE", "EXDATE" and "EXRULE" properties contained within the iCalendar
|
||||
// object. The "DTSTART" property defines the first instance in the recurrence set. Multiple instances of the "RRULE"
|
||||
// and "EXRULE" properties can also be specified to define more sophisticated recurrence sets. The final recurrence set
|
||||
// is generated by gathering all of the start date-times generated by any of the specified "RRULE" and "RDATE"
|
||||
// properties, and then excluding any start date and times which fall within the union of start date and times
|
||||
// generated by any specified "EXRULE" and "EXDATE" properties. This implies that start date and times within exclusion
|
||||
// related properties (i.e., "EXDATE" and "EXRULE") take precedence over those specified by inclusion properties
|
||||
// (i.e., "RDATE" and "RRULE"). Where duplicate instances are generated by the "RRULE" and "RDATE" properties, only
|
||||
// one recurrence is considered. Duplicate instances are ignored.
|
||||
//
|
||||
// The "EXDATE" property can be used to exclude the value specified in "DTSTART". However, in such cases the original
|
||||
// "DTSTART" date MUST still be maintained by the calendaring and scheduling system because the original "DTSTART"
|
||||
// value has inherent usage dependencies by other properties such as the "RECURRENCE-ID".
|
||||
type ExceptionDateTimes DateTimes
|
||||
|
||||
// The recurrence dates, if specified, are used in computing the recurrence set. The recurrence set is the complete set
|
||||
// of recurrence instances for a calendar component. The recurrence set is generated by considering the initial
|
||||
// "DTSTART" property along with the "RRULE", "RDATE", "EXDATE" and "EXRULE" properties contained within the iCalendar
|
||||
// object. The "DTSTART" property defines the first instance in the recurrence set. Multiple instances of the "RRULE"
|
||||
// and "EXRULE" properties can also be specified to define more sophisticated recurrence sets. The final recurrence set
|
||||
// is generated by gathering all of the start date-times generated by any of the specified "RRULE" and "RDATE"
|
||||
// properties, and then excluding any start date and times which fall within the union of start date and times
|
||||
// generated by any specified "EXRULE" and "EXDATE" properties. This implies that start date and times within exclusion
|
||||
// related properties (i.e., "EXDATE" and "EXRULE") take precedence over those specified by inclusion properties
|
||||
// (i.e., "RDATE" and "RRULE"). Where duplicate instances are generated by the "RRULE" and "RDATE" properties, only
|
||||
// one recurrence is considered. Duplicate instances are ignored.
|
||||
type RecurrenceDateTimes DateTimes
|
||||
|
||||
// creates a new icalendar datetime representation
|
||||
func NewDateTime(t time.Time) *DateTime {
|
||||
return &DateTime{t: t.Truncate(time.Second)}
|
||||
}
|
||||
|
||||
// creates a new icalendar datetime array representation
|
||||
func NewDateTimes(dates ...*DateTime) DateTimes {
|
||||
return DateTimes(dates)
|
||||
}
|
||||
|
||||
// creates a new icalendar datetime array representation
|
||||
func NewExceptionDateTimes(dates ...*DateTime) *ExceptionDateTimes {
|
||||
datetimes := NewDateTimes(dates...)
|
||||
return (*ExceptionDateTimes)(&datetimes)
|
||||
}
|
||||
|
||||
// creates a new icalendar datetime array representation
|
||||
func NewRecurrenceDateTimes(dates ...*DateTime) *RecurrenceDateTimes {
|
||||
datetimes := NewDateTimes(dates...)
|
||||
return (*RecurrenceDateTimes)(&datetimes)
|
||||
}
|
||||
|
||||
// checks to see if two datetimes are equal
|
||||
func (d *DateTime) Equals(test *DateTime) bool {
|
||||
return d.t.Equal(test.t)
|
||||
}
|
||||
|
||||
// returns the native time for the datetime object
|
||||
func (d *DateTime) NativeTime() time.Time {
|
||||
return d.t
|
||||
}
|
||||
|
||||
// encodes the datetime value for the iCalendar specification
|
||||
func (d *DateTime) EncodeICalValue() (string, error) {
|
||||
val := d.t.Format(DateTimeFormatString)
|
||||
loc := d.t.Location()
|
||||
if loc == time.UTC {
|
||||
val = fmt.Sprintf("%sZ", val)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// decodes the datetime value from the iCalendar specification
|
||||
func (d *DateTime) DecodeICalValue(value string) error {
|
||||
layout := DateTimeFormatString
|
||||
if strings.HasSuffix(value, "Z") {
|
||||
layout = UTCDateTimeFormatString
|
||||
} else if len(value) == 8 {
|
||||
layout = DateFormatString
|
||||
}
|
||||
var err error
|
||||
d.t, err = time.ParseInLocation(layout, value, time.UTC)
|
||||
if err != nil {
|
||||
return utils.NewError(d.DecodeICalValue, "unable to parse datetime value", d, err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// encodes the datetime params for the iCalendar specification
|
||||
func (d *DateTime) EncodeICalParams() (params properties.Params, err error) {
|
||||
loc := d.t.Location()
|
||||
if loc != time.UTC {
|
||||
params = properties.Params{properties.TimeZoneIdPropertyName: loc.String()}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// decodes the datetime params from the iCalendar specification
|
||||
func (d *DateTime) DecodeICalParams(params properties.Params) error {
|
||||
layout := DateTimeFormatString
|
||||
value := d.t.Format(layout)
|
||||
if name, found := params[properties.TimeZoneIdPropertyName]; !found {
|
||||
return nil
|
||||
} else if loc, err := time.LoadLocation(name); err != nil {
|
||||
return utils.NewError(d.DecodeICalValue, "unable to parse timezone", d, err)
|
||||
} else if t, err := time.ParseInLocation(layout, value, loc); err != nil {
|
||||
return utils.NewError(d.DecodeICalValue, "unable to parse datetime value", d, err)
|
||||
} else {
|
||||
d.t = t
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// validates the datetime value against the iCalendar specification
|
||||
func (d *DateTime) ValidateICalValue() error {
|
||||
|
||||
loc := d.t.Location()
|
||||
|
||||
if loc == time.Local {
|
||||
msg := "DateTime location may not Local, please use UTC or explicit Location"
|
||||
return utils.NewError(d.ValidateICalValue, msg, d, nil)
|
||||
}
|
||||
|
||||
if loc.String() == "" {
|
||||
msg := "DateTime location must have a valid name"
|
||||
return utils.NewError(d.ValidateICalValue, msg, d, nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodes the datetime value for the iCalendar specification
|
||||
func (d *DateTime) String() string {
|
||||
if s, err := d.EncodeICalValue(); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
// encodes a list of datetime values for the iCalendar specification
|
||||
func (ds *DateTimes) EncodeICalValue() (string, error) {
|
||||
var csv CSV
|
||||
for i, d := range *ds {
|
||||
if s, err := d.EncodeICalValue(); err != nil {
|
||||
msg := fmt.Sprintf("unable to encode datetime at index %d", i)
|
||||
return "", utils.NewError(ds.EncodeICalValue, msg, ds, err)
|
||||
} else {
|
||||
csv = append(csv, s)
|
||||
}
|
||||
}
|
||||
return csv.EncodeICalValue()
|
||||
}
|
||||
|
||||
// encodes a list of datetime params for the iCalendar specification
|
||||
func (ds *DateTimes) EncodeICalParams() (params properties.Params, err error) {
|
||||
if len(*ds) > 0 {
|
||||
params, err = (*ds)[0].EncodeICalParams()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// decodes a list of datetime params from the iCalendar specification
|
||||
func (ds *DateTimes) DecodeICalParams(params properties.Params) error {
|
||||
for i, d := range *ds {
|
||||
if err := d.DecodeICalParams(params); err != nil {
|
||||
msg := fmt.Sprintf("unable to decode datetime params for index %d", i)
|
||||
return utils.NewError(ds.DecodeICalValue, msg, ds, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodes a list of datetime values for the iCalendar specification
|
||||
func (ds *DateTimes) DecodeICalValue(value string) error {
|
||||
csv := new(CSV)
|
||||
if err := csv.DecodeICalValue(value); err != nil {
|
||||
return utils.NewError(ds.DecodeICalValue, "unable to decode datetime list as CSV", ds, err)
|
||||
}
|
||||
for i, value := range *csv {
|
||||
d := new(DateTime)
|
||||
if err := d.DecodeICalValue(value); err != nil {
|
||||
msg := fmt.Sprintf("unable to decode datetime at index %d", i)
|
||||
return utils.NewError(ds.DecodeICalValue, msg, ds, err)
|
||||
} else {
|
||||
*ds = append(*ds, d)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodes exception date times property name for icalendar
|
||||
func (e *ExceptionDateTimes) EncodeICalName() (properties.PropertyName, error) {
|
||||
return properties.ExceptionDateTimesPropertyName, nil
|
||||
}
|
||||
|
||||
// encodes recurrence date times property name for icalendar
|
||||
func (r *RecurrenceDateTimes) EncodeICalName() (properties.PropertyName, error) {
|
||||
return properties.RecurrenceDateTimesPropertyName, nil
|
||||
}
|
||||
|
||||
// encodes exception date times property value for icalendar
|
||||
func (e *ExceptionDateTimes) EncodeICalValue() (string, error) {
|
||||
return (*DateTimes)(e).EncodeICalValue()
|
||||
}
|
||||
|
||||
// encodes recurrence date times property value for icalendar
|
||||
func (r *RecurrenceDateTimes) EncodeICalValue() (string, error) {
|
||||
return (*DateTimes)(r).EncodeICalValue()
|
||||
}
|
||||
|
||||
// decodes exception date times property value for icalendar
|
||||
func (e *ExceptionDateTimes) DecodeICalValue(value string) error {
|
||||
return (*DateTimes)(e).DecodeICalValue(value)
|
||||
}
|
||||
|
||||
// decodes recurrence date times property value for icalendar
|
||||
func (r *RecurrenceDateTimes) DecodeICalValue(value string) error {
|
||||
return (*DateTimes)(r).DecodeICalValue(value)
|
||||
}
|
||||
|
||||
// encodes exception date times property params for icalendar
|
||||
func (e *ExceptionDateTimes) EncodeICalParams() (params properties.Params, err error) {
|
||||
return (*DateTimes)(e).EncodeICalParams()
|
||||
}
|
||||
|
||||
// encodes recurrence date times property params for icalendar
|
||||
func (r *RecurrenceDateTimes) EncodeICalParams() (params properties.Params, err error) {
|
||||
return (*DateTimes)(r).EncodeICalParams()
|
||||
}
|
||||
|
||||
// encodes exception date times property params for icalendar
|
||||
func (e *ExceptionDateTimes) DecodeICalParams(params properties.Params) error {
|
||||
return (*DateTimes)(e).DecodeICalParams(params)
|
||||
}
|
||||
|
||||
// encodes recurrence date times property params for icalendar
|
||||
func (r *RecurrenceDateTimes) DecodeICalParams(params properties.Params) error {
|
||||
return (*DateTimes)(r).DecodeICalParams(params)
|
||||
}
|
||||
Reference in New Issue
Block a user