Skip to content

Commit 4646753

Browse files
committed
OnvifDevice may get just LocalTime in GetSystemDateAndTime. Fix calculate timeDiff in this case.
1 parent 7a070f4 commit 4646753

File tree

3 files changed

+301
-2
lines changed

3 files changed

+301
-2
lines changed

onvif/posix_timezone.go

+285
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
package onvif
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strconv"
7+
"strings"
8+
"time"
9+
)
10+
11+
type PosixTimezone struct {
12+
Name string
13+
Offset posixTimezoneOffset
14+
Dailing *posixTimezoneDailing
15+
}
16+
17+
type posixTimezoneOffset struct {
18+
Hours int
19+
Minutes int
20+
}
21+
22+
type posixTimezoneDailing struct {
23+
Name string
24+
Offset posixTimezoneOffset
25+
From posixTimezoneTime
26+
To posixTimezoneTime
27+
}
28+
29+
type posixTimezoneTime struct {
30+
Month int
31+
Week int
32+
Weekday int
33+
Hours int
34+
Minutes int
35+
Seconds int
36+
}
37+
38+
func newPosixTimezoneDailing(st *posixTimezoneOffset, name string) *posixTimezoneDailing {
39+
return &posixTimezoneDailing{
40+
Name: name,
41+
Offset: posixTimezoneOffset{
42+
Hours: st.Hours + 1, //TODO: + или -
43+
},
44+
From: posixTimezoneTime{
45+
Month: 3,
46+
Week: 2,
47+
Weekday: 0,
48+
Hours: 2,
49+
},
50+
To: posixTimezoneTime{
51+
Month: 11,
52+
Week: 1,
53+
Weekday: 0,
54+
Hours: 2,
55+
},
56+
}
57+
}
58+
59+
func ParsePosixTimezone(timezone string) (*PosixTimezone, error) {
60+
type PosixTimezonePart int
61+
const (
62+
ST PosixTimezonePart = iota + 1
63+
STOFFSET
64+
STOFFSETMINUTES
65+
DT
66+
DTOFFSET
67+
DTOFFSETMINUTES
68+
)
69+
70+
var err error
71+
tz := PosixTimezone{}
72+
73+
parts := strings.Split(timezone, `,`)
74+
if len(parts) != 1 && len(parts) != 3 {
75+
return nil, errors.New("invalid format")
76+
}
77+
78+
local := parts[0]
79+
state, start := ST, 0
80+
81+
for i, ch := range local {
82+
nstate, nstart := state, start
83+
84+
switch state {
85+
case ST:
86+
if ch < 'A' || ch > 'Z' {
87+
tz.Name = local[start:i]
88+
nstate, nstart = STOFFSET, i
89+
}
90+
case STOFFSET:
91+
if ch >= 'A' && ch <= 'Z' {
92+
tz.Offset.Hours, err = strconv.Atoi(local[start:i])
93+
nstate, nstart = DT, i
94+
}
95+
if ch == ':' || ch == ' ' {
96+
tz.Offset.Hours, err = strconv.Atoi(local[start:i])
97+
nstate, nstart = STOFFSETMINUTES, i+1
98+
}
99+
case STOFFSETMINUTES:
100+
if ch >= 'A' && ch <= 'Z' {
101+
tz.Offset.Minutes, err = strconv.Atoi(local[start:i])
102+
nstate, nstart = DT, i
103+
}
104+
case DT:
105+
if ch < 'A' && ch > 'Z' {
106+
tz.Dailing = newPosixTimezoneDailing(&tz.Offset, local[start:i])
107+
nstate, nstart = DTOFFSET, i
108+
}
109+
case DTOFFSET:
110+
if ch == ':' || ch == ' ' {
111+
tz.Dailing.Offset.Hours, err = strconv.Atoi(local[start:i])
112+
nstate, nstart = DTOFFSETMINUTES, i+1
113+
}
114+
case DTOFFSETMINUTES:
115+
}
116+
117+
if err != nil {
118+
//TODO: add state, start to err desc
119+
return nil, err
120+
}
121+
state, start = nstate, nstart
122+
}
123+
124+
switch state {
125+
case ST:
126+
tz.Name = local[start:]
127+
case STOFFSET:
128+
tz.Offset.Hours, err = strconv.Atoi(local[start:])
129+
case STOFFSETMINUTES:
130+
tz.Offset.Minutes, err = strconv.Atoi(local[start:])
131+
case DT:
132+
tz.Dailing = newPosixTimezoneDailing(&tz.Offset, local[start:])
133+
case DTOFFSET:
134+
tz.Dailing.Offset.Hours, err = strconv.Atoi(local[start:])
135+
case DTOFFSETMINUTES:
136+
tz.Dailing.Offset.Minutes, err = strconv.Atoi(local[start:])
137+
}
138+
if err != nil {
139+
//TODO: add state, start to err desc
140+
return nil, err
141+
}
142+
143+
if len(parts) > 1 {
144+
err = parsePosixTimezoneTime(&tz.Dailing.From, parts[1])
145+
if err != nil {
146+
return &tz, err
147+
}
148+
err = parsePosixTimezoneTime(&tz.Dailing.To, parts[2])
149+
if err != nil {
150+
return &tz, err
151+
}
152+
}
153+
154+
return &tz, nil
155+
}
156+
157+
// date/time, time is optional.
158+
// Here, date is in the Mm.n.d format, where:
159+
// * Mm (1-12) for 12 months
160+
// * n (1-5) 1 for the first week and 5 for the last week in the month
161+
// * d (0-6) 0 for Sunday and 6 for Saturday
162+
// Example: M3.2.0/2:00:00
163+
func parsePosixTimezoneTime(t *posixTimezoneTime, timePart string) error {
164+
type PosixTimezoneTimePart int
165+
const (
166+
M PosixTimezoneTimePart = iota + 1
167+
month
168+
week
169+
weekend
170+
hours
171+
minutes
172+
seconds
173+
)
174+
175+
state, start := M, 0
176+
177+
for i, ch := range timePart {
178+
nstate, nstart := state, start
179+
var err error
180+
181+
switch state {
182+
case M:
183+
if ch != 'M' {
184+
return errors.New("invalid format")
185+
}
186+
nstate, nstart = month, i+1
187+
case month:
188+
if ch == '.' {
189+
t.Month, err = strconv.Atoi(timePart[start:i])
190+
nstate, nstart = week, i+1
191+
}
192+
case week:
193+
if ch == '.' {
194+
t.Week, err = strconv.Atoi(timePart[start:i])
195+
nstate, nstart = weekend, i+1
196+
}
197+
case weekend:
198+
if ch == '/' {
199+
t.Weekday, err = strconv.Atoi(timePart[start:i])
200+
nstate, nstart = hours, i+1
201+
}
202+
case hours:
203+
if ch == ':' {
204+
t.Hours, err = strconv.Atoi(timePart[start:i])
205+
nstate, nstart = minutes, i+1
206+
}
207+
case minutes:
208+
if ch == ':' {
209+
t.Minutes, err = strconv.Atoi(timePart[start:i])
210+
nstate, nstart = seconds, i+1
211+
}
212+
case seconds:
213+
}
214+
215+
if err != nil {
216+
//TODO: add state, start to err desc
217+
return err
218+
}
219+
state, start = nstate, nstart
220+
}
221+
222+
var err error
223+
switch state {
224+
case weekend:
225+
t.Weekday, err = strconv.Atoi(timePart[start:])
226+
case seconds:
227+
t.Seconds, err = strconv.Atoi(timePart[start:])
228+
}
229+
if err != nil {
230+
//TODO: add state, start to err desc
231+
return err
232+
}
233+
234+
return nil
235+
}
236+
237+
func getTimeOfYear(year int, t posixTimezoneTime) time.Time {
238+
res := time.Date(
239+
year, time.Month(t.Month), 1,
240+
t.Hours, t.Minutes, t.Seconds, 0,
241+
time.UTC)
242+
243+
weekday := int(res.Weekday())
244+
res = res.AddDate(0, 0, (7-weekday+t.Weekday)%7)
245+
246+
if t.Week < 5 {
247+
res = res.AddDate(0, 0, 7*(t.Week-1))
248+
} else {
249+
res = res.AddDate(0, 0, 7*(t.Week-1))
250+
tt := res.AddDate(0, 0, 7)
251+
if int(tt.Month()) != t.Month {
252+
return res
253+
} else {
254+
return tt
255+
}
256+
}
257+
258+
return res
259+
}
260+
261+
func (tz *PosixTimezone) LocalToUTC(t time.Time) time.Time {
262+
offset := tz.Offset
263+
264+
if tz.Dailing != nil {
265+
from := getTimeOfYear(t.Year(), tz.Dailing.From)
266+
to := getTimeOfYear(t.Year(), tz.Dailing.To)
267+
diffFrom, diffTo := t.Sub(from), t.Sub(to)
268+
269+
fmt.Println(t, from, to)
270+
271+
if tz.Dailing.From.Month < tz.Dailing.To.Month {
272+
if int64(diffFrom) > 0 && int64(diffTo) < 0 {
273+
offset = tz.Dailing.Offset
274+
}
275+
} else {
276+
if int64(diffFrom) > 0 || int64(diffTo) < 0 {
277+
offset = tz.Dailing.Offset
278+
}
279+
}
280+
}
281+
282+
duration := time.Duration(offset.Hours)*time.Hour + time.Duration(offset.Minutes)*time.Minute
283+
284+
return t.Add(duration)
285+
}

onvif/types.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -1137,7 +1137,20 @@ type SystemDateTime struct {
11371137
Extension SystemDateTimeExtension
11381138
}
11391139

1140-
func (d *DateTime) GetTime() time.Time {
1140+
func (d *SystemDateTime) GetUTCTime() (time.Time, error) {
1141+
if d.UTCDateTime != nil {
1142+
return d.UTCDateTime.getTime(), nil
1143+
}
1144+
1145+
tz, err := ParsePosixTimezone(string(d.TimeZone.TZ))
1146+
if err != nil {
1147+
return time.Now().UTC(), err
1148+
}
1149+
1150+
return tz.LocalToUTC(d.LocalDateTime.getTime()), nil
1151+
}
1152+
1153+
func (d *DateTime) getTime() time.Time {
11411154
return time.Date(
11421155
d.Date.Year, time.Month(d.Date.Month), d.Date.Day,
11431156
d.Time.Hour, d.Time.Minute, d.Time.Second, 0,

onvifdevice.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ func (onvifDevice *OnvifDevice) Initialize() error {
6666
if err != nil {
6767
return err
6868
}
69-
deviceTime := systemDateAndTimeResponse.SystemDateAndTime.UTCDateTime.GetTime()
69+
//TODO: error mean parse error of Timezone. deviceTime equal time.Now().UTC()
70+
deviceTime, _ := systemDateAndTimeResponse.SystemDateAndTime.GetUTCTime()
7071
onvifDevice.auth.timeDiff = deviceTime.Sub(currentTime)
7172

7273
capabilitiesResponse, err := deviceService.GetCapabilities()

0 commit comments

Comments
 (0)