Repository: flatpickr/flatpickr
Pull request #2483 (2021-07-05 17:17:44)

Fix maxTime bound bug
With setting maxBound 3:30 (am) and minBound 5:30 (am) the previous implementation would set time as 5:30 (am) for 3:20 (am), which is incorrect.


Patch:
diff --git a/src/index.ts b/src/index.ts
index a19acd74..35834e04 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -30,6 +30,8 @@ import {
   duration,
   isBetween,
   getDefaultHours,
+  calculateSecondsSinceMidnight,
+  parseSeconds,
 } from "./utils/dates";
 
 import { tokenRegex, monthToStr } from "./utils/formatting";
@@ -252,19 +254,27 @@ function FlatpickrInstance(
       self.config.minTime !== undefined &&
       self.config.minTime > self.config.maxTime
     ) {
-      if (
-        hours >= self.config.maxTime.getHours() &&
-        hours < self.config.minTime.getHours()
-      ) {
-        hours = self.config.minTime.getHours();
-        if (
-          hours === self.config.minTime.getHours() &&
-          minutes < self.config.minTime.getMinutes()
-        )
-          minutes = self.config.minTime.getMinutes();
+      const minBound = calculateSecondsSinceMidnight(
+        self.config.minTime.getHours(),
+        self.config.minTime.getMinutes(),
+        self.config.minTime.getSeconds()
+      );
+      const maxBound = calculateSecondsSinceMidnight(
+        self.config.maxTime.getHours(),
+        self.config.maxTime.getMinutes(),
+        self.config.maxTime.getSeconds()
+      );
+      const currentTime = calculateSecondsSinceMidnight(
+        hours,
+        minutes,
+        seconds
+      );
 
-        if (minutes === self.config.minTime.getMinutes())
-          seconds = Math.max(seconds, self.config.minTime.getSeconds());
+      if (currentTime > maxBound && currentTime < minBound) {
+        const result = parseSeconds(minBound);
+        hours = result[0];
+        minutes = result[1];
+        seconds = result[2];
       }
     } else {
       if (limitMaxHours) {
diff --git a/src/plugins/minMaxTimePlugin.ts b/src/plugins/minMaxTimePlugin.ts
index f8f56d2f..425b8483 100644
--- a/src/plugins/minMaxTimePlugin.ts
+++ b/src/plugins/minMaxTimePlugin.ts
@@ -1,8 +1,10 @@
 import { Instance } from "../types/instance";
 import {
+  calculateSecondsSinceMidnight,
   compareDates,
   compareTimes,
   createDateFormatter,
+  parseSeconds,
 } from "../utils/dates";
 
 import { Plugin } from "../types/options";
@@ -69,26 +71,59 @@ function minMaxTimePlugin(config: Config = {}): Plugin {
           (fp.config.minTime as Date).setDate(latest.getDate());
           (fp.config.maxTime as Date).setDate(latest.getDate());
 
-          if (compareDates(latest, fp.config.maxTime as Date, false) > 0) {
-            fp.setDate(
-              new Date(latest.getTime()).setHours(
-                (fp.config.maxTime as Date).getHours(),
-                (fp.config.maxTime as Date).getMinutes(),
-                (fp.config.maxTime as Date).getSeconds(),
-                (fp.config.maxTime as Date).getMilliseconds()
-              ),
-              false
+          if ((fp.config.minTime as Date) > (fp.config.maxTime as Date)) {
+            const minBound = calculateSecondsSinceMidnight(
+              (fp.config.minTime as Date).getHours(),
+              (fp.config.minTime as Date).getMinutes(),
+              (fp.config.minTime as Date).getSeconds()
             );
-          } else if (compareDates(latest, fp.config.minTime as Date, false) < 0)
-            fp.setDate(
-              new Date(latest.getTime()).setHours(
-                (fp.config.minTime as Date).getHours(),
-                (fp.config.minTime as Date).getMinutes(),
-                (fp.config.minTime as Date).getSeconds(),
-                (fp.config.minTime as Date).getMilliseconds()
-              ),
-              false
+            const maxBound = calculateSecondsSinceMidnight(
+              (fp.config.maxTime as Date).getHours(),
+              (fp.config.maxTime as Date).getMinutes(),
+              (fp.config.maxTime as Date).getSeconds()
+            );
+            const currentTime = calculateSecondsSinceMidnight(
+              latest.getHours(),
+              latest.getMinutes(),
+              latest.getSeconds()
             );
+
+            if (currentTime > maxBound && currentTime < minBound) {
+              const result = parseSeconds(minBound);
+              fp.setDate(
+                new Date(latest.getTime()).setHours(
+                  result[0],
+                  result[1],
+                  result[2]
+                ),
+                false
+              );
+            }
+          } else {
+            if (compareDates(latest, fp.config.maxTime as Date, false) > 0) {
+              fp.setDate(
+                new Date(latest.getTime()).setHours(
+                  (fp.config.maxTime as Date).getHours(),
+                  (fp.config.maxTime as Date).getMinutes(),
+                  (fp.config.maxTime as Date).getSeconds(),
+                  (fp.config.maxTime as Date).getMilliseconds()
+                ),
+                false
+              );
+            } else if (
+              compareDates(latest, fp.config.minTime as Date, false) < 0
+            ) {
+              fp.setDate(
+                new Date(latest.getTime()).setHours(
+                  (fp.config.minTime as Date).getHours(),
+                  (fp.config.minTime as Date).getMinutes(),
+                  (fp.config.minTime as Date).getSeconds(),
+                  (fp.config.minTime as Date).getMilliseconds()
+                ),
+                false
+              );
+            }
+          }
         } else {
           const newMinMax = state.defaults || {
             minTime: undefined,
diff --git a/src/utils/dates.ts b/src/utils/dates.ts
index 4500e37b..5a8c5566 100644
--- a/src/utils/dates.ts
+++ b/src/utils/dates.ts
@@ -154,6 +154,20 @@ export const isBetween = (ts: number, ts1: number, ts2: number) => {
   return ts > Math.min(ts1, ts2) && ts < Math.max(ts1, ts2);
 };
 
+export const calculateSecondsSinceMidnight = (
+  hours: number,
+  minutes: number,
+  seconds: number
+) => {
+  return hours * 3600 + minutes * 60 + seconds;
+};
+
+export const parseSeconds = (secondsSinceMidnight: number) => {
+  const hours = Math.floor(secondsSinceMidnight / 3600),
+    minutes = (secondsSinceMidnight - hours * 3600) / 60;
+  return [hours, minutes, secondsSinceMidnight - hours * 3600 - minutes * 60];
+};
+
 export const duration = {
   DAY: 86400000,
 };


Test patch:
diff --git a/__tests__/src/index.spec.ts b/__tests__/src/index.spec.ts
index f8ba8635..b4fcf394 100644
--- a/__tests__/src/index.spec.ts
+++ b/__tests__/src/index.spec.ts
@@ -1282,12 +1282,18 @@ describe("flatpickr", () => {
         enableTime: true,
         minTime: "05:30",
         maxTime: "03:30",
-        defaultDate: "2021-07-01 3:30",
+        defaultDate: "2021-07-01 1:29",
       });
 
       fp.open();
 
-      expect(fp.input.value).toEqual("2021-07-01 03:30");
+      expect(fp.input.value).toEqual("2021-07-01 01:29");
+
+      incrementTime("hourElement", +1);
+      expect(fp.input.value).toEqual("2021-07-01 02:29");
+
+      incrementTime("hourElement", +1);
+      expect(fp.input.value).toEqual("2021-07-01 03:29");
 
       incrementTime("hourElement", +1);
       expect(fp.input.value).toEqual("2021-07-01 05:30");