Repository: moment/luxon
Pull request #719 (2020-05-31 15:19:34)

RelativeTime test fails on last day of the month
Seems that this will fail one day per month


Patch:
diff --git a/docs/formatting.md b/docs/formatting.md
index 74d3ba60..414f38ba 100644
--- a/docs/formatting.md
+++ b/docs/formatting.md
@@ -89,6 +89,7 @@ Here's the full set of provided presets using the October 14, 1983 at 13:30:23 a
 | --------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------- | -------------------------------------------------------- |
 | DATE_SHORT                  | short date                                                         | 10/14/1983                                                 | 14/10/1983                                               |
 | DATE_MED                    | abbreviated date                                                   | Oct 14, 1983                                               | 14 oct. 1983                                             |
+| DATE_MED_WITH_WEEKDAY       | abbreviated date with weekday                                      | Fri, Oct 14, 1983                                          | ven. 14 oct. 1983                                        |
 | DATE_FULL                   | full date                                                          | October 14, 1983                                           | 14 octobre 1983                                          |
 | DATE_HUGE                   | full date with weekday                                             | Tuesday, October 14, 1983                                  | vendredi 14 octobre 1983                                 |
 | TIME_SIMPLE                 | time                                                               | 1:30 PM                                                    | 13:30                                                    |
diff --git a/src/datetime.js b/src/datetime.js
index c123b853..27089bf7 100644
--- a/src/datetime.js
+++ b/src/datetime.js
@@ -1943,6 +1943,14 @@ export default class DateTime {
     return Formats.DATE_MED;
   }
 
+  /**
+   * {@link toLocaleString} format like 'Fri, Oct 14, 1983'
+   * @type {Object}
+   */
+  static get DATE_MED_WITH_WEEKDAY() {
+    return Formats.DATE_MED_WITH_WEEKDAY;
+  }
+
   /**
    * {@link toLocaleString} format like 'October 14, 1983'
    * @type {Object}
diff --git a/src/duration.js b/src/duration.js
index ad973ead..3c6889a6 100644
--- a/src/duration.js
+++ b/src/duration.js
@@ -37,6 +37,7 @@ const lowOrderMatrix = {
   casualMatrix = Object.assign(
     {
       years: {
+        quarters: 4,
         months: 12,
         weeks: 52,
         days: 365,
@@ -51,6 +52,7 @@ const lowOrderMatrix = {
         days: 91,
         hours: 91 * 24,
         minutes: 91 * 24 * 60,
+        seconds: 91 * 24 * 60 * 60,
         milliseconds: 91 * 24 * 60 * 60 * 1000
       },
       months: {
@@ -69,6 +71,7 @@ const lowOrderMatrix = {
   accurateMatrix = Object.assign(
     {
       years: {
+        quarters: 4,
         months: 12,
         weeks: daysInYearAccurate / 7,
         days: daysInYearAccurate,
@@ -588,8 +591,6 @@ export default class Duration {
       vals = this.toObject();
     let lastUnit;
 
-    normalizeValues(this.matrix, vals);
-
     for (const k of orderedUnits) {
       if (units.indexOf(k) >= 0) {
         lastUnit = k;
diff --git a/src/impl/english.js b/src/impl/english.js
index 100d5796..633f5943 100644
--- a/src/impl/english.js
+++ b/src/impl/english.js
@@ -187,6 +187,8 @@ export function formatString(knownFormat) {
       return "M/d/yyyy";
     case stringify(Formats.DATE_MED):
       return "LLL d, yyyy";
+    case stringify(Formats.DATE_MED_WITH_WEEKDAY):
+      return "EEE, LLL d, yyyy";
     case stringify(Formats.DATE_FULL):
       return "LLLL d, yyyy";
     case stringify(Formats.DATE_HUGE):
diff --git a/src/impl/formats.js b/src/impl/formats.js
index 8c66b9a3..25210f6f 100644
--- a/src/impl/formats.js
+++ b/src/impl/formats.js
@@ -18,6 +18,13 @@ export const DATE_MED = {
   day: n
 };
 
+export const DATE_MED_WITH_WEEKDAY = {
+  year: n,
+  month: s,
+  day: n,
+  weekday: s
+};
+
 export const DATE_FULL = {
   year: n,
   month: l,


Test patch:
diff --git a/test/datetime/degrade.test.js b/test/datetime/degrade.test.js
index 9bca381f..b2cdec38 100644
--- a/test/datetime/degrade.test.js
+++ b/test/datetime/degrade.test.js
@@ -29,6 +29,7 @@ Helpers.withoutIntl("DateTime#toLocaleString supports known configurations", ()
 
   expected.set(DateTime.DATE_SHORT, "8/6/2014");
   expected.set(DateTime.DATE_MED, "Aug 6, 2014");
+  expected.set(DateTime.DATE_MED_WITH_WEEKDAY, "Wed, Aug 6, 2014");
   expected.set(DateTime.DATE_FULL, "August 6, 2014");
   expected.set(DateTime.DATE_HUGE, "Wednesday, August 6, 2014");
   expected.set(DateTime.TIME_SIMPLE, "9:15 AM");
diff --git a/test/datetime/diff.test.js b/test/datetime/diff.test.js
index 3fc10b1e..9a68875f 100644
--- a/test/datetime/diff.test.js
+++ b/test/datetime/diff.test.js
@@ -216,12 +216,10 @@ test("DateTime#diff handles fractional days as fractions of those specific days"
 });
 
 test("DateTime#diff is precise for lower order units", () => {
-  if (DateTime.local().zoneName === "America/New_York") {
-    // spring forward skips one hour
-    expect(
-      diffObjs({ year: 2016, month: 5, day: 5 }, { year: 2016, month: 1, day: 1 }, "hours")
-    ).toEqual({ hours: 2999 });
-  }
+  // spring forward skips one hour
+  expect(
+    diffObjs({ year: 2016, month: 5, day: 5 }, { year: 2016, month: 1, day: 1 }, "hours")
+  ).toEqual({ hours: 2999 });
 });
 
 test("DateTime#diff passes through options", () => {
diff --git a/test/datetime/relative.test.js b/test/datetime/relative.test.js
index 2bccc5fc..da4b8ef7 100644
--- a/test/datetime/relative.test.js
+++ b/test/datetime/relative.test.js
@@ -107,11 +107,11 @@ test("DateTime#toRelativeCalendar uses the calendar", () => {
 });
 
 test("DateTime#toRelativeCalendar picks the correct unit with no options", () => {
-  expect(
-    DateTime.local()
-      .plus({ days: 1 })
-      .toRelativeCalendar()
-  ).toBe("tomorrow");
+  const now = DateTime.local();
+  const isLastDayOfMonth = now.endOf("month").day === now.day;
+  expect(now.plus({ days: 1 }).toRelativeCalendar()).toBe(
+    isLastDayOfMonth ? "next month" : "tomorrow"
+  );
 });
 
 test("DateTime#toRelativeCalendar returns null when used on an invalid date", () => {
diff --git a/test/datetime/zone.test.js b/test/datetime/zone.test.js
index 4d023253..b5157ddd 100644
--- a/test/datetime/zone.test.js
+++ b/test/datetime/zone.test.js
@@ -250,10 +250,8 @@ test("Etc/GMT zones work even though V8 does not support them", () => {
 //------
 
 test("The local zone does local stuff", () => {
-  if (DateTime.local().zoneName === "America/New_York") {
-    expect(DateTime.local(2016, 8, 6).offsetNameLong).toBe("Eastern Daylight Time");
-    expect(DateTime.local(2016, 8, 6).offsetNameShort).toBe("EDT");
-  }
+  expect(DateTime.local(2016, 8, 6).offsetNameLong).toBe("Eastern Daylight Time");
+  expect(DateTime.local(2016, 8, 6).offsetNameShort).toBe("EDT");
 });
 
 //------
diff --git a/test/duration/units.test.js b/test/duration/units.test.js
index 5026ce59..4357b1d1 100644
--- a/test/duration/units.test.js
+++ b/test/duration/units.test.js
@@ -58,7 +58,7 @@ test("Duration#shiftTo without any units no-ops", () => {
   expect(dur.toObject()).toEqual({ years: 3 });
 });
 
-test("Duration#shifTo accumulates when rolling up", () => {
+test("Duration#shiftTo accumulates when rolling up", () => {
   expect(
     Duration.fromObject({ minutes: 59, seconds: 183 })
       .shiftTo("hours", "minutes", "seconds")
@@ -66,7 +66,7 @@ test("Duration#shifTo accumulates when rolling up", () => {
   ).toEqual({ hours: 1, minutes: 2, seconds: 3 });
 });
 
-test("Duration#shifTo keeps unecessary higher-order negative units 0", () => {
+test("Duration#shiftTo keeps unnecessary higher-order negative units 0", () => {
   expect(
     Duration.fromObject({ milliseconds: -100 })
       .shiftTo("hours", "minutes", "seconds")
@@ -74,6 +74,16 @@ test("Duration#shifTo keeps unecessary higher-order negative units 0", () => {
   ).toEqual({ hours: 0, minutes: 0, seconds: -0.1 });
 });
 
+test("Duration#shiftTo does not normalize values", () => {
+  // Normalizing would convert to { quarters: 4, months: 1, days: 10 }
+  // which would be converted back to 404 days instead
+  expect(
+    Duration.fromObject({ quarters: 0, months: 0, days: 400 })
+      .shiftTo("days")
+      .toObject()
+  ).toEqual({ days: 400 });
+});
+
 //------
 // #normalize()
 //-------
@@ -125,6 +135,34 @@ test("Duration#normalize maintains invalidity", () => {
   expect(dur.invalidReason).toBe("because");
 });
 
+test("Duration#normalize can convert all unit pairs", () => {
+  const units = [
+    "years",
+    "quarters",
+    "months",
+    "weeks",
+    "days",
+    "hours",
+    "minutes",
+    "seconds",
+    "milliseconds"
+  ];
+
+  for (let i = 0; i < units.length; i++) {
+    for (let j = i + 1; j < units.length; j++) {
+      const duration = Duration.fromObject({ [units[i]]: 1, [units[j]]: 2 });
+      const normalizedDuration = duration.normalize().toObject();
+      expect(normalizedDuration[units[i]]).not.toBe(NaN);
+      expect(normalizedDuration[units[j]]).not.toBe(NaN);
+
+      const accurateDuration = duration.reconfigure({ conversionAccuracy: "longterm" });
+      const normalizedAccurateDuration = accurateDuration.normalize().toObject();
+      expect(normalizedAccurateDuration[units[i]]).not.toBe(NaN);
+      expect(normalizedAccurateDuration[units[j]]).not.toBe(NaN);
+    }
+  }
+});
+
 //------
 // #as()
 //-------