266 lines
9.7 KiB
Go
266 lines
9.7 KiB
Go
|
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)
|
||
|
}
|