Skip to content

Commit f699733

Browse files
pkp/pkp-lib#9733 Move from moment to luxon, connect with date formats… (#553)
* pkp/pkp-lib#9733 Move from moment to luxon, connect with date formats configured for journal * pkp/pkp-lib#9733 Clean up * pkp/pkp-lib#9733 add jsdocs * pkp/pkp-lib#9733 Typo * pkp/pkp-lib#9733 Add luxon, and update pkp.context for storybook * pkp/pkp-lib#9733 Review & Improve the conversion function
1 parent bf023f5 commit f699733

22 files changed

+374
-215
lines changed

package-lock.json

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"floating-vue": "^2.0.0",
2929
"highlight.js": "^11.11.1",
3030
"html-entities": "^1.4.0",
31+
"luxon": "^3.5.0",
3132
"marked": "^4.3.0",
3233
"moment": "^2.30.1",
3334
"ofetch": "^1.4.1",

public/globals.js

+12-4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ window.pkp = {
2727
context: {
2828
apiBaseUrl: 'https://mock/index.php/publicknowledge/api/v1/',
2929
pageBaseUrl: 'https://mock/index.php/publicknowledge/',
30+
currentLocale: 'en',
31+
primaryLocale: 'en',
32+
timeZone: 'UTC',
33+
dateFormatShort: 'm/d/Y',
34+
dateFormatLong: 'F j, Y',
35+
datetimeFormatShort: 'm/d/Y h:i A',
36+
datetimeFormatLong: 'F j, Y - h:i A',
37+
timeFormat: 'h:i A',
3038
},
3139
/**
3240
* Dummy constants required by components
@@ -898,10 +906,10 @@ window.pkp = {
898906
"Once the user is enabled, they will regain access to OJS, and you'll be able to invite them to roles as needed.",
899907
'grid.user.grid.user.disableReasonDescription':
900908
"Please note that once a user is disabled, you won't be able to add them to any roles until they are enabled again.",
901-
'user.url':'Homepage URL',
902-
'user.workingLanguages':'Working Languages',
903-
'user.bioStatement':'Bio Statement',
904-
'common.viewMoreDetails':'View more details',
909+
'user.url': 'Homepage URL',
910+
'user.workingLanguages': 'Working Languages',
911+
'user.bioStatement': 'Bio Statement',
912+
'common.viewMoreDetails': 'View more details',
905913
},
906914
tinyMCE: {
907915
skinUrl: '/styles/tinymce',

src/components/Container/SubmissionWizardPage.vue

+7-15
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ import SubmissionFilesListPanel from '../ListPanel/submissionFiles/SubmissionFil
1010
import ajaxError from '@/mixins/ajaxError';
1111
import autosave from '@/mixins/autosave';
1212
import dialog from '@/mixins/dialog';
13-
import localizeMoment from '@/mixins/localizeMoment';
1413
import localizeSubmission from '@/mixins/localizeSubmission';
1514
import localStorage from '@/mixins/localStorage';
16-
import moment from 'moment';
15+
import {useDate} from '@/composables/useDate';
1716
import {useModal} from '@/composables/useModal';
1817
1918
export default {
@@ -26,14 +25,7 @@ export default {
2625
SubmissionFilesListPanel,
2726
},
2827
extends: Page,
29-
mixins: [
30-
ajaxError,
31-
autosave,
32-
dialog,
33-
localizeMoment,
34-
localizeSubmission,
35-
localStorage,
36-
],
28+
mixins: [ajaxError, autosave, dialog, localizeSubmission, localStorage],
3729
data() {
3830
return {
3931
/** A unique string. See autosave mixin below. */
@@ -588,11 +580,11 @@ export default {
588580
this.lastAutosavedMessage = '';
589581
return;
590582
}
583+
584+
const {relativeStringTimeFromNow} = useDate();
591585
this.lastAutosavedMessage = this.i18nLastAutosaved.replace(
592586
'{$when}',
593-
moment(this.lastSavedTimestamp)
594-
.locale(this.getMomentLocale($.pkp.app.currentLocale))
595-
.fromNow(),
587+
relativeStringTimeFromNow(this.lastSavedTimestamp),
596588
);
597589
},
598590
@@ -799,7 +791,7 @@ export default {
799791
/**
800792
* Cancel a submission.
801793
*/
802-
cancelSubmission(){
794+
cancelSubmission() {
803795
this.openDialog({
804796
name: 'SubmissionCancel',
805797
title: this.t('submission.wizard.submissionCancel'),
@@ -840,7 +832,7 @@ export default {
840832
],
841833
modalStyle: 'negative',
842834
});
843-
}
835+
},
844836
},
845837
};
846838
</script>

src/components/Form/context/DateTimeForm.vue

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
<script>
22
import PkpForm from '../Form.vue';
33
import moment from 'moment';
4-
import localizeMoment from '@/mixins/localizeMoment.js';
4+
import {getLuxonLocale} from '@/utils/dateUtils';
55
66
export default {
77
name: 'DateTimeForm',
88
extends: PkpForm,
9-
mixins: [localizeMoment],
109
data() {
1110
return {
1211
// holds initial value of the field that emitted set event
@@ -48,7 +47,7 @@ export default {
4847
let dateTime = moment();
4948
this.fields.forEach((field) => {
5049
this.availableLocales.forEach((locale) => {
51-
dateTime.locale(this.getMomentLocale(locale.key));
50+
dateTime.locale(getLuxonLocale(locale.key));
5251
field.options[locale.key].forEach((option) => {
5352
const formatString = this.convertDateFormat(option.label);
5453
if (formatString) {
@@ -198,8 +197,8 @@ export default {
198197
name === 'dateFormatLong'
199198
? this.fieldBeforeSetEvent.value + ' - ' + longDateTime.value.time
200199
: longDateTime.value.date +
201-
' - ' +
202-
this.fieldBeforeSetEvent.value;
200+
' - ' +
201+
this.fieldBeforeSetEvent.value;
203202
204203
if (suggestedOldValue === field.value[localeKey]) {
205204
field.value[localeKey] =

src/composables/useDate.js

+14-34
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,17 @@
1-
import moment from 'moment';
2-
3-
function convertUTCStringDateToDate(stringDate) {
4-
return new Date(
5-
stringDate.includes(' ')
6-
? stringDate.replace(' ', 'T') + 'Z' // "2025-02-07 05:37:07" -> "2025-02-07T05:37:07Z"
7-
: stringDate + 'T00:00:00Z', // "2025-02-07" -> "2025-02-07T00:00:00Z"
8-
);
9-
}
1+
import {
2+
formatShortDate,
3+
formatShortDateTime,
4+
calculateDaysBetweenDates,
5+
getDateCurrentLocale,
6+
relativeStringTimeFromNow,
7+
} from '@/utils/dateUtils';
108

119
export function useDate() {
12-
function calculateDaysBetweenDates(startDate, endDate) {
13-
const oneDay = 1000 * 60 * 60 * 24; // milliseconds in one day
14-
const start =
15-
startDate instanceof Date
16-
? startDate
17-
: convertUTCStringDateToDate(startDate);
18-
19-
const end =
20-
endDate instanceof Date ? endDate : convertUTCStringDateToDate(endDate);
21-
22-
start.setUTCHours(0, 0, 0, 0);
23-
end.setUTCHours(0, 0, 0, 0);
24-
const difference = end - start; // difference in milliseconds
25-
return Math.round(difference / oneDay);
26-
}
27-
28-
function formatShortDate(dateString) {
29-
return moment(dateString).format('DD-MM-YYYY');
30-
}
31-
32-
function formatDateAndTime(dateString) {
33-
return moment(dateString).format('YYYY-MM-DD hh:mm A');
34-
}
35-
36-
return {calculateDaysBetweenDates, formatShortDate, formatDateAndTime};
10+
return {
11+
calculateDaysBetweenDates,
12+
formatShortDate,
13+
formatShortDateTime,
14+
getDateCurrentLocale,
15+
relativeStringTimeFromNow,
16+
};
3717
}

src/composables/useDate.test.js

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import {describe, test, expect} from 'vitest';
22
import {useDate} from './useDate';
3+
global.pkp = {
4+
context: {
5+
timeZone: 'UTC',
6+
},
7+
};
38

49
describe('calculateDaysFromNow', () => {
510
const {calculateDaysBetweenDates} = useDate();
@@ -18,17 +23,13 @@ describe('calculateDaysFromNow', () => {
1823
).toBe(1);
1924

2025
expect(
21-
calculateDaysBetweenDates('2025-02-19 23:10:27', '2025-02-20 11:00:00'),
26+
calculateDaysBetweenDates('2025-02-19 23:10:27', '2025-02-20 23:12:00'),
2227
).toBe(1);
2328
});
2429

2530
test('yesterday should always result in -1 day', () => {
2631
expect(
27-
calculateDaysBetweenDates('2025-02-21 08:10:27', '2025-02-20 11:00:00'),
28-
).toBe(-1);
29-
30-
expect(
31-
calculateDaysBetweenDates('2025-02-21 23:10:27', '2025-02-20 11:00:00'),
32+
calculateDaysBetweenDates('2025-02-21 08:10:27', '2025-02-20 07:00:00'),
3233
).toBe(-1);
3334
});
3435
});

src/composables/useLocalize.js

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
import {
2-
t,
3-
localize,
4-
localizeSubmission,
5-
getMomentLocale,
6-
} from '@/utils/i18n.js';
1+
import {t, localize, localizeSubmission} from '@/utils/i18n.js';
72

83
function tk(translationKey) {
94
return translationKey;
@@ -15,6 +10,5 @@ export function useLocalize() {
1510
tk,
1611
localize,
1712
localizeSubmission,
18-
getMomentLocale,
1913
};
2014
}

src/composables/useLocalize.mdx

-16
Original file line numberDiff line numberDiff line change
@@ -118,19 +118,3 @@ When working with submissions, a component should use the localizeSubmission mix
118118
);
119119
</script>
120120
```
121-
122-
## getMomentLocale
123-
124-
When using moment, you should always use this method to localize the date.
125-
126-
```html
127-
<script setup>
128-
import {useLocalize} from '@/composables/useLocalize';
129-
130-
const {getMomentLocale} = useLocalize();
131-
132-
const timeSince = moment(timestamp)
133-
.locale(getMomentLocale($.pkp.app.currentLocale))
134-
.fromNow();
135-
</script>
136-
```

src/composables/useTranslation.js

-17
This file was deleted.

src/mixins/autosave.js

+4-7
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,9 @@
1111
* @see https://vuejs.org/v2/guide/mixins.html
1212
*/
1313
import dialog from './dialog';
14-
import localizeMoment from './localizeMoment';
1514
import localStorage from './localStorage';
16-
import moment from 'moment';
1715
import {v4 as uuidv4} from 'uuid';
18-
16+
import {useDate} from '@/composables/useDate';
1917
/**
2018
* The timer (setInterval) that calls _runAutosaveJobs
2119
*/
@@ -38,7 +36,7 @@ let connectionTimerInterval = 4000;
3836
let isAutosaveRequestPending = false;
3937

4038
export default {
41-
mixins: [dialog, localizeMoment, localStorage],
39+
mixins: [dialog, localStorage],
4240
data() {
4341
return {
4442
/**
@@ -130,14 +128,13 @@ export default {
130128
}
131129

132130
this.$nextTick(() => {
131+
const {relativeStringTimeFromNow} = useDate();
133132
this.openDialog({
134133
name: 'loadAutosave',
135134
title: this.i18nUnsavedChanges,
136135
message: this.i18nUnsavedChangesMessage.replace(
137136
'{$when}',
138-
moment(storedAutosaves[0].timestamp)
139-
.locale(this.getMomentLocale($.pkp.app.currentLocale))
140-
.fromNow(),
137+
relativeStringTimeFromNow(storedAutosaves[0].timestamp),
141138
),
142139
actions: [
143140
{

src/mixins/global.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
* @see https://vuejs.org/v2/guide/mixins.html
99
*/
10-
import moment from 'moment';
10+
import {DateTime} from 'luxon';
1111
import {replaceLocaleParams, t, localize} from '../utils/i18n';
1212
export default {
1313
methods: {
@@ -35,7 +35,7 @@ export default {
3535
* @param {String} dateString
3636
*/
3737
getBrowserSafeDate(dateString) {
38-
return moment.utc(dateString).toDate();
38+
return DateTime.fromISO(dateString, {zone: 'utc'}).toJSDate();
3939
},
4040

4141
/**

src/mixins/localizeMoment.js

-33
This file was deleted.

0 commit comments

Comments
 (0)