Skip to content

Commit

Permalink
Add support for TimePeriodType relative EndTime (#18)
Browse files Browse the repository at this point in the history
If only EndTime is provided and it is a duration, it has to decrease
over time. To do this without actually changing the data, it will always
be transformed into an absolute time in Marshal and returned as an up to
date relative duration in Unmarshal and a helper method

Fixes #12
  • Loading branch information
DerAndereAndi authored May 9, 2024
2 parents 0b4b37a + f98bda8 commit e636ba0
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 1 deletion.
97 changes: 96 additions & 1 deletion model/commondatatypes_additions.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package model

import (
"encoding/json"
"errors"
"fmt"
"math"
Expand All @@ -11,6 +12,95 @@ import (
"github.com/rickb777/date/period"
)

// TimePeriodType

func NewTimePeriodTypeWithRelativeEndTime(duration time.Duration) *TimePeriodType {
now := time.Now().UTC()
endTime := now.Add(duration)
value := &TimePeriodType{
EndTime: NewAbsoluteOrRelativeTimeTypeFromTime(endTime),
}
return value
}

// helper type to modify EndTime field value in json.Marshal and
// json.Unmarshal to allow provide accurate relative durations
//
// If only EndTime is provided and it is a duration, it has to
// decrease over time. To do this without actually changing
// the data, it will always be transformed into an absolute time
// in Marshal and returned as an up to date relative duration
// in Unmarshal
type tempTimePeriodType TimePeriodType

func setTimePeriodTypeEndTime(t *tempTimePeriodType) {
if t.StartTime != nil || t.EndTime == nil {
return
}

duration, err := t.EndTime.GetTimeDuration()
if err != nil {
return
}

time := time.Now().UTC().Add(duration)
t.EndTime = NewAbsoluteOrRelativeTimeTypeFromTime(time)
}

func getTimePeriodTypeDuration(t *TimePeriodType) (time.Duration, error) {
if t.StartTime != nil || t.EndTime == nil {
return 0, errors.New("invalid data format")
}

if t.EndTime.IsRelativeTime() {
return getTimeDurationFromString(string(*t.EndTime))
}

endTime, err := t.EndTime.GetTime()
if err != nil {
return 0, err
}

now := time.Now().UTC()
duration := endTime.Sub(now)
duration = duration.Round(time.Second)

return duration, nil
}

// when startTime is empty and endTime is an absolute time,
// then endTime should be returned as an relative timestamp
func (t TimePeriodType) MarshalJSON() ([]byte, error) {
temp := tempTimePeriodType(t)

if duration, err := getTimePeriodTypeDuration(&t); err == nil {
temp.EndTime = NewAbsoluteOrRelativeTimeTypeFromDuration(duration)
}

return json.Marshal(temp)
}

// when startTime is empty and endTime is a relative time,
// then endTime should be written as an absolute timestamp
func (t *TimePeriodType) UnmarshalJSON(data []byte) error {
var temp tempTimePeriodType
if err := json.Unmarshal(data, &temp); err != nil {
return err
}

setTimePeriodTypeEndTime(&temp)

*t = TimePeriodType(temp)

return nil
}

// Return the current duration if StartTime is nil and EndTime is not
// otherwise returns an error
func (t *TimePeriodType) GetDuration() (time.Duration, error) {
return getTimePeriodTypeDuration(t)
}

// TimeType xs:time

func NewTimeType(t string) *TimeType {
Expand Down Expand Up @@ -69,7 +159,7 @@ func NewDateTimeType(t string) *DateTimeType {
}

func NewDateTimeTypeFromTime(t time.Time) *DateTimeType {
s := t.UTC().Format("2006-01-02T15:04:05Z")
s := t.Round(time.Second).UTC().Format("2006-01-02T15:04:05Z")
return NewDateTimeType(s)
}

Expand Down Expand Up @@ -153,6 +243,11 @@ func (a *AbsoluteOrRelativeTimeType) GetTime() (time.Time, error) {
return r, nil
}

func (a *AbsoluteOrRelativeTimeType) IsRelativeTime() bool {
_, err := getTimeDurationFromString(string(*a))
return err == nil
}

func (a *AbsoluteOrRelativeTimeType) GetDurationType() (*DurationType, error) {
value, err := a.GetTimeDuration()
if err != nil {
Expand Down
42 changes: 42 additions & 0 deletions model/commondatatypes_additions_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,55 @@
package model

import (
"encoding/json"
"testing"
"time"

"github.com/enbility/spine-go/util"
"github.com/stretchr/testify/assert"
)

func TestTimePeriodType(t *testing.T) {
tc := &TimePeriodType{}
duration, err := tc.GetDuration()
assert.NotNil(t, err)
assert.Equal(t, time.Duration(0), duration)

tc = &TimePeriodType{
EndTime: NewAbsoluteOrRelativeTimeTypeFromDuration(time.Minute * 1),
}
duration, err = tc.GetDuration()
assert.Nil(t, err)
assert.Equal(t, time.Minute*1, duration)

tc = NewTimePeriodTypeWithRelativeEndTime(time.Minute * 1)

duration, err = tc.GetDuration()
assert.Nil(t, err)
assert.Equal(t, time.Minute*1, duration)

data, err := json.Marshal(tc)
assert.Nil(t, err)
assert.NotNil(t, data)
assert.Equal(t, "{\"endTime\":\"PT1M\"}", string(data))

var tp1 TimePeriodType
err = json.Unmarshal(data, &tp1)
assert.Nil(t, err)
assert.Equal(t, *tc.EndTime, *tp1.EndTime)

time.Sleep(time.Second * 1)

duration, err = tc.GetDuration()
assert.Nil(t, err)
assert.Equal(t, time.Second*59, duration)

data, err = json.Marshal(tc)
assert.Nil(t, err)
assert.NotNil(t, data)
assert.Equal(t, "{\"endTime\":\"PT59S\"}", string(data))
}

func TestTimeType(t *testing.T) {
tc := []struct {
in string
Expand Down

0 comments on commit e636ba0

Please sign in to comment.