1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
package iso8601
import (
"testing"
"time"
)
func TestValidate(t *testing.T) {
tests := []struct {
value string
flags ValidFlags
valid bool
}{
// valid
{"2018-01-01T23:42:59.123456789Z", Strict, true},
{"2018-01-01T23:42:59.123456789+07:00", Strict, true},
{"2018-01-01T23:42:59.123456789-07:00", Strict, true},
{"2018-01-01T23:42:59.000+07:00", Strict, true},
{"2018-01-01", Flexible, true},
{"2018-01-01 23:42:59", Flexible, true},
{"2018-01-01T23:42:59.123-0700", Flexible, true},
// invalid
{"", Flexible, false}, // empty string
{"whatever", Flexible, false}, // not a time
{"2018-01-01", Strict, false}, // missing time
{"2018-01-01 23:42:59-0700", Strict, false}, // missing subsecond
{"2018-01-01T23:42:59.123456789+0700", Strict, false}, // don't allow numeric time zone
{"2018_01-01T23:42:59.123456789Z", Strict, false}, // invalid date separator (first)
{"2018-01_01T23:42:59.123456789Z", Strict, false}, // invalid date separator (second)
{"2018-01-01 23:42:59.123456789Z", Strict, false}, // invalid date-time separator
{"2018-01-01T23-42:59.123456789Z", Strict, false}, // invalid time separator (first)
{"2018-01-01T23:42-59.123456789Z", Strict, false}, // invalid time separator (second)
{"2018-01-01T23:42:59,123456789Z", Strict, false}, // invalid decimal separator
{"2018-01-01T23:42:59.123456789", Strict, false}, // missing timezone
{"18-01-01T23:42:59.123456789Z", Strict, false}, // 2-digit year
{"2018-1-01T23:42:59.123456789Z", Strict, false}, // 1-digit month
{"2018-01-1T23:42:59.123456789Z", Strict, false}, // 1-digit day
{"2018-01-01T3:42:59.123456789Z", Strict, false}, // 1-digit hour
{"2018-01-01T23:2:59.123456789Z", Strict, false}, // 1-digit minute
{"2018-01-01T23:42:9.123456789Z", Strict, false}, // 1-digit second
{"2018-01-01T23:42:59.Z", Strict, false}, // not enough subsecond digits
{"2018-01-01T23:42:59.1234567890Z", Strict, false}, // too many subsecond digits
{"2018-01-01T23:42:59.123456789+7:00", Strict, false}, // 1-digit timezone hour
{"2018-01-01T23:42:59.123456789+07:0", Strict, false}, // 1-digit timezone minute
{"2018-01-01_23:42:59", Flexible, false}, // invalid date-time separator (not a space)
}
for _, test := range tests {
if test.valid != Valid(test.value, test.flags) {
t.Errorf("%q expected Valid to return %t", test.value, test.valid)
} else if test.valid {
if !isIsoString(test.value) {
t.Errorf("behavior mismatch, isIsoString says %q must not be a valid date", test.value)
}
} else if test.flags != Strict {
if isIsoString(test.value) {
t.Errorf("behavior mismatch, isIsoString says %q must be a valid date", test.value)
}
}
}
}
func BenchmarkValidate(b *testing.B) {
b.Run("success", benchmarkValidateSuccess)
b.Run("failure", benchmarkValidateFailure)
}
func benchmarkValidateSuccess(b *testing.B) {
for range b.N {
if !Valid("2018-01-01T23:42:59.123456789Z", Flexible) {
b.Fatal("not valid")
}
}
}
func benchmarkValidateFailure(b *testing.B) {
for range b.N {
if Valid("2018-01-01T23:42:59 oops!", Flexible) {
b.Fatal("valid but should not")
}
}
}
func BenchmarkTimeParse(b *testing.B) {
b.Run("success", benchmarkTimeParseSuccess)
b.Run("failure", benchmarkTimeParseFailure)
}
func benchmarkTimeParseSuccess(b *testing.B) {
for range b.N {
if _, err := time.Parse(time.RFC3339Nano, "2018-01-01T23:42:59.123456789Z"); err != nil {
b.Fatal("not valid")
}
}
}
func benchmarkTimeParseFailure(b *testing.B) {
for range b.N {
if _, err := time.Parse(time.RFC3339Nano, "2018-01-01T23:42:59 oops!"); err == nil {
b.Fatal("valid but should not")
}
}
}
// =============================================================================
// This code is extracted from a library we had that we are replacing with this
// package, we use it to verify that the behavior matches.
// =============================================================================
var validDates = [...]string{
time.RFC3339Nano,
time.RFC3339,
"2006-01-02T15:04:05.999-0700",
"2006-01-02 15:04:05",
"2006-01-02",
}
func isIsoString(str string) bool {
// Per RFC3339Nano Spec a date should never be more than 35 chars.
if len(str) > 36 {
return false
}
for _, format := range validDates {
if _, err := time.Parse(format, str); err == nil {
return true
}
}
return false
}
|