diff --git a/docs/parsing.md b/docs/parsing.md index f3b98493e..e3fad2872 100644 --- a/docs/parsing.md +++ b/docs/parsing.md @@ -205,13 +205,13 @@ Because Luxon was able to parse the string without difficulty, the output is a l | a | | meridiem | `AM` | | d | | day of the month, no padding | `6` | | dd | | day of the month, padded to 2 | `06` | -| E | c | day of the week, as number from 1-7 (Monday is 1, Sunday is 7) | `3` | -| EEE | ccc | day of the week, as an abbreviate localized string | `Wed` | -| EEEE | cccc | day of the week, as an unabbreviated localized string | `Wednesday` | -| M | L | month as an unpadded number | `8` | -| MM | LL | month as an padded number | `08` | -| MMM | LLL | month as an abbreviated localized string | `Aug` | -| MMMM | LLLL | month as an unabbreviated localized string | `August` | +| c | E | day of the week, as number from 1-7 (Monday is 1, Sunday is 7) | `3` | +| ccc | EEE | day of the week, as an abbreviate localized string | `Wed` | +| cccc | EEEE | day of the week, as an unabbreviated localized string | `Wednesday` | +| L | M | month as an unpadded number | `8` | +| LL | MM | month as an padded number | `08` | +| LLL | MMM | month as an abbreviated localized string | `Aug` | +| LLLL | MMMM | month as an unabbreviated localized string | `August` | | y | | year, 1-6 digits, very literally | `2014` | | yy | | two-digit year, interpreted as > 1960 by default (also accepts 4) | `14` | | yyyy | | four-digit year | `2014` | diff --git a/src/impl/regexParser.js b/src/impl/regexParser.js index 807a42ea2..fcd6d45ff 100644 --- a/src/impl/regexParser.js +++ b/src/impl/regexParser.js @@ -159,6 +159,7 @@ function extractISODuration(match) { // I'm just going to ignore that const obsOffsets = { GMT: 0, + UT: 0, EDT: -4 * 60, EST: -5 * 60, CDT: -5 * 60, diff --git a/src/zones/fixedOffsetZone.js b/src/zones/fixedOffsetZone.js index b1ab4ab1d..bc46e24aa 100644 --- a/src/zones/fixedOffsetZone.js +++ b/src/zones/fixedOffsetZone.js @@ -50,6 +50,8 @@ export default class FixedOffsetZone extends Zone { super(); /** @private **/ this.fixed = offset; + /** @private **/ + this.valid = Number.isInteger(offset); } /** @@ -140,11 +142,11 @@ export default class FixedOffsetZone extends Zone { /** * Return whether this Zone is valid: - * All fixed offset zones are valid. + * All fixed offset zones are valid, provided they were constructed with a numeric offset. * @override * @type {boolean} */ get isValid() { - return true; + return this.valid; } } diff --git a/test/datetime/regexParse.test.js b/test/datetime/regexParse.test.js index 85d12002e..f4c54154f 100644 --- a/test/datetime/regexParse.test.js +++ b/test/datetime/regexParse.test.js @@ -831,6 +831,20 @@ test("DateTime.fromRFC2822() can use a weird subset of offset abbreviations", () }); }); +test("DateTime.fromRFC2822() can use the obsolete UT zone", () => { + const dt = DateTime.fromRFC2822("01 Nov 2016 13:23:12 UT"); + expect(dt.isValid).toBe(true); + expect(dt.toUTC().toObject()).toEqual({ + year: 2016, + month: 11, + day: 1, + hour: 13, + minute: 23, + second: 12, + millisecond: 0, + }); +}); + //------ // .fromHTTP //------- diff --git a/test/zones/fixedOffset.test.js b/test/zones/fixedOffset.test.js index 606b38406..b33260d71 100644 --- a/test/zones/fixedOffset.test.js +++ b/test/zones/fixedOffset.test.js @@ -1,5 +1,5 @@ /* global test expect */ -import { FixedOffsetZone, IANAZone } from "../../src/luxon"; +import { DateTime, FixedOffsetZone, IANAZone } from "../../src/luxon"; test("FixedOffsetZone.utcInstance returns a singleton", () => { expect(FixedOffsetZone.utcInstance).toBe(FixedOffsetZone.utcInstance); @@ -63,6 +63,30 @@ test("FixedOffsetZone.formatOffset prints the correct sign before the offset", ( expect(FixedOffsetZone.instance(300).formatOffset(0, "short")).toBe("+05:00"); }); +test("FixedOffsetZone is valid when constructed with a numeric offset", () => { + expect(new FixedOffsetZone(0).isValid).toBe(true); + expect(new FixedOffsetZone(120).isValid).toBe(true); + expect(new FixedOffsetZone(-300).isValid).toBe(true); + expect(FixedOffsetZone.instance(60).isValid).toBe(true); +}); + +test("FixedOffsetZone is invalid when constructed with a non-numeric offset", () => { + // see https://github.com/moment/luxon/issues/1068 + expect(new FixedOffsetZone("CDT").isValid).toBe(false); + expect(new FixedOffsetZone("5").isValid).toBe(false); + expect(new FixedOffsetZone("abc").isValid).toBe(false); + expect(new FixedOffsetZone(NaN).isValid).toBe(false); + expect(new FixedOffsetZone(undefined).isValid).toBe(false); + expect(new FixedOffsetZone(null).isValid).toBe(false); + expect(new FixedOffsetZone({}).isValid).toBe(false); +}); + +test("A DateTime in an invalid FixedOffsetZone is itself invalid", () => { + const dt = DateTime.fromMillis(0, { zone: new FixedOffsetZone("CDT") }); + expect(dt.isValid).toBe(false); + expect(dt.invalidReason).toBe("unsupported zone"); +}); + test("FixedOffsetZone.equals requires both zones to be fixed", () => { expect(FixedOffsetZone.utcInstance.equals(IANAZone.create("UTC"))).toBe(false); });