Skip to content

Commit 1f7e173

Browse files
committed
replace moment
1 parent 93391e5 commit 1f7e173

File tree

3 files changed

+68
-53
lines changed

3 files changed

+68
-53
lines changed

packages/volto/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@
245245
"react-simple-code-editor": "0.7.1",
246246
"react-sortable-hoc": "2.0.0",
247247
"react-test-renderer": "18.2.0",
248+
"react-time-picker": "^7.0.0",
248249
"react-toastify": "5.5.0",
249250
"react-transition-group": "4.4.5",
250251
"react-virtualized": "9.22.3",

packages/volto/src/components/manage/Widgets/DatetimeWidget.jsx

+47-38
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useEffect } from 'react';
1+
import { useState, useEffect } from 'react';
22
import PropTypes from 'prop-types';
33
import { defineMessages, useIntl } from 'react-intl';
44
import loadable from '@loadable/component';
@@ -12,11 +12,10 @@ import leftKey from '@plone/volto/icons/left-key.svg';
1212
import rightKey from '@plone/volto/icons/right-key.svg';
1313
import clearSVG from '@plone/volto/icons/clear.svg';
1414

15-
import 'rc-time-picker/assets/index.css';
1615
import 'react-dates/initialize';
1716
import 'react-dates/lib/css/_datepicker.css';
1817

19-
const TimePicker = loadable(() => import('rc-time-picker'));
18+
const TimePicker = loadable(() => import('react-time-picker'));
2019

2120
const messages = defineMessages({
2221
date: {
@@ -73,7 +72,6 @@ const DatetimeWidgetComponent = (props) => {
7372
resettable,
7473
reactDates,
7574
widgetOptions,
76-
moment,
7775
value,
7876
onChange,
7977
dateOnly,
@@ -91,19 +89,14 @@ const DatetimeWidgetComponent = (props) => {
9189
const { SingleDatePicker } = reactDates;
9290

9391
useEffect(() => {
94-
const parsedDateTime = parseDateTime(
95-
toBackendLang(lang),
96-
value,
97-
undefined,
98-
moment.default,
99-
);
100-
setIsDefault(
101-
parsedDateTime?.toISOString() === moment.default().utc().toISOString(),
102-
);
103-
}, [value, lang, moment]);
92+
const parsedDateTime = parseDateTime(toBackendLang(lang), value);
93+
const nowUTC = new Date().toISOString();
94+
95+
setIsDefault(parsedDateTime?.toISOString() === nowUTC);
96+
}, [value, lang]);
10497

10598
const getInternalValue = () => {
106-
return parseDateTime(toBackendLang(lang), value, undefined, moment.default);
99+
return parseDateTime(toBackendLang(lang), value, undefined);
107100
};
108101

109102
const getDateOnly = () => {
@@ -113,28 +106,38 @@ const DatetimeWidgetComponent = (props) => {
113106
const onDateChange = (date) => {
114107
if (date) {
115108
const isDateOnly = getDateOnly();
116-
const base = (getInternalValue() || moment.default()).set({
117-
year: date.year(),
118-
month: date.month(),
119-
date: date.date(),
120-
...(isDateOnly ? defaultTimeDateOnly : {}),
121-
});
109+
const current = getInternalValue() || new Date();
110+
111+
const updated = new Date(current);
112+
updated.setFullYear(date.year());
113+
updated.setMonth(date.month());
114+
updated.setDate(date.date());
115+
116+
if (isDateOnly) {
117+
updated.setHours(defaultTimeDateOnly.hour);
118+
updated.setMinutes(defaultTimeDateOnly.minute);
119+
updated.setSeconds(defaultTimeDateOnly.second);
120+
}
121+
122122
const dateValue = isDateOnly
123-
? base.format('YYYY-MM-DD')
124-
: base.toISOString();
123+
? updated.toISOString().split('T')[0] // YYYY-MM-DD
124+
: updated.toISOString();
125+
125126
onChange(id, dateValue);
126127
}
127128
setIsDefault(false);
128129
};
129130

130131
const onTimeChange = (time) => {
131132
if (time) {
132-
const base = (getInternalValue() || moment.default()).set({
133-
hours: time?.hours() ?? 0,
134-
minutes: time?.minutes() ?? 0,
135-
seconds: 0,
136-
});
137-
const dateValue = base.toISOString();
133+
const current = getInternalValue() || new Date();
134+
const updated = new Date(current);
135+
136+
updated.setHours(time?.hours() ?? 0);
137+
updated.setMinutes(time?.minutes() ?? 0);
138+
updated.setSeconds(0);
139+
140+
const dateValue = updated.toISOString();
138141
onChange(id, dateValue);
139142
}
140143
};
@@ -168,9 +171,13 @@ const DatetimeWidgetComponent = (props) => {
168171
{...(noPastDates ? {} : { isOutsideRange: () => false })}
169172
onFocusChange={onFocusChange}
170173
noBorder
171-
displayFormat={moment.default
172-
.localeData(toBackendLang(lang))
173-
.longDateFormat('L')}
174+
displayFormat={(date) =>
175+
date?.toLocaleDateString(toBackendLang(lang), {
176+
year: 'numeric',
177+
month: '2-digit',
178+
day: '2-digit',
179+
})
180+
}
174181
navPrev={<PrevIcon />}
175182
navNext={<NextIcon />}
176183
id={`${id}-date`}
@@ -192,9 +199,13 @@ const DatetimeWidgetComponent = (props) => {
192199
showSecond={false}
193200
use12Hours={lang === 'en'}
194201
id={`${id}-time`}
195-
format={moment.default
196-
.localeData(toBackendLang(lang))
197-
.longDateFormat('LT')}
202+
format={(date) =>
203+
date?.toLocaleTimeString(toBackendLang(lang), {
204+
hour: '2-digit',
205+
minute: '2-digit',
206+
hour12: lang === 'en',
207+
})
208+
}
198209
placeholder={intl.formatMessage(messages.time)}
199210
focusOnOpen
200211
placement="bottomRight"
@@ -240,6 +251,4 @@ DatetimeWidgetComponent.defaultProps = {
240251
resettable: true,
241252
};
242253

243-
export default injectLazyLibs(['reactDates', 'moment'])(
244-
DatetimeWidgetComponent,
245-
);
254+
export default injectLazyLibs(['reactDates'])(DatetimeWidgetComponent);

packages/volto/src/helpers/Utils/Utils.jsx

+20-15
Original file line numberDiff line numberDiff line change
@@ -156,25 +156,30 @@ export const getColor = (name) => {
156156
* @param {string} format Date format of choice
157157
* @returns {Object|string} Moment object or string if format is set
158158
*/
159-
export const parseDateTime = (locale, value, format, moment) => {
160-
// Used to set a server timezone or UTC as default
161-
moment.updateLocale(locale, moment.localeData(locale)._config); // copy locale to moment-timezone
162-
let datetime = null;
159+
export const parseDateTime = (locale, value, format) => {
160+
if (!value) return null;
163161

164-
if (value) {
165-
// check if datetime has timezone, otherwise assumes it's UTC
166-
datetime =
167-
!value.match(/T/) || value.match(/T(.)*(-|\+|Z)/g)
168-
? // Since we assume UTC everywhere, then transform to local (momentjs default)
169-
moment(value)
170-
: // This might happen in old Plone versions dates
171-
moment(`${value}Z`);
162+
let dateValue = value;
163+
164+
// If the value does NOT have a timezone, assume it's UTC and append "Z"
165+
const hasTimezone =
166+
value.includes('T') && /(-|\+|Z)/.test(value.split('T')[1]);
167+
if (!hasTimezone) {
168+
dateValue = `${value}Z`;
172169
}
173170

174-
if (format && datetime) {
175-
return datetime.format(format);
171+
const date = new Date(dateValue);
172+
173+
if (isNaN(date.getTime())) {
174+
return null; // invalid date
176175
}
177-
return datetime;
176+
177+
if (format) {
178+
// Example: { year: 'numeric', month: '2-digit', day: '2-digit' }
179+
return new Intl.DateTimeFormat(locale, format).format(date);
180+
}
181+
182+
return date;
178183
};
179184

180185
/**

0 commit comments

Comments
 (0)