@kalyx/react 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @kalyx/react
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 104bbf2: feat: full `displayTimezone` support across all pickers (v0.4)
8
+
9
+ All four pickers (`DatePicker`, `RangePicker`, `TimePicker`, `DateTimePicker`) and their corresponding hooks (`useDatePicker`, `useRangePicker`, `useTimePicker`) now accept a `displayTimezone` prop/option.
10
+
11
+ When set, the value stored via `onChange` is the **civil midnight of the selected day in the target timezone** (in UTC-ISO form), eliminating the classic "day off by one" bug that affects picker libraries bound to `new Date()`. Input formatting, calendar highlighting, and the time-of-day controls all follow the display timezone — including DST-aware offsets for zones like `America/New_York` and `Europe/London`.
12
+
13
+ `DateFnsAdapter` now honors the `timezone` argument on `format`, `isSameDay`, `startOfDay`, and `today` (previously declared-but-ignored). Core also exposes new helpers:
14
+
15
+ - `civilMidnightFromUtcDay(iso, tz)`
16
+ - `getTimeInTimezone(iso, tz)`
17
+ - `setTimeInTimezone(iso, partial, tz)`
18
+
19
+ No breaking changes — omitting `displayTimezone` keeps the existing UTC semantics.
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies [b3a8897]
24
+ - Updated dependencies [104bbf2]
25
+ - @kalyx/core@0.4.0
26
+
3
27
  ## 0.3.0
4
28
 
5
29
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -30,6 +30,7 @@ function DatePickerRoot({
30
30
  weekStartsOn = 0,
31
31
  displayFormat = "yyyy-MM-dd",
32
32
  locale = "en-US",
33
+ displayTimezone,
33
34
  adapter = core.DateFnsAdapter,
34
35
  labels: labelsProp,
35
36
  children
@@ -43,10 +44,10 @@ function DatePickerRoot({
43
44
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
44
45
  const [isOpen, setIsOpen] = react.useState(false);
45
46
  const [viewMonth, setViewMonth] = react.useState(
46
- currentValue ?? adapter.today()
47
+ currentValue ?? adapter.today(displayTimezone)
47
48
  );
48
49
  const [focusedDate, setFocusedDate] = react.useState(
49
- currentValue ?? adapter.today()
50
+ currentValue ?? adapter.today(displayTimezone)
50
51
  );
51
52
  const mergedLabels = react.useMemo(
52
53
  () => ({ ...core.DEFAULT_DATEPICKER_LABELS, ...labelsProp }),
@@ -60,21 +61,22 @@ function DatePickerRoot({
60
61
  const selectDate = react.useCallback(
61
62
  (iso) => {
62
63
  if (isDisabled || readOnly) return;
64
+ const normalized = iso && displayTimezone ? core.civilMidnightFromUtcDay(iso, displayTimezone) : iso;
63
65
  if (!isControlled) {
64
- setUncontrolledValue(iso);
66
+ setUncontrolledValue(normalized);
65
67
  }
66
- onChange?.(iso);
68
+ onChange?.(normalized);
67
69
  setIsOpen(false);
68
70
  },
69
- [isControlled, isDisabled, readOnly, onChange]
71
+ [isControlled, isDisabled, readOnly, onChange, displayTimezone]
70
72
  );
71
73
  const open = react.useCallback(() => {
72
74
  if (isDisabled || readOnly) return;
73
75
  setIsOpen(true);
74
- const target = currentValue ?? adapter.today();
76
+ const target = currentValue ?? adapter.today(displayTimezone);
75
77
  setViewMonth(target);
76
78
  setFocusedDate(target);
77
- }, [isDisabled, readOnly, currentValue, adapter]);
79
+ }, [isDisabled, readOnly, currentValue, adapter, displayTimezone]);
78
80
  const close = react.useCallback(() => {
79
81
  setIsOpen(false);
80
82
  }, []);
@@ -103,6 +105,7 @@ function DatePickerRoot({
103
105
  weekStartsOn,
104
106
  displayFormat,
105
107
  locale,
108
+ displayTimezone,
106
109
  isDisabled,
107
110
  isReadOnly: readOnly,
108
111
  pickerId,
@@ -122,6 +125,7 @@ function DatePickerRoot({
122
125
  weekStartsOn,
123
126
  displayFormat,
124
127
  locale,
128
+ displayTimezone,
125
129
  isDisabled,
126
130
  readOnly,
127
131
  pickerId,
@@ -138,7 +142,7 @@ var DatePickerInput = react.forwardRef(
138
142
  let formattedValue = "";
139
143
  if (ctx.value) {
140
144
  try {
141
- formattedValue = ctx.adapter.format(ctx.value, displayFormat);
145
+ formattedValue = ctx.adapter.format(ctx.value, displayFormat, ctx.displayTimezone);
142
146
  } catch {
143
147
  formattedValue = ctx.value;
144
148
  }
@@ -386,13 +390,14 @@ function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
386
390
  const ctx = useDatePickerContext("DatePicker.Calendar");
387
391
  const gridRef = react.useRef(null);
388
392
  const [announcement, setAnnouncement] = react.useState("");
389
- const { adapter, viewMonth, focusedDate, weekStartsOn, disabled, locale } = ctx;
393
+ const { adapter, viewMonth, focusedDate, weekStartsOn, disabled, locale, displayTimezone } = ctx;
390
394
  const weekdays = core.getWeekdayNames(locale, weekStartsOn);
391
395
  const weeks = core.getCalendarDays(viewMonth, adapter, {
392
396
  weekStartsOn,
393
397
  selected: ctx.value,
394
398
  focusedDate,
395
- disabled
399
+ disabled,
400
+ timezone: displayTimezone
396
401
  });
397
402
  const year = adapter.getYear(viewMonth);
398
403
  const month = adapter.getMonth(viewMonth);
@@ -808,6 +813,7 @@ function RangePickerRoot({
808
813
  weekStartsOn = 0,
809
814
  displayFormat = "yyyy-MM-dd",
810
815
  locale = "en-US",
816
+ displayTimezone,
811
817
  adapter = core.DateFnsAdapter,
812
818
  labels: labelsProp,
813
819
  children
@@ -823,10 +829,10 @@ function RangePickerRoot({
823
829
  const [selectingTarget, setSelectingTarget] = react.useState("start");
824
830
  const [hoverDate, setHoverDate] = react.useState(null);
825
831
  const [viewMonth, setViewMonth] = react.useState(
826
- currentValue.start ?? adapter.today()
832
+ currentValue.start ?? adapter.today(displayTimezone)
827
833
  );
828
834
  const [focusedDate, setFocusedDate] = react.useState(
829
- currentValue.start ?? adapter.today()
835
+ currentValue.start ?? adapter.today(displayTimezone)
830
836
  );
831
837
  const mergedLabels = react.useMemo(
832
838
  () => ({ ...core.DEFAULT_RANGEPICKER_LABELS, ...labelsProp }),
@@ -850,23 +856,24 @@ function RangePickerRoot({
850
856
  const selectDate = react.useCallback(
851
857
  (iso) => {
852
858
  if (isDisabled || readOnly) return;
859
+ const normalized = displayTimezone ? core.civilMidnightFromUtcDay(iso, displayTimezone) : iso;
853
860
  if (selectingTarget === "start") {
854
- const newRange = { start: iso, end: null };
861
+ const newRange = { start: normalized, end: null };
855
862
  setRange(newRange);
856
863
  setSelectingTarget("end");
857
864
  setHoverDate(null);
858
865
  } else {
859
866
  const start = currentValue.start;
860
867
  if (!start) {
861
- setRange({ start: iso, end: null });
868
+ setRange({ start: normalized, end: null });
862
869
  setSelectingTarget("end");
863
870
  return;
864
871
  }
865
872
  let newRange;
866
- if (adapter.isBefore(iso, start)) {
867
- newRange = { start: iso, end: start };
873
+ if (adapter.isBefore(normalized, start)) {
874
+ newRange = { start: normalized, end: start };
868
875
  } else {
869
- newRange = { start, end: iso };
876
+ newRange = { start, end: normalized };
870
877
  }
871
878
  setRange(newRange);
872
879
  setSelectingTarget("start");
@@ -874,18 +881,18 @@ function RangePickerRoot({
874
881
  setIsOpen(false);
875
882
  }
876
883
  },
877
- [isDisabled, readOnly, selectingTarget, currentValue.start, adapter, setRange]
884
+ [isDisabled, readOnly, selectingTarget, currentValue.start, adapter, setRange, displayTimezone]
878
885
  );
879
886
  const open = react.useCallback(() => {
880
887
  if (isDisabled || readOnly) return;
881
888
  setIsOpen(true);
882
- const target = currentValue.start ?? adapter.today();
889
+ const target = currentValue.start ?? adapter.today(displayTimezone);
883
890
  setViewMonth(target);
884
891
  setFocusedDate(target);
885
892
  if (currentValue.start && currentValue.end) {
886
893
  setSelectingTarget("start");
887
894
  }
888
- }, [isDisabled, readOnly, currentValue, adapter]);
895
+ }, [isDisabled, readOnly, currentValue, adapter, displayTimezone]);
889
896
  const close = react.useCallback(() => {
890
897
  setIsOpen(false);
891
898
  setHoverDate(null);
@@ -916,6 +923,7 @@ function RangePickerRoot({
916
923
  weekStartsOn,
917
924
  displayFormat,
918
925
  locale,
926
+ displayTimezone,
919
927
  isDisabled,
920
928
  isReadOnly: readOnly,
921
929
  pickerId,
@@ -938,6 +946,7 @@ function RangePickerRoot({
938
946
  weekStartsOn,
939
947
  displayFormat,
940
948
  locale,
949
+ displayTimezone,
941
950
  isDisabled,
942
951
  readOnly,
943
952
  pickerId,
@@ -954,7 +963,7 @@ var RangePickerInput = react.forwardRef(
954
963
  let displayValue = "";
955
964
  if (value) {
956
965
  try {
957
- displayValue = ctx.adapter.format(value, displayFormat);
966
+ displayValue = ctx.adapter.format(value, displayFormat, ctx.displayTimezone);
958
967
  } catch {
959
968
  displayValue = value;
960
969
  }
@@ -1059,7 +1068,8 @@ function RangePickerCalendar({ classNames, ...props }) {
1059
1068
  disabled,
1060
1069
  value,
1061
1070
  hoverDate,
1062
- selectingTarget
1071
+ selectingTarget,
1072
+ displayTimezone
1063
1073
  } = ctx;
1064
1074
  const { locale } = ctx;
1065
1075
  const weekdays = core.getWeekdayNames(locale, weekStartsOn);
@@ -1068,7 +1078,8 @@ function RangePickerCalendar({ classNames, ...props }) {
1068
1078
  focusedDate,
1069
1079
  disabled,
1070
1080
  range: value,
1071
- rangeHover: hoverDate
1081
+ rangeHover: hoverDate,
1082
+ timezone: displayTimezone
1072
1083
  });
1073
1084
  const year = adapter.getYear(viewMonth);
1074
1085
  const month = adapter.getMonth(viewMonth);
@@ -1391,6 +1402,7 @@ function TimePickerRoot({
1391
1402
  format = "24h",
1392
1403
  step = 1,
1393
1404
  withSeconds = false,
1405
+ displayTimezone,
1394
1406
  disabled = false,
1395
1407
  readOnly = false,
1396
1408
  labels: labelsProp,
@@ -1407,17 +1419,20 @@ function TimePickerRoot({
1407
1419
  );
1408
1420
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
1409
1421
  const baseIso = currentValue ?? getDefaultIso();
1410
- const currentTime = react.useMemo(() => core.getTime(baseIso), [baseIso]);
1422
+ const currentTime = react.useMemo(
1423
+ () => displayTimezone ? core.getTimeInTimezone(baseIso, displayTimezone) : core.getTime(baseIso),
1424
+ [baseIso, displayTimezone]
1425
+ );
1411
1426
  const setTime = react.useCallback(
1412
1427
  (partial) => {
1413
1428
  if (disabled || readOnly) return;
1414
- const newIso = core.setTime(baseIso, partial);
1429
+ const newIso = displayTimezone ? core.setTimeInTimezone(baseIso, partial, displayTimezone) : core.setTime(baseIso, partial);
1415
1430
  if (!isControlled) {
1416
1431
  setUncontrolledValue(newIso);
1417
1432
  }
1418
1433
  onChange?.(newIso);
1419
1434
  },
1420
- [disabled, readOnly, baseIso, isControlled, onChange]
1435
+ [disabled, readOnly, baseIso, isControlled, onChange, displayTimezone]
1421
1436
  );
1422
1437
  const contextValue = react.useMemo(
1423
1438
  () => ({
@@ -1426,13 +1441,14 @@ function TimePickerRoot({
1426
1441
  format,
1427
1442
  step,
1428
1443
  withSeconds,
1444
+ displayTimezone,
1429
1445
  isDisabled: disabled,
1430
1446
  isReadOnly: readOnly,
1431
1447
  currentTime,
1432
1448
  pickerId,
1433
1449
  labels: mergedLabels
1434
1450
  }),
1435
- [currentValue, setTime, format, step, withSeconds, disabled, readOnly, currentTime, pickerId, mergedLabels]
1451
+ [currentValue, setTime, format, step, withSeconds, displayTimezone, disabled, readOnly, currentTime, pickerId, mergedLabels]
1436
1452
  );
1437
1453
  return /* @__PURE__ */ jsxRuntime.jsx(TimePickerContext.Provider, { value: contextValue, children });
1438
1454
  }
@@ -1693,6 +1709,7 @@ function DateTimePickerRoot({
1693
1709
  weekStartsOn = 0,
1694
1710
  displayFormat = "yyyy-MM-dd HH:mm",
1695
1711
  locale = "en-US",
1712
+ displayTimezone,
1696
1713
  adapter = core.DateFnsAdapter,
1697
1714
  labels: labelsProp,
1698
1715
  children
@@ -1714,10 +1731,10 @@ function DateTimePickerRoot({
1714
1731
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
1715
1732
  const [isOpen, setIsOpen] = react.useState(false);
1716
1733
  const [viewMonth, setViewMonth] = react.useState(
1717
- currentValue ?? adapter.today()
1734
+ currentValue ?? adapter.today(displayTimezone)
1718
1735
  );
1719
1736
  const [focusedDate, setFocusedDate] = react.useState(
1720
- currentValue ?? adapter.today()
1737
+ currentValue ?? adapter.today(displayTimezone)
1721
1738
  );
1722
1739
  const isDisabled = typeof disabled === "boolean" ? disabled : false;
1723
1740
  const disabledRules = react.useMemo(
@@ -1725,7 +1742,10 @@ function DateTimePickerRoot({
1725
1742
  [disabled]
1726
1743
  );
1727
1744
  const baseIso = currentValue ?? getDefaultIso2();
1728
- const currentTime = react.useMemo(() => core.getTime(baseIso), [baseIso]);
1745
+ const currentTime = react.useMemo(
1746
+ () => displayTimezone ? core.getTimeInTimezone(baseIso, displayTimezone) : core.getTime(baseIso),
1747
+ [baseIso, displayTimezone]
1748
+ );
1729
1749
  const updateValue = react.useCallback(
1730
1750
  (next) => {
1731
1751
  if (isDisabled || readOnly) return;
@@ -1742,27 +1762,28 @@ function DateTimePickerRoot({
1742
1762
  updateValue(null);
1743
1763
  return;
1744
1764
  }
1745
- const time = currentValue ? core.getTime(currentValue) : currentTime;
1746
- const merged = core.setTime(newDateIso, time);
1765
+ const normalizedDate = displayTimezone ? core.civilMidnightFromUtcDay(newDateIso, displayTimezone) : newDateIso;
1766
+ const time = currentValue ? displayTimezone ? core.getTimeInTimezone(currentValue, displayTimezone) : core.getTime(currentValue) : currentTime;
1767
+ const merged = displayTimezone ? core.setTimeInTimezone(normalizedDate, time, displayTimezone) : core.setTime(normalizedDate, time);
1747
1768
  updateValue(merged);
1748
1769
  },
1749
- [currentValue, currentTime, updateValue]
1770
+ [currentValue, currentTime, updateValue, displayTimezone]
1750
1771
  );
1751
1772
  const setTime = react.useCallback(
1752
1773
  (partial) => {
1753
1774
  const base = currentValue ?? getDefaultIso2();
1754
- const merged = core.setTime(base, partial);
1775
+ const merged = displayTimezone ? core.setTimeInTimezone(base, partial, displayTimezone) : core.setTime(base, partial);
1755
1776
  updateValue(merged);
1756
1777
  },
1757
- [currentValue, updateValue]
1778
+ [currentValue, updateValue, displayTimezone]
1758
1779
  );
1759
1780
  const open = react.useCallback(() => {
1760
1781
  if (isDisabled || readOnly) return;
1761
1782
  setIsOpen(true);
1762
- const target = currentValue ?? adapter.today();
1783
+ const target = currentValue ?? adapter.today(displayTimezone);
1763
1784
  setViewMonth(target);
1764
1785
  setFocusedDate(target);
1765
- }, [isDisabled, readOnly, currentValue, adapter]);
1786
+ }, [isDisabled, readOnly, currentValue, adapter, displayTimezone]);
1766
1787
  const close = react.useCallback(() => {
1767
1788
  setIsOpen(false);
1768
1789
  }, []);
@@ -1788,6 +1809,7 @@ function DateTimePickerRoot({
1788
1809
  weekStartsOn,
1789
1810
  displayFormat,
1790
1811
  locale,
1812
+ displayTimezone,
1791
1813
  isDisabled,
1792
1814
  isReadOnly: readOnly,
1793
1815
  pickerId,
@@ -1807,6 +1829,7 @@ function DateTimePickerRoot({
1807
1829
  weekStartsOn,
1808
1830
  displayFormat,
1809
1831
  locale,
1832
+ displayTimezone,
1810
1833
  isDisabled,
1811
1834
  readOnly,
1812
1835
  pickerId,
@@ -1820,13 +1843,14 @@ function DateTimePickerRoot({
1820
1843
  format,
1821
1844
  step,
1822
1845
  withSeconds: false,
1846
+ displayTimezone,
1823
1847
  isDisabled,
1824
1848
  isReadOnly: readOnly,
1825
1849
  currentTime,
1826
1850
  pickerId,
1827
1851
  labels: mergedTimeLabels
1828
1852
  }),
1829
- [currentValue, setTime, format, step, isDisabled, readOnly, currentTime, pickerId, mergedTimeLabels]
1853
+ [currentValue, setTime, format, step, displayTimezone, isDisabled, readOnly, currentTime, pickerId, mergedTimeLabels]
1830
1854
  );
1831
1855
  return /* @__PURE__ */ jsxRuntime.jsx(DatePickerContext.Provider, { value: dateContext, children: /* @__PURE__ */ jsxRuntime.jsx(TimePickerContext.Provider, { value: timeContext, children }) });
1832
1856
  }
@@ -1836,7 +1860,9 @@ var DateTimePickerInput = react.forwardRef(
1836
1860
  let displayValue = "";
1837
1861
  if (ctx.value) {
1838
1862
  try {
1839
- displayValue = `${ctx.adapter.format(ctx.value, "yyyy-MM-dd")} ${core.formatTimeString(core.getTime(ctx.value))}`;
1863
+ const datePart = ctx.adapter.format(ctx.value, "yyyy-MM-dd", ctx.displayTimezone);
1864
+ const time = ctx.displayTimezone ? core.getTimeInTimezone(ctx.value, ctx.displayTimezone) : core.getTime(ctx.value);
1865
+ displayValue = `${datePart} ${core.formatTimeString(time)}`;
1840
1866
  } catch {
1841
1867
  displayValue = ctx.value;
1842
1868
  }
@@ -1906,7 +1932,8 @@ function useDatePicker(options = {}) {
1906
1932
  onChange,
1907
1933
  disabled = [],
1908
1934
  weekStartsOn = 0,
1909
- adapter = core.DateFnsAdapter
1935
+ adapter = core.DateFnsAdapter,
1936
+ displayTimezone
1910
1937
  } = options;
1911
1938
  const pickerId = react.useId();
1912
1939
  const isControlled = react.useRef(controlledValue !== void 0).current;
@@ -1915,24 +1942,29 @@ function useDatePicker(options = {}) {
1915
1942
  );
1916
1943
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
1917
1944
  const [isOpen, setIsOpen] = react.useState(false);
1918
- const [viewMonth, setViewMonth] = react.useState(currentValue ?? adapter.today());
1919
- const [focusedDate, setFocusedDate] = react.useState(currentValue ?? adapter.today());
1945
+ const [viewMonth, setViewMonth] = react.useState(
1946
+ currentValue ?? adapter.today(displayTimezone)
1947
+ );
1948
+ const [focusedDate, setFocusedDate] = react.useState(
1949
+ currentValue ?? adapter.today(displayTimezone)
1950
+ );
1920
1951
  const selectDate = react.useCallback(
1921
1952
  (iso) => {
1953
+ const normalized = iso && displayTimezone ? core.civilMidnightFromUtcDay(iso, displayTimezone) : iso;
1922
1954
  if (!isControlled) {
1923
- setUncontrolledValue(iso);
1955
+ setUncontrolledValue(normalized);
1924
1956
  }
1925
- onChange?.(iso);
1957
+ onChange?.(normalized);
1926
1958
  setIsOpen(false);
1927
1959
  },
1928
- [isControlled, onChange]
1960
+ [isControlled, onChange, displayTimezone]
1929
1961
  );
1930
1962
  const open = react.useCallback(() => {
1931
1963
  setIsOpen(true);
1932
- const target = currentValue ?? adapter.today();
1964
+ const target = currentValue ?? adapter.today(displayTimezone);
1933
1965
  setViewMonth(target);
1934
1966
  setFocusedDate(target);
1935
- }, [currentValue, adapter]);
1967
+ }, [currentValue, adapter, displayTimezone]);
1936
1968
  const close = react.useCallback(() => {
1937
1969
  setIsOpen(false);
1938
1970
  }, []);
@@ -1954,7 +1986,8 @@ function useDatePicker(options = {}) {
1954
1986
  weekStartsOn,
1955
1987
  selected: currentValue,
1956
1988
  focusedDate,
1957
- disabled
1989
+ disabled,
1990
+ timezone: displayTimezone
1958
1991
  });
1959
1992
  return {
1960
1993
  value: currentValue,
@@ -1982,7 +2015,8 @@ function useRangePicker(options = {}) {
1982
2015
  onChange,
1983
2016
  disabled = [],
1984
2017
  weekStartsOn = 0,
1985
- adapter = core.DateFnsAdapter
2018
+ adapter = core.DateFnsAdapter,
2019
+ displayTimezone
1986
2020
  } = options;
1987
2021
  const pickerId = react.useId();
1988
2022
  const isControlled = react.useRef(controlledValue !== void 0).current;
@@ -1994,10 +2028,10 @@ function useRangePicker(options = {}) {
1994
2028
  const [selectingTarget, setSelectingTarget] = react.useState("start");
1995
2029
  const [hoverDate, setHoverDate] = react.useState(null);
1996
2030
  const [viewMonth, setViewMonth] = react.useState(
1997
- currentValue.start ?? adapter.today()
2031
+ currentValue.start ?? adapter.today(displayTimezone)
1998
2032
  );
1999
2033
  const [focusedDate, setFocusedDate] = react.useState(
2000
- currentValue.start ?? adapter.today()
2034
+ currentValue.start ?? adapter.today(displayTimezone)
2001
2035
  );
2002
2036
  const setRange = react.useCallback(
2003
2037
  (range) => {
@@ -2010,35 +2044,36 @@ function useRangePicker(options = {}) {
2010
2044
  );
2011
2045
  const selectDate = react.useCallback(
2012
2046
  (iso) => {
2047
+ const normalized = displayTimezone ? core.civilMidnightFromUtcDay(iso, displayTimezone) : iso;
2013
2048
  if (selectingTarget === "start") {
2014
- setRange({ start: iso, end: null });
2049
+ setRange({ start: normalized, end: null });
2015
2050
  setSelectingTarget("end");
2016
2051
  setHoverDate(null);
2017
2052
  } else {
2018
2053
  const start = currentValue.start;
2019
2054
  if (!start) {
2020
- setRange({ start: iso, end: null });
2055
+ setRange({ start: normalized, end: null });
2021
2056
  setSelectingTarget("end");
2022
2057
  return;
2023
2058
  }
2024
- const newRange = adapter.isBefore(iso, start) ? { start: iso, end: start } : { start, end: iso };
2059
+ const newRange = adapter.isBefore(normalized, start) ? { start: normalized, end: start } : { start, end: normalized };
2025
2060
  setRange(newRange);
2026
2061
  setSelectingTarget("start");
2027
2062
  setHoverDate(null);
2028
2063
  setIsOpen(false);
2029
2064
  }
2030
2065
  },
2031
- [selectingTarget, currentValue.start, adapter, setRange]
2066
+ [selectingTarget, currentValue.start, adapter, setRange, displayTimezone]
2032
2067
  );
2033
2068
  const open = react.useCallback(() => {
2034
2069
  setIsOpen(true);
2035
- const target = currentValue.start ?? adapter.today();
2070
+ const target = currentValue.start ?? adapter.today(displayTimezone);
2036
2071
  setViewMonth(target);
2037
2072
  setFocusedDate(target);
2038
2073
  if (currentValue.start && currentValue.end) {
2039
2074
  setSelectingTarget("start");
2040
2075
  }
2041
- }, [currentValue, adapter]);
2076
+ }, [currentValue, adapter, displayTimezone]);
2042
2077
  const close = react.useCallback(() => {
2043
2078
  setIsOpen(false);
2044
2079
  setHoverDate(null);
@@ -2062,7 +2097,8 @@ function useRangePicker(options = {}) {
2062
2097
  focusedDate,
2063
2098
  disabled,
2064
2099
  range: currentValue,
2065
- rangeHover: hoverDate
2100
+ rangeHover: hoverDate,
2101
+ timezone: displayTimezone
2066
2102
  });
2067
2103
  return {
2068
2104
  value: currentValue,
@@ -2095,7 +2131,8 @@ function useTimePicker(options = {}) {
2095
2131
  defaultValue,
2096
2132
  onChange,
2097
2133
  format = "24h",
2098
- step = 1
2134
+ step = 1,
2135
+ displayTimezone
2099
2136
  } = options;
2100
2137
  const pickerId = react.useId();
2101
2138
  const isControlled = react.useRef(controlledValue !== void 0).current;
@@ -2104,16 +2141,19 @@ function useTimePicker(options = {}) {
2104
2141
  );
2105
2142
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
2106
2143
  const baseIso = currentValue ?? getDefaultIso3();
2107
- const currentTime = react.useMemo(() => core.getTime(baseIso), [baseIso]);
2144
+ const currentTime = react.useMemo(
2145
+ () => displayTimezone ? core.getTimeInTimezone(baseIso, displayTimezone) : core.getTime(baseIso),
2146
+ [baseIso, displayTimezone]
2147
+ );
2108
2148
  const setTime = react.useCallback(
2109
2149
  (partial) => {
2110
- const newIso = core.setTime(baseIso, partial);
2150
+ const newIso = displayTimezone ? core.setTimeInTimezone(baseIso, partial, displayTimezone) : core.setTime(baseIso, partial);
2111
2151
  if (!isControlled) {
2112
2152
  setUncontrolledValue(newIso);
2113
2153
  }
2114
2154
  onChange?.(newIso);
2115
2155
  },
2116
- [baseIso, isControlled, onChange]
2156
+ [baseIso, isControlled, onChange, displayTimezone]
2117
2157
  );
2118
2158
  const period = format === "12h" ? core.to12Hour(currentTime.hours).period : null;
2119
2159
  const displayHour = format === "12h" ? core.to12Hour(currentTime.hours).hours12 : currentTime.hours;