Skip to content

Commit 732362d

Browse files
authored
Merge pull request #37 from miyukki/fix-duration-unmarshal
Implement to unmarshal PT duration
2 parents 9d576ab + fb82bae commit 732362d

File tree

2 files changed

+109
-0
lines changed

2 files changed

+109
-0
lines changed

mpd/duration.go

+89
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,80 @@ 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+
var dateStr, timeStr string
160+
if i := strings.IndexByte(str[offset:], 'T'); i != -1 {
161+
dateStr = str[offset : offset+i]
162+
timeStr = str[offset+i+1:]
163+
} else {
164+
dateStr = str[offset:]
165+
}
166+
167+
var sum float64
168+
if len(dateStr) > 0 {
169+
if i := strings.IndexByte(dateStr, 'Y'); i != -1 {
170+
return 0, errors.New("input duration contains Years notation")
171+
}
172+
173+
if i := strings.IndexByte(dateStr, 'M'); i != -1 {
174+
return 0, errors.New("input duration contains Months notation")
175+
}
176+
177+
if i := strings.IndexByte(dateStr, 'D'); i != -1 {
178+
days, err := strconv.Atoi(dateStr[0:i])
179+
if err != nil {
180+
return 0, err
181+
}
182+
sum += float64(days) * 86400
183+
}
184+
}
185+
186+
if len(timeStr) > 0 {
187+
var pos int
188+
if i := strings.IndexByte(timeStr[pos:], 'H'); i != -1 {
189+
hours, err := strconv.ParseInt(timeStr[pos:pos+i], 10, 64)
190+
if err != nil {
191+
return 0, err
192+
}
193+
sum += float64(hours) * 3600
194+
pos += i + 1
195+
}
196+
if i := strings.IndexByte(timeStr[pos:], 'M'); i != -1 {
197+
minutes, err := strconv.ParseInt(timeStr[pos:pos+i], 10, 64)
198+
if err != nil {
199+
return 0, err
200+
}
201+
sum += float64(minutes) * 60
202+
pos += i + 1
203+
}
204+
if i := strings.IndexByte(timeStr[pos:], 'S'); i != -1 {
205+
seconds, err := strconv.ParseFloat(timeStr[pos:pos+i], 64)
206+
if err != nil {
207+
return 0, err
208+
}
209+
sum += seconds
210+
}
211+
}
212+
213+
if minus {
214+
sum = -sum
215+
}
216+
return time.Duration(sum * float64(time.Second)), nil
217+
}

mpd/duration_test.go

+20
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,23 @@ 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+
"P1DT2H": (26 * time.Hour).Seconds(),
33+
"PT20M": (20 * time.Minute).Seconds(),
34+
"-P60D": -(60 * 24 * time.Hour).Seconds(),
35+
"PT1M30.5S": (time.Minute + 30*time.Second + 500*time.Millisecond).Seconds(),
36+
}
37+
for ins, ex := range in {
38+
act, err := parseDuration(ins)
39+
assert.NoError(t, err, ins)
40+
assert.Equal(t, ex, act.Seconds(), ins)
41+
}
42+
}

0 commit comments

Comments
 (0)