@contentful/field-editor-date 2.0.8-canary.2 → 2.0.8
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/dist/cjs/DatepickerInput.js +10 -2
- package/dist/cjs/TimepickerInput.js +48 -19
- package/dist/cjs/utils/data.spec.js +9 -336
- package/dist/cjs/utils/date.js +38 -68
- package/dist/esm/DatepickerInput.js +5 -2
- package/dist/esm/TimepickerInput.js +43 -19
- package/dist/esm/utils/data.spec.js +5 -337
- package/dist/esm/utils/date.js +33 -68
- package/dist/types/DatepickerInput.d.ts +3 -2
- package/dist/types/types.d.ts +2 -1
- package/dist/types/utils/date.d.ts +1 -4
- package/package.json +3 -3
|
@@ -11,6 +11,12 @@ Object.defineProperty(exports, "DatepickerInput", {
|
|
|
11
11
|
const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
|
|
12
12
|
const _f36components = require("@contentful/f36-components");
|
|
13
13
|
const _css = require("@emotion/css");
|
|
14
|
+
const _moment = /*#__PURE__*/ _interop_require_default(require("moment"));
|
|
15
|
+
function _interop_require_default(obj) {
|
|
16
|
+
return obj && obj.__esModule ? obj : {
|
|
17
|
+
default: obj
|
|
18
|
+
};
|
|
19
|
+
}
|
|
14
20
|
function _getRequireWildcardCache(nodeInterop) {
|
|
15
21
|
if (typeof WeakMap !== "function") return null;
|
|
16
22
|
var cacheBabelInterop = new WeakMap();
|
|
@@ -69,12 +75,14 @@ const DatepickerInput = (props)=>{
|
|
|
69
75
|
toDate
|
|
70
76
|
];
|
|
71
77
|
}, []);
|
|
72
|
-
const
|
|
78
|
+
const dateObj = props.value?.toObject();
|
|
79
|
+
const selectedDate = dateObj ? new Date(dateObj.years, dateObj.months, dateObj.date) : undefined;
|
|
73
80
|
return /*#__PURE__*/ _react.default.createElement(_f36components.Datepicker, {
|
|
74
81
|
className: styles.root,
|
|
75
82
|
selected: selectedDate,
|
|
76
83
|
onSelect: (day)=>{
|
|
77
|
-
|
|
84
|
+
const momentDay = day ? (0, _moment.default)(day) : undefined;
|
|
85
|
+
props.onChange(momentDay);
|
|
78
86
|
},
|
|
79
87
|
inputProps: {
|
|
80
88
|
isDisabled: props.disabled,
|
|
@@ -11,7 +11,12 @@ Object.defineProperty(exports, "TimepickerInput", {
|
|
|
11
11
|
const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
|
|
12
12
|
const _f36components = require("@contentful/f36-components");
|
|
13
13
|
const _css = require("@emotion/css");
|
|
14
|
-
const
|
|
14
|
+
const _moment = /*#__PURE__*/ _interop_require_default(require("moment"));
|
|
15
|
+
function _interop_require_default(obj) {
|
|
16
|
+
return obj && obj.__esModule ? obj : {
|
|
17
|
+
default: obj
|
|
18
|
+
};
|
|
19
|
+
}
|
|
15
20
|
function _getRequireWildcardCache(nodeInterop) {
|
|
16
21
|
if (typeof WeakMap !== "function") return null;
|
|
17
22
|
var cacheBabelInterop = new WeakMap();
|
|
@@ -55,52 +60,76 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
55
60
|
}
|
|
56
61
|
const validInputFormats = [
|
|
57
62
|
'hh:mm a',
|
|
63
|
+
'hh:mm A',
|
|
58
64
|
'h:mm a',
|
|
65
|
+
'h:mm A',
|
|
59
66
|
'hh:mm',
|
|
60
|
-
'kk:mm',
|
|
61
67
|
'k:mm',
|
|
68
|
+
'kk:mm',
|
|
62
69
|
'h a',
|
|
63
|
-
'
|
|
64
|
-
'
|
|
65
|
-
'
|
|
70
|
+
'h A',
|
|
71
|
+
'h',
|
|
72
|
+
'hh',
|
|
73
|
+
'HH'
|
|
66
74
|
];
|
|
67
|
-
const REF_DATE = new Date(2000, 0, 1);
|
|
68
75
|
function parseRawInput(raw) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
let time = null;
|
|
77
|
+
for(let i = 0; i < validInputFormats.length; i++){
|
|
78
|
+
const date = (0, _moment.default)(raw, validInputFormats[i]);
|
|
79
|
+
if (date.isValid()) {
|
|
80
|
+
time = date;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
72
83
|
}
|
|
73
|
-
return
|
|
84
|
+
return time;
|
|
74
85
|
}
|
|
75
|
-
const getDefaultTime = ()=>
|
|
76
|
-
|
|
86
|
+
const getDefaultTime = ()=>{
|
|
87
|
+
return (0, _moment.default)(`12:00 AM`, 'hh:mm A');
|
|
88
|
+
};
|
|
89
|
+
const formatToString = (uses12hClock, value)=>{
|
|
90
|
+
return uses12hClock ? value.format('hh:mm A') : value.format('HH:mm');
|
|
91
|
+
};
|
|
77
92
|
const TimepickerInput = ({ disabled, uses12hClock, time = '12:00', ampm = 'AM', onChange })=>{
|
|
78
93
|
const [selectedTime, setSelectedTime] = (0, _react.useState)(()=>{
|
|
79
94
|
return formatToString(uses12hClock, getDefaultTime());
|
|
80
95
|
});
|
|
81
96
|
(0, _react.useEffect)(()=>{
|
|
82
|
-
setSelectedTime(formatToString(uses12hClock, (0,
|
|
97
|
+
setSelectedTime(formatToString(uses12hClock, (0, _moment.default)(`${time} ${ampm}`, 'hh:mm A')));
|
|
83
98
|
}, [
|
|
84
99
|
time,
|
|
85
100
|
ampm,
|
|
86
101
|
uses12hClock
|
|
87
102
|
]);
|
|
88
103
|
const handleChange = (0, _react.useCallback)((e)=>{
|
|
89
|
-
|
|
90
|
-
|
|
104
|
+
const raw = e.currentTarget.value;
|
|
105
|
+
setSelectedTime(raw);
|
|
106
|
+
const parsedTime = parseRawInput(raw);
|
|
107
|
+
if (parsedTime) {
|
|
108
|
+
onChange({
|
|
109
|
+
time: parsedTime.format('hh:mm'),
|
|
110
|
+
ampm: parsedTime.format('A')
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}, [
|
|
114
|
+
onChange
|
|
115
|
+
]);
|
|
91
116
|
const handleFocus = (0, _react.useCallback)((e)=>{
|
|
92
117
|
e.preventDefault();
|
|
93
118
|
e.target.select();
|
|
94
119
|
}, []);
|
|
95
|
-
const handleBlur = ()=>{
|
|
120
|
+
const handleBlur = (0, _react.useCallback)(()=>{
|
|
96
121
|
const parsedTime = parseRawInput(selectedTime);
|
|
97
122
|
const value = parsedTime ?? getDefaultTime();
|
|
98
123
|
setSelectedTime(formatToString(uses12hClock, value));
|
|
99
124
|
onChange({
|
|
100
|
-
time:
|
|
101
|
-
ampm:
|
|
125
|
+
time: value.format('hh:mm'),
|
|
126
|
+
ampm: value.format('A')
|
|
102
127
|
});
|
|
103
|
-
}
|
|
128
|
+
}, [
|
|
129
|
+
selectedTime,
|
|
130
|
+
uses12hClock,
|
|
131
|
+
onChange
|
|
132
|
+
]);
|
|
104
133
|
return /*#__PURE__*/ _react.default.createElement(_f36components.Flex, {
|
|
105
134
|
className: (0, _css.css)({
|
|
106
135
|
width: '145px'
|
|
@@ -2,13 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
+
const _moment = /*#__PURE__*/ _interop_require_default(require("moment"));
|
|
5
6
|
const _date = require("./date");
|
|
7
|
+
function _interop_require_default(obj) {
|
|
8
|
+
return obj && obj.__esModule ? obj : {
|
|
9
|
+
default: obj
|
|
10
|
+
};
|
|
11
|
+
}
|
|
6
12
|
describe('date utils', ()=>{
|
|
7
13
|
describe('buildFieldValue', ()=>{
|
|
8
14
|
it('should work properly', ()=>{
|
|
9
15
|
expect((0, _date.buildFieldValue)({
|
|
10
16
|
data: {
|
|
11
|
-
date:
|
|
17
|
+
date: (0, _moment.default)('2018-02-02'),
|
|
12
18
|
time: '05:00',
|
|
13
19
|
ampm: 'PM',
|
|
14
20
|
utcOffset: '+03:00'
|
|
@@ -21,7 +27,7 @@ describe('date utils', ()=>{
|
|
|
21
27
|
});
|
|
22
28
|
expect((0, _date.buildFieldValue)({
|
|
23
29
|
data: {
|
|
24
|
-
date:
|
|
30
|
+
date: (0, _moment.default)('2015-01-14'),
|
|
25
31
|
time: '05:00',
|
|
26
32
|
ampm: 'AM',
|
|
27
33
|
utcOffset: '-05:00'
|
|
@@ -34,7 +40,7 @@ describe('date utils', ()=>{
|
|
|
34
40
|
});
|
|
35
41
|
expect((0, _date.buildFieldValue)({
|
|
36
42
|
data: {
|
|
37
|
-
date:
|
|
43
|
+
date: (0, _moment.default)('2015-01-14'),
|
|
38
44
|
time: '17:00',
|
|
39
45
|
ampm: 'PM',
|
|
40
46
|
utcOffset: '-05:00'
|
|
@@ -46,338 +52,5 @@ describe('date utils', ()=>{
|
|
|
46
52
|
valid: '2015-01-14T17:00-05:00'
|
|
47
53
|
});
|
|
48
54
|
});
|
|
49
|
-
it('returns date-only format when usesTime and usesTimezone are false', ()=>{
|
|
50
|
-
expect((0, _date.buildFieldValue)({
|
|
51
|
-
data: {
|
|
52
|
-
date: new Date('2020-06-15'),
|
|
53
|
-
time: '10:30',
|
|
54
|
-
ampm: 'AM',
|
|
55
|
-
utcOffset: '+00:00'
|
|
56
|
-
},
|
|
57
|
-
usesTimezone: false,
|
|
58
|
-
usesTime: false
|
|
59
|
-
})).toEqual({
|
|
60
|
-
invalid: false,
|
|
61
|
-
valid: '2020-06-15'
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
it('returns datetime without timezone when usesTime=true, usesTimezone=false', ()=>{
|
|
65
|
-
expect((0, _date.buildFieldValue)({
|
|
66
|
-
data: {
|
|
67
|
-
date: new Date('2020-06-15'),
|
|
68
|
-
time: '14:30',
|
|
69
|
-
ampm: 'PM',
|
|
70
|
-
utcOffset: '+00:00'
|
|
71
|
-
},
|
|
72
|
-
usesTimezone: false,
|
|
73
|
-
usesTime: true
|
|
74
|
-
})).toEqual({
|
|
75
|
-
invalid: false,
|
|
76
|
-
valid: '2020-06-15T14:30'
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
it('returns valid: null when no date is provided', ()=>{
|
|
80
|
-
expect((0, _date.buildFieldValue)({
|
|
81
|
-
data: {
|
|
82
|
-
time: '10:00',
|
|
83
|
-
ampm: 'AM',
|
|
84
|
-
utcOffset: '+00:00'
|
|
85
|
-
},
|
|
86
|
-
usesTimezone: true,
|
|
87
|
-
usesTime: true
|
|
88
|
-
})).toEqual({
|
|
89
|
-
invalid: false,
|
|
90
|
-
valid: null
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
it('handles midnight (12:00 AM) correctly', ()=>{
|
|
94
|
-
expect((0, _date.buildFieldValue)({
|
|
95
|
-
data: {
|
|
96
|
-
date: new Date('2021-03-01'),
|
|
97
|
-
time: '12:00',
|
|
98
|
-
ampm: 'AM',
|
|
99
|
-
utcOffset: '+00:00'
|
|
100
|
-
},
|
|
101
|
-
usesTimezone: true,
|
|
102
|
-
usesTime: true
|
|
103
|
-
})).toEqual({
|
|
104
|
-
invalid: false,
|
|
105
|
-
valid: '2021-03-01T00:00+00:00'
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
it('handles noon (12:00 PM) correctly', ()=>{
|
|
109
|
-
expect((0, _date.buildFieldValue)({
|
|
110
|
-
data: {
|
|
111
|
-
date: new Date('2021-03-01'),
|
|
112
|
-
time: '12:00',
|
|
113
|
-
ampm: 'PM',
|
|
114
|
-
utcOffset: '+00:00'
|
|
115
|
-
},
|
|
116
|
-
usesTimezone: true,
|
|
117
|
-
usesTime: true
|
|
118
|
-
})).toEqual({
|
|
119
|
-
invalid: false,
|
|
120
|
-
valid: '2021-03-01T12:00+00:00'
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
it('preserves half-hour offset +05:30 (India)', ()=>{
|
|
124
|
-
expect((0, _date.buildFieldValue)({
|
|
125
|
-
data: {
|
|
126
|
-
date: new Date('2023-08-15'),
|
|
127
|
-
time: '10:00',
|
|
128
|
-
ampm: 'AM',
|
|
129
|
-
utcOffset: '+05:30'
|
|
130
|
-
},
|
|
131
|
-
usesTimezone: true,
|
|
132
|
-
usesTime: true
|
|
133
|
-
})).toEqual({
|
|
134
|
-
invalid: false,
|
|
135
|
-
valid: '2023-08-15T10:00+05:30'
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
it('preserves negative half-hour offset -09:30', ()=>{
|
|
139
|
-
expect((0, _date.buildFieldValue)({
|
|
140
|
-
data: {
|
|
141
|
-
date: new Date('2023-08-15'),
|
|
142
|
-
time: '03:30',
|
|
143
|
-
ampm: 'AM',
|
|
144
|
-
utcOffset: '-09:30'
|
|
145
|
-
},
|
|
146
|
-
usesTimezone: true,
|
|
147
|
-
usesTime: true
|
|
148
|
-
})).toEqual({
|
|
149
|
-
invalid: false,
|
|
150
|
-
valid: '2023-08-15T03:30-09:30'
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
it('preserves quarter-hour offset +05:45 (Nepal)', ()=>{
|
|
154
|
-
expect((0, _date.buildFieldValue)({
|
|
155
|
-
data: {
|
|
156
|
-
date: new Date('2023-08-15'),
|
|
157
|
-
time: '05:45',
|
|
158
|
-
ampm: 'AM',
|
|
159
|
-
utcOffset: '+05:45'
|
|
160
|
-
},
|
|
161
|
-
usesTimezone: true,
|
|
162
|
-
usesTime: true
|
|
163
|
-
})).toEqual({
|
|
164
|
-
invalid: false,
|
|
165
|
-
valid: '2023-08-15T05:45+05:45'
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
it('preserves UTC +00:00 offset', ()=>{
|
|
169
|
-
expect((0, _date.buildFieldValue)({
|
|
170
|
-
data: {
|
|
171
|
-
date: new Date('2023-01-01'),
|
|
172
|
-
time: '00:00',
|
|
173
|
-
ampm: 'AM',
|
|
174
|
-
utcOffset: '+00:00'
|
|
175
|
-
},
|
|
176
|
-
usesTimezone: true,
|
|
177
|
-
usesTime: true
|
|
178
|
-
})).toEqual({
|
|
179
|
-
invalid: false,
|
|
180
|
-
valid: '2023-01-01T00:00+00:00'
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
it('preserves far-west offset -12:00', ()=>{
|
|
184
|
-
expect((0, _date.buildFieldValue)({
|
|
185
|
-
data: {
|
|
186
|
-
date: new Date('2023-01-01'),
|
|
187
|
-
time: '23:59',
|
|
188
|
-
ampm: 'PM',
|
|
189
|
-
utcOffset: '-12:00'
|
|
190
|
-
},
|
|
191
|
-
usesTimezone: true,
|
|
192
|
-
usesTime: true
|
|
193
|
-
})).toEqual({
|
|
194
|
-
invalid: false,
|
|
195
|
-
valid: '2023-01-01T23:59-12:00'
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
it('preserves far-east offset +14:00', ()=>{
|
|
199
|
-
expect((0, _date.buildFieldValue)({
|
|
200
|
-
data: {
|
|
201
|
-
date: new Date('2023-01-01'),
|
|
202
|
-
time: '01:00',
|
|
203
|
-
ampm: 'AM',
|
|
204
|
-
utcOffset: '+14:00'
|
|
205
|
-
},
|
|
206
|
-
usesTimezone: true,
|
|
207
|
-
usesTime: true
|
|
208
|
-
})).toEqual({
|
|
209
|
-
invalid: false,
|
|
210
|
-
valid: '2023-01-01T01:00+14:00'
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
it('11:59 PM → 23:59', ()=>{
|
|
214
|
-
expect((0, _date.buildFieldValue)({
|
|
215
|
-
data: {
|
|
216
|
-
date: new Date('2023-06-01'),
|
|
217
|
-
time: '11:59',
|
|
218
|
-
ampm: 'PM',
|
|
219
|
-
utcOffset: '+00:00'
|
|
220
|
-
},
|
|
221
|
-
usesTimezone: true,
|
|
222
|
-
usesTime: true
|
|
223
|
-
})).toEqual({
|
|
224
|
-
invalid: false,
|
|
225
|
-
valid: '2023-06-01T23:59+00:00'
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
it('01:00 AM → 01:00 (no shift)', ()=>{
|
|
229
|
-
expect((0, _date.buildFieldValue)({
|
|
230
|
-
data: {
|
|
231
|
-
date: new Date('2023-06-01'),
|
|
232
|
-
time: '01:00',
|
|
233
|
-
ampm: 'AM',
|
|
234
|
-
utcOffset: '+00:00'
|
|
235
|
-
},
|
|
236
|
-
usesTimezone: true,
|
|
237
|
-
usesTime: true
|
|
238
|
-
})).toEqual({
|
|
239
|
-
invalid: false,
|
|
240
|
-
valid: '2023-06-01T01:00+00:00'
|
|
241
|
-
});
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
describe('userInputFromDatetime', ()=>{
|
|
245
|
-
it('parses a full ISO datetime string with timezone', ()=>{
|
|
246
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
247
|
-
value: '2018-02-02T17:00+03:00',
|
|
248
|
-
uses12hClock: false
|
|
249
|
-
});
|
|
250
|
-
expect(result.time).toBe('17:00');
|
|
251
|
-
expect(result.ampm).toBe('PM');
|
|
252
|
-
expect(result.utcOffset).toBe('+03:00');
|
|
253
|
-
});
|
|
254
|
-
it('parses a full ISO datetime string with 12h clock', ()=>{
|
|
255
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
256
|
-
value: '2018-02-02T05:00+03:00',
|
|
257
|
-
uses12hClock: true
|
|
258
|
-
});
|
|
259
|
-
expect(result.time).toBe('05:00');
|
|
260
|
-
expect(result.ampm).toBe('AM');
|
|
261
|
-
expect(result.utcOffset).toBe('+03:00');
|
|
262
|
-
});
|
|
263
|
-
it('returns defaults when value is null', ()=>{
|
|
264
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
265
|
-
value: null,
|
|
266
|
-
uses12hClock: false
|
|
267
|
-
});
|
|
268
|
-
expect(result.date).toBeUndefined();
|
|
269
|
-
expect(result.ampm).toBe((0, _date.getDefaultAMPM)());
|
|
270
|
-
expect(result.utcOffset).toBe((0, _date.getDefaultUtcOffset)());
|
|
271
|
-
});
|
|
272
|
-
it('returns defaults when value is undefined', ()=>{
|
|
273
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
274
|
-
value: undefined,
|
|
275
|
-
uses12hClock: false
|
|
276
|
-
});
|
|
277
|
-
expect(result.date).toBeUndefined();
|
|
278
|
-
expect(result.ampm).toBe((0, _date.getDefaultAMPM)());
|
|
279
|
-
});
|
|
280
|
-
it('returns defaults when value is empty string', ()=>{
|
|
281
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
282
|
-
value: '',
|
|
283
|
-
uses12hClock: false
|
|
284
|
-
});
|
|
285
|
-
expect(result.date).toBeUndefined();
|
|
286
|
-
});
|
|
287
|
-
it('parses a date-only string', ()=>{
|
|
288
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
289
|
-
value: '2022-09-16',
|
|
290
|
-
uses12hClock: false
|
|
291
|
-
});
|
|
292
|
-
expect(result.utcOffset).toBe('+00:00');
|
|
293
|
-
});
|
|
294
|
-
it('preserves raw time and does not shift by system timezone (positive offset)', ()=>{
|
|
295
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
296
|
-
value: '2020-03-15T14:00+05:30',
|
|
297
|
-
uses12hClock: false
|
|
298
|
-
});
|
|
299
|
-
expect(result.time).toBe('14:00');
|
|
300
|
-
expect(result.ampm).toBe('PM');
|
|
301
|
-
expect(result.utcOffset).toBe('+05:30');
|
|
302
|
-
});
|
|
303
|
-
it('preserves raw time and does not shift by system timezone (negative offset)', ()=>{
|
|
304
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
305
|
-
value: '2020-03-15T08:30-05:30',
|
|
306
|
-
uses12hClock: false
|
|
307
|
-
});
|
|
308
|
-
expect(result.time).toBe('08:30');
|
|
309
|
-
expect(result.ampm).toBe('AM');
|
|
310
|
-
expect(result.utcOffset).toBe('-05:30');
|
|
311
|
-
});
|
|
312
|
-
it('handles UTC "Z" suffix — offset is +00:00, time is not shifted', ()=>{
|
|
313
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
314
|
-
value: '2021-06-01T12:00Z',
|
|
315
|
-
uses12hClock: false
|
|
316
|
-
});
|
|
317
|
-
expect(result.time).toBe('12:00');
|
|
318
|
-
expect(result.ampm).toBe('PM');
|
|
319
|
-
expect(result.utcOffset).toBe('Z');
|
|
320
|
-
});
|
|
321
|
-
it('handles midnight UTC correctly', ()=>{
|
|
322
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
323
|
-
value: '2021-06-01T00:00Z',
|
|
324
|
-
uses12hClock: false
|
|
325
|
-
});
|
|
326
|
-
expect(result.time).toBe('00:00');
|
|
327
|
-
expect(result.ampm).toBe('AM');
|
|
328
|
-
});
|
|
329
|
-
it('converts PM hour to 12h display (17:00 → 05:00 PM)', ()=>{
|
|
330
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
331
|
-
value: '2018-02-02T17:00+03:00',
|
|
332
|
-
uses12hClock: true
|
|
333
|
-
});
|
|
334
|
-
expect(result.time).toBe('05:00');
|
|
335
|
-
expect(result.ampm).toBe('PM');
|
|
336
|
-
});
|
|
337
|
-
it('converts noon to 12h display (12:00 → 12:00 PM)', ()=>{
|
|
338
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
339
|
-
value: '2021-03-01T12:00+00:00',
|
|
340
|
-
uses12hClock: true
|
|
341
|
-
});
|
|
342
|
-
expect(result.time).toBe('12:00');
|
|
343
|
-
expect(result.ampm).toBe('PM');
|
|
344
|
-
});
|
|
345
|
-
it('converts midnight to 12h display (00:00 → 12:00 AM)', ()=>{
|
|
346
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
347
|
-
value: '2021-03-01T00:00+00:00',
|
|
348
|
-
uses12hClock: true
|
|
349
|
-
});
|
|
350
|
-
expect(result.time).toBe('12:00');
|
|
351
|
-
expect(result.ampm).toBe('AM');
|
|
352
|
-
});
|
|
353
|
-
it('handles quarter-hour offset +05:45 (Nepal)', ()=>{
|
|
354
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
355
|
-
value: '2023-07-20T09:45+05:45',
|
|
356
|
-
uses12hClock: false
|
|
357
|
-
});
|
|
358
|
-
expect(result.time).toBe('09:45');
|
|
359
|
-
expect(result.utcOffset).toBe('+05:45');
|
|
360
|
-
});
|
|
361
|
-
it('handles -12:00 (far-west) offset without date shift', ()=>{
|
|
362
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
363
|
-
value: '2023-01-01T23:59-12:00',
|
|
364
|
-
uses12hClock: false
|
|
365
|
-
});
|
|
366
|
-
expect(result.time).toBe('23:59');
|
|
367
|
-
expect(result.utcOffset).toBe('-12:00');
|
|
368
|
-
});
|
|
369
|
-
it('handles +14:00 (far-east) offset without date shift', ()=>{
|
|
370
|
-
const result = (0, _date.userInputFromDatetime)({
|
|
371
|
-
value: '2023-01-01T01:00+14:00',
|
|
372
|
-
uses12hClock: false
|
|
373
|
-
});
|
|
374
|
-
expect(result.time).toBe('01:00');
|
|
375
|
-
expect(result.utcOffset).toBe('+14:00');
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
describe('getDefaultAMPM', ()=>{
|
|
379
|
-
it('returns AM', ()=>{
|
|
380
|
-
expect((0, _date.getDefaultAMPM)()).toBe('AM');
|
|
381
|
-
});
|
|
382
55
|
});
|
|
383
56
|
});
|
package/dist/cjs/utils/date.js
CHANGED
|
@@ -22,36 +22,32 @@ _export(exports, {
|
|
|
22
22
|
return userInputFromDatetime;
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
const _moment = /*#__PURE__*/ _interop_require_default(require("moment"));
|
|
26
|
+
function _interop_require_default(obj) {
|
|
27
|
+
return obj && obj.__esModule ? obj : {
|
|
28
|
+
default: obj
|
|
29
|
+
};
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return
|
|
31
|
+
const ZONE_RX = /(Z|[+-]\d{2}[:+]?\d{2})$/;
|
|
32
|
+
function startOfToday(format) {
|
|
33
|
+
return (0, _moment.default)().set({
|
|
34
|
+
hours: 0,
|
|
35
|
+
minutes: 0
|
|
36
|
+
}).format(format);
|
|
33
37
|
}
|
|
34
|
-
function
|
|
38
|
+
function fieldValueToMoment(datetimeString) {
|
|
35
39
|
if (!datetimeString) {
|
|
36
40
|
return null;
|
|
37
41
|
}
|
|
38
|
-
const
|
|
39
|
-
|
|
42
|
+
const datetime = (0, _moment.default)(datetimeString);
|
|
43
|
+
if (ZONE_RX.test(datetimeString)) {
|
|
44
|
+
datetime.utcOffset(datetimeString);
|
|
45
|
+
}
|
|
46
|
+
return datetime;
|
|
40
47
|
}
|
|
41
48
|
function timeFromUserInput(input) {
|
|
42
49
|
const timeInput = input.time || '00:00';
|
|
43
|
-
|
|
44
|
-
let hours = (0, _datefns.getHours)(parsed);
|
|
45
|
-
const minutes = (0, _datefns.getMinutes)(parsed);
|
|
46
|
-
if (input.ampm === 'PM' && hours < 12) {
|
|
47
|
-
hours += 12;
|
|
48
|
-
} else if (input.ampm === 'AM' && hours === 12) {
|
|
49
|
-
hours = 0;
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
hours,
|
|
53
|
-
minutes
|
|
54
|
-
};
|
|
50
|
+
return _moment.default.utc(timeInput + '!' + input.ampm, 'HH:mm!A');
|
|
55
51
|
}
|
|
56
52
|
function datetimeFromUserInput(input) {
|
|
57
53
|
if (!input.date) {
|
|
@@ -59,14 +55,12 @@ function datetimeFromUserInput(input) {
|
|
|
59
55
|
valid: null
|
|
60
56
|
};
|
|
61
57
|
}
|
|
62
|
-
const
|
|
63
|
-
const date = (
|
|
64
|
-
hours,
|
|
65
|
-
minutes
|
|
66
|
-
seconds: 0,
|
|
67
|
-
milliseconds: 0
|
|
58
|
+
const time = timeFromUserInput(input);
|
|
59
|
+
const date = _moment.default.parseZone(input.utcOffset, 'Z').set(input.date.toObject()).set({
|
|
60
|
+
hours: time.hours(),
|
|
61
|
+
minutes: time.minutes()
|
|
68
62
|
});
|
|
69
|
-
if (
|
|
63
|
+
if (date.isValid()) {
|
|
70
64
|
return {
|
|
71
65
|
valid: date
|
|
72
66
|
};
|
|
@@ -84,58 +78,34 @@ function buildFieldValue({ data, usesTime, usesTimezone }) {
|
|
|
84
78
|
invalid: true
|
|
85
79
|
};
|
|
86
80
|
}
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
valid: null,
|
|
90
|
-
invalid: false
|
|
91
|
-
};
|
|
92
|
-
}
|
|
81
|
+
let format;
|
|
93
82
|
if (usesTimezone) {
|
|
94
|
-
|
|
95
|
-
valid: (0, _datefns.format)(date.valid, "yyyy-MM-dd'T'HH:mm") + data.utcOffset,
|
|
96
|
-
invalid: false
|
|
97
|
-
};
|
|
83
|
+
format = 'YYYY-MM-DDTHH:mmZ';
|
|
98
84
|
} else if (usesTime) {
|
|
99
|
-
|
|
100
|
-
valid: (0, _datefns.format)(date.valid, "yyyy-MM-dd'T'HH:mm"),
|
|
101
|
-
invalid: false
|
|
102
|
-
};
|
|
85
|
+
format = 'YYYY-MM-DDTHH:mm';
|
|
103
86
|
} else {
|
|
104
|
-
|
|
105
|
-
valid: (0, _datefns.format)(date.valid, 'yyyy-MM-dd'),
|
|
106
|
-
invalid: false
|
|
107
|
-
};
|
|
87
|
+
format = 'YYYY-MM-DD';
|
|
108
88
|
}
|
|
89
|
+
return {
|
|
90
|
+
valid: date?.valid ? date.valid.format(format) : null,
|
|
91
|
+
invalid: false
|
|
92
|
+
};
|
|
109
93
|
}
|
|
110
94
|
function getDefaultAMPM() {
|
|
111
95
|
return 'AM';
|
|
112
96
|
}
|
|
113
97
|
function getDefaultUtcOffset() {
|
|
114
|
-
return
|
|
115
|
-
}
|
|
116
|
-
function extractTimeFromIso(value) {
|
|
117
|
-
const match = value.match(/T(\d{2}:\d{2})/);
|
|
118
|
-
return match ? match[1] : undefined;
|
|
98
|
+
return startOfToday('Z');
|
|
119
99
|
}
|
|
120
100
|
function userInputFromDatetime({ value, uses12hClock }) {
|
|
121
|
-
const datetime =
|
|
122
|
-
if (datetime
|
|
123
|
-
const
|
|
124
|
-
const timeDate = rawTime ? (0, _datefns.parse)(rawTime, 'HH:mm', new Date(0)) : datetime;
|
|
125
|
-
const hours = (0, _datefns.getHours)(timeDate);
|
|
126
|
-
const ampm = hours < 12 ? 'AM' : 'PM';
|
|
127
|
-
let time;
|
|
128
|
-
if (uses12hClock) {
|
|
129
|
-
const h12 = hours % 12 || 12;
|
|
130
|
-
time = `${String(h12).padStart(2, '0')}:${String((0, _datefns.getMinutes)(timeDate)).padStart(2, '0')}`;
|
|
131
|
-
} else {
|
|
132
|
-
time = rawTime ?? (0, _datefns.format)(datetime, 'HH:mm');
|
|
133
|
-
}
|
|
101
|
+
const datetime = fieldValueToMoment(value);
|
|
102
|
+
if (datetime) {
|
|
103
|
+
const timeFormat = uses12hClock ? 'hh:mm' : 'HH:mm';
|
|
134
104
|
return {
|
|
135
105
|
date: datetime,
|
|
136
|
-
time,
|
|
137
|
-
ampm,
|
|
138
|
-
utcOffset:
|
|
106
|
+
time: datetime.format(timeFormat),
|
|
107
|
+
ampm: datetime.format('A'),
|
|
108
|
+
utcOffset: datetime.format('Z')
|
|
139
109
|
};
|
|
140
110
|
} else {
|
|
141
111
|
return {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import { Datepicker } from '@contentful/f36-components';
|
|
3
3
|
import { css } from '@emotion/css';
|
|
4
|
+
import moment from 'moment';
|
|
4
5
|
const YEARS_INTO_FUTURE = 100;
|
|
5
6
|
const MIN_YEAR = '1000';
|
|
6
7
|
const styles = {
|
|
@@ -18,12 +19,14 @@ export const DatepickerInput = (props)=>{
|
|
|
18
19
|
toDate
|
|
19
20
|
];
|
|
20
21
|
}, []);
|
|
21
|
-
const
|
|
22
|
+
const dateObj = props.value?.toObject();
|
|
23
|
+
const selectedDate = dateObj ? new Date(dateObj.years, dateObj.months, dateObj.date) : undefined;
|
|
22
24
|
return /*#__PURE__*/ React.createElement(Datepicker, {
|
|
23
25
|
className: styles.root,
|
|
24
26
|
selected: selectedDate,
|
|
25
27
|
onSelect: (day)=>{
|
|
26
|
-
|
|
28
|
+
const momentDay = day ? moment(day) : undefined;
|
|
29
|
+
props.onChange(momentDay);
|
|
27
30
|
},
|
|
28
31
|
inputProps: {
|
|
29
32
|
isDisabled: props.disabled,
|
|
@@ -1,55 +1,79 @@
|
|
|
1
1
|
import React, { useState, useCallback, useEffect } from 'react';
|
|
2
2
|
import { TextInput, Flex } from '@contentful/f36-components';
|
|
3
3
|
import { css } from '@emotion/css';
|
|
4
|
-
import
|
|
4
|
+
import moment from 'moment';
|
|
5
5
|
const validInputFormats = [
|
|
6
6
|
'hh:mm a',
|
|
7
|
+
'hh:mm A',
|
|
7
8
|
'h:mm a',
|
|
9
|
+
'h:mm A',
|
|
8
10
|
'hh:mm',
|
|
9
|
-
'kk:mm',
|
|
10
11
|
'k:mm',
|
|
12
|
+
'kk:mm',
|
|
11
13
|
'h a',
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
14
|
+
'h A',
|
|
15
|
+
'h',
|
|
16
|
+
'hh',
|
|
17
|
+
'HH'
|
|
15
18
|
];
|
|
16
|
-
const REF_DATE = new Date(2000, 0, 1);
|
|
17
19
|
function parseRawInput(raw) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
let time = null;
|
|
21
|
+
for(let i = 0; i < validInputFormats.length; i++){
|
|
22
|
+
const date = moment(raw, validInputFormats[i]);
|
|
23
|
+
if (date.isValid()) {
|
|
24
|
+
time = date;
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
21
27
|
}
|
|
22
|
-
return
|
|
28
|
+
return time;
|
|
23
29
|
}
|
|
24
|
-
const getDefaultTime = ()=>
|
|
25
|
-
|
|
30
|
+
const getDefaultTime = ()=>{
|
|
31
|
+
return moment(`12:00 AM`, 'hh:mm A');
|
|
32
|
+
};
|
|
33
|
+
const formatToString = (uses12hClock, value)=>{
|
|
34
|
+
return uses12hClock ? value.format('hh:mm A') : value.format('HH:mm');
|
|
35
|
+
};
|
|
26
36
|
export const TimepickerInput = ({ disabled, uses12hClock, time = '12:00', ampm = 'AM', onChange })=>{
|
|
27
37
|
const [selectedTime, setSelectedTime] = useState(()=>{
|
|
28
38
|
return formatToString(uses12hClock, getDefaultTime());
|
|
29
39
|
});
|
|
30
40
|
useEffect(()=>{
|
|
31
|
-
setSelectedTime(formatToString(uses12hClock,
|
|
41
|
+
setSelectedTime(formatToString(uses12hClock, moment(`${time} ${ampm}`, 'hh:mm A')));
|
|
32
42
|
}, [
|
|
33
43
|
time,
|
|
34
44
|
ampm,
|
|
35
45
|
uses12hClock
|
|
36
46
|
]);
|
|
37
47
|
const handleChange = useCallback((e)=>{
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
const raw = e.currentTarget.value;
|
|
49
|
+
setSelectedTime(raw);
|
|
50
|
+
const parsedTime = parseRawInput(raw);
|
|
51
|
+
if (parsedTime) {
|
|
52
|
+
onChange({
|
|
53
|
+
time: parsedTime.format('hh:mm'),
|
|
54
|
+
ampm: parsedTime.format('A')
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}, [
|
|
58
|
+
onChange
|
|
59
|
+
]);
|
|
40
60
|
const handleFocus = useCallback((e)=>{
|
|
41
61
|
e.preventDefault();
|
|
42
62
|
e.target.select();
|
|
43
63
|
}, []);
|
|
44
|
-
const handleBlur = ()=>{
|
|
64
|
+
const handleBlur = useCallback(()=>{
|
|
45
65
|
const parsedTime = parseRawInput(selectedTime);
|
|
46
66
|
const value = parsedTime ?? getDefaultTime();
|
|
47
67
|
setSelectedTime(formatToString(uses12hClock, value));
|
|
48
68
|
onChange({
|
|
49
|
-
time: format(
|
|
50
|
-
ampm: format(
|
|
69
|
+
time: value.format('hh:mm'),
|
|
70
|
+
ampm: value.format('A')
|
|
51
71
|
});
|
|
52
|
-
}
|
|
72
|
+
}, [
|
|
73
|
+
selectedTime,
|
|
74
|
+
uses12hClock,
|
|
75
|
+
onChange
|
|
76
|
+
]);
|
|
53
77
|
return /*#__PURE__*/ React.createElement(Flex, {
|
|
54
78
|
className: css({
|
|
55
79
|
width: '145px'
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import moment from 'moment';
|
|
2
|
+
import { buildFieldValue } from './date';
|
|
2
3
|
describe('date utils', ()=>{
|
|
3
4
|
describe('buildFieldValue', ()=>{
|
|
4
5
|
it('should work properly', ()=>{
|
|
5
6
|
expect(buildFieldValue({
|
|
6
7
|
data: {
|
|
7
|
-
date:
|
|
8
|
+
date: moment('2018-02-02'),
|
|
8
9
|
time: '05:00',
|
|
9
10
|
ampm: 'PM',
|
|
10
11
|
utcOffset: '+03:00'
|
|
@@ -17,7 +18,7 @@ describe('date utils', ()=>{
|
|
|
17
18
|
});
|
|
18
19
|
expect(buildFieldValue({
|
|
19
20
|
data: {
|
|
20
|
-
date:
|
|
21
|
+
date: moment('2015-01-14'),
|
|
21
22
|
time: '05:00',
|
|
22
23
|
ampm: 'AM',
|
|
23
24
|
utcOffset: '-05:00'
|
|
@@ -30,7 +31,7 @@ describe('date utils', ()=>{
|
|
|
30
31
|
});
|
|
31
32
|
expect(buildFieldValue({
|
|
32
33
|
data: {
|
|
33
|
-
date:
|
|
34
|
+
date: moment('2015-01-14'),
|
|
34
35
|
time: '17:00',
|
|
35
36
|
ampm: 'PM',
|
|
36
37
|
utcOffset: '-05:00'
|
|
@@ -42,338 +43,5 @@ describe('date utils', ()=>{
|
|
|
42
43
|
valid: '2015-01-14T17:00-05:00'
|
|
43
44
|
});
|
|
44
45
|
});
|
|
45
|
-
it('returns date-only format when usesTime and usesTimezone are false', ()=>{
|
|
46
|
-
expect(buildFieldValue({
|
|
47
|
-
data: {
|
|
48
|
-
date: new Date('2020-06-15'),
|
|
49
|
-
time: '10:30',
|
|
50
|
-
ampm: 'AM',
|
|
51
|
-
utcOffset: '+00:00'
|
|
52
|
-
},
|
|
53
|
-
usesTimezone: false,
|
|
54
|
-
usesTime: false
|
|
55
|
-
})).toEqual({
|
|
56
|
-
invalid: false,
|
|
57
|
-
valid: '2020-06-15'
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
it('returns datetime without timezone when usesTime=true, usesTimezone=false', ()=>{
|
|
61
|
-
expect(buildFieldValue({
|
|
62
|
-
data: {
|
|
63
|
-
date: new Date('2020-06-15'),
|
|
64
|
-
time: '14:30',
|
|
65
|
-
ampm: 'PM',
|
|
66
|
-
utcOffset: '+00:00'
|
|
67
|
-
},
|
|
68
|
-
usesTimezone: false,
|
|
69
|
-
usesTime: true
|
|
70
|
-
})).toEqual({
|
|
71
|
-
invalid: false,
|
|
72
|
-
valid: '2020-06-15T14:30'
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
it('returns valid: null when no date is provided', ()=>{
|
|
76
|
-
expect(buildFieldValue({
|
|
77
|
-
data: {
|
|
78
|
-
time: '10:00',
|
|
79
|
-
ampm: 'AM',
|
|
80
|
-
utcOffset: '+00:00'
|
|
81
|
-
},
|
|
82
|
-
usesTimezone: true,
|
|
83
|
-
usesTime: true
|
|
84
|
-
})).toEqual({
|
|
85
|
-
invalid: false,
|
|
86
|
-
valid: null
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
it('handles midnight (12:00 AM) correctly', ()=>{
|
|
90
|
-
expect(buildFieldValue({
|
|
91
|
-
data: {
|
|
92
|
-
date: new Date('2021-03-01'),
|
|
93
|
-
time: '12:00',
|
|
94
|
-
ampm: 'AM',
|
|
95
|
-
utcOffset: '+00:00'
|
|
96
|
-
},
|
|
97
|
-
usesTimezone: true,
|
|
98
|
-
usesTime: true
|
|
99
|
-
})).toEqual({
|
|
100
|
-
invalid: false,
|
|
101
|
-
valid: '2021-03-01T00:00+00:00'
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
it('handles noon (12:00 PM) correctly', ()=>{
|
|
105
|
-
expect(buildFieldValue({
|
|
106
|
-
data: {
|
|
107
|
-
date: new Date('2021-03-01'),
|
|
108
|
-
time: '12:00',
|
|
109
|
-
ampm: 'PM',
|
|
110
|
-
utcOffset: '+00:00'
|
|
111
|
-
},
|
|
112
|
-
usesTimezone: true,
|
|
113
|
-
usesTime: true
|
|
114
|
-
})).toEqual({
|
|
115
|
-
invalid: false,
|
|
116
|
-
valid: '2021-03-01T12:00+00:00'
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
it('preserves half-hour offset +05:30 (India)', ()=>{
|
|
120
|
-
expect(buildFieldValue({
|
|
121
|
-
data: {
|
|
122
|
-
date: new Date('2023-08-15'),
|
|
123
|
-
time: '10:00',
|
|
124
|
-
ampm: 'AM',
|
|
125
|
-
utcOffset: '+05:30'
|
|
126
|
-
},
|
|
127
|
-
usesTimezone: true,
|
|
128
|
-
usesTime: true
|
|
129
|
-
})).toEqual({
|
|
130
|
-
invalid: false,
|
|
131
|
-
valid: '2023-08-15T10:00+05:30'
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
it('preserves negative half-hour offset -09:30', ()=>{
|
|
135
|
-
expect(buildFieldValue({
|
|
136
|
-
data: {
|
|
137
|
-
date: new Date('2023-08-15'),
|
|
138
|
-
time: '03:30',
|
|
139
|
-
ampm: 'AM',
|
|
140
|
-
utcOffset: '-09:30'
|
|
141
|
-
},
|
|
142
|
-
usesTimezone: true,
|
|
143
|
-
usesTime: true
|
|
144
|
-
})).toEqual({
|
|
145
|
-
invalid: false,
|
|
146
|
-
valid: '2023-08-15T03:30-09:30'
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
it('preserves quarter-hour offset +05:45 (Nepal)', ()=>{
|
|
150
|
-
expect(buildFieldValue({
|
|
151
|
-
data: {
|
|
152
|
-
date: new Date('2023-08-15'),
|
|
153
|
-
time: '05:45',
|
|
154
|
-
ampm: 'AM',
|
|
155
|
-
utcOffset: '+05:45'
|
|
156
|
-
},
|
|
157
|
-
usesTimezone: true,
|
|
158
|
-
usesTime: true
|
|
159
|
-
})).toEqual({
|
|
160
|
-
invalid: false,
|
|
161
|
-
valid: '2023-08-15T05:45+05:45'
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
it('preserves UTC +00:00 offset', ()=>{
|
|
165
|
-
expect(buildFieldValue({
|
|
166
|
-
data: {
|
|
167
|
-
date: new Date('2023-01-01'),
|
|
168
|
-
time: '00:00',
|
|
169
|
-
ampm: 'AM',
|
|
170
|
-
utcOffset: '+00:00'
|
|
171
|
-
},
|
|
172
|
-
usesTimezone: true,
|
|
173
|
-
usesTime: true
|
|
174
|
-
})).toEqual({
|
|
175
|
-
invalid: false,
|
|
176
|
-
valid: '2023-01-01T00:00+00:00'
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
it('preserves far-west offset -12:00', ()=>{
|
|
180
|
-
expect(buildFieldValue({
|
|
181
|
-
data: {
|
|
182
|
-
date: new Date('2023-01-01'),
|
|
183
|
-
time: '23:59',
|
|
184
|
-
ampm: 'PM',
|
|
185
|
-
utcOffset: '-12:00'
|
|
186
|
-
},
|
|
187
|
-
usesTimezone: true,
|
|
188
|
-
usesTime: true
|
|
189
|
-
})).toEqual({
|
|
190
|
-
invalid: false,
|
|
191
|
-
valid: '2023-01-01T23:59-12:00'
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
it('preserves far-east offset +14:00', ()=>{
|
|
195
|
-
expect(buildFieldValue({
|
|
196
|
-
data: {
|
|
197
|
-
date: new Date('2023-01-01'),
|
|
198
|
-
time: '01:00',
|
|
199
|
-
ampm: 'AM',
|
|
200
|
-
utcOffset: '+14:00'
|
|
201
|
-
},
|
|
202
|
-
usesTimezone: true,
|
|
203
|
-
usesTime: true
|
|
204
|
-
})).toEqual({
|
|
205
|
-
invalid: false,
|
|
206
|
-
valid: '2023-01-01T01:00+14:00'
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
it('11:59 PM → 23:59', ()=>{
|
|
210
|
-
expect(buildFieldValue({
|
|
211
|
-
data: {
|
|
212
|
-
date: new Date('2023-06-01'),
|
|
213
|
-
time: '11:59',
|
|
214
|
-
ampm: 'PM',
|
|
215
|
-
utcOffset: '+00:00'
|
|
216
|
-
},
|
|
217
|
-
usesTimezone: true,
|
|
218
|
-
usesTime: true
|
|
219
|
-
})).toEqual({
|
|
220
|
-
invalid: false,
|
|
221
|
-
valid: '2023-06-01T23:59+00:00'
|
|
222
|
-
});
|
|
223
|
-
});
|
|
224
|
-
it('01:00 AM → 01:00 (no shift)', ()=>{
|
|
225
|
-
expect(buildFieldValue({
|
|
226
|
-
data: {
|
|
227
|
-
date: new Date('2023-06-01'),
|
|
228
|
-
time: '01:00',
|
|
229
|
-
ampm: 'AM',
|
|
230
|
-
utcOffset: '+00:00'
|
|
231
|
-
},
|
|
232
|
-
usesTimezone: true,
|
|
233
|
-
usesTime: true
|
|
234
|
-
})).toEqual({
|
|
235
|
-
invalid: false,
|
|
236
|
-
valid: '2023-06-01T01:00+00:00'
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
describe('userInputFromDatetime', ()=>{
|
|
241
|
-
it('parses a full ISO datetime string with timezone', ()=>{
|
|
242
|
-
const result = userInputFromDatetime({
|
|
243
|
-
value: '2018-02-02T17:00+03:00',
|
|
244
|
-
uses12hClock: false
|
|
245
|
-
});
|
|
246
|
-
expect(result.time).toBe('17:00');
|
|
247
|
-
expect(result.ampm).toBe('PM');
|
|
248
|
-
expect(result.utcOffset).toBe('+03:00');
|
|
249
|
-
});
|
|
250
|
-
it('parses a full ISO datetime string with 12h clock', ()=>{
|
|
251
|
-
const result = userInputFromDatetime({
|
|
252
|
-
value: '2018-02-02T05:00+03:00',
|
|
253
|
-
uses12hClock: true
|
|
254
|
-
});
|
|
255
|
-
expect(result.time).toBe('05:00');
|
|
256
|
-
expect(result.ampm).toBe('AM');
|
|
257
|
-
expect(result.utcOffset).toBe('+03:00');
|
|
258
|
-
});
|
|
259
|
-
it('returns defaults when value is null', ()=>{
|
|
260
|
-
const result = userInputFromDatetime({
|
|
261
|
-
value: null,
|
|
262
|
-
uses12hClock: false
|
|
263
|
-
});
|
|
264
|
-
expect(result.date).toBeUndefined();
|
|
265
|
-
expect(result.ampm).toBe(getDefaultAMPM());
|
|
266
|
-
expect(result.utcOffset).toBe(getDefaultUtcOffset());
|
|
267
|
-
});
|
|
268
|
-
it('returns defaults when value is undefined', ()=>{
|
|
269
|
-
const result = userInputFromDatetime({
|
|
270
|
-
value: undefined,
|
|
271
|
-
uses12hClock: false
|
|
272
|
-
});
|
|
273
|
-
expect(result.date).toBeUndefined();
|
|
274
|
-
expect(result.ampm).toBe(getDefaultAMPM());
|
|
275
|
-
});
|
|
276
|
-
it('returns defaults when value is empty string', ()=>{
|
|
277
|
-
const result = userInputFromDatetime({
|
|
278
|
-
value: '',
|
|
279
|
-
uses12hClock: false
|
|
280
|
-
});
|
|
281
|
-
expect(result.date).toBeUndefined();
|
|
282
|
-
});
|
|
283
|
-
it('parses a date-only string', ()=>{
|
|
284
|
-
const result = userInputFromDatetime({
|
|
285
|
-
value: '2022-09-16',
|
|
286
|
-
uses12hClock: false
|
|
287
|
-
});
|
|
288
|
-
expect(result.utcOffset).toBe('+00:00');
|
|
289
|
-
});
|
|
290
|
-
it('preserves raw time and does not shift by system timezone (positive offset)', ()=>{
|
|
291
|
-
const result = userInputFromDatetime({
|
|
292
|
-
value: '2020-03-15T14:00+05:30',
|
|
293
|
-
uses12hClock: false
|
|
294
|
-
});
|
|
295
|
-
expect(result.time).toBe('14:00');
|
|
296
|
-
expect(result.ampm).toBe('PM');
|
|
297
|
-
expect(result.utcOffset).toBe('+05:30');
|
|
298
|
-
});
|
|
299
|
-
it('preserves raw time and does not shift by system timezone (negative offset)', ()=>{
|
|
300
|
-
const result = userInputFromDatetime({
|
|
301
|
-
value: '2020-03-15T08:30-05:30',
|
|
302
|
-
uses12hClock: false
|
|
303
|
-
});
|
|
304
|
-
expect(result.time).toBe('08:30');
|
|
305
|
-
expect(result.ampm).toBe('AM');
|
|
306
|
-
expect(result.utcOffset).toBe('-05:30');
|
|
307
|
-
});
|
|
308
|
-
it('handles UTC "Z" suffix — offset is +00:00, time is not shifted', ()=>{
|
|
309
|
-
const result = userInputFromDatetime({
|
|
310
|
-
value: '2021-06-01T12:00Z',
|
|
311
|
-
uses12hClock: false
|
|
312
|
-
});
|
|
313
|
-
expect(result.time).toBe('12:00');
|
|
314
|
-
expect(result.ampm).toBe('PM');
|
|
315
|
-
expect(result.utcOffset).toBe('Z');
|
|
316
|
-
});
|
|
317
|
-
it('handles midnight UTC correctly', ()=>{
|
|
318
|
-
const result = userInputFromDatetime({
|
|
319
|
-
value: '2021-06-01T00:00Z',
|
|
320
|
-
uses12hClock: false
|
|
321
|
-
});
|
|
322
|
-
expect(result.time).toBe('00:00');
|
|
323
|
-
expect(result.ampm).toBe('AM');
|
|
324
|
-
});
|
|
325
|
-
it('converts PM hour to 12h display (17:00 → 05:00 PM)', ()=>{
|
|
326
|
-
const result = userInputFromDatetime({
|
|
327
|
-
value: '2018-02-02T17:00+03:00',
|
|
328
|
-
uses12hClock: true
|
|
329
|
-
});
|
|
330
|
-
expect(result.time).toBe('05:00');
|
|
331
|
-
expect(result.ampm).toBe('PM');
|
|
332
|
-
});
|
|
333
|
-
it('converts noon to 12h display (12:00 → 12:00 PM)', ()=>{
|
|
334
|
-
const result = userInputFromDatetime({
|
|
335
|
-
value: '2021-03-01T12:00+00:00',
|
|
336
|
-
uses12hClock: true
|
|
337
|
-
});
|
|
338
|
-
expect(result.time).toBe('12:00');
|
|
339
|
-
expect(result.ampm).toBe('PM');
|
|
340
|
-
});
|
|
341
|
-
it('converts midnight to 12h display (00:00 → 12:00 AM)', ()=>{
|
|
342
|
-
const result = userInputFromDatetime({
|
|
343
|
-
value: '2021-03-01T00:00+00:00',
|
|
344
|
-
uses12hClock: true
|
|
345
|
-
});
|
|
346
|
-
expect(result.time).toBe('12:00');
|
|
347
|
-
expect(result.ampm).toBe('AM');
|
|
348
|
-
});
|
|
349
|
-
it('handles quarter-hour offset +05:45 (Nepal)', ()=>{
|
|
350
|
-
const result = userInputFromDatetime({
|
|
351
|
-
value: '2023-07-20T09:45+05:45',
|
|
352
|
-
uses12hClock: false
|
|
353
|
-
});
|
|
354
|
-
expect(result.time).toBe('09:45');
|
|
355
|
-
expect(result.utcOffset).toBe('+05:45');
|
|
356
|
-
});
|
|
357
|
-
it('handles -12:00 (far-west) offset without date shift', ()=>{
|
|
358
|
-
const result = userInputFromDatetime({
|
|
359
|
-
value: '2023-01-01T23:59-12:00',
|
|
360
|
-
uses12hClock: false
|
|
361
|
-
});
|
|
362
|
-
expect(result.time).toBe('23:59');
|
|
363
|
-
expect(result.utcOffset).toBe('-12:00');
|
|
364
|
-
});
|
|
365
|
-
it('handles +14:00 (far-east) offset without date shift', ()=>{
|
|
366
|
-
const result = userInputFromDatetime({
|
|
367
|
-
value: '2023-01-01T01:00+14:00',
|
|
368
|
-
uses12hClock: false
|
|
369
|
-
});
|
|
370
|
-
expect(result.time).toBe('01:00');
|
|
371
|
-
expect(result.utcOffset).toBe('+14:00');
|
|
372
|
-
});
|
|
373
|
-
});
|
|
374
|
-
describe('getDefaultAMPM', ()=>{
|
|
375
|
-
it('returns AM', ()=>{
|
|
376
|
-
expect(getDefaultAMPM()).toBe('AM');
|
|
377
|
-
});
|
|
378
46
|
});
|
|
379
47
|
});
|
package/dist/esm/utils/date.js
CHANGED
|
@@ -1,33 +1,24 @@
|
|
|
1
|
-
import
|
|
1
|
+
import moment from 'moment';
|
|
2
2
|
const ZONE_RX = /(Z|[+-]\d{2}[:+]?\d{2})$/;
|
|
3
|
-
function
|
|
4
|
-
return
|
|
3
|
+
function startOfToday(format) {
|
|
4
|
+
return moment().set({
|
|
5
|
+
hours: 0,
|
|
6
|
+
minutes: 0
|
|
7
|
+
}).format(format);
|
|
5
8
|
}
|
|
6
|
-
function
|
|
7
|
-
const match = datetimeString.match(ZONE_RX);
|
|
8
|
-
return match ? match[1] : '+00:00';
|
|
9
|
-
}
|
|
10
|
-
function fieldValueToDate(datetimeString) {
|
|
9
|
+
function fieldValueToMoment(datetimeString) {
|
|
11
10
|
if (!datetimeString) {
|
|
12
11
|
return null;
|
|
13
12
|
}
|
|
14
|
-
const
|
|
15
|
-
|
|
13
|
+
const datetime = moment(datetimeString);
|
|
14
|
+
if (ZONE_RX.test(datetimeString)) {
|
|
15
|
+
datetime.utcOffset(datetimeString);
|
|
16
|
+
}
|
|
17
|
+
return datetime;
|
|
16
18
|
}
|
|
17
19
|
function timeFromUserInput(input) {
|
|
18
20
|
const timeInput = input.time || '00:00';
|
|
19
|
-
|
|
20
|
-
let hours = getHours(parsed);
|
|
21
|
-
const minutes = getMinutes(parsed);
|
|
22
|
-
if (input.ampm === 'PM' && hours < 12) {
|
|
23
|
-
hours += 12;
|
|
24
|
-
} else if (input.ampm === 'AM' && hours === 12) {
|
|
25
|
-
hours = 0;
|
|
26
|
-
}
|
|
27
|
-
return {
|
|
28
|
-
hours,
|
|
29
|
-
minutes
|
|
30
|
-
};
|
|
21
|
+
return moment.utc(timeInput + '!' + input.ampm, 'HH:mm!A');
|
|
31
22
|
}
|
|
32
23
|
function datetimeFromUserInput(input) {
|
|
33
24
|
if (!input.date) {
|
|
@@ -35,14 +26,12 @@ function datetimeFromUserInput(input) {
|
|
|
35
26
|
valid: null
|
|
36
27
|
};
|
|
37
28
|
}
|
|
38
|
-
const
|
|
39
|
-
const date = set(input.date
|
|
40
|
-
hours,
|
|
41
|
-
minutes
|
|
42
|
-
seconds: 0,
|
|
43
|
-
milliseconds: 0
|
|
29
|
+
const time = timeFromUserInput(input);
|
|
30
|
+
const date = moment.parseZone(input.utcOffset, 'Z').set(input.date.toObject()).set({
|
|
31
|
+
hours: time.hours(),
|
|
32
|
+
minutes: time.minutes()
|
|
44
33
|
});
|
|
45
|
-
if (isValid(
|
|
34
|
+
if (date.isValid()) {
|
|
46
35
|
return {
|
|
47
36
|
valid: date
|
|
48
37
|
};
|
|
@@ -60,58 +49,34 @@ export function buildFieldValue({ data, usesTime, usesTimezone }) {
|
|
|
60
49
|
invalid: true
|
|
61
50
|
};
|
|
62
51
|
}
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
valid: null,
|
|
66
|
-
invalid: false
|
|
67
|
-
};
|
|
68
|
-
}
|
|
52
|
+
let format;
|
|
69
53
|
if (usesTimezone) {
|
|
70
|
-
|
|
71
|
-
valid: format(date.valid, "yyyy-MM-dd'T'HH:mm") + data.utcOffset,
|
|
72
|
-
invalid: false
|
|
73
|
-
};
|
|
54
|
+
format = 'YYYY-MM-DDTHH:mmZ';
|
|
74
55
|
} else if (usesTime) {
|
|
75
|
-
|
|
76
|
-
valid: format(date.valid, "yyyy-MM-dd'T'HH:mm"),
|
|
77
|
-
invalid: false
|
|
78
|
-
};
|
|
56
|
+
format = 'YYYY-MM-DDTHH:mm';
|
|
79
57
|
} else {
|
|
80
|
-
|
|
81
|
-
valid: format(date.valid, 'yyyy-MM-dd'),
|
|
82
|
-
invalid: false
|
|
83
|
-
};
|
|
58
|
+
format = 'YYYY-MM-DD';
|
|
84
59
|
}
|
|
60
|
+
return {
|
|
61
|
+
valid: date?.valid ? date.valid.format(format) : null,
|
|
62
|
+
invalid: false
|
|
63
|
+
};
|
|
85
64
|
}
|
|
86
65
|
export function getDefaultAMPM() {
|
|
87
66
|
return 'AM';
|
|
88
67
|
}
|
|
89
68
|
export function getDefaultUtcOffset() {
|
|
90
|
-
return
|
|
91
|
-
}
|
|
92
|
-
function extractTimeFromIso(value) {
|
|
93
|
-
const match = value.match(/T(\d{2}:\d{2})/);
|
|
94
|
-
return match ? match[1] : undefined;
|
|
69
|
+
return startOfToday('Z');
|
|
95
70
|
}
|
|
96
71
|
export function userInputFromDatetime({ value, uses12hClock }) {
|
|
97
|
-
const datetime =
|
|
98
|
-
if (datetime
|
|
99
|
-
const
|
|
100
|
-
const timeDate = rawTime ? parse(rawTime, 'HH:mm', new Date(0)) : datetime;
|
|
101
|
-
const hours = getHours(timeDate);
|
|
102
|
-
const ampm = hours < 12 ? 'AM' : 'PM';
|
|
103
|
-
let time;
|
|
104
|
-
if (uses12hClock) {
|
|
105
|
-
const h12 = hours % 12 || 12;
|
|
106
|
-
time = `${String(h12).padStart(2, '0')}:${String(getMinutes(timeDate)).padStart(2, '0')}`;
|
|
107
|
-
} else {
|
|
108
|
-
time = rawTime ?? format(datetime, 'HH:mm');
|
|
109
|
-
}
|
|
72
|
+
const datetime = fieldValueToMoment(value);
|
|
73
|
+
if (datetime) {
|
|
74
|
+
const timeFormat = uses12hClock ? 'hh:mm' : 'HH:mm';
|
|
110
75
|
return {
|
|
111
76
|
date: datetime,
|
|
112
|
-
time,
|
|
113
|
-
ampm,
|
|
114
|
-
utcOffset:
|
|
77
|
+
time: datetime.format(timeFormat),
|
|
78
|
+
ampm: datetime.format('A'),
|
|
79
|
+
utcOffset: datetime.format('Z')
|
|
115
80
|
};
|
|
116
81
|
} else {
|
|
117
82
|
return {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import moment from 'moment';
|
|
2
3
|
export type DatePickerProps = {
|
|
3
|
-
value?:
|
|
4
|
-
onChange: (val:
|
|
4
|
+
value?: moment.Moment;
|
|
5
|
+
onChange: (val: moment.Moment | undefined) => void;
|
|
5
6
|
disabled?: boolean;
|
|
6
7
|
};
|
|
7
8
|
export declare const DatepickerInput: (props: DatePickerProps) => React.JSX.Element;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -13,10 +13,7 @@ export declare function buildFieldValue({ data, usesTime, usesTimezone, }: {
|
|
|
13
13
|
invalid: boolean;
|
|
14
14
|
valid?: undefined;
|
|
15
15
|
} | {
|
|
16
|
-
valid: null;
|
|
17
|
-
invalid: boolean;
|
|
18
|
-
} | {
|
|
19
|
-
valid: string;
|
|
16
|
+
valid: string | null;
|
|
20
17
|
invalid: boolean;
|
|
21
18
|
};
|
|
22
19
|
export declare function getDefaultAMPM(): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/field-editor-date",
|
|
3
|
-
"version": "2.0.8
|
|
3
|
+
"version": "2.0.8",
|
|
4
4
|
"main": "dist/cjs/index.js",
|
|
5
5
|
"module": "dist/esm/index.js",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@contentful/f36-tokens": "^6.1.2",
|
|
42
42
|
"@contentful/field-editor-shared": "^3.1.4",
|
|
43
43
|
"@emotion/css": "^11.13.5",
|
|
44
|
-
"
|
|
44
|
+
"moment": "^2.20.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@babel/core": "^7.7.4",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"publishConfig": {
|
|
58
58
|
"registry": "https://npm.pkg.github.com/"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "d4f28439b71dc035df4efd4fde6284e46961e4fd"
|
|
61
61
|
}
|