Repository: moment/luxon (js)

Issue:
Timezone offsets are output with long decimals from floating point rounding errors in Firefox 
This issue is present in Firefox 77. Timezone offsets, whether output by the format string token "ZZZ" or the toISO() method, have long floating point rounding errors in the minutes portion of the offset (i.e. `50.6000000000000002` rather than `50`)

Here are two examples:

```
> DateTime.fromJSDate(new Date(-62090696591000)).toFormat(`yyyy-MM-dd'T'HH:mm:ss.SSSZZ`)
 "0002-06-04T10:26:13.000-05:50.60000000000002"
```

```
> DateTime.fromJSDate(new Date(-62090696591000)).toISO()
"0002-06-04T10:26:13.000-05:50.60000000000002"
```

I believe that the root of the problem is that for this particular timestamp, Firefox returns 350.6 as the timezone offset:
```
> new Date(-62090696591000).getTimezoneOffset()
350.6
```

Then in src/impl/util.js `formatOffset`, the `minutes` value (which presumably should always be an integer) is not rounded. 

Pull request:
Round minutes in timezone offset display. Fixes #724
Applied a `trunc` on the number of minutes in the offset, since that seems to be what is done in the other browsers.
Also the ISO 8601 wikipedia page does not mention fractional minutes in the format.

Note that this should be merged after #756, which affects the same line, since it will need a rebase (this commit also modified some formatting logic).

Patch:
diff --git a/src/impl/util.js b/src/impl/util.js
index 767f35d1..b345c694 100644
--- a/src/impl/util.js
+++ b/src/impl/util.js
@@ -263,18 +263,17 @@ export function normalizeObject(obj, normalizer, nonUnitKeys) {
 }
 
 export function formatOffset(offset, format) {
-  const hours = Math.trunc(offset / 60),
-    minutes = Math.abs(offset % 60),
-    sign = offset >= 0 ? "+" : "-",
-    base = `${sign}${Math.abs(hours)}`;
+  const hours = Math.trunc(Math.abs(offset / 60)),
+    minutes = Math.trunc(Math.abs(offset % 60)),
+    sign = offset >= 0 ? "+" : "-";
 
   switch (format) {
     case "short":
-      return `${sign}${padStart(Math.abs(hours), 2)}:${padStart(minutes, 2)}`;
+      return `${sign}${padStart(hours, 2)}:${padStart(minutes, 2)}`;
     case "narrow":
-      return minutes > 0 ? `${base}:${minutes}` : base;
+      return `${sign}${hours}${minutes > 0 ? `:${minutes}` : ""}`;
     case "techie":
-      return `${sign}${padStart(Math.abs(hours), 2)}${padStart(minutes, 2)}`;
+      return `${sign}${padStart(hours, 2)}${padStart(minutes, 2)}`;
     default:
       throw new RangeError(`Value format ${format} is out of range for property format`);
   }


Test patch:
diff --git a/test/datetime/format.test.js b/test/datetime/format.test.js
index 0bbfcc0f..2634b6cf 100644
--- a/test/datetime/format.test.js
+++ b/test/datetime/format.test.js
@@ -54,6 +54,15 @@ test("DateTime#toISO() returns null for invalid DateTimes", () => {
   expect(invalid.toISO()).toBe(null);
 });
 
+// #724, Firefox specific issue, offset prints as '-05:50.60000000000002'
+test("DateTime#toISO() rounds fractional timezone minute offsets", () => {
+  expect(
+    DateTime.fromMillis(-62090696591000)
+      .setZone("America/Chicago")
+      .toISO()
+  ).toBe("0002-06-04T10:26:13.000-05:50");
+});
+
 //------
 // #toISODate()
 //------