@atlaskit/datetime-picker 19.3.2 → 19.4.1

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,25 @@
1
1
  # @atlaskit/datetime-picker
2
2
 
3
+ ## 19.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`efaa75ad1bb94`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/efaa75ad1bb94) -
8
+ Preserve supplied ISO offsets when editing datetime values behind
9
+ `platform-dst-dtp-preserve-offset`.
10
+
11
+ ## 19.4.0
12
+
13
+ ### Minor Changes
14
+
15
+ - [`cf5cda7218d8e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/cf5cda7218d8e) -
16
+ Behind the `platform-dst-locale-aware-time-parsing` feature gate, parse localized editable time
17
+ input while retaining ISO output and legacy English forms.
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies
22
+
3
23
  ## 19.3.2
4
24
 
5
25
  ### Patch Changes
@@ -47,7 +47,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
47
47
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
48
48
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
49
49
  var packageName = "@atlaskit/datetime-picker";
50
- var packageVersion = "19.3.1";
50
+ var packageVersion = "19.4.0";
51
51
  var styles = {
52
52
  pickerContainerStyle: "_kqswh2mm",
53
53
  dropdownIndicatorStyles: "_1ul91k8s _1tke1k8s _1e0c1txw _4cvr1h6o _1bah1h6o",
@@ -35,7 +35,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
35
35
  // for typing `process`
36
36
  // oxlint-disable-next-line @atlassian/no-restricted-imports
37
37
  var packageName = "@atlaskit/datetime-picker";
38
- var packageVersion = "19.3.1";
38
+ var packageVersion = "19.4.0";
39
39
  var analyticsAttributes = {
40
40
  componentName: 'dateTimePicker',
41
41
  packageName: packageName,
@@ -83,6 +83,36 @@ var getStyles = function getStyles(isInputMotionEnabled, roundedCorners) {
83
83
  }
84
84
  };
85
85
  };
86
+ var parseDateTimeValueWithOffset = function parseDateTimeValueWithOffset(value) {
87
+ var parsed = (0, _dateFns.parseISO)(value);
88
+ var zoneStart = value.endsWith('Z') ? value.length - 1 : Math.max(value.lastIndexOf('+'), value.lastIndexOf('-'));
89
+ if (!(0, _dateFns.isValid)(parsed) || value[10] !== 'T' || zoneStart <= 15) {
90
+ return undefined;
91
+ }
92
+ var zoneValue = value.slice(zoneStart);
93
+ if (!/^(?:Z|[+-]\d{2}(?::?\d{2})?)$/.test(zoneValue)) {
94
+ return undefined;
95
+ }
96
+ return {
97
+ dateValue: value.slice(0, 10),
98
+ timeValue: value.slice(11, 16),
99
+ zoneValue: zoneValue
100
+ };
101
+ };
102
+ var parseDateTimeValue = function parseDateTimeValue(value) {
103
+ if ((0, _fg.fg)('platform-dst-dtp-preserve-offset')) {
104
+ var hasExplicitZone = value.endsWith('Z') || Math.max(value.lastIndexOf('+'), value.lastIndexOf('-')) > 15;
105
+ if (hasExplicitZone) {
106
+ return parseDateTimeValueWithOffset(value);
107
+ }
108
+ }
109
+ var parsed = (0, _dateFns.parseISO)(value);
110
+ return (0, _dateFns.isValid)(parsed) ? {
111
+ dateValue: (0, _dateFns.format)(parsed, (0, _parseTokens.convertTokens)('YYYY-MM-DD')),
112
+ timeValue: (0, _dateFns.format)(parsed, (0, _parseTokens.convertTokens)('HH:mm')),
113
+ zoneValue: (0, _dateFns.format)(parsed, (0, _parseTokens.convertTokens)('ZZ'))
114
+ } : undefined;
115
+ };
86
116
 
87
117
  /**
88
118
  * Two action types keep the reducer focused:
@@ -187,13 +217,10 @@ var DateTimePicker = /*#__PURE__*/(0, _react.forwardRef)(function (_ref2, ref) {
187
217
  zoneValue: state.zoneValue
188
218
  };
189
219
  }
190
- var parsed = (0, _dateFns.parseISO)(newValue);
191
- return (0, _dateFns.isValid)(parsed) ? {
192
- value: newValue,
193
- dateValue: (0, _dateFns.format)(parsed, (0, _parseTokens.convertTokens)('YYYY-MM-DD')),
194
- timeValue: (0, _dateFns.format)(parsed, (0, _parseTokens.convertTokens)('HH:mm')),
195
- zoneValue: (0, _dateFns.format)(parsed, (0, _parseTokens.convertTokens)('ZZ'))
196
- } : {
220
+ var parsed = parseDateTimeValue(newValue);
221
+ return parsed ? _objectSpread({
222
+ value: newValue
223
+ }, parsed) : {
197
224
  value: newValue,
198
225
  dateValue: '',
199
226
  timeValue: '',
@@ -228,13 +255,10 @@ var DateTimePicker = /*#__PURE__*/(0, _react.forwardRef)(function (_ref2, ref) {
228
255
  zoneValue: ''
229
256
  };
230
257
  }
231
- var parsed = (0, _dateFns.parseISO)(initialValue);
232
- return (0, _dateFns.isValid)(parsed) ? {
233
- value: initialValue,
234
- dateValue: (0, _dateFns.format)(parsed, (0, _parseTokens.convertTokens)('YYYY-MM-DD')),
235
- timeValue: (0, _dateFns.format)(parsed, (0, _parseTokens.convertTokens)('HH:mm')),
236
- zoneValue: (0, _dateFns.format)(parsed, (0, _parseTokens.convertTokens)('ZZ'))
237
- } : {
258
+ var parsed = parseDateTimeValue(initialValue);
259
+ return parsed ? _objectSpread({
260
+ value: initialValue
261
+ }, parsed) : {
238
262
  value: initialValue,
239
263
  dateValue: initialDate,
240
264
  timeValue: initialTime,
@@ -288,12 +312,8 @@ var DateTimePicker = /*#__PURE__*/(0, _react.forwardRef)(function (_ref2, ref) {
288
312
  zoneValue: providedZoneValue
289
313
  };
290
314
  }
291
- var parsed = (0, _dateFns.parseISO)(value);
292
- return (0, _dateFns.isValid)(parsed) ? {
293
- dateValue: (0, _dateFns.format)(parsed, (0, _parseTokens.convertTokens)('YYYY-MM-DD')),
294
- timeValue: (0, _dateFns.format)(parsed, (0, _parseTokens.convertTokens)('HH:mm')),
295
- zoneValue: (0, _dateFns.format)(parsed, (0, _parseTokens.convertTokens)('ZZ'))
296
- } : {
315
+ var parsed = parseDateTimeValue(value);
316
+ return parsed || {
297
317
  dateValue: dtState.dateValue,
298
318
  timeValue: dtState.timeValue,
299
319
  zoneValue: dtState.zoneValue
@@ -19,6 +19,7 @@ var _usePlatformLeafEventHandler = require("@atlaskit/analytics-next/usePlatform
19
19
  var _button = _interopRequireDefault(require("@atlaskit/button/icon/button"));
20
20
  var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
21
21
  var _clock = _interopRequireDefault(require("@atlaskit/icon/core/clock"));
22
+ var _getTimeFormat = require("@atlaskit/locale/get-time-format");
22
23
  var _localizationProvider = require("@atlaskit/locale/localization-provider");
23
24
  var _fg = require("@atlaskit/platform-feature-flags/fg");
24
25
  var _styles = require("@atlaskit/react-select/styles");
@@ -37,7 +38,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
37
38
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
38
39
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } // oxlint-disable-next-line @atlassian/no-restricted-imports
39
40
  var packageName = "@atlaskit/datetime-picker";
40
- var packageVersion = "19.3.1";
41
+ var packageVersion = "19.4.0";
41
42
  var defaultTimeFormat = 'h:mma';
42
43
  var menuStyles = {
43
44
  /* Need to remove default absolute positioning as that causes issues with position fixed */
@@ -105,10 +106,7 @@ var TimePicker = /*#__PURE__*/(0, _react.forwardRef)(function (_ref2, _ref) {
105
106
  providedOnChange = _ref2$onChange === void 0 ? _noop.default : _ref2$onChange,
106
107
  _ref2$onFocus = _ref2.onFocus,
107
108
  providedOnFocus = _ref2$onFocus === void 0 ? _noop.default : _ref2$onFocus,
108
- _ref2$parseInputValue = _ref2.parseInputValue,
109
- parseInputValue = _ref2$parseInputValue === void 0 ? function (time, _timeFormat) {
110
- return (0, _parseTime.default)(time);
111
- } : _ref2$parseInputValue,
109
+ parseInputValue = _ref2.parseInputValue,
112
110
  placeholder = _ref2.placeholder,
113
111
  _ref2$selectProps = _ref2.selectProps,
114
112
  selectProps = _ref2$selectProps === void 0 ? {} : _ref2$selectProps,
@@ -153,6 +151,10 @@ var TimePicker = /*#__PURE__*/(0, _react.forwardRef)(function (_ref2, _ref) {
153
151
  _useState10 = (0, _slicedToArray2.default)(_useState1, 2),
154
152
  value = _useState10[0],
155
153
  setValue = _useState10[1];
154
+ var l10n = (0, _localizationProvider.createLocalizationProvider)(locale);
155
+ var localeTimeFormat = (0, _react.useMemo)(function () {
156
+ return (0, _fg.fg)('platform-dst-locale-aware-time-parsing') ? (0, _getTimeFormat.getTimeFormat)(locale) : undefined;
157
+ }, [locale]);
156
158
 
157
159
  // Hack to force update: https://legacy.reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate
158
160
  var _useReducer = (0, _react.useReducer)(function (x) {
@@ -199,7 +201,7 @@ var TimePicker = /*#__PURE__*/(0, _react.forwardRef)(function (_ref2, _ref) {
199
201
  if (timeIsEditable) {
200
202
  var sanitizedInput;
201
203
  try {
202
- sanitizedInput = parseInputValue(inputValue, timeFormat || defaultTimeFormat);
204
+ sanitizedInput = parseInputValue ? parseInputValue(inputValue, timeFormat || defaultTimeFormat) : (0, _parseTime.default)(inputValue, localeTimeFormat);
203
205
  } catch (_unused) {
204
206
  return; // do nothing, the main validation should happen in the form
205
207
  }
@@ -272,7 +274,6 @@ var TimePicker = /*#__PURE__*/(0, _react.forwardRef)(function (_ref2, _ref) {
272
274
  };
273
275
  var ICON_PADDING = 2;
274
276
  var GRID_SIZE = 8;
275
- var l10n = (0, _localizationProvider.createLocalizationProvider)(locale);
276
277
  var _selectProps$styles = selectProps.styles,
277
278
  selectStyles = _selectProps$styles === void 0 ? {} : _selectProps$styles,
278
279
  otherSelectProps = (0, _objectWithoutProperties2.default)(selectProps, _excluded);
@@ -8,12 +8,12 @@ function isValid(timeString) {
8
8
  /**
9
9
  * Regex match for `12:34`, `12:34:56`, `1:23:56 p`, `1:23:56PM`, and a bit more…
10
10
  */
11
- var time = timeString.trim().match(/([012]?[\d])(?::([0-5][\d]))?(?::([0-5][\d]))?\s*([ap]m?)?/i);
11
+ var time = timeString.trim().match(/^([012]?[\d])(?:[:.]?([0-5][\d]))?(?:[:.]?([0-5][\d]))?\s*([ap](?:\s*\.?\s*m\.?)?)?$/i);
12
12
 
13
13
  /**
14
14
  * Regex match for `1234`, `12:34`, `12.34`, `123456`, `12:34:56`, `12.34.56`
15
15
  */
16
- var time24hr = timeString.trim().match(/([012][\d])[:.]?([0-5][\d])([:.]?([0-5][\d]))?/);
16
+ var time24hr = timeString.trim().match(/^([012][\d])[:.]?([0-5][\d])([:.]?([0-5][\d]))?$/);
17
17
 
18
18
  /**
19
19
  * Convert `2:34:56 pm` down to `23456`
@@ -1,12 +1,17 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
6
7
  exports.default = parseTime;
8
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
7
10
  var _convertTo24hrTime = require("./convert-to24hr-time");
8
11
  var _isValid = require("./is-valid");
9
12
  var _removeSpacer = require("./remove-spacer");
13
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
14
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
10
15
  function assignToDate(time) {
11
16
  var dateTime = new Date();
12
17
  dateTime.setHours(time.hour);
@@ -15,8 +20,26 @@ function assignToDate(time) {
15
20
  dateTime.setSeconds(time.seconds || 0, 0);
16
21
  return dateTime;
17
22
  }
18
- function parseTime(time) {
23
+ function parseLegacyTime(time) {
24
+ var allowCompactTime = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
19
25
  var trimmedTime = time.toString().trim();
26
+ var compactTime = allowCompactTime ? trimmedTime.match(/^\d{5}$/) : null;
27
+ if (compactTime) {
28
+ var normalizedTime = "0".concat(compactTime[0]);
29
+ var hour = Number(normalizedTime.slice(0, 2));
30
+ var minute = Number(normalizedTime.slice(2, 4));
31
+ var seconds = Number(normalizedTime.slice(4, 6));
32
+ if (minute <= 59 && seconds <= 59) {
33
+ return assignToDate({
34
+ hour: hour,
35
+ minute: minute,
36
+ seconds: seconds
37
+ });
38
+ }
39
+ }
40
+ if (/^\d{5}$/.test(trimmedTime)) {
41
+ throw RangeError('invalid time format');
42
+ }
20
43
  if (!(0, _isValid.isValid)(trimmedTime)) {
21
44
  throw RangeError('invalid time format');
22
45
  }
@@ -25,5 +48,160 @@ function parseTime(time) {
25
48
  if (!time2) {
26
49
  throw RangeError('invalid time format');
27
50
  }
28
- return assignToDate(time2);
51
+ return assignToDate(_objectSpread(_objectSpread({}, time2), {}, {
52
+ hour: time2.hour === 24 ? 0 : time2.hour
53
+ }));
54
+ }
55
+ var escapeRegExp = function escapeRegExp(value) {
56
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
57
+ };
58
+ var normalizeDigits = function normalizeDigits(time, timeFormat) {
59
+ return Array.from(time, function (character) {
60
+ return timeFormat.digits[character] || character;
61
+ }).join('');
62
+ };
63
+ var isValidHourForCycle = function isValidHourForCycle(hour, hourCycle) {
64
+ return hourCycle === 'h11' ? hour <= 11 : hourCycle === 'h12' ? hour >= 1 && hour <= 12 : hour <= 24;
65
+ };
66
+ function parseExactLocalizedTime(time, timeFormat) {
67
+ if (!timeFormat.timeParts) {
68
+ return undefined;
69
+ }
70
+ var dayPeriods = Object.entries(timeFormat.dayPeriods).filter(function (_ref) {
71
+ var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
72
+ value = _ref2[1];
73
+ return value;
74
+ });
75
+ var groupTypes = [];
76
+ var patternParts = timeFormat.timeParts.map(function (part) {
77
+ if (part.type === 'hour' || part.type === 'minute' || part.type === 'second') {
78
+ groupTypes.push(part.type);
79
+ return part.type === 'hour' ? '(\\d{1,2})' : '(\\d{2})';
80
+ }
81
+ if (part.type === 'dayPeriod') {
82
+ if (dayPeriods.length === 0) {
83
+ return null;
84
+ }
85
+ groupTypes.push('dayPeriod');
86
+ return "(".concat(dayPeriods.map(function (_ref3) {
87
+ var _ref4 = (0, _slicedToArray2.default)(_ref3, 2),
88
+ value = _ref4[1];
89
+ return escapeRegExp(value);
90
+ }).join('|'), ")");
91
+ }
92
+ if (part.type === 'literal') {
93
+ return escapeRegExp(part.value);
94
+ }
95
+ return null;
96
+ });
97
+ if (patternParts.some(function (part) {
98
+ return part === null;
99
+ })) {
100
+ return undefined;
101
+ }
102
+ var match = time.match(new RegExp("^".concat(patternParts.join(''), "$"), 'u'));
103
+ if (!match) {
104
+ return undefined;
105
+ }
106
+ var values = Object.fromEntries(groupTypes.map(function (type, index) {
107
+ return [type, match[index + 1]];
108
+ }));
109
+ if (!values.hour || !values.minute) {
110
+ return undefined;
111
+ }
112
+ var hour = Number(values.hour);
113
+ var minute = Number(values.minute);
114
+ var seconds = Number(values.second || '0');
115
+ if (minute > 59 || seconds > 59) {
116
+ return null;
117
+ }
118
+ if (values.dayPeriod) {
119
+ var _dayPeriods$find;
120
+ var period = (_dayPeriods$find = dayPeriods.find(function (_ref5) {
121
+ var _ref6 = (0, _slicedToArray2.default)(_ref5, 2),
122
+ value = _ref6[1];
123
+ return value === values.dayPeriod;
124
+ })) === null || _dayPeriods$find === void 0 ? void 0 : _dayPeriods$find[0];
125
+ if (!period || !isValidHourForCycle(hour, timeFormat.hourCycle)) {
126
+ return null;
127
+ }
128
+ return assignToDate({
129
+ hour: period === 'pm' ? hour % 12 + 12 : hour % 12,
130
+ minute: minute,
131
+ seconds: seconds
132
+ });
133
+ }
134
+ if (hour > 24) {
135
+ return null;
136
+ }
137
+ return assignToDate({
138
+ hour: hour === 24 ? 0 : hour,
139
+ minute: minute,
140
+ seconds: seconds
141
+ });
142
+ }
143
+ function parseLocalizedTime(normalizedTime, timeFormat) {
144
+ var dayPeriods = Object.entries(timeFormat.dayPeriods).filter(function (_ref7) {
145
+ var _ref8 = (0, _slicedToArray2.default)(_ref7, 2),
146
+ value = _ref8[1];
147
+ return value;
148
+ });
149
+ var markers = dayPeriods.filter(function (_ref9) {
150
+ var _ref0 = (0, _slicedToArray2.default)(_ref9, 2),
151
+ value = _ref0[1];
152
+ return new RegExp(escapeRegExp(value), 'u').test(normalizedTime);
153
+ });
154
+ if (markers.length === 0) {
155
+ return undefined;
156
+ }
157
+ if (markers.length !== 1) {
158
+ return null;
159
+ }
160
+ var _markers = (0, _slicedToArray2.default)(markers, 1),
161
+ _markers$ = (0, _slicedToArray2.default)(_markers[0], 2),
162
+ period = _markers$[0],
163
+ marker = _markers$[1];
164
+ var numericTime = normalizedTime.replace(new RegExp(escapeRegExp(marker), 'u'), '').trim();
165
+ var match = numericTime.match(/^(\d{1,2})(?:[:.](\d{2}))?(?:[:.](\d{2}))?$/);
166
+ if (!match) {
167
+ return null;
168
+ }
169
+ var _match = (0, _slicedToArray2.default)(match, 4),
170
+ hourValue = _match[1],
171
+ _match$ = _match[2],
172
+ minuteValue = _match$ === void 0 ? '0' : _match$,
173
+ _match$2 = _match[3],
174
+ secondValue = _match$2 === void 0 ? '0' : _match$2;
175
+ var hour = Number(hourValue);
176
+ var minute = Number(minuteValue);
177
+ var seconds = Number(secondValue);
178
+ if (!isValidHourForCycle(hour, timeFormat.hourCycle) || minute > 59 || seconds > 59) {
179
+ return null;
180
+ }
181
+ return assignToDate({
182
+ hour: period === 'pm' ? hour % 12 + 12 : hour % 12,
183
+ minute: minute,
184
+ seconds: seconds
185
+ });
186
+ }
187
+ function parseTime(time, timeFormat) {
188
+ if (timeFormat) {
189
+ var normalizedTime = normalizeDigits(time, timeFormat);
190
+ var exactLocalizedTime = parseExactLocalizedTime(normalizedTime, timeFormat);
191
+ if (exactLocalizedTime instanceof Date) {
192
+ return exactLocalizedTime;
193
+ }
194
+ if (exactLocalizedTime === null) {
195
+ throw RangeError('invalid time format');
196
+ }
197
+ var localizedTime = parseLocalizedTime(normalizedTime, timeFormat);
198
+ if (localizedTime instanceof Date) {
199
+ return localizedTime;
200
+ }
201
+ if (localizedTime === null) {
202
+ throw RangeError('invalid time format');
203
+ }
204
+ return parseLegacyTime(normalizedTime, true);
205
+ }
206
+ return parseLegacyTime(time);
29
207
  }
@@ -32,7 +32,7 @@ import { parseDate } from '../internal/parse-date';
32
32
  import { PickerButtonContainer } from '../internal/picker-button-container';
33
33
  import { makeSingleValue } from '../internal/single-value';
34
34
  const packageName = "@atlaskit/datetime-picker";
35
- const packageVersion = "19.3.1";
35
+ const packageVersion = "19.4.0";
36
36
  const styles = {
37
37
  pickerContainerStyle: "_kqswh2mm",
38
38
  dropdownIndicatorStyles: "_1ul91k8s _1tke1k8s _1e0c1txw _4cvr1h6o _1bah1h6o",
@@ -22,7 +22,7 @@ import { convertTokens } from '../internal/parse-tokens';
22
22
  import DatePicker from './date-picker';
23
23
  import TimePicker from './time-picker';
24
24
  const packageName = "@atlaskit/datetime-picker";
25
- const packageVersion = "19.3.1";
25
+ const packageVersion = "19.4.0";
26
26
  const analyticsAttributes = {
27
27
  componentName: 'dateTimePicker',
28
28
  packageName,
@@ -70,6 +70,36 @@ const getStyles = (isInputMotionEnabled, roundedCorners) => ({
70
70
  }
71
71
  })
72
72
  });
73
+ const parseDateTimeValueWithOffset = value => {
74
+ const parsed = parseISO(value);
75
+ const zoneStart = value.endsWith('Z') ? value.length - 1 : Math.max(value.lastIndexOf('+'), value.lastIndexOf('-'));
76
+ if (!isValid(parsed) || value[10] !== 'T' || zoneStart <= 15) {
77
+ return undefined;
78
+ }
79
+ const zoneValue = value.slice(zoneStart);
80
+ if (!/^(?:Z|[+-]\d{2}(?::?\d{2})?)$/.test(zoneValue)) {
81
+ return undefined;
82
+ }
83
+ return {
84
+ dateValue: value.slice(0, 10),
85
+ timeValue: value.slice(11, 16),
86
+ zoneValue
87
+ };
88
+ };
89
+ const parseDateTimeValue = value => {
90
+ if (fg('platform-dst-dtp-preserve-offset')) {
91
+ const hasExplicitZone = value.endsWith('Z') || Math.max(value.lastIndexOf('+'), value.lastIndexOf('-')) > 15;
92
+ if (hasExplicitZone) {
93
+ return parseDateTimeValueWithOffset(value);
94
+ }
95
+ }
96
+ const parsed = parseISO(value);
97
+ return isValid(parsed) ? {
98
+ dateValue: format(parsed, convertTokens('YYYY-MM-DD')),
99
+ timeValue: format(parsed, convertTokens('HH:mm')),
100
+ zoneValue: format(parsed, convertTokens('ZZ'))
101
+ } : undefined;
102
+ };
73
103
 
74
104
  /**
75
105
  * Two action types keep the reducer focused:
@@ -160,12 +190,10 @@ const DateTimePicker = /*#__PURE__*/forwardRef(({
160
190
  zoneValue: state.zoneValue
161
191
  };
162
192
  }
163
- const parsed = parseISO(newValue);
164
- return isValid(parsed) ? {
193
+ const parsed = parseDateTimeValue(newValue);
194
+ return parsed ? {
165
195
  value: newValue,
166
- dateValue: format(parsed, convertTokens('YYYY-MM-DD')),
167
- timeValue: format(parsed, convertTokens('HH:mm')),
168
- zoneValue: format(parsed, convertTokens('ZZ'))
196
+ ...parsed
169
197
  } : {
170
198
  value: newValue,
171
199
  dateValue: '',
@@ -202,12 +230,10 @@ const DateTimePicker = /*#__PURE__*/forwardRef(({
202
230
  zoneValue: ''
203
231
  };
204
232
  }
205
- const parsed = parseISO(initialValue);
206
- return isValid(parsed) ? {
233
+ const parsed = parseDateTimeValue(initialValue);
234
+ return parsed ? {
207
235
  value: initialValue,
208
- dateValue: format(parsed, convertTokens('YYYY-MM-DD')),
209
- timeValue: format(parsed, convertTokens('HH:mm')),
210
- zoneValue: format(parsed, convertTokens('ZZ'))
236
+ ...parsed
211
237
  } : {
212
238
  value: initialValue,
213
239
  dateValue: initialDate,
@@ -259,12 +285,8 @@ const DateTimePicker = /*#__PURE__*/forwardRef(({
259
285
  zoneValue: providedZoneValue
260
286
  };
261
287
  }
262
- const parsed = parseISO(value);
263
- return isValid(parsed) ? {
264
- dateValue: format(parsed, convertTokens('YYYY-MM-DD')),
265
- timeValue: format(parsed, convertTokens('HH:mm')),
266
- zoneValue: format(parsed, convertTokens('ZZ'))
267
- } : {
288
+ const parsed = parseDateTimeValue(value);
289
+ return parsed || {
268
290
  dateValue: dtState.dateValue,
269
291
  timeValue: dtState.timeValue,
270
292
  zoneValue: dtState.zoneValue
@@ -2,7 +2,7 @@
2
2
  import _extends from "@babel/runtime/helpers/extends";
3
3
  import "./time-picker.compiled.css";
4
4
  import { ax, ix } from "@compiled/react/runtime";
5
- import React, { forwardRef, useCallback, useEffect, useId, useReducer, useState } from 'react';
5
+ import React, { forwardRef, useCallback, useEffect, useId, useMemo, useReducer, useState } from 'react';
6
6
 
7
7
  // oxlint-disable-next-line @atlassian/no-restricted-imports
8
8
  import { format, isValid } from 'date-fns';
@@ -10,6 +10,7 @@ import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next/usePlatfor
10
10
  import IconButton from '@atlaskit/button/icon/button';
11
11
  import __noop from '@atlaskit/ds-lib/noop';
12
12
  import ClockIcon from '@atlaskit/icon/core/clock';
13
+ import { getTimeFormat } from '@atlaskit/locale/get-time-format';
13
14
  import { createLocalizationProvider } from '@atlaskit/locale/localization-provider';
14
15
  import { fg } from '@atlaskit/platform-feature-flags/fg';
15
16
  import { mergeStyles } from '@atlaskit/react-select/styles';
@@ -24,7 +25,7 @@ import { PickerButtonContainer } from '../internal/picker-button-container';
24
25
  import { placeholderDatetime } from '../internal/placeholder-date-time';
25
26
  import { makeSingleValue } from '../internal/single-value';
26
27
  const packageName = "@atlaskit/datetime-picker";
27
- const packageVersion = "19.3.1";
28
+ const packageVersion = "19.4.0";
28
29
  const defaultTimeFormat = 'h:mma';
29
30
  const menuStyles = {
30
31
  /* Need to remove default absolute positioning as that causes issues with position fixed */
@@ -74,7 +75,7 @@ const TimePicker = /*#__PURE__*/forwardRef(({
74
75
  onBlur: providedOnBlur = __noop,
75
76
  onChange: providedOnChange = __noop,
76
77
  onFocus: providedOnFocus = __noop,
77
- parseInputValue = (time, _timeFormat) => parseTime(time),
78
+ parseInputValue,
78
79
  placeholder,
79
80
  selectProps = {},
80
81
  shouldShowTimeButton = false,
@@ -97,6 +98,8 @@ const TimePicker = /*#__PURE__*/forwardRef(({
97
98
  const [isOpen, setIsOpen] = useState(defaultIsOpen);
98
99
  const [shouldFocusTimeInput, setShouldFocusTimeInput] = useState(false);
99
100
  const [value, setValue] = useState(defaultValue);
101
+ const l10n = createLocalizationProvider(locale);
102
+ const localeTimeFormat = useMemo(() => fg('platform-dst-locale-aware-time-parsing') ? getTimeFormat(locale) : undefined, [locale]);
100
103
 
101
104
  // Hack to force update: https://legacy.reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate
102
105
  const [, forceUpdate] = useReducer(x => x + 1, 0);
@@ -140,7 +143,7 @@ const TimePicker = /*#__PURE__*/forwardRef(({
140
143
  if (timeIsEditable) {
141
144
  let sanitizedInput;
142
145
  try {
143
- sanitizedInput = parseInputValue(inputValue, timeFormat || defaultTimeFormat);
146
+ sanitizedInput = parseInputValue ? parseInputValue(inputValue, timeFormat || defaultTimeFormat) : parseTime(inputValue, localeTimeFormat);
144
147
  } catch {
145
148
  return; // do nothing, the main validation should happen in the form
146
149
  }
@@ -215,7 +218,6 @@ const TimePicker = /*#__PURE__*/forwardRef(({
215
218
  };
216
219
  const ICON_PADDING = 2;
217
220
  const GRID_SIZE = 8;
218
- const l10n = createLocalizationProvider(locale);
219
221
  const {
220
222
  styles: selectStyles = {},
221
223
  ...otherSelectProps
@@ -2,12 +2,12 @@ export function isValid(timeString) {
2
2
  /**
3
3
  * Regex match for `12:34`, `12:34:56`, `1:23:56 p`, `1:23:56PM`, and a bit more…
4
4
  */
5
- const time = timeString.trim().match(/([012]?[\d])(?::([0-5][\d]))?(?::([0-5][\d]))?\s*([ap]m?)?/i);
5
+ const time = timeString.trim().match(/^([012]?[\d])(?:[:.]?([0-5][\d]))?(?:[:.]?([0-5][\d]))?\s*([ap](?:\s*\.?\s*m\.?)?)?$/i);
6
6
 
7
7
  /**
8
8
  * Regex match for `1234`, `12:34`, `12.34`, `123456`, `12:34:56`, `12.34.56`
9
9
  */
10
- const time24hr = timeString.trim().match(/([012][\d])[:.]?([0-5][\d])([:.]?([0-5][\d]))?/);
10
+ const time24hr = timeString.trim().match(/^([012][\d])[:.]?([0-5][\d])([:.]?([0-5][\d]))?$/);
11
11
 
12
12
  /**
13
13
  * Convert `2:34:56 pm` down to `23456`
@@ -9,8 +9,25 @@ function assignToDate(time) {
9
9
  dateTime.setSeconds(time.seconds || 0, 0);
10
10
  return dateTime;
11
11
  }
12
- export default function parseTime(time) {
12
+ function parseLegacyTime(time, allowCompactTime = false) {
13
13
  const trimmedTime = time.toString().trim();
14
+ const compactTime = allowCompactTime ? trimmedTime.match(/^\d{5}$/) : null;
15
+ if (compactTime) {
16
+ const normalizedTime = `0${compactTime[0]}`;
17
+ const hour = Number(normalizedTime.slice(0, 2));
18
+ const minute = Number(normalizedTime.slice(2, 4));
19
+ const seconds = Number(normalizedTime.slice(4, 6));
20
+ if (minute <= 59 && seconds <= 59) {
21
+ return assignToDate({
22
+ hour,
23
+ minute,
24
+ seconds
25
+ });
26
+ }
27
+ }
28
+ if (/^\d{5}$/.test(trimmedTime)) {
29
+ throw RangeError('invalid time format');
30
+ }
14
31
  if (!isValid(trimmedTime)) {
15
32
  throw RangeError('invalid time format');
16
33
  }
@@ -19,5 +36,121 @@ export default function parseTime(time) {
19
36
  if (!time2) {
20
37
  throw RangeError('invalid time format');
21
38
  }
22
- return assignToDate(time2);
39
+ return assignToDate({
40
+ ...time2,
41
+ hour: time2.hour === 24 ? 0 : time2.hour
42
+ });
43
+ }
44
+ const escapeRegExp = value => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
45
+ const normalizeDigits = (time, timeFormat) => Array.from(time, character => timeFormat.digits[character] || character).join('');
46
+ const isValidHourForCycle = (hour, hourCycle) => hourCycle === 'h11' ? hour <= 11 : hourCycle === 'h12' ? hour >= 1 && hour <= 12 : hour <= 24;
47
+ function parseExactLocalizedTime(time, timeFormat) {
48
+ if (!timeFormat.timeParts) {
49
+ return undefined;
50
+ }
51
+ const dayPeriods = Object.entries(timeFormat.dayPeriods).filter(([, value]) => value);
52
+ const groupTypes = [];
53
+ const patternParts = timeFormat.timeParts.map(part => {
54
+ if (part.type === 'hour' || part.type === 'minute' || part.type === 'second') {
55
+ groupTypes.push(part.type);
56
+ return part.type === 'hour' ? '(\\d{1,2})' : '(\\d{2})';
57
+ }
58
+ if (part.type === 'dayPeriod') {
59
+ if (dayPeriods.length === 0) {
60
+ return null;
61
+ }
62
+ groupTypes.push('dayPeriod');
63
+ return `(${dayPeriods.map(([, value]) => escapeRegExp(value)).join('|')})`;
64
+ }
65
+ if (part.type === 'literal') {
66
+ return escapeRegExp(part.value);
67
+ }
68
+ return null;
69
+ });
70
+ if (patternParts.some(part => part === null)) {
71
+ return undefined;
72
+ }
73
+ const match = time.match(new RegExp(`^${patternParts.join('')}$`, 'u'));
74
+ if (!match) {
75
+ return undefined;
76
+ }
77
+ const values = Object.fromEntries(groupTypes.map((type, index) => [type, match[index + 1]]));
78
+ if (!values.hour || !values.minute) {
79
+ return undefined;
80
+ }
81
+ const hour = Number(values.hour);
82
+ const minute = Number(values.minute);
83
+ const seconds = Number(values.second || '0');
84
+ if (minute > 59 || seconds > 59) {
85
+ return null;
86
+ }
87
+ if (values.dayPeriod) {
88
+ var _dayPeriods$find;
89
+ const period = (_dayPeriods$find = dayPeriods.find(([, value]) => value === values.dayPeriod)) === null || _dayPeriods$find === void 0 ? void 0 : _dayPeriods$find[0];
90
+ if (!period || !isValidHourForCycle(hour, timeFormat.hourCycle)) {
91
+ return null;
92
+ }
93
+ return assignToDate({
94
+ hour: period === 'pm' ? hour % 12 + 12 : hour % 12,
95
+ minute,
96
+ seconds
97
+ });
98
+ }
99
+ if (hour > 24) {
100
+ return null;
101
+ }
102
+ return assignToDate({
103
+ hour: hour === 24 ? 0 : hour,
104
+ minute,
105
+ seconds
106
+ });
107
+ }
108
+ function parseLocalizedTime(normalizedTime, timeFormat) {
109
+ const dayPeriods = Object.entries(timeFormat.dayPeriods).filter(([, value]) => value);
110
+ const markers = dayPeriods.filter(([, value]) => new RegExp(escapeRegExp(value), 'u').test(normalizedTime));
111
+ if (markers.length === 0) {
112
+ return undefined;
113
+ }
114
+ if (markers.length !== 1) {
115
+ return null;
116
+ }
117
+ const [[period, marker]] = markers;
118
+ const numericTime = normalizedTime.replace(new RegExp(escapeRegExp(marker), 'u'), '').trim();
119
+ const match = numericTime.match(/^(\d{1,2})(?:[:.](\d{2}))?(?:[:.](\d{2}))?$/);
120
+ if (!match) {
121
+ return null;
122
+ }
123
+ const [, hourValue, minuteValue = '0', secondValue = '0'] = match;
124
+ const hour = Number(hourValue);
125
+ const minute = Number(minuteValue);
126
+ const seconds = Number(secondValue);
127
+ if (!isValidHourForCycle(hour, timeFormat.hourCycle) || minute > 59 || seconds > 59) {
128
+ return null;
129
+ }
130
+ return assignToDate({
131
+ hour: period === 'pm' ? hour % 12 + 12 : hour % 12,
132
+ minute,
133
+ seconds
134
+ });
135
+ }
136
+ export default function parseTime(time, timeFormat) {
137
+ if (timeFormat) {
138
+ const normalizedTime = normalizeDigits(time, timeFormat);
139
+ const exactLocalizedTime = parseExactLocalizedTime(normalizedTime, timeFormat);
140
+ if (exactLocalizedTime instanceof Date) {
141
+ return exactLocalizedTime;
142
+ }
143
+ if (exactLocalizedTime === null) {
144
+ throw RangeError('invalid time format');
145
+ }
146
+ const localizedTime = parseLocalizedTime(normalizedTime, timeFormat);
147
+ if (localizedTime instanceof Date) {
148
+ return localizedTime;
149
+ }
150
+ if (localizedTime === null) {
151
+ throw RangeError('invalid time format');
152
+ }
153
+ return parseLegacyTime(normalizedTime, true);
154
+ }
155
+ return parseLegacyTime(time);
23
156
  }
@@ -40,7 +40,7 @@ import { parseDate } from '../internal/parse-date';
40
40
  import { PickerButtonContainer } from '../internal/picker-button-container';
41
41
  import { makeSingleValue } from '../internal/single-value';
42
42
  var packageName = "@atlaskit/datetime-picker";
43
- var packageVersion = "19.3.1";
43
+ var packageVersion = "19.4.0";
44
44
  var styles = {
45
45
  pickerContainerStyle: "_kqswh2mm",
46
46
  dropdownIndicatorStyles: "_1ul91k8s _1tke1k8s _1e0c1txw _4cvr1h6o _1bah1h6o",
@@ -29,7 +29,7 @@ import { convertTokens } from '../internal/parse-tokens';
29
29
  import DatePicker from './date-picker';
30
30
  import TimePicker from './time-picker';
31
31
  var packageName = "@atlaskit/datetime-picker";
32
- var packageVersion = "19.3.1";
32
+ var packageVersion = "19.4.0";
33
33
  var analyticsAttributes = {
34
34
  componentName: 'dateTimePicker',
35
35
  packageName: packageName,
@@ -77,6 +77,36 @@ var getStyles = function getStyles(isInputMotionEnabled, roundedCorners) {
77
77
  }
78
78
  };
79
79
  };
80
+ var parseDateTimeValueWithOffset = function parseDateTimeValueWithOffset(value) {
81
+ var parsed = parseISO(value);
82
+ var zoneStart = value.endsWith('Z') ? value.length - 1 : Math.max(value.lastIndexOf('+'), value.lastIndexOf('-'));
83
+ if (!isValid(parsed) || value[10] !== 'T' || zoneStart <= 15) {
84
+ return undefined;
85
+ }
86
+ var zoneValue = value.slice(zoneStart);
87
+ if (!/^(?:Z|[+-]\d{2}(?::?\d{2})?)$/.test(zoneValue)) {
88
+ return undefined;
89
+ }
90
+ return {
91
+ dateValue: value.slice(0, 10),
92
+ timeValue: value.slice(11, 16),
93
+ zoneValue: zoneValue
94
+ };
95
+ };
96
+ var parseDateTimeValue = function parseDateTimeValue(value) {
97
+ if (fg('platform-dst-dtp-preserve-offset')) {
98
+ var hasExplicitZone = value.endsWith('Z') || Math.max(value.lastIndexOf('+'), value.lastIndexOf('-')) > 15;
99
+ if (hasExplicitZone) {
100
+ return parseDateTimeValueWithOffset(value);
101
+ }
102
+ }
103
+ var parsed = parseISO(value);
104
+ return isValid(parsed) ? {
105
+ dateValue: format(parsed, convertTokens('YYYY-MM-DD')),
106
+ timeValue: format(parsed, convertTokens('HH:mm')),
107
+ zoneValue: format(parsed, convertTokens('ZZ'))
108
+ } : undefined;
109
+ };
80
110
 
81
111
  /**
82
112
  * Two action types keep the reducer focused:
@@ -181,13 +211,10 @@ var DateTimePicker = /*#__PURE__*/forwardRef(function (_ref2, ref) {
181
211
  zoneValue: state.zoneValue
182
212
  };
183
213
  }
184
- var parsed = parseISO(newValue);
185
- return isValid(parsed) ? {
186
- value: newValue,
187
- dateValue: format(parsed, convertTokens('YYYY-MM-DD')),
188
- timeValue: format(parsed, convertTokens('HH:mm')),
189
- zoneValue: format(parsed, convertTokens('ZZ'))
190
- } : {
214
+ var parsed = parseDateTimeValue(newValue);
215
+ return parsed ? _objectSpread({
216
+ value: newValue
217
+ }, parsed) : {
191
218
  value: newValue,
192
219
  dateValue: '',
193
220
  timeValue: '',
@@ -222,13 +249,10 @@ var DateTimePicker = /*#__PURE__*/forwardRef(function (_ref2, ref) {
222
249
  zoneValue: ''
223
250
  };
224
251
  }
225
- var parsed = parseISO(initialValue);
226
- return isValid(parsed) ? {
227
- value: initialValue,
228
- dateValue: format(parsed, convertTokens('YYYY-MM-DD')),
229
- timeValue: format(parsed, convertTokens('HH:mm')),
230
- zoneValue: format(parsed, convertTokens('ZZ'))
231
- } : {
252
+ var parsed = parseDateTimeValue(initialValue);
253
+ return parsed ? _objectSpread({
254
+ value: initialValue
255
+ }, parsed) : {
232
256
  value: initialValue,
233
257
  dateValue: initialDate,
234
258
  timeValue: initialTime,
@@ -282,12 +306,8 @@ var DateTimePicker = /*#__PURE__*/forwardRef(function (_ref2, ref) {
282
306
  zoneValue: providedZoneValue
283
307
  };
284
308
  }
285
- var parsed = parseISO(value);
286
- return isValid(parsed) ? {
287
- dateValue: format(parsed, convertTokens('YYYY-MM-DD')),
288
- timeValue: format(parsed, convertTokens('HH:mm')),
289
- zoneValue: format(parsed, convertTokens('ZZ'))
290
- } : {
309
+ var parsed = parseDateTimeValue(value);
310
+ return parsed || {
291
311
  dateValue: dtState.dateValue,
292
312
  timeValue: dtState.timeValue,
293
313
  zoneValue: dtState.zoneValue
@@ -8,7 +8,7 @@ import "./time-picker.compiled.css";
8
8
  import { ax, ix } from "@compiled/react/runtime";
9
9
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
10
10
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
11
- import React, { forwardRef, useCallback, useEffect, useId, useReducer, useState } from 'react';
11
+ import React, { forwardRef, useCallback, useEffect, useId, useMemo, useReducer, useState } from 'react';
12
12
 
13
13
  // oxlint-disable-next-line @atlassian/no-restricted-imports
14
14
  import { format, isValid } from 'date-fns';
@@ -16,6 +16,7 @@ import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next/usePlatfor
16
16
  import IconButton from '@atlaskit/button/icon/button';
17
17
  import __noop from '@atlaskit/ds-lib/noop';
18
18
  import ClockIcon from '@atlaskit/icon/core/clock';
19
+ import { getTimeFormat } from '@atlaskit/locale/get-time-format';
19
20
  import { createLocalizationProvider } from '@atlaskit/locale/localization-provider';
20
21
  import { fg } from '@atlaskit/platform-feature-flags/fg';
21
22
  import { mergeStyles } from '@atlaskit/react-select/styles';
@@ -30,7 +31,7 @@ import { PickerButtonContainer } from '../internal/picker-button-container';
30
31
  import { placeholderDatetime } from '../internal/placeholder-date-time';
31
32
  import { makeSingleValue } from '../internal/single-value';
32
33
  var packageName = "@atlaskit/datetime-picker";
33
- var packageVersion = "19.3.1";
34
+ var packageVersion = "19.4.0";
34
35
  var defaultTimeFormat = 'h:mma';
35
36
  var menuStyles = {
36
37
  /* Need to remove default absolute positioning as that causes issues with position fixed */
@@ -98,10 +99,7 @@ var TimePicker = /*#__PURE__*/forwardRef(function (_ref2, _ref) {
98
99
  providedOnChange = _ref2$onChange === void 0 ? __noop : _ref2$onChange,
99
100
  _ref2$onFocus = _ref2.onFocus,
100
101
  providedOnFocus = _ref2$onFocus === void 0 ? __noop : _ref2$onFocus,
101
- _ref2$parseInputValue = _ref2.parseInputValue,
102
- parseInputValue = _ref2$parseInputValue === void 0 ? function (time, _timeFormat) {
103
- return parseTime(time);
104
- } : _ref2$parseInputValue,
102
+ parseInputValue = _ref2.parseInputValue,
105
103
  placeholder = _ref2.placeholder,
106
104
  _ref2$selectProps = _ref2.selectProps,
107
105
  selectProps = _ref2$selectProps === void 0 ? {} : _ref2$selectProps,
@@ -146,6 +144,10 @@ var TimePicker = /*#__PURE__*/forwardRef(function (_ref2, _ref) {
146
144
  _useState10 = _slicedToArray(_useState1, 2),
147
145
  value = _useState10[0],
148
146
  setValue = _useState10[1];
147
+ var l10n = createLocalizationProvider(locale);
148
+ var localeTimeFormat = useMemo(function () {
149
+ return fg('platform-dst-locale-aware-time-parsing') ? getTimeFormat(locale) : undefined;
150
+ }, [locale]);
149
151
 
150
152
  // Hack to force update: https://legacy.reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate
151
153
  var _useReducer = useReducer(function (x) {
@@ -192,7 +194,7 @@ var TimePicker = /*#__PURE__*/forwardRef(function (_ref2, _ref) {
192
194
  if (timeIsEditable) {
193
195
  var sanitizedInput;
194
196
  try {
195
- sanitizedInput = parseInputValue(inputValue, timeFormat || defaultTimeFormat);
197
+ sanitizedInput = parseInputValue ? parseInputValue(inputValue, timeFormat || defaultTimeFormat) : parseTime(inputValue, localeTimeFormat);
196
198
  } catch (_unused) {
197
199
  return; // do nothing, the main validation should happen in the form
198
200
  }
@@ -265,7 +267,6 @@ var TimePicker = /*#__PURE__*/forwardRef(function (_ref2, _ref) {
265
267
  };
266
268
  var ICON_PADDING = 2;
267
269
  var GRID_SIZE = 8;
268
- var l10n = createLocalizationProvider(locale);
269
270
  var _selectProps$styles = selectProps.styles,
270
271
  selectStyles = _selectProps$styles === void 0 ? {} : _selectProps$styles,
271
272
  otherSelectProps = _objectWithoutProperties(selectProps, _excluded);
@@ -2,12 +2,12 @@ export function isValid(timeString) {
2
2
  /**
3
3
  * Regex match for `12:34`, `12:34:56`, `1:23:56 p`, `1:23:56PM`, and a bit more…
4
4
  */
5
- var time = timeString.trim().match(/([012]?[\d])(?::([0-5][\d]))?(?::([0-5][\d]))?\s*([ap]m?)?/i);
5
+ var time = timeString.trim().match(/^([012]?[\d])(?:[:.]?([0-5][\d]))?(?:[:.]?([0-5][\d]))?\s*([ap](?:\s*\.?\s*m\.?)?)?$/i);
6
6
 
7
7
  /**
8
8
  * Regex match for `1234`, `12:34`, `12.34`, `123456`, `12:34:56`, `12.34.56`
9
9
  */
10
- var time24hr = timeString.trim().match(/([012][\d])[:.]?([0-5][\d])([:.]?([0-5][\d]))?/);
10
+ var time24hr = timeString.trim().match(/^([012][\d])[:.]?([0-5][\d])([:.]?([0-5][\d]))?$/);
11
11
 
12
12
  /**
13
13
  * Convert `2:34:56 pm` down to `23456`
@@ -1,3 +1,7 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
1
5
  import { convertTo24hrTime } from './convert-to24hr-time';
2
6
  import { isValid } from './is-valid';
3
7
  import { removeSpacer } from './remove-spacer';
@@ -9,8 +13,26 @@ function assignToDate(time) {
9
13
  dateTime.setSeconds(time.seconds || 0, 0);
10
14
  return dateTime;
11
15
  }
12
- export default function parseTime(time) {
16
+ function parseLegacyTime(time) {
17
+ var allowCompactTime = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
13
18
  var trimmedTime = time.toString().trim();
19
+ var compactTime = allowCompactTime ? trimmedTime.match(/^\d{5}$/) : null;
20
+ if (compactTime) {
21
+ var normalizedTime = "0".concat(compactTime[0]);
22
+ var hour = Number(normalizedTime.slice(0, 2));
23
+ var minute = Number(normalizedTime.slice(2, 4));
24
+ var seconds = Number(normalizedTime.slice(4, 6));
25
+ if (minute <= 59 && seconds <= 59) {
26
+ return assignToDate({
27
+ hour: hour,
28
+ minute: minute,
29
+ seconds: seconds
30
+ });
31
+ }
32
+ }
33
+ if (/^\d{5}$/.test(trimmedTime)) {
34
+ throw RangeError('invalid time format');
35
+ }
14
36
  if (!isValid(trimmedTime)) {
15
37
  throw RangeError('invalid time format');
16
38
  }
@@ -19,5 +41,160 @@ export default function parseTime(time) {
19
41
  if (!time2) {
20
42
  throw RangeError('invalid time format');
21
43
  }
22
- return assignToDate(time2);
44
+ return assignToDate(_objectSpread(_objectSpread({}, time2), {}, {
45
+ hour: time2.hour === 24 ? 0 : time2.hour
46
+ }));
47
+ }
48
+ var escapeRegExp = function escapeRegExp(value) {
49
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
50
+ };
51
+ var normalizeDigits = function normalizeDigits(time, timeFormat) {
52
+ return Array.from(time, function (character) {
53
+ return timeFormat.digits[character] || character;
54
+ }).join('');
55
+ };
56
+ var isValidHourForCycle = function isValidHourForCycle(hour, hourCycle) {
57
+ return hourCycle === 'h11' ? hour <= 11 : hourCycle === 'h12' ? hour >= 1 && hour <= 12 : hour <= 24;
58
+ };
59
+ function parseExactLocalizedTime(time, timeFormat) {
60
+ if (!timeFormat.timeParts) {
61
+ return undefined;
62
+ }
63
+ var dayPeriods = Object.entries(timeFormat.dayPeriods).filter(function (_ref) {
64
+ var _ref2 = _slicedToArray(_ref, 2),
65
+ value = _ref2[1];
66
+ return value;
67
+ });
68
+ var groupTypes = [];
69
+ var patternParts = timeFormat.timeParts.map(function (part) {
70
+ if (part.type === 'hour' || part.type === 'minute' || part.type === 'second') {
71
+ groupTypes.push(part.type);
72
+ return part.type === 'hour' ? '(\\d{1,2})' : '(\\d{2})';
73
+ }
74
+ if (part.type === 'dayPeriod') {
75
+ if (dayPeriods.length === 0) {
76
+ return null;
77
+ }
78
+ groupTypes.push('dayPeriod');
79
+ return "(".concat(dayPeriods.map(function (_ref3) {
80
+ var _ref4 = _slicedToArray(_ref3, 2),
81
+ value = _ref4[1];
82
+ return escapeRegExp(value);
83
+ }).join('|'), ")");
84
+ }
85
+ if (part.type === 'literal') {
86
+ return escapeRegExp(part.value);
87
+ }
88
+ return null;
89
+ });
90
+ if (patternParts.some(function (part) {
91
+ return part === null;
92
+ })) {
93
+ return undefined;
94
+ }
95
+ var match = time.match(new RegExp("^".concat(patternParts.join(''), "$"), 'u'));
96
+ if (!match) {
97
+ return undefined;
98
+ }
99
+ var values = Object.fromEntries(groupTypes.map(function (type, index) {
100
+ return [type, match[index + 1]];
101
+ }));
102
+ if (!values.hour || !values.minute) {
103
+ return undefined;
104
+ }
105
+ var hour = Number(values.hour);
106
+ var minute = Number(values.minute);
107
+ var seconds = Number(values.second || '0');
108
+ if (minute > 59 || seconds > 59) {
109
+ return null;
110
+ }
111
+ if (values.dayPeriod) {
112
+ var _dayPeriods$find;
113
+ var period = (_dayPeriods$find = dayPeriods.find(function (_ref5) {
114
+ var _ref6 = _slicedToArray(_ref5, 2),
115
+ value = _ref6[1];
116
+ return value === values.dayPeriod;
117
+ })) === null || _dayPeriods$find === void 0 ? void 0 : _dayPeriods$find[0];
118
+ if (!period || !isValidHourForCycle(hour, timeFormat.hourCycle)) {
119
+ return null;
120
+ }
121
+ return assignToDate({
122
+ hour: period === 'pm' ? hour % 12 + 12 : hour % 12,
123
+ minute: minute,
124
+ seconds: seconds
125
+ });
126
+ }
127
+ if (hour > 24) {
128
+ return null;
129
+ }
130
+ return assignToDate({
131
+ hour: hour === 24 ? 0 : hour,
132
+ minute: minute,
133
+ seconds: seconds
134
+ });
135
+ }
136
+ function parseLocalizedTime(normalizedTime, timeFormat) {
137
+ var dayPeriods = Object.entries(timeFormat.dayPeriods).filter(function (_ref7) {
138
+ var _ref8 = _slicedToArray(_ref7, 2),
139
+ value = _ref8[1];
140
+ return value;
141
+ });
142
+ var markers = dayPeriods.filter(function (_ref9) {
143
+ var _ref0 = _slicedToArray(_ref9, 2),
144
+ value = _ref0[1];
145
+ return new RegExp(escapeRegExp(value), 'u').test(normalizedTime);
146
+ });
147
+ if (markers.length === 0) {
148
+ return undefined;
149
+ }
150
+ if (markers.length !== 1) {
151
+ return null;
152
+ }
153
+ var _markers = _slicedToArray(markers, 1),
154
+ _markers$ = _slicedToArray(_markers[0], 2),
155
+ period = _markers$[0],
156
+ marker = _markers$[1];
157
+ var numericTime = normalizedTime.replace(new RegExp(escapeRegExp(marker), 'u'), '').trim();
158
+ var match = numericTime.match(/^(\d{1,2})(?:[:.](\d{2}))?(?:[:.](\d{2}))?$/);
159
+ if (!match) {
160
+ return null;
161
+ }
162
+ var _match = _slicedToArray(match, 4),
163
+ hourValue = _match[1],
164
+ _match$ = _match[2],
165
+ minuteValue = _match$ === void 0 ? '0' : _match$,
166
+ _match$2 = _match[3],
167
+ secondValue = _match$2 === void 0 ? '0' : _match$2;
168
+ var hour = Number(hourValue);
169
+ var minute = Number(minuteValue);
170
+ var seconds = Number(secondValue);
171
+ if (!isValidHourForCycle(hour, timeFormat.hourCycle) || minute > 59 || seconds > 59) {
172
+ return null;
173
+ }
174
+ return assignToDate({
175
+ hour: period === 'pm' ? hour % 12 + 12 : hour % 12,
176
+ minute: minute,
177
+ seconds: seconds
178
+ });
179
+ }
180
+ export default function parseTime(time, timeFormat) {
181
+ if (timeFormat) {
182
+ var normalizedTime = normalizeDigits(time, timeFormat);
183
+ var exactLocalizedTime = parseExactLocalizedTime(normalizedTime, timeFormat);
184
+ if (exactLocalizedTime instanceof Date) {
185
+ return exactLocalizedTime;
186
+ }
187
+ if (exactLocalizedTime === null) {
188
+ throw RangeError('invalid time format');
189
+ }
190
+ var localizedTime = parseLocalizedTime(normalizedTime, timeFormat);
191
+ if (localizedTime instanceof Date) {
192
+ return localizedTime;
193
+ }
194
+ if (localizedTime === null) {
195
+ throw RangeError('invalid time format');
196
+ }
197
+ return parseLegacyTime(normalizedTime, true);
198
+ }
199
+ return parseLegacyTime(time);
23
200
  }
@@ -1 +1,2 @@
1
- export default function parseTime(time: string): string | Date;
1
+ import type { TimeFormat } from '@atlaskit/locale/get-time-format';
2
+ export default function parseTime(time: string, timeFormat?: TimeFormat): string | Date;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/datetime-picker",
3
- "version": "19.3.2",
3
+ "version": "19.4.1",
4
4
  "description": "A date time picker allows the user to select an associated date and time.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -35,21 +35,21 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@atlaskit/analytics-next": "^12.6.0",
38
- "@atlaskit/button": "^25.4.0",
38
+ "@atlaskit/button": "^25.6.0",
39
39
  "@atlaskit/calendar": "^19.5.0",
40
40
  "@atlaskit/css": "^1.1.0",
41
41
  "@atlaskit/ds-lib": "^8.0.0",
42
42
  "@atlaskit/icon": "^38.0.0",
43
- "@atlaskit/layering": "^6.0.0",
44
- "@atlaskit/locale": "^5.4.0",
43
+ "@atlaskit/layering": "^6.1.0",
44
+ "@atlaskit/locale": "^5.5.0",
45
45
  "@atlaskit/platform-feature-flags": "^2.2.0",
46
46
  "@atlaskit/popper": "^9.4.0",
47
- "@atlaskit/primitives": "^22.5.0",
47
+ "@atlaskit/primitives": "^22.6.0",
48
48
  "@atlaskit/react-compiler-gating": "^0.2.0",
49
49
  "@atlaskit/react-select": "^4.12.0",
50
50
  "@atlaskit/select": "^23.1.0",
51
- "@atlaskit/tokens": "^20.4.0",
52
- "@atlaskit/top-layer": "^6.2.0",
51
+ "@atlaskit/tokens": "^20.7.0",
52
+ "@atlaskit/top-layer": "^6.7.0",
53
53
  "@babel/runtime": "^7.0.0",
54
54
  "@compiled/react": "^1.0.2",
55
55
  "date-fns": "^2.17.0"
@@ -67,8 +67,9 @@
67
67
  "@atlaskit/form": "^18.2.0",
68
68
  "@atlaskit/heading": "^8.0.0",
69
69
  "@atlaskit/link": "^5.1.0",
70
- "@atlaskit/modal-dialog": "^17.0.0",
71
- "@atlaskit/popup": "^9.0.0",
70
+ "@atlaskit/menu": "^11.0.0",
71
+ "@atlaskit/modal-dialog": "^17.1.0",
72
+ "@atlaskit/popup": "^10.0.0",
72
73
  "@atlaskit/range": "^12.2.0",
73
74
  "@atlaskit/section-message": "^10.2.0",
74
75
  "@atlaskit/textfield": "^10.2.0",
@@ -91,9 +92,15 @@
91
92
  "platform-dst-top-layer": {
92
93
  "type": "boolean"
93
94
  },
95
+ "platform-dst-dtp-preserve-offset": {
96
+ "type": "boolean"
97
+ },
94
98
  "platform-dst-motion-uplift-input": {
95
99
  "type": "boolean",
96
100
  "referenceOnly": true
101
+ },
102
+ "platform-dst-locale-aware-time-parsing": {
103
+ "type": "boolean"
97
104
  }
98
105
  },
99
106
  "homepage": "https://atlassian.design/components/datetime-picker/"