Skip to content

Commit 4ccd8a8

Browse files
authored
Check for remaining data with Zulu zone (#31)
* Add tests specific to ParseISOZone, UnexpectedCharacterError is now as value for comparison. Check Zulu time does not have remaining data (#30). Fix typo in ErrRemainingData * Check for zero length zone in ParseISOZone
1 parent 42c543c commit 4ccd8a8

File tree

3 files changed

+108
-7
lines changed

3 files changed

+108
-7
lines changed

error.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import (
77

88
var (
99
// ErrZoneCharacters indicates an incorrect amount of characters was passed to ParseISOZone.
10-
ErrZoneCharacters = errors.New("iso8601: Expected between 3 and 6 characters for zone information")
10+
ErrZoneCharacters = errors.New("iso8601: Expected 1 or between 3 and 6 characters for zone information")
1111

1212
// ErrInvalidZone indicates an invalid timezone per the standard that doesn't violate any specific
1313
// character parsing rules.
1414
ErrInvalidZone = errors.New("iso8601: Specified zone is invalid")
1515

1616
// ErrRemainingData indicates that there is extra data after a `Z` character.
17-
ErrRemainingData = errors.New("iso8601: Unexepected remaining data after `Z`")
17+
ErrRemainingData = errors.New("iso8601: Unexpected remaining data after `Z`")
1818

1919
// ErrNotString indicates that a non string type was passed to the UnmarshalJSON method of `Time`.
2020
ErrNotString = errors.New("iso8601: Invalid json type (expected string)")
@@ -25,15 +25,15 @@ var (
2525
)
2626

2727
func newUnexpectedCharacterError(c byte) error {
28-
return &UnexpectedCharacterError{Character: c}
28+
return UnexpectedCharacterError{Character: c}
2929
}
3030

3131
// UnexpectedCharacterError indicates the parser scanned a character that was not expected at that time.
3232
type UnexpectedCharacterError struct {
3333
Character byte
3434
}
3535

36-
func (e *UnexpectedCharacterError) Error() string {
36+
func (e UnexpectedCharacterError) Error() string {
3737
return fmt.Sprintf("iso8601: Unexpected character `%c`", e.Character)
3838
}
3939

iso8601.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,16 @@ const (
3636
// +01:45
3737
// +0145
3838
func ParseISOZone(inp []byte) (*time.Location, error) {
39-
if len(inp) != 1 && (len(inp) < 3 || len(inp) > 6) {
39+
if len(inp) == 0 {
4040
return nil, ErrZoneCharacters
4141
}
42+
4243
var neg bool
4344
switch inp[0] {
4445
case 'Z', 'z':
46+
if len(inp) != 1 {
47+
return nil, ErrRemainingData
48+
}
4549
return time.UTC, nil
4650
case '+':
4751
case '-':
@@ -50,6 +54,10 @@ func ParseISOZone(inp []byte) (*time.Location, error) {
5054
return nil, newUnexpectedCharacterError(inp[0])
5155
}
5256

57+
if len(inp) < 3 || len(inp) > 6 {
58+
return nil, ErrZoneCharacters
59+
}
60+
5361
var offset int
5462

5563
var z uint

iso8601_test.go

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package iso8601
22

33
import (
4+
"errors"
45
"testing"
56
"time"
67
)
@@ -27,8 +28,8 @@ type TestCase struct {
2728
func (tc TestCase) CheckError(err error, t *testing.T) bool {
2829
if err != nil {
2930
if tc.ShouldInvalidRange {
30-
re, ok := err.(*RangeError)
31-
if !ok {
31+
var re *RangeError
32+
if !errors.As(err, &re) {
3233
t.Fatalf("Found error %s of type %T but was expecting a RangeError", err, err)
3334
}
3435

@@ -333,6 +334,18 @@ var cases = []TestCase{
333334
Using: "2017-+04-24T09:41:34.502-00:00",
334335
ShouldFailParse: true,
335336
},
337+
{
338+
Using: "2017-01-01T00:00:60.000Z+",
339+
ShouldFailParse: true,
340+
},
341+
{
342+
Using: "2017-01-01T00:00:60.000Zz",
343+
ShouldFailParse: true,
344+
},
345+
{
346+
Using: "2017-01-01T00:00:60.000Z00:00",
347+
ShouldFailParse: true,
348+
},
336349

337350
// Invalid Range Test Cases
338351
{
@@ -467,3 +480,83 @@ func TestParseStringInLocation(t *testing.T) {
467480
)
468481
}
469482
}
483+
484+
type ZoneTestCase struct {
485+
Using string
486+
Zone float64
487+
Expect error
488+
}
489+
490+
func TestParseISOZone(t *testing.T) {
491+
var zoneTestCases = []ZoneTestCase{
492+
{
493+
Using: "",
494+
Expect: ErrZoneCharacters,
495+
},
496+
{
497+
Using: "Z",
498+
Zone: 0,
499+
},
500+
{
501+
Using: "z",
502+
Zone: 0,
503+
},
504+
{
505+
Using: "+00:00",
506+
Zone: 0,
507+
},
508+
{
509+
Using: "00:00",
510+
Expect: UnexpectedCharacterError{Character: '0'},
511+
},
512+
{
513+
Using: "-00:00",
514+
Expect: ErrInvalidZone,
515+
},
516+
{
517+
Using: "-05:30",
518+
Zone: -5.5,
519+
},
520+
{
521+
Using: "+05:30",
522+
Zone: 5.5,
523+
},
524+
{
525+
Using: "-0530",
526+
Zone: -5.5,
527+
},
528+
{
529+
Using: "Zz",
530+
Expect: ErrRemainingData,
531+
},
532+
{
533+
Using: "^",
534+
Expect: UnexpectedCharacterError{Character: '^'},
535+
},
536+
{
537+
Using: "-01",
538+
Zone: -1,
539+
},
540+
}
541+
542+
for _, tc := range zoneTestCases {
543+
t.Run(tc.Using, func(t *testing.T) {
544+
z, err := ParseISOZone([]byte(tc.Using))
545+
if !errors.Is(err, tc.Expect) {
546+
t.Errorf("ParseISOZone expected to return error %v (%T), got %v (%T)", tc.Expect, tc.Expect, err, err)
547+
return
548+
}
549+
550+
if tc.Expect != nil {
551+
return
552+
}
553+
554+
ts := time.Date(2024, 1, 1, 1, 1, 1, 0, z)
555+
_, offset := ts.Zone()
556+
557+
if offset := float64(offset) / 3600; offset != tc.Zone {
558+
t.Errorf("ParseISOZone expected to return zone %v, got %v", tc.Zone, offset)
559+
}
560+
})
561+
}
562+
}

0 commit comments

Comments
 (0)