133 lines
2.8 KiB
Go
133 lines
2.8 KiB
Go
|
package values
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"github.com/dolanor/caldav-go/utils"
|
||
|
"log"
|
||
|
"math"
|
||
|
"regexp"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
var _ = log.Print
|
||
|
|
||
|
// a representation of duration for iCalendar
|
||
|
type Duration struct {
|
||
|
d time.Duration
|
||
|
}
|
||
|
|
||
|
// breaks apart the duration into its component time parts
|
||
|
func (d *Duration) Decompose() (weeks, days, hours, minutes, seconds int64) {
|
||
|
|
||
|
// chip away at this
|
||
|
rem := time.Duration(math.Abs(float64(d.d)))
|
||
|
|
||
|
div := time.Hour * 24 * 7
|
||
|
weeks = int64(rem / div)
|
||
|
rem = rem % div
|
||
|
div = div / 7
|
||
|
days = int64(rem / div)
|
||
|
rem = rem % div
|
||
|
div = div / 24
|
||
|
hours = int64(rem / div)
|
||
|
rem = rem % div
|
||
|
div = div / 60
|
||
|
minutes = int64(rem / div)
|
||
|
rem = rem % div
|
||
|
div = div / 60
|
||
|
seconds = int64(rem / div)
|
||
|
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
// returns the native golang duration
|
||
|
func (d *Duration) NativeDuration() time.Duration {
|
||
|
return d.d
|
||
|
}
|
||
|
|
||
|
// returns true if the duration is negative
|
||
|
func (d *Duration) IsPast() bool {
|
||
|
return d.d < 0
|
||
|
}
|
||
|
|
||
|
// encodes the duration of time into iCalendar format
|
||
|
func (d *Duration) EncodeICalValue() (string, error) {
|
||
|
var parts []string
|
||
|
weeks, days, hours, minutes, seconds := d.Decompose()
|
||
|
if d.IsPast() {
|
||
|
parts = append(parts, "-")
|
||
|
}
|
||
|
parts = append(parts, "P")
|
||
|
if weeks > 0 {
|
||
|
parts = append(parts, fmt.Sprintf("%dW", weeks))
|
||
|
}
|
||
|
if days > 0 {
|
||
|
parts = append(parts, fmt.Sprintf("%dD", days))
|
||
|
}
|
||
|
if hours > 0 || minutes > 0 || seconds > 0 {
|
||
|
parts = append(parts, "T")
|
||
|
if hours > 0 {
|
||
|
parts = append(parts, fmt.Sprintf("%dH", hours))
|
||
|
}
|
||
|
if minutes > 0 {
|
||
|
parts = append(parts, fmt.Sprintf("%dM", minutes))
|
||
|
}
|
||
|
if seconds > 0 {
|
||
|
parts = append(parts, fmt.Sprintf("%dS", seconds))
|
||
|
}
|
||
|
}
|
||
|
return strings.Join(parts, ""), nil
|
||
|
}
|
||
|
|
||
|
var durationRegEx = regexp.MustCompile("(\\d+)(\\w)")
|
||
|
|
||
|
// decodes the duration of time from iCalendar format
|
||
|
func (d *Duration) DecodeICalValue(value string) error {
|
||
|
var seconds int64
|
||
|
var isPast = strings.HasPrefix(value, "-P")
|
||
|
var matches = durationRegEx.FindAllStringSubmatch(value, -1)
|
||
|
for _, match := range matches {
|
||
|
var multiplier int64
|
||
|
ivalue, err := strconv.ParseInt(match[1], 10, 64)
|
||
|
if err != nil {
|
||
|
return utils.NewError(d.DecodeICalValue, "unable to decode duration value "+match[1], d, nil)
|
||
|
}
|
||
|
switch match[2] {
|
||
|
case "S":
|
||
|
multiplier = 1
|
||
|
case "M":
|
||
|
multiplier = 60
|
||
|
case "H":
|
||
|
multiplier = 60 * 60
|
||
|
case "D":
|
||
|
multiplier = 60 * 60 * 24
|
||
|
case "W":
|
||
|
multiplier = 60 * 60 * 24 * 7
|
||
|
default:
|
||
|
return utils.NewError(d.DecodeICalValue, "unable to decode duration segment "+match[2], d, nil)
|
||
|
}
|
||
|
seconds = seconds + multiplier*ivalue
|
||
|
}
|
||
|
d.d = time.Duration(seconds) * time.Second
|
||
|
if isPast {
|
||
|
d.d = -d.d
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (d *Duration) String() string {
|
||
|
if s, err := d.EncodeICalValue(); err != nil {
|
||
|
panic(err)
|
||
|
} else {
|
||
|
return s
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// creates a new iCalendar duration representation
|
||
|
func NewDuration(d time.Duration) *Duration {
|
||
|
return &Duration{d: d}
|
||
|
}
|