Repository: moment/luxon
Pull request #652 (2020-01-26 10:52:12)

2.0 take 2 merge master 1.22.0


Patch:
diff --git a/benchmarks/datetime.js b/benchmarks/datetime.js
index b27c37c4..376737e1 100644
--- a/benchmarks/datetime.js
+++ b/benchmarks/datetime.js
@@ -54,6 +54,9 @@ suite
   .add("DateTime#toLocaleString", () => {
     dt.toLocaleString();
   })
+  .add("DateTime#toRelativeCalendar", () => {
+    dt.toRelativeCalendar({ base: DateTime.local(), locale: "fi" });
+  })
   .on("cycle", event => {
     console.log(String(event.target));
   })
diff --git a/changelog.md b/changelog.md
index 88084a08..ddfd9430 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,11 @@
 # Changelog
 
+## 1.22.0
+
+ * Fix setZone's handling of pre-1970 dates with milisecond components
+ * Fix keepLocalTime for large jumps near the target zone's DST
+ * Fix cache perf for toRelative()
+
 ## 1.21.3
 
  * Fix parsing of meridiems in macro tokens in newer versions of v8
diff --git a/docs/errors.md b/docs/errors.md
index fd9abf21..e37e7504 100644
--- a/docs/errors.md
+++ b/docs/errors.md
@@ -63,7 +63,7 @@ Invalid DateTime objects are happy to tell you why they're invalid. `invalidReas
 ```js
 var dt = DateTime.local().setZone("America/Blorp");
 dt.invalidReason; //=>  'unsupported zone'
-dt.invalidExplantion; //=> 'the zone "America/Blorp" is not supported'
+dt.invalidExplanation; //=> 'the zone "America/Blorp" is not supported'
 ```
 
 ### throwOnInvalid
diff --git a/docs/tour.md b/docs/tour.md
index fcfe9920..d178e9bb 100644
--- a/docs/tour.md
+++ b/docs/tour.md
@@ -206,7 +206,7 @@ later = DateTime.local(2020, 10, 12);
 i = Interval.fromDateTimes(now, later);
 
 i.length()                             //=> 97098768468
-i.length('years', true)                //=> 3.0762420239726027
+i.length('years')                //=> 3.0762420239726027
 i.contains(DateTime.local(2019))       //=> true
 
 i.toISO()       //=> '2017-09-14T04:07:11.532-04:00/2020-10-12T00:00:00.000-04:00'
diff --git a/package-lock.json b/package-lock.json
index 426f210e..d49b3b55 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "luxon",
-  "version": "1.21.3",
+  "version": "1.22.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/package.json b/package.json
index 35deb553..d40b2501 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "luxon",
-  "version": "1.21.3",
+  "version": "1.22.0",
   "description": "Immutable date wrapper",
   "author": "Isaac Cambron",
   "keywords": [
diff --git a/src/datetime.js b/src/datetime.js
index f382d8ef..db17355c 100644
--- a/src/datetime.js
+++ b/src/datetime.js
@@ -410,9 +410,10 @@ export default class DateTime {
       o = null;
     const unchanged = config.old && config.old.ts === this.ts && config.old.zone.equals(zone);
 
-    c = unchanged ? config.old.c : tsToObj(this.ts, zone.offset(this.ts));
+    const offset = zone.offset(this.ts);
+    c = unchanged ? config.old.c : tsToObj(this.ts, offset);
     if (Number.isNaN(c.year)) throw new InvalidArgumentError("invalid timestamp");
-    o = unchanged ? config.old.o : zone.offset(this.ts);
+    o = unchanged ? config.old.o : offset;
 
     /**
      * @access private
@@ -1204,7 +1205,7 @@ export default class DateTime {
     } else {
       let newTS = this.ts;
       if (keepLocalTime) {
-        const offsetGuess = this.o - zone.offset(this.ts);
+        const offsetGuess = zone.offset(this.ts);
         const asObj = this.toObject();
         [newTS] = objToTS(asObj, offsetGuess, zone);
       }
diff --git a/src/impl/locale.js b/src/impl/locale.js
index 9f879602..8a0e3a07 100644
--- a/src/impl/locale.js
+++ b/src/impl/locale.js
@@ -28,7 +28,8 @@ function getCachedINF(locString, opts = {}) {
 
 let intlRelCache = {};
 function getCachedRTF(locString, opts = {}) {
-  const key = JSON.stringify([locString, opts]);
+  const { base, ...cacheKeyOpts } = opts; // exclude `base` from the options
+  const key = JSON.stringify([locString, cacheKeyOpts]);
   let inf = intlRelCache[key];
   if (!inf) {
     inf = new Intl.RelativeTimeFormat(locString, opts);
diff --git a/src/zones/IANAZone.js b/src/zones/IANAZone.js
index 148d4018..7137d693 100644
--- a/src/zones/IANAZone.js
+++ b/src/zones/IANAZone.js
@@ -171,7 +171,8 @@ export default class IANAZone extends Zone {
     });
 
     let asTS = date.valueOf();
-    asTS -= asTS % 1000;
+    const over = asTS % 1000;
+    asTS -= over >= 0 ? over : 1000 + over;
     return (asUTC - asTS) / (60 * 1000);
   }
 


Test patch:
diff --git a/test/datetime/zone.test.js b/test/datetime/zone.test.js
index f3a3ab03..fcf1655d 100644
--- a/test/datetime/zone.test.js
+++ b/test/datetime/zone.test.js
@@ -195,10 +195,24 @@ test("DateTime#setZone with keepLocalTime can span wacky offsets", () => {
   expect(d2.hour).toBe(0);
 });
 
+test("DateTime#setZone with keepLocalTime handles zones with very different offsets than the current one", () => {
+  const local = DateTime.local(2016, 10, 30, 2, 59);
+  const zoned = local.setZone("Europe/Athens", { keepLocalTime: true });
+  expect(zoned.hour).toBe(2);
+});
+
 test("DateTime#setZone rejects jibberish", () => {
   expect(() => dt().setZone("blorp")).toThrow(InvalidZoneError);
 });
 
+// #650
+test("DateTime#setZone works for dates before 1970 with milliseconds", () => {
+  const offset = DateTime.fromJSDate(new Date("1967-01-01T00:00:00.001Z")).setZone(
+    "America/New_York"
+  ).offset;
+  expect(offset).toBe(-300);
+});
+
 //------
 // #isInDST()
 //------