Skip to content

Commit 04fd8f5

Browse files
committed
Implement to unmarshal PT duration
1 parent 9d576ab commit 04fd8f5

File tree

2 files changed

+129
-0
lines changed

2 files changed

+129
-0
lines changed

mpd/duration.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ package mpd
44

55
import (
66
"encoding/xml"
7+
"errors"
8+
"strconv"
9+
"strings"
710
"time"
811
)
912

@@ -13,6 +16,15 @@ func (d Duration) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
1316
return xml.Attr{name, d.String()}, nil
1417
}
1518

19+
func (d *Duration) UnmarshalXMLAttr(attr xml.Attr) error {
20+
dur, err := parseDuration(attr.Value)
21+
if err != nil {
22+
return err
23+
}
24+
*d = Duration(dur)
25+
return nil
26+
}
27+
1628
// String renders a Duration in XML Duration Data Type format
1729
func (d *Duration) String() string {
1830
// Largest time is 2540400h10m10.000000000s
@@ -126,3 +138,95 @@ func fmtInt(buf []byte, v uint64) int {
126138
}
127139
return w
128140
}
141+
142+
func parseDuration(str string) (time.Duration, error) {
143+
if len(str) < 3 {
144+
return 0, errors.New("input duration too short")
145+
}
146+
147+
var minus bool
148+
offset := 0
149+
if str[offset] == '-' {
150+
minus = true
151+
offset++
152+
}
153+
154+
if str[offset] != 'P' {
155+
return 0, errors.New("input duration does not have a valid prefix")
156+
}
157+
offset++
158+
159+
base := time.Unix(0, 0)
160+
t := base
161+
162+
var dateStr, timeStr string
163+
if i := strings.IndexByte(str[offset:], 'T'); i != -1 {
164+
dateStr = str[offset : offset+i]
165+
timeStr = str[offset+i+1:]
166+
} else {
167+
dateStr = str[offset:]
168+
}
169+
170+
if len(dateStr) > 0 {
171+
var pos int
172+
var err error
173+
var years, months, days int
174+
if i := strings.IndexByte(dateStr[pos:], 'Y'); i != -1 {
175+
years, err = strconv.Atoi(dateStr[pos : pos+i])
176+
if err != nil {
177+
return 0, err
178+
}
179+
pos += i + 1
180+
}
181+
182+
if i := strings.IndexByte(dateStr[pos:], 'M'); i != -1 {
183+
months, err = strconv.Atoi(dateStr[pos : pos+i])
184+
if err != nil {
185+
return 0, err
186+
}
187+
pos += i + 1
188+
}
189+
190+
if i := strings.IndexByte(dateStr[pos:], 'D'); i != -1 {
191+
days, err = strconv.Atoi(dateStr[pos : pos+i])
192+
if err != nil {
193+
return 0, err
194+
}
195+
}
196+
t = t.AddDate(years, months, days)
197+
}
198+
199+
if len(timeStr) > 0 {
200+
var pos int
201+
var sum float64
202+
if i := strings.IndexByte(timeStr[pos:], 'H'); i != -1 {
203+
hours, err := strconv.ParseInt(timeStr[pos:pos+i], 10, 64)
204+
if err != nil {
205+
return 0, err
206+
}
207+
sum += float64(hours) * 3600
208+
pos += i + 1
209+
}
210+
if i := strings.IndexByte(timeStr[pos:], 'M'); i != -1 {
211+
minutes, err := strconv.ParseInt(timeStr[pos:pos+i], 10, 64)
212+
if err != nil {
213+
return 0, err
214+
}
215+
sum += float64(minutes) * 60
216+
pos += i + 1
217+
}
218+
if i := strings.IndexByte(timeStr[pos:], 'S'); i != -1 {
219+
seconds, err := strconv.ParseFloat(timeStr[pos:pos+i], 64)
220+
if err != nil {
221+
return 0, err
222+
}
223+
sum += seconds
224+
}
225+
t = t.Add(time.Duration(sum * float64(time.Second)))
226+
}
227+
228+
if minus {
229+
return -t.Sub(base), nil
230+
}
231+
return t.Sub(base), nil
232+
}

mpd/duration_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,28 @@ func TestDuration(t *testing.T) {
2020
assert.Equal(t, ex, dur.String())
2121
}
2222
}
23+
24+
func TestParseDuration(t *testing.T) {
25+
in := map[string]float64{
26+
"PT0S": 0,
27+
"PT1M": 60,
28+
"PT2H": 7200,
29+
"PT6M16S": 376,
30+
"PT1.97S": 1.97,
31+
"PT1H2M3.456S": 3723.456,
32+
"P1YT2M3S": (365*24*time.Hour + 2*time.Minute + 3*time.Second).Seconds(),
33+
"P1Y2M3DT12H34M56.78S": (365*24*time.Hour + 59*24*time.Hour + 3*24*time.Hour + 12*time.Hour + 34*time.Minute + 56*time.Second + 780*time.Millisecond).Seconds(),
34+
"P1DT2H": (26 * time.Hour).Seconds(),
35+
"P20M": (608 * 24 * time.Hour).Seconds(),
36+
"PT20M": (20 * time.Minute).Seconds(),
37+
"P0Y20M0D": (608 * 24 * time.Hour).Seconds(),
38+
"P0Y": 0,
39+
"-P60D": -(60 * 24 * time.Hour).Seconds(),
40+
"PT1M30.5S": (time.Minute + 30*time.Second + 500*time.Millisecond).Seconds(),
41+
}
42+
for ins, ex := range in {
43+
act, err := parseDuration(ins)
44+
assert.NoError(t, err, ins)
45+
assert.Equal(t, ex, act.Seconds(), ins)
46+
}
47+
}

0 commit comments

Comments
 (0)