Skip to content

Commit

Permalink
Treat date-only formats as UTC and date-time as local timezone. Added…
Browse files Browse the repository at this point in the history
… support for additional datetime formats. Fixes #281, fixes #292.
  • Loading branch information
dop251 committed Sep 4, 2021
1 parent 07a7fd9 commit 6338b32
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 104 deletions.
95 changes: 62 additions & 33 deletions date.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,51 +24,80 @@ type dateObject struct {
msec int64
}

type dateLayoutDesc struct {
layout string
dateOnly bool
}

var (
dateLayoutList = []string{
"2006-01-02T15:04:05Z0700",
"2006-01-02T15:04:05",
"2006-01-02",
"2006-01-02 15:04:05",
time.RFC1123,
time.RFC1123Z,
dateTimeLayout,
time.UnixDate,
time.ANSIC,
time.RubyDate,
"Mon, 02 Jan 2006 15:04:05 GMT-0700 (MST)",
"Mon, 02 Jan 2006 15:04:05 -0700 (MST)",

"2006",
"2006-01",

"2006T15:04",
"2006-01T15:04",
"2006-01-02T15:04",

"2006T15:04:05",
"2006-01T15:04:05",

"2006T15:04Z0700",
"2006-01T15:04Z0700",
"2006-01-02T15:04Z0700",

"2006T15:04:05Z0700",
"2006-01T15:04:05Z0700",
dateLayoutsNumeric = []dateLayoutDesc{
{layout: "2006-01-02T15:04:05Z0700"},
{layout: "2006-01-02T15:04:05"},
{layout: "2006-01-02", dateOnly: true},
{layout: "2006-01-02 15:04:05"},

{layout: "2006", dateOnly: true},
{layout: "2006-01", dateOnly: true},

{layout: "2006T15:04"},
{layout: "2006-01T15:04"},
{layout: "2006-01-02T15:04"},

{layout: "2006T15:04:05"},
{layout: "2006-01T15:04:05"},

{layout: "2006T15:04Z0700"},
{layout: "2006-01T15:04Z0700"},
{layout: "2006-01-02T15:04Z0700"},

{layout: "2006T15:04:05Z0700"},
{layout: "2006-01T15:04:05Z0700"},
}

dateLayoutsAlpha = []dateLayoutDesc{
{layout: time.RFC1123},
{layout: time.RFC1123Z},
{layout: dateTimeLayout},
{layout: time.UnixDate},
{layout: time.ANSIC},
{layout: time.RubyDate},
{layout: "Mon, _2 Jan 2006 15:04:05 GMT-0700 (MST)"},
{layout: "Mon, _2 Jan 2006 15:04:05 -0700 (MST)"},
{layout: "Jan _2, 2006", dateOnly: true},
}
)

func dateParse(date string) (time.Time, bool) {
var t time.Time
var err error
for _, layout := range dateLayoutList {
t, err = parseDate(layout, date, time.UTC)
var layouts []dateLayoutDesc
if len(date) > 0 {
first := date[0]
if first <= '9' && (first >= '0' || first == '-' || first == '+') {
layouts = dateLayoutsNumeric
} else {
layouts = dateLayoutsAlpha
}
} else {
return time.Time{}, false
}
for _, desc := range layouts {
var defLoc *time.Location
if desc.dateOnly {
defLoc = time.UTC
} else {
defLoc = time.Local
}
t, err = parseDate(desc.layout, date, defLoc)
if err == nil {
break
}
}
if err != nil {
return time.Time{}, false
}
unix := timeToMsec(t)
return t, err == nil && unix >= -maxTime && unix <= maxTime
return t, unix >= -maxTime && unix <= maxTime
}

func (r *Runtime) newDateObject(t time.Time, isSet bool, proto *Object) *Object {
Expand Down
9 changes: 7 additions & 2 deletions date_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package goja
// - 6-digit extended years are supported in place of long year (2006) in the form of +123456
// - Timezone formats tolerate colons, e.g. -0700 will parse -07:00
// - Short week day will also parse long week day
// - Short month ("Jan") will also parse long month ("January")
// - Long day ("02") will also parse short day ("2").
// - Timezone in brackets, "(MST)", will match any string in brackets (e.g. "(GMT Standard Time)")
// - If offset is not set and timezone name is unknown, an error is returned
// - If offset and timezone name are both set the offset takes precedence and the resulting Location will be FixedZone("", offset)
Expand Down Expand Up @@ -133,7 +135,10 @@ func parseDate(layout, value string, defaultLocation *time.Location) (time.Time,
}

case stdMonth:
month, value, err = lookup(shortMonthNames, value)
month, value, err = lookup(longMonthNames, value)
if err != nil {
month, value, err = lookup(shortMonthNames, value)
}
month++
case stdLongMonth:
month, value, err = lookup(longMonthNames, value)
Expand All @@ -155,7 +160,7 @@ func parseDate(layout, value string, defaultLocation *time.Location) (time.Time,
if std == stdUnderDay && len(value) > 0 && value[0] == ' ' {
value = value[1:]
}
day, value, err = getnum(value, std == stdZeroDay)
day, value, err = getnum(value, false)
if day < 0 {
// Note that we allow any one- or two-digit day here.
rangeErrString = "day"
Expand Down
120 changes: 51 additions & 69 deletions date_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,91 +304,73 @@ func TestDateSetters(t *testing.T) {

func TestDateParse(t *testing.T) {
const SCRIPT = `
var zero = new Date(0);
var zero = new Date(0);
assert.sameValue(zero.valueOf(), Date.parse(zero.toString()),
"Date.parse(zeroDate.toString())");
assert.sameValue(zero.valueOf(), Date.parse(zero.toUTCString()),
"Date.parse(zeroDate.toUTCString())");
assert.sameValue(zero.valueOf(), Date.parse(zero.toISOString()),
"Date.parse(zeroDate.toISOString())");
assert.sameValue(zero.valueOf(), Date.parse(zero.toString()),
"Date.parse(zeroDate.toString())");
assert.sameValue(zero.valueOf(), Date.parse(zero.toUTCString()),
"Date.parse(zeroDate.toUTCString())");
assert.sameValue(zero.valueOf(), Date.parse(zero.toISOString()),
"Date.parse(zeroDate.toISOString())");
assert.sameValue(Date.parse("Mon, 02 Jan 2006 15:04:05 MST"), 1136239445000,
"Date.parse(\"Mon, 02 Jan 2006 15:04:05 MST\")");
assert.sameValue(Date.parse("Mon, 02 Jan 2006 15:04:05 GMT-07:00 (MST)"), 1136239445000,
"Date.parse(\"Mon, 02 Jan 2006 15:04:05 GMT-07:00 (MST)\")");
assert.sameValue(Date.parse("Mon, 02 Jan 2006 15:04:05 -07:00 (MST)"), 1136239445000,
"Date.parse(\"Mon, 02 Jan 2006 15:04:05 -07:00 (MST)\")");
assert.sameValue(Date.parse("Monday, 02 Jan 2006 15:04:05 -0700 (MST)"), 1136239445000,
"Date.parse(\"Monday, 02 Jan 2006 15:04:05 -0700 (MST)\")");
assert.sameValue(Date.parse("Mon Jan 02 2006 15:04:05 GMT-0700 (GMT Standard Time)"), 1136239445000,
"Date.parse(\"Mon Jan 02 2006 15:04:05 GMT-0700 (GMT Standard Time)\")");
assert.sameValue(Date.parse("2006-01-02T15:04:05.000Z"), 1136214245000,
"Date.parse(\"2006-01-02T15:04:05.000Z\")");
assert.sameValue(Date.parse("2006-06-02T15:04:05.000"), 1149260645000,
"Date.parse(\"2006-01-02T15:04:05.000\")");
assert.sameValue(Date.parse("2006-01-02T15:04:05"), 1136214245000,
"Date.parse(\"2006-01-02T15:04:05\")");
assert.sameValue(Date.parse("2006-01-02"), 1136160000000,
"Date.parse(\"2006-01-02\")");
assert.sameValue(Date.parse("2006T15:04-0700"), 1136153040000,
"Date.parse(\"2006T15:04-0700\")");
assert.sameValue(Date.parse("2006T15:04Z"), 1136127840000,
"Date.parse(\"2006T15:04Z\")");
assert.sameValue(Date.parse("Mon Jan 2 15:04:05 MST 2006"), 1136239445000,
"Date.parse(\"Mon Jan 2 15:04:05 MST 2006\")");
function testParse(str, expected) {
assert.sameValue(Date.parse(str), expected, str);
}
assert.sameValue(Date.parse("Mon Jan 02 15:04:05 MST 2006"), 1136239445000,
"Date.parse(\"Mon Jan 02 15:04:05 MST 2006\")");
testParse("Mon, 02 Jan 2006 15:04:05 MST", 1136239445000);
testParse("Tue, 22 Jun 2021 13:54:40 GMT", 1624370080000);
testParse("Tuesday, 22 Jun 2021 13:54:40 GMT", 1624370080000);
testParse("Mon, 02 Jan 2006 15:04:05 GMT-07:00 (MST)", 1136239445000);
testParse("Mon, 02 Jan 2006 15:04:05 -07:00 (MST)", 1136239445000);
testParse("Monday, 02 Jan 2006 15:04:05 -0700 (MST)", 1136239445000);
testParse("Mon Jan 02 2006 15:04:05 GMT-0700 (GMT Standard Time)", 1136239445000);
testParse("Mon Jan 2 15:04:05 MST 2006", 1136239445000);
testParse("Mon Jan 02 15:04:05 MST 2006", 1136239445000);
testParse("Mon Jan 02 15:04:05 -0700 2006", 1136239445000);
assert.sameValue(Date.parse("Mon Jan 02 15:04:05 -0700 2006"), 1136239445000,
"Date.parse(\"Mon Jan 02 15:04:05 -0700 2006\")");
testParse("December 04, 1986", 534038400000);
testParse("Dec 04, 1986", 534038400000);
testParse("Dec 4, 1986", 534038400000);
assert.sameValue(Date.parse("2019-01-01T12:00:00.52Z"), 1546344000520,
"Date.parse(\"2019-01-01T12:00:00.52\")");
testParse("2006-01-02T15:04:05.000Z", 1136214245000);
testParse("2006-06-02T15:04:05.000", 1149275045000);
testParse("2006-01-02T15:04:05", 1136232245000);
testParse("2006-01-02", 1136160000000);
testParse("2006T15:04-0700", 1136153040000);
testParse("2006T15:04Z", 1136127840000);
testParse("2019-01-01T12:00:00.52Z", 1546344000520);
var d = new Date("Mon, 02 Jan 2006 15:04:05 MST");
var d = new Date("Mon, 02 Jan 2006 15:04:05 MST");
assert.sameValue(d.getUTCHours(), 22,
"new Date(\"Mon, 02 Jan 2006 15:04:05 MST\").getUTCHours()");
assert.sameValue(d.getUTCHours(), 22,
"new Date(\"Mon, 02 Jan 2006 15:04:05 MST\").getUTCHours()");
assert.sameValue(d.getHours(), 17,
"new Date(\"Mon, 02 Jan 2006 15:04:05 MST\").getHours()");
assert.sameValue(d.getHours(), 17,
"new Date(\"Mon, 02 Jan 2006 15:04:05 MST\").getHours()");
assert.sameValue(Date.parse("Mon, 02 Jan 2006 15:04:05 zzz"), NaN,
"Date.parse(\"Mon, 02 Jan 2006 15:04:05 zzz\")");
assert.sameValue(Date.parse("Mon, 02 Jan 2006 15:04:05 zzz"), NaN,
"Date.parse(\"Mon, 02 Jan 2006 15:04:05 zzz\")");
assert.sameValue(Date.parse("Mon, 02 Jan 2006 15:04:05 ZZZ"), NaN,
"Date.parse(\"Mon, 02 Jan 2006 15:04:05 ZZZ\")");
assert.sameValue(Date.parse("Mon, 02 Jan 2006 15:04:05 ZZZ"), NaN,
"Date.parse(\"Mon, 02 Jan 2006 15:04:05 ZZZ\")");
var minDateStr = "-271821-04-20T00:00:00.000Z";
var minDate = new Date(-8640000000000000);
var minDateStr = "-271821-04-20T00:00:00.000Z";
var minDate = new Date(-8640000000000000);
assert.sameValue(minDate.toISOString(), minDateStr, "minDateStr");
assert.sameValue(Date.parse(minDateStr), minDate.valueOf(), "parse minDateStr");
assert.sameValue(minDate.toISOString(), minDateStr, "minDateStr");
assert.sameValue(Date.parse(minDateStr), minDate.valueOf(), "parse minDateStr");
var maxDateStr = "+275760-09-13T00:00:00.000Z";
var maxDate = new Date(8640000000000000);
var maxDateStr = "+275760-09-13T00:00:00.000Z";
var maxDate = new Date(8640000000000000);
assert.sameValue(maxDate.toISOString(), maxDateStr, "maxDateStr");
assert.sameValue(Date.parse(maxDateStr), maxDate.valueOf(), "parse maxDateStr");
assert.sameValue(maxDate.toISOString(), maxDateStr, "maxDateStr");
assert.sameValue(Date.parse(maxDateStr), maxDate.valueOf(), "parse maxDateStr");
var belowRange = "-271821-04-19T23:59:59.999Z";
var aboveRange = "+275760-09-13T00:00:00.001Z";
var belowRange = "-271821-04-19T23:59:59.999Z";
var aboveRange = "+275760-09-13T00:00:00.001Z";
assert.sameValue(Date.parse(belowRange), NaN, "parse below minimum time value");
assert.sameValue(Date.parse(aboveRange), NaN, "parse above maximum time value");
assert.sameValue(Date.parse(belowRange), NaN, "parse below minimum time value");
assert.sameValue(Date.parse(aboveRange), NaN, "parse above maximum time value");
`

l := time.Local
Expand Down

0 comments on commit 6338b32

Please sign in to comment.