github.com/enbility/spine-go@v0.7.0/model/commondatatypes_additions.go (about) 1 package model 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "math" 8 "strconv" 9 "strings" 10 "time" 11 12 "github.com/rickb777/date/period" 13 ) 14 15 // TimePeriodType 16 17 func NewTimePeriodTypeWithRelativeEndTime(duration time.Duration) *TimePeriodType { 18 now := time.Now().UTC() 19 endTime := now.Add(duration) 20 value := &TimePeriodType{ 21 EndTime: NewAbsoluteOrRelativeTimeTypeFromTime(endTime), 22 } 23 return value 24 } 25 26 // helper type to modify EndTime field value in json.Marshal and 27 // json.Unmarshal to allow provide accurate relative durations 28 // 29 // If only EndTime is provided and it is a duration, it has to 30 // decrease over time. To do this without actually changing 31 // the data, it will always be transformed into an absolute time 32 // in Marshal and returned as an up to date relative duration 33 // in Unmarshal 34 type tempTimePeriodType TimePeriodType 35 36 func setTimePeriodTypeEndTime(t *tempTimePeriodType) { 37 if t.StartTime != nil || t.EndTime == nil { 38 return 39 } 40 41 duration, err := t.EndTime.GetTimeDuration() 42 if err != nil { 43 return 44 } 45 46 time := time.Now().UTC().Add(duration) 47 t.EndTime = NewAbsoluteOrRelativeTimeTypeFromTime(time) 48 } 49 50 func getTimePeriodTypeDuration(t *TimePeriodType) (time.Duration, error) { 51 if t.StartTime != nil || t.EndTime == nil { 52 return 0, errors.New("invalid data format") 53 } 54 55 if t.EndTime.IsRelativeTime() { 56 return getTimeDurationFromString(string(*t.EndTime)) 57 } 58 59 endTime, err := t.EndTime.GetTime() 60 if err != nil { 61 return 0, err 62 } 63 64 now := time.Now().UTC() 65 duration := endTime.Sub(now) 66 duration = duration.Round(time.Second) 67 68 return duration, nil 69 } 70 71 // when startTime is empty and endTime is an absolute time, 72 // then endTime should be returned as an relative timestamp 73 func (t TimePeriodType) MarshalJSON() ([]byte, error) { 74 temp := tempTimePeriodType(t) 75 76 if duration, err := getTimePeriodTypeDuration(&t); err == nil { 77 temp.EndTime = NewAbsoluteOrRelativeTimeTypeFromDuration(duration) 78 } 79 80 return json.Marshal(temp) 81 } 82 83 // when startTime is empty and endTime is a relative time, 84 // then endTime should be written as an absolute timestamp 85 func (t *TimePeriodType) UnmarshalJSON(data []byte) error { 86 var temp tempTimePeriodType 87 if err := json.Unmarshal(data, &temp); err != nil { 88 return err 89 } 90 91 setTimePeriodTypeEndTime(&temp) 92 93 *t = TimePeriodType(temp) 94 95 return nil 96 } 97 98 // Return the current duration if StartTime is nil and EndTime is not 99 // otherwise returns an error 100 func (t *TimePeriodType) GetDuration() (time.Duration, error) { 101 return getTimePeriodTypeDuration(t) 102 } 103 104 // TimeType xs:time 105 106 func NewTimeType(t string) *TimeType { 107 value := TimeType(t) 108 return &value 109 } 110 111 func (s *TimeType) GetTime() (time.Time, error) { 112 allowedFormats := []string{ 113 "15:04:05.999999999", 114 "15:04:05.999999999Z", 115 "15:04:05", 116 "15:04:05Z", 117 "15:04:05+07:00", 118 "15:04:05-07:00", 119 } 120 121 for _, format := range allowedFormats { 122 if value, err := time.ParseInLocation(format, string(*s), time.UTC); err == nil { 123 return value, nil 124 } 125 } 126 127 return time.Time{}, errors.New("unsupported time format") 128 } 129 130 // DateType xs:date 131 132 func NewDateType(t string) *DateType { 133 value := DateType(t) 134 return &value 135 } 136 137 // 2001-10-26, 2001-10-26+02:00, 2001-10-26Z, 2001-10-26+00:00, -2001-10-26, or -20000-04-01 138 func (d *DateType) GetTime() (time.Time, error) { 139 allowedFormats := []string{ 140 "2006-01-02", 141 "2006-01-02Z", 142 "2006-01-02+07:00", 143 } 144 145 for _, format := range allowedFormats { 146 if value, err := time.ParseInLocation(format, string(*d), time.UTC); err == nil { 147 return value, nil 148 } 149 } 150 151 return time.Time{}, errors.New("unsupported date format") 152 } 153 154 // DateTimeType xs:datetime 155 156 func NewDateTimeType(t string) *DateTimeType { 157 value := DateTimeType(t) 158 return &value 159 } 160 161 func NewDateTimeTypeFromTime(t time.Time) *DateTimeType { 162 s := t.Round(time.Second).UTC().Format("2006-01-02T15:04:05Z") 163 return NewDateTimeType(s) 164 } 165 166 func (d *DateTimeType) GetTime() (time.Time, error) { 167 allowedFormats := []string{ 168 "2006-01-02T15:04:05.999999999", 169 "2006-01-02T15:04:05.999999999Z", 170 "2006-01-02T15:04:05", 171 "2006-01-02T15:04:05Z", 172 } 173 174 for _, format := range allowedFormats { 175 if value, err := time.ParseInLocation(format, string(*d), time.UTC); err == nil { 176 return value, nil 177 } 178 } 179 180 return time.Time{}, errors.New("unsupported datetime format") 181 } 182 183 // DurationType 184 185 func NewDurationType(duration time.Duration) *DurationType { 186 d, _ := period.NewOf(duration) 187 value := DurationType(d.String()) 188 return &value 189 } 190 191 func (d *DurationType) GetTimeDuration() (time.Duration, error) { 192 return getTimeDurationFromString(string(*d)) 193 } 194 195 // helper for DurationType and AbsoluteOrRelativeTimeType 196 func getTimeDurationFromString(s string) (time.Duration, error) { 197 p, err := period.Parse(string(s)) 198 if err != nil { 199 return 0, err 200 } 201 202 return p.DurationApprox(), nil 203 } 204 205 // AbsoluteOrRelativeTimeType 206 // can be of type TimeType or DurationType 207 208 func NewAbsoluteOrRelativeTimeType(s string) *AbsoluteOrRelativeTimeType { 209 value := AbsoluteOrRelativeTimeType(s) 210 return &value 211 } 212 213 func NewAbsoluteOrRelativeTimeTypeFromDuration(t time.Duration) *AbsoluteOrRelativeTimeType { 214 s := NewDurationType(t) 215 value := AbsoluteOrRelativeTimeType(*s) 216 return &value 217 } 218 219 func NewAbsoluteOrRelativeTimeTypeFromTime(t time.Time) *AbsoluteOrRelativeTimeType { 220 s := NewDateTimeTypeFromTime(t) 221 value := AbsoluteOrRelativeTimeType(*s) 222 return &value 223 } 224 225 func (a *AbsoluteOrRelativeTimeType) GetDateTimeType() *DateTimeType { 226 value := NewDateTimeType(string(*a)) 227 return value 228 } 229 230 func (a *AbsoluteOrRelativeTimeType) GetTime() (time.Time, error) { 231 value := NewDateTimeType(string(*a)) 232 t, err := value.GetTime() 233 if err == nil { 234 return t, nil 235 } 236 237 // Check if this is a relative time 238 d, err := getTimeDurationFromString(string(*a)) 239 if err != nil { 240 return time.Time{}, err 241 } 242 r := time.Now().Add(d) 243 return r, nil 244 } 245 246 func (a *AbsoluteOrRelativeTimeType) IsRelativeTime() bool { 247 _, err := getTimeDurationFromString(string(*a)) 248 return err == nil 249 } 250 251 func (a *AbsoluteOrRelativeTimeType) GetDurationType() (*DurationType, error) { 252 value, err := a.GetTimeDuration() 253 if err != nil { 254 return nil, err 255 } 256 257 return NewDurationType(value), nil 258 } 259 260 func (a *AbsoluteOrRelativeTimeType) GetTimeDuration() (time.Duration, error) { 261 return getTimeDurationFromString(string(*a)) 262 } 263 264 // ScaledNumberType 265 266 func (m *ScaledNumberType) GetValue() float64 { 267 if m.Number == nil { 268 return 0 269 } 270 var scale float64 = 0 271 if m.Scale != nil { 272 scale = float64(*m.Scale) 273 } 274 return float64(*m.Number) * math.Pow(10, scale) 275 } 276 277 func NewScaledNumberType(value float64) *ScaledNumberType { 278 m := &ScaledNumberType{} 279 280 numberOfDecimals := 0 281 temp := strconv.FormatFloat(value, 'f', -1, 64) 282 index := strings.IndexByte(temp, '.') 283 if index > -1 { 284 numberOfDecimals = len(temp) - index - 1 285 } 286 287 // We limit this to 4 digits for now 288 if numberOfDecimals > 4 { 289 numberOfDecimals = 4 290 } 291 292 numberValue := NumberType(math.Trunc(value * math.Pow(10, float64(numberOfDecimals)))) 293 m.Number = &numberValue 294 295 var scaleValue ScaleType 296 if numberValue != 0 { 297 scaleValue = ScaleType(-numberOfDecimals) 298 } else { 299 scaleValue = ScaleType(0) 300 } 301 m.Scale = &scaleValue 302 303 return m 304 } 305 306 // DeviceAddressType 307 308 var _ UpdateHelper = (*DeviceAddressType)(nil) 309 310 func (r *DeviceAddressType) String() string { 311 if r == nil { 312 return "" 313 } 314 315 var result = "" 316 if r.Device != nil { 317 result += string(*r.Device) 318 } 319 320 return result 321 } 322 323 // EntityAddressType 324 325 var _ UpdateHelper = (*EntityAddressType)(nil) 326 327 func (r *EntityAddressType) String() string { 328 if r == nil { 329 return "" 330 } 331 332 var result = "" 333 if r.Device != nil { 334 result += string(*r.Device) 335 } 336 result += ":[" 337 for index, id := range r.Entity { 338 if index > 0 { 339 result += "," 340 } 341 result += fmt.Sprintf("%d", id) 342 } 343 result += "]:" 344 return result 345 } 346 347 // FeatureAddressType 348 349 var _ UpdateHelper = (*FeatureAddressType)(nil) 350 351 func (r *FeatureAddressType) String() string { 352 if r == nil { 353 return "" 354 } 355 356 var result = "" 357 if r.Device != nil { 358 result += string(*r.Device) 359 } 360 result += ":[" 361 for index, id := range r.Entity { 362 if index > 0 { 363 result += "," 364 } 365 result += fmt.Sprintf("%d", id) 366 } 367 result += "]:" 368 if r.Feature != nil { 369 result += fmt.Sprintf("%d", *r.Feature) 370 } 371 return result 372 }