diff --git a/gpx.go b/gpx.go
index 5b22296..6157ad7 100644
--- a/gpx.go
+++ b/gpx.go
@@ -5,6 +5,7 @@ package gpx
import (
"encoding/xml"
+ "errors"
"fmt"
"io"
"strconv"
@@ -20,7 +21,10 @@ const (
https = "https://"
)
-var timeLayout = time.RFC3339Nano
+var timeLayouts = []string{
+ time.RFC3339Nano,
+ "2006-01-02T15:04:05.999999999",
+}
// StartElement is the XML start element for GPX files.
var StartElement = xml.StartElement{
@@ -33,6 +37,8 @@ var copyrightYearLayouts = []string{
"2006-07:00",
}
+var errNoTimeLayout = errors.New("no time layout")
+
// A BoundsType is a boundsType.
type BoundsType struct {
MinLat float64 `xml:"minlat,attr"`
@@ -207,7 +213,15 @@ func Read(r io.Reader, options ...ReadOption) (*GPX, error) {
// WithTimeLayout applies a custom time layout for the decoding of the GPX source.
func WithTimeLayout(layout string) ReadOption {
return func() {
- timeLayout = layout
+ timeLayouts = []string{layout}
+ }
+}
+
+// WithTimeLayouts applies a custom time layouts for the decoding of the GPX
+// source.
+func WithTimeLayouts(layouts []string) ReadOption {
+ return func() {
+ timeLayouts = layouts
}
}
@@ -280,6 +294,43 @@ func (g *GPX) WriteIndent(w io.Writer, prefix, indent string) error {
return e.EncodeElement(g, StartElement)
}
+// UnmarshalXML implements xml.Unmarshaler.UnmarshalXML.
+func (m *MetadataType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ var e struct {
+ Name string `xml:"name"`
+ Desc string `xml:"desc"`
+ Author *PersonType `xml:"author"`
+ Copyright *CopyrightType `xml:"copyright"`
+ Link []*LinkType `xml:"link"`
+ Time string `xml:"time"`
+ Keywords string `xml:"keywords"`
+ Bounds *BoundsType `xml:"bounds"`
+ Extensions *ExtensionsType `xml:"extensions"`
+ }
+ if err := d.DecodeElement(&e, &start); err != nil {
+ return err
+ }
+ mt := MetadataType{
+ Name: e.Name,
+ Desc: e.Desc,
+ Author: e.Author,
+ Copyright: e.Copyright,
+ Link: e.Link,
+ Keywords: e.Keywords,
+ Bounds: e.Bounds,
+ Extensions: e.Extensions,
+ }
+ if e.Time != "" {
+ t, err := parseTime(e.Time)
+ if err != nil {
+ return err
+ }
+ mt.Time = t
+ }
+ *m = mt
+ return nil
+}
+
// NewRteType returns a new RteType with geometry g.
func NewRteType(g *geom.LineString) *RteType {
return &RteType{
@@ -386,7 +437,7 @@ func (w *WptType) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return err
}
if !w.Time.IsZero() {
- if err := maybeEmitStringElement(e, "time", w.Time.UTC().Format(timeLayout)); err != nil {
+ if err := maybeEmitStringElement(e, "time", w.Time.UTC().Format(timeLayouts[0])); err != nil {
return err
}
}
@@ -505,7 +556,7 @@ func (w *WptType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
Extensions: e.Extensions,
}
if e.Time != "" {
- t, err := time.ParseInLocation(timeLayout, e.Time, time.UTC)
+ t, err := parseTime(e.Time)
if err != nil {
return err
}
@@ -601,3 +652,16 @@ func newWptTypes(g *geom.LineString) []*WptType {
}
return wpts
}
+
+func parseTime(value string) (time.Time, error) {
+ firstErr := errNoTimeLayout
+ for i, timeLayout := range timeLayouts {
+ switch t, err := time.Parse(timeLayout, value); {
+ case err == nil:
+ return t, nil
+ case i == 0:
+ firstErr = err
+ }
+ }
+ return time.Time{}, firstErr
+}
diff --git a/gpx_test.go b/gpx_test.go
index 9053ed5..c4bb946 100644
--- a/gpx_test.go
+++ b/gpx_test.go
@@ -17,6 +17,42 @@ import (
gpx "github.com/twpayne/go-gpx"
)
+func TestMetadata(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ data string
+ expected *gpx.MetadataType
+ }{
+ {
+ name: "rfc3339",
+ data: ``,
+ expected: &gpx.MetadataType{
+ Time: time.Date(2004, 4, 12, 13, 20, 0, 0, time.UTC),
+ },
+ },
+ {
+ name: "rfc3339_without_timezone",
+ data: ``,
+ expected: &gpx.MetadataType{
+ Time: time.Date(2004, 4, 12, 13, 20, 0, 0, time.UTC),
+ },
+ },
+ {
+ name: "rfc3339_with_milliseconds_without_timezone",
+ data: ``,
+ expected: &gpx.MetadataType{
+ Time: time.Date(2004, 4, 12, 13, 20, 0, 123000000, time.UTC),
+ },
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ var gotMetadata gpx.MetadataType
+ assert.NoError(t, xml.Unmarshal([]byte(tc.data), &gotMetadata))
+ assert.Equal(t, tc.expected, &gotMetadata)
+ })
+ }
+}
+
func TestWpt(t *testing.T) {
for i, tc := range []struct {
data string
diff --git a/testdata/hhs_77yl9msjcr.gpx b/testdata/hhs_77yl9msjcr.gpx
new file mode 100644
index 0000000..8f82309
--- /dev/null
+++ b/testdata/hhs_77yl9msjcr.gpx
@@ -0,0 +1,3 @@
+
+
+Harzer-Hexen-Stieghttps://www.openstreetmap.org/copyrightWaymarked TrailsHarzer-Hexen-Stieg
\ No newline at end of file