Repository: moment/luxon (js)

Issue:
Settings.throwOnInvalid = true breaks Interval.fromISO for <start>/<duration> ISO input
When ```Settings.throwOnInvalid = true``` is set the following code in Interval.js static fromISO
```js
const start = DateTime.fromISO(s, opts),
        end = DateTime.fromISO(e, opts);
```

throws which means that the subsequent handling of duration ISO is never reached.

Reproduction:
```js
Settings.throwOnInvalid = true
const interval = Interval.fromISO('2020-06-22T17:30:00.000+02:00/PT5H30M')
```
Throws:
```
InvalidDateTimeError: Invalid DateTime: unparsable: the input "PT5H30M" can't be parsed as ISO 8601
```

Pull request:
Interval creation supports Settings.throwOnInvalid. Fixes #728
Adding try blocks since this method relies on the `isValid` property.

Patch:
diff --git a/docs/formatting.md b/docs/formatting.md
index 414f38ba..1e59a884 100644
--- a/docs/formatting.md
+++ b/docs/formatting.md
@@ -133,7 +133,7 @@ The supported tokens are described in the table below.
 
 ### Intl
 
-All of the strings (e.g. month names and weekday names) are internationalized by introspecting strings generated by the Intl API. Thus they exact strings you get are implementation-specific.
+All of the strings (e.g. month names and weekday names) are internationalized by introspecting strings generated by the Intl API. Thus the exact strings you get are implementation-specific.
 
 ```js
 DateTime.fromISO('2014-08-06T13:07:04.054')
@@ -239,4 +239,4 @@ The macro options available correspond one-to-one with the preset formats define
 | FFF              |              | verbose localized date and time with seconds                   | August 6, 2014, 1:07:04 PM EDT                              |
 | FFFF             |              | extra verbose localized date and time with seconds             | Wednesday, August 6, 2014, 1:07:04 PM Eastern Daylight Time |
 | X                |              | unix timestamp in seconds                                      | 1407287224                                                  |
-| x                |              | unix timestamp in milliseconds                                 | 1407287224054                                              |
+| x                |              | unix timestamp in milliseconds                                 | 1407287224054                                               |
diff --git a/src/interval.js b/src/interval.js
index 51cd6b07..0fd901ad 100644
--- a/src/interval.js
+++ b/src/interval.js
@@ -134,19 +134,32 @@ export default class Interval {
   static fromISO(text, opts) {
     const [s, e] = (text || "").split("/", 2);
     if (s && e) {
-      const start = DateTime.fromISO(s, opts),
+      let start, startIsValid;
+      try {
+        start = DateTime.fromISO(s, opts);
+        startIsValid = start.isValid;
+      } catch (e) {
+        startIsValid = false;
+      }
+
+      let end, endIsValid;
+      try {
         end = DateTime.fromISO(e, opts);
+        endIsValid = end.isValid;
+      } catch (e) {
+        endIsValid = false;
+      }
 
-      if (start.isValid && end.isValid) {
+      if (startIsValid && endIsValid) {
         return Interval.fromDateTimes(start, end);
       }
 
-      if (start.isValid) {
+      if (startIsValid) {
         const dur = Duration.fromISO(e, opts);
         if (dur.isValid) {
           return Interval.after(start, dur);
         }
-      } else if (end.isValid) {
+      } else if (endIsValid) {
         const dur = Duration.fromISO(s, opts);
         if (dur.isValid) {
           return Interval.before(end, dur);


Test patch:
diff --git a/test/interval/parse.test.js b/test/interval/parse.test.js
index b7bad590..d2bd58cf 100644
--- a/test/interval/parse.test.js
+++ b/test/interval/parse.test.js
@@ -1,5 +1,8 @@
 /* global test expect */
 import { Interval } from "../../src/luxon";
+import Helpers from "../helpers";
+
+const withThrowOnInvalid = Helpers.setUnset("throwOnInvalid");
 
 //------
 // .fromISO()
@@ -117,6 +120,17 @@ test("Interval.fromISO accepts a zone argument", () => {
   expect(durDate.start.zoneName).toBe("Europe/Paris");
 });
 
+// #728
+test("Interval.fromISO works with Settings.throwOnInvalid", () => {
+  withThrowOnInvalid(true, () => {
+    const dateDur = Interval.fromISO("2020-06-22T17:30:00.000+02:00/PT5H30M");
+    expect(dateDur.isValid).toBe(true);
+
+    const durDate = Interval.fromISO("PT5H30M/2020-06-22T17:30:00.000+02:00");
+    expect(durDate.isValid).toBe(true);
+  });
+});
+
 const badInputs = [
   null,
   "",