Repository: moment/luxon
Pull request #1707 (2025-05-21 08:53:07)

build: use the es6 build for ESM exports
Currently it seems that Metro (from React Native) isn't resolving Luxon correctly due to the incorrect exports configuration. 

It seems that ESM imports prefer the source code instead of the built code. Changing this to the es6 build resolves the issue.

This is the error with the old export: 
![image](https://github.com/user-attachments/assets/8a8f1934-a4c0-45f5-9a7c-662bded702de)


linux-foundation-easycla[bot]:  <a href="https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/19772872/47118129/1707/#/?version=2"><img src="https://s3.amazonaws.com/cla-project-logo-prod/cla-not-signed.svg" alt="CLA Not Signed" align="left" height="28" width="328"></a><br/><br /><ul><li><a href='https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/19772872/47118129/1707/#/?version=2' target='_blank'>:x:</a> - login: @pataar / name: Pieter Willekens . The commit (9588598ea9b09e46566cf53f971267452034599c) is not authorized under a signed CLA. <a href='https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/19772872/47118129/1707/#/?version=2' target='_blank'>Please click here to be authorized</a>. For further assistance with EasyCLA, <a href='https://jira.linuxfoundation.org/servicedesk/customer/portal/4' target='_blank'>please submit a support request ticket</a>.</li></ul><!-- Date Modified: 2025-05-21 08:53:10.711992 -->

Patch:
diff --git a/package.json b/package.json
index fdbb56cb..ee6e526b 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
   "repository": "https://github.com/moment/luxon",
   "exports": {
     ".": {
-      "import": "./src/luxon.js",
+      "import": "./build/es6/luxon.js",
       "require": "./build/node/luxon.js"
     },
     "./package.json": "./package.json"
diff --git a/src/impl/locale.js b/src/impl/locale.js
index c284c7ff..76f08a75 100644
--- a/src/impl/locale.js
+++ b/src/impl/locale.js
@@ -432,10 +432,18 @@ export default class Locale {
 
   months(length, format = false) {
     return listStuff(this, length, English.months, () => {
+      // Workaround for "ja" locale: formatToParts does not label all parts of the month
+      // as "month" and for this locale there is no difference between "format" and "non-format".
+      // As such, just use format() instead of formatToParts() and take the whole string
+      const monthSpecialCase = this.intl === "ja" || this.intl.startsWith("ja-");
+      format &= !monthSpecialCase;
       const intl = format ? { month: length, day: "numeric" } : { month: length },
         formatStr = format ? "format" : "standalone";
       if (!this.monthsCache[formatStr][length]) {
-        this.monthsCache[formatStr][length] = mapMonths((dt) => this.extract(dt, intl, "month"));
+        const mapper = !monthSpecialCase
+          ? (dt) => this.extract(dt, intl, "month")
+          : (dt) => this.dtFormatter(dt, intl).format();
+        this.monthsCache[formatStr][length] = mapMonths(mapper);
       }
       return this.monthsCache[formatStr][length];
     });
diff --git a/src/impl/util.js b/src/impl/util.js
index 8f8c47b5..91642502 100644
--- a/src/impl/util.js
+++ b/src/impl/util.js
@@ -284,7 +284,7 @@ export function signedOffset(offHourStr, offMinuteStr) {
 
 export function asNumber(value) {
   const numericValue = Number(value);
-  if (typeof value === "boolean" || value === "" || Number.isNaN(numericValue))
+  if (typeof value === "boolean" || value === "" || !Number.isFinite(numericValue))
     throw new InvalidArgumentError(`Invalid unit value ${value}`);
   return numericValue;
 }


Test patch:
diff --git a/test/datetime/create.test.js b/test/datetime/create.test.js
index a3ec7326..5dddc14b 100644
--- a/test/datetime/create.test.js
+++ b/test/datetime/create.test.js
@@ -360,6 +360,12 @@ test("DateTime.fromMillis(ms) does not accept out-of-bounds numbers", () => {
   expect(DateTime.fromMillis(8.64e15 + 1).isValid).toBe(false);
 });
 
+test("DateTime.fromMillis(ms) does not accept non-finite numbers", () => {
+  expect(DateTime.fromMillis(Infinity).isValid).toBe(false);
+  expect(DateTime.fromMillis(-Infinity).isValid).toBe(false);
+  expect(DateTime.fromMillis(NaN).isValid).toBe(false);
+});
+
 //------
 // .fromSeconds()
 //-------
@@ -386,6 +392,17 @@ test("DateTime.fromSeconds(seconds) throws InvalidArgumentError for non-numeric
   expect(() => DateTime.fromSeconds("slurp")).toThrow();
 });
 
+test("DateTime.fromSeconds(seconds) does not accept out-of-bounds numbers", () => {
+  expect(DateTime.fromSeconds(-8.64e12 - 1).isValid).toBe(false);
+  expect(DateTime.fromSeconds(8.64e12 + 1).isValid).toBe(false);
+});
+
+test("DateTime.fromSeconds(seconds) does not accept non-finite numbers", () => {
+  expect(DateTime.fromSeconds(Infinity).isValid).toBe(false);
+  expect(DateTime.fromSeconds(-Infinity).isValid).toBe(false);
+  expect(DateTime.fromSeconds(NaN).isValid).toBe(false);
+});
+
 //------
 // .fromObject()
 //-------
@@ -506,6 +523,8 @@ test("DateTime.fromObject() throws with invalid value types", () => {
   expect(() => DateTime.fromObject({ year: "blorp" })).toThrow();
   expect(() => DateTime.fromObject({ year: "" })).toThrow();
   expect(() => DateTime.fromObject({ month: NaN })).toThrow();
+  expect(() => DateTime.fromObject({ month: Infinity })).toThrow();
+  expect(() => DateTime.fromObject({ month: -Infinity })).toThrow();
   expect(() => DateTime.fromObject({ day: true })).toThrow();
   expect(() => DateTime.fromObject({ day: false })).toThrow();
   expect(() => DateTime.fromObject({ hour: {} })).toThrow();
diff --git a/test/duration/create.test.js b/test/duration/create.test.js
index 1a6cbb22..945d88f2 100644
--- a/test/duration/create.test.js
+++ b/test/duration/create.test.js
@@ -129,7 +129,9 @@ it("Duration.fromDurationLike returns passed Duration", () => {
   expect(dur).toStrictEqual(durFromObject);
 });
 
-it("Duration.fromDurationLike returns passed Duration", () => {
+it("Duration.fromDurationLike throws for invalid inputs", () => {
   expect(() => Duration.fromDurationLike("foo")).toThrow();
   expect(() => Duration.fromDurationLike(null)).toThrow();
+  expect(() => Duration.fromDurationLike(Infinity)).toThrow();
+  expect(() => Duration.fromDurationLike(NaN)).toThrow();
 });
diff --git a/test/info/listers.test.js b/test/info/listers.test.js
index 7b4597d0..6f4ef610 100644
--- a/test/info/listers.test.js
+++ b/test/info/listers.test.js
@@ -136,21 +136,29 @@ test("Info.months respects the locale", () => {
     "১২",
   ]);
 
-  // these should arguably be 1月, 2月, etc, but this at least documents how it works
-  expect(Info.months("short", { locale: "ja-JP" })).toEqual([
-    "1",
-    "2",
-    "3",
-    "4",
-    "5",
-    "6",
-    "7",
-    "8",
-    "9",
-    "10",
-    "11",
-    "12",
-  ]);
+  // formatToParts outputs 月 as "literal" instead of "month", but it arguably is part of the name of the month.
+  // this tests that we correctly work around this
+  // see https://github.com/moment/luxon/issues/549
+  for (const locale of ["ja", "ja-JP"]) {
+    for (const length of ["long", "short", "narrow"]) {
+      for (const formatter of [Info.months, Info.monthsFormat]) {
+        expect(formatter(length, { locale })).toEqual([
+          "1月",
+          "2月",
+          "3月",
+          "4月",
+          "5月",
+          "6月",
+          "7月",
+          "8月",
+          "9月",
+          "10月",
+          "11月",
+          "12月",
+        ]);
+      }
+    }
+  }
 
   expect(Info.monthsFormat("long", { locale: "ru" })).toEqual([
     "января",