Browse Source

Treat date-only formats as UTC and date-time as local timezone. Added support for additional datetime formats. Fixes #281, fixes #292.

Dmitry Panov 4 years ago
parent
commit
6338b32468
3 changed files with 120 additions and 104 deletions
  1. 62 33
      date.go
  2. 7 2
      date_parser.go
  3. 51 69
      date_test.go

+ 62 - 33
date.go

@@ -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 {

+ 7 - 2
date_parser.go

@@ -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)
@@ -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)
@@ -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"

+ 51 - 69
date_test.go

@@ -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