@prezly/theme-kit-intl 10.6.1 → 10.8.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/formatting/datetime/lib.cjs +19 -0
- package/build/formatting/datetime/lib.d.ts +2 -0
- package/build/formatting/datetime/lib.mjs +19 -0
- package/build/formatting/datetime/lib.test.cjs +56 -0
- package/build/formatting/datetime/lib.test.mjs +57 -1
- package/build/formatting/datetime/momentFormat.cjs +118 -0
- package/build/formatting/datetime/momentFormat.d.ts +15 -0
- package/build/formatting/datetime/momentFormat.mjs +111 -0
- package/build/formatting/datetime/momentFormat.test.cjs +22 -0
- package/build/formatting/datetime/momentFormat.test.d.ts +1 -0
- package/build/formatting/datetime/momentFormat.test.mjs +20 -0
- package/package.json +3 -3
|
@@ -8,6 +8,7 @@ exports.formatTime = formatTime;
|
|
|
8
8
|
exports.getTimeFormat = exports.getDateFormat = void 0;
|
|
9
9
|
var _index = require("../../locales/index.cjs");
|
|
10
10
|
var _index2 = require("../../utils/index.cjs");
|
|
11
|
+
var _momentFormat = require("./momentFormat.cjs");
|
|
11
12
|
var _shared = require("./shared.cjs");
|
|
12
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; }
|
|
13
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) { _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; }
|
|
@@ -32,15 +33,33 @@ var getTimeFormat = exports.getTimeFormat = (0, _index2.withCache)(function (loc
|
|
|
32
33
|
timeZoneName: 'longOffset'
|
|
33
34
|
}, options));
|
|
34
35
|
});
|
|
36
|
+
var getTimezoneOffsetFormat = (0, _index2.withCache)((locale, timeZone) => new Intl.DateTimeFormat(_index.Locale.from(locale).isoCode, {
|
|
37
|
+
timeZone,
|
|
38
|
+
timeZoneName: 'longOffset',
|
|
39
|
+
hour: 'numeric'
|
|
40
|
+
}));
|
|
41
|
+
function getTimezoneOffsetString(date, locale, timezone) {
|
|
42
|
+
var _parts$find$value, _parts$find;
|
|
43
|
+
var parts = getTimezoneOffsetFormat(locale, timezone).formatToParts(date);
|
|
44
|
+
return (_parts$find$value = (_parts$find = parts.find(p => p.type === 'timeZoneName')) === null || _parts$find === void 0 ? void 0 : _parts$find.value) !== null && _parts$find$value !== void 0 ? _parts$find$value : '';
|
|
45
|
+
}
|
|
35
46
|
function formatDate(input, context) {
|
|
36
47
|
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
37
48
|
var dateTime = (0, _shared.toDate)(input);
|
|
49
|
+
if (context.dateFormat) {
|
|
50
|
+
return (0, _momentFormat.formatWithMomentPattern)(dateTime, context.dateFormat, context.locale, context.timezone);
|
|
51
|
+
}
|
|
38
52
|
var format = getDateFormat(context.locale, context.timezone, options);
|
|
39
53
|
return format.format(dateTime);
|
|
40
54
|
}
|
|
41
55
|
function formatTime(input, context) {
|
|
42
56
|
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
43
57
|
var dateTime = (0, _shared.toDate)(input);
|
|
58
|
+
if (context.timeFormat) {
|
|
59
|
+
var formatted = (0, _momentFormat.formatWithMomentPattern)(dateTime, context.timeFormat, context.locale, context.timezone);
|
|
60
|
+
var offset = getTimezoneOffsetString(dateTime, context.locale, context.timezone);
|
|
61
|
+
return offset ? "".concat(formatted, " ").concat(offset) : formatted;
|
|
62
|
+
}
|
|
44
63
|
var format = getTimeFormat(context.locale, context.timezone, options);
|
|
45
64
|
return format.format(dateTime);
|
|
46
65
|
}
|
|
@@ -5,8 +5,10 @@ export declare const getTimeFormat: (locale: Locale.Code, timeZone: string, opti
|
|
|
5
5
|
export declare function formatDate(input: Date | Iso8601Date | UnixTimestampInSeconds, context: {
|
|
6
6
|
locale: Locale.Code;
|
|
7
7
|
timezone: Timezone;
|
|
8
|
+
dateFormat?: string;
|
|
8
9
|
}, options?: Intl.DateTimeFormatOptions): string;
|
|
9
10
|
export declare function formatTime(input: Date | Iso8601Date | UnixTimestampInSeconds, context: {
|
|
10
11
|
locale: Locale.Code;
|
|
11
12
|
timezone: Timezone;
|
|
13
|
+
timeFormat?: string;
|
|
12
14
|
}, options?: Intl.DateTimeFormatOptions): string;
|
|
@@ -5,6 +5,7 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
|
|
|
5
5
|
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
6
6
|
import { Locale } from "../../locales/index.mjs";
|
|
7
7
|
import { withCache } from "../../utils/index.mjs";
|
|
8
|
+
import { formatWithMomentPattern } from "./momentFormat.mjs";
|
|
8
9
|
import { toDate } from "./shared.mjs";
|
|
9
10
|
export var getDateFormat = withCache(function (locale, timeZone) {
|
|
10
11
|
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
@@ -24,15 +25,33 @@ export var getTimeFormat = withCache(function (locale, timeZone) {
|
|
|
24
25
|
timeZoneName: 'longOffset'
|
|
25
26
|
}, options));
|
|
26
27
|
});
|
|
28
|
+
var getTimezoneOffsetFormat = withCache((locale, timeZone) => new Intl.DateTimeFormat(Locale.from(locale).isoCode, {
|
|
29
|
+
timeZone,
|
|
30
|
+
timeZoneName: 'longOffset',
|
|
31
|
+
hour: 'numeric'
|
|
32
|
+
}));
|
|
33
|
+
function getTimezoneOffsetString(date, locale, timezone) {
|
|
34
|
+
var _parts$find$value, _parts$find;
|
|
35
|
+
var parts = getTimezoneOffsetFormat(locale, timezone).formatToParts(date);
|
|
36
|
+
return (_parts$find$value = (_parts$find = parts.find(p => p.type === 'timeZoneName')) === null || _parts$find === void 0 ? void 0 : _parts$find.value) !== null && _parts$find$value !== void 0 ? _parts$find$value : '';
|
|
37
|
+
}
|
|
27
38
|
export function formatDate(input, context) {
|
|
28
39
|
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
29
40
|
var dateTime = toDate(input);
|
|
41
|
+
if (context.dateFormat) {
|
|
42
|
+
return formatWithMomentPattern(dateTime, context.dateFormat, context.locale, context.timezone);
|
|
43
|
+
}
|
|
30
44
|
var format = getDateFormat(context.locale, context.timezone, options);
|
|
31
45
|
return format.format(dateTime);
|
|
32
46
|
}
|
|
33
47
|
export function formatTime(input, context) {
|
|
34
48
|
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
35
49
|
var dateTime = toDate(input);
|
|
50
|
+
if (context.timeFormat) {
|
|
51
|
+
var formatted = formatWithMomentPattern(dateTime, context.timeFormat, context.locale, context.timezone);
|
|
52
|
+
var offset = getTimezoneOffsetString(dateTime, context.locale, context.timezone);
|
|
53
|
+
return offset ? "".concat(formatted, " ").concat(offset) : formatted;
|
|
54
|
+
}
|
|
36
55
|
var format = getTimeFormat(context.locale, context.timezone, options);
|
|
37
56
|
return format.format(dateTime);
|
|
38
57
|
}
|
|
@@ -11,4 +11,60 @@ describe('getTimeFormat', () => {
|
|
|
11
11
|
test.each(_index.supportedLocales)('should be safe to use with any of the supported locales: %s', locale => {
|
|
12
12
|
expect((0, _lib.getTimeFormat)(_index.Locale.from(locale).code, 'Europe/London')).toBeInstanceOf(Intl.DateTimeFormat);
|
|
13
13
|
});
|
|
14
|
+
});
|
|
15
|
+
describe('formatDate', () => {
|
|
16
|
+
var date = new Date('2024-03-15T10:00:00.000Z');
|
|
17
|
+
it('formats using locale defaults when no dateFormat is provided', () => {
|
|
18
|
+
var result = (0, _lib.formatDate)(date, {
|
|
19
|
+
locale: 'en',
|
|
20
|
+
timezone: 'Europe/London'
|
|
21
|
+
});
|
|
22
|
+
expect(result).toMatch(/March/);
|
|
23
|
+
expect(result).toContain('2024');
|
|
24
|
+
});
|
|
25
|
+
it('formats using a moment-style dateFormat when provided', () => {
|
|
26
|
+
expect((0, _lib.formatDate)(date, {
|
|
27
|
+
locale: 'en',
|
|
28
|
+
timezone: 'Europe/London',
|
|
29
|
+
dateFormat: 'DD/MM/YYYY'
|
|
30
|
+
})).toBe('15/03/2024');
|
|
31
|
+
expect((0, _lib.formatDate)(date, {
|
|
32
|
+
locale: 'en',
|
|
33
|
+
timezone: 'Europe/London',
|
|
34
|
+
dateFormat: 'MMM D, YYYY'
|
|
35
|
+
})).toBe('Mar 15, 2024');
|
|
36
|
+
expect((0, _lib.formatDate)(date, {
|
|
37
|
+
locale: 'en',
|
|
38
|
+
timezone: 'Europe/London',
|
|
39
|
+
dateFormat: 'DD.MM.YYYY'
|
|
40
|
+
})).toBe('15.03.2024');
|
|
41
|
+
});
|
|
42
|
+
it('respects the provided timezone when using a moment-style dateFormat', () => {
|
|
43
|
+
// 2024-03-15 23:30 UTC is already 2024-03-16 in Tokyo
|
|
44
|
+
var lateNight = new Date('2024-03-15T23:30:00.000Z');
|
|
45
|
+
expect((0, _lib.formatDate)(lateNight, {
|
|
46
|
+
locale: 'en',
|
|
47
|
+
timezone: 'Asia/Tokyo',
|
|
48
|
+
dateFormat: 'DD/MM/YYYY'
|
|
49
|
+
})).toBe('16/03/2024');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('formatTime', () => {
|
|
53
|
+
var date = new Date('2024-03-15T10:00:00.000Z');
|
|
54
|
+
it('appends the timezone offset when a moment-style timeFormat is provided', () => {
|
|
55
|
+
var result = (0, _lib.formatTime)(date, {
|
|
56
|
+
locale: 'en',
|
|
57
|
+
timezone: 'Europe/London',
|
|
58
|
+
timeFormat: 'HH:mm'
|
|
59
|
+
});
|
|
60
|
+
expect(result).toMatch(/^10:00 GMT[+-]\d{2}:\d{2}$/);
|
|
61
|
+
});
|
|
62
|
+
it('supports 12-hour moment-style format', () => {
|
|
63
|
+
var result = (0, _lib.formatTime)(date, {
|
|
64
|
+
locale: 'en',
|
|
65
|
+
timezone: 'Europe/London',
|
|
66
|
+
timeFormat: 'hh:mm a'
|
|
67
|
+
});
|
|
68
|
+
expect(result).toMatch(/^10:00 (am|AM) GMT[+-]\d{2}:\d{2}$/);
|
|
69
|
+
});
|
|
14
70
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Locale, supportedLocales } from "../../locales/index.mjs";
|
|
2
|
-
import { getDateFormat, getTimeFormat } from "./lib.mjs";
|
|
2
|
+
import { formatDate, formatTime, getDateFormat, getTimeFormat } from "./lib.mjs";
|
|
3
3
|
describe('getDateFormat', () => {
|
|
4
4
|
test.each(supportedLocales)('should be safe to use with any of the supported locales: %s', locale => {
|
|
5
5
|
expect(getDateFormat(Locale.from(locale).code, 'Europe/London')).toBeInstanceOf(Intl.DateTimeFormat);
|
|
@@ -9,4 +9,60 @@ describe('getTimeFormat', () => {
|
|
|
9
9
|
test.each(supportedLocales)('should be safe to use with any of the supported locales: %s', locale => {
|
|
10
10
|
expect(getTimeFormat(Locale.from(locale).code, 'Europe/London')).toBeInstanceOf(Intl.DateTimeFormat);
|
|
11
11
|
});
|
|
12
|
+
});
|
|
13
|
+
describe('formatDate', () => {
|
|
14
|
+
var date = new Date('2024-03-15T10:00:00.000Z');
|
|
15
|
+
it('formats using locale defaults when no dateFormat is provided', () => {
|
|
16
|
+
var result = formatDate(date, {
|
|
17
|
+
locale: 'en',
|
|
18
|
+
timezone: 'Europe/London'
|
|
19
|
+
});
|
|
20
|
+
expect(result).toMatch(/March/);
|
|
21
|
+
expect(result).toContain('2024');
|
|
22
|
+
});
|
|
23
|
+
it('formats using a moment-style dateFormat when provided', () => {
|
|
24
|
+
expect(formatDate(date, {
|
|
25
|
+
locale: 'en',
|
|
26
|
+
timezone: 'Europe/London',
|
|
27
|
+
dateFormat: 'DD/MM/YYYY'
|
|
28
|
+
})).toBe('15/03/2024');
|
|
29
|
+
expect(formatDate(date, {
|
|
30
|
+
locale: 'en',
|
|
31
|
+
timezone: 'Europe/London',
|
|
32
|
+
dateFormat: 'MMM D, YYYY'
|
|
33
|
+
})).toBe('Mar 15, 2024');
|
|
34
|
+
expect(formatDate(date, {
|
|
35
|
+
locale: 'en',
|
|
36
|
+
timezone: 'Europe/London',
|
|
37
|
+
dateFormat: 'DD.MM.YYYY'
|
|
38
|
+
})).toBe('15.03.2024');
|
|
39
|
+
});
|
|
40
|
+
it('respects the provided timezone when using a moment-style dateFormat', () => {
|
|
41
|
+
// 2024-03-15 23:30 UTC is already 2024-03-16 in Tokyo
|
|
42
|
+
var lateNight = new Date('2024-03-15T23:30:00.000Z');
|
|
43
|
+
expect(formatDate(lateNight, {
|
|
44
|
+
locale: 'en',
|
|
45
|
+
timezone: 'Asia/Tokyo',
|
|
46
|
+
dateFormat: 'DD/MM/YYYY'
|
|
47
|
+
})).toBe('16/03/2024');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('formatTime', () => {
|
|
51
|
+
var date = new Date('2024-03-15T10:00:00.000Z');
|
|
52
|
+
it('appends the timezone offset when a moment-style timeFormat is provided', () => {
|
|
53
|
+
var result = formatTime(date, {
|
|
54
|
+
locale: 'en',
|
|
55
|
+
timezone: 'Europe/London',
|
|
56
|
+
timeFormat: 'HH:mm'
|
|
57
|
+
});
|
|
58
|
+
expect(result).toMatch(/^10:00 GMT[+-]\d{2}:\d{2}$/);
|
|
59
|
+
});
|
|
60
|
+
it('supports 12-hour moment-style format', () => {
|
|
61
|
+
var result = formatTime(date, {
|
|
62
|
+
locale: 'en',
|
|
63
|
+
timezone: 'Europe/London',
|
|
64
|
+
timeFormat: 'hh:mm a'
|
|
65
|
+
});
|
|
66
|
+
expect(result).toMatch(/^10:00 (am|AM) GMT[+-]\d{2}:\d{2}$/);
|
|
67
|
+
});
|
|
12
68
|
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.formatWithMomentPattern = formatWithMomentPattern;
|
|
7
|
+
var _index = require("../../locales/index.cjs");
|
|
8
|
+
var _index2 = require("../../utils/index.cjs");
|
|
9
|
+
/**
|
|
10
|
+
* Format a Date using a moment.js-style format string against a specific locale and timezone.
|
|
11
|
+
*
|
|
12
|
+
* The Prezly backend stores Newsroom date and time formats in moment.js syntax
|
|
13
|
+
* (see Newsroom['date_format'] and Newsroom['time_format']). This formatter
|
|
14
|
+
* supports the subset of moment tokens that show up in those formats and uses
|
|
15
|
+
* `Intl.DateTimeFormat.formatToParts` so the output is correctly localized and
|
|
16
|
+
* timezone-aware without pulling in a heavy dependency.
|
|
17
|
+
*
|
|
18
|
+
* Supported Newsroom date formats: D/M/YY, M/D/YY, YY/M/D, MMM D, YY,
|
|
19
|
+
* MMM D, YYYY, DD/MM/YYYY, MM/DD/YYYY, DD.MM.YYYY.
|
|
20
|
+
* Supported Newsroom time formats: hh:mm a, HH:mm.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
var TOKEN_PATTERN = /YYYY|YY|MMMM|MMM|MM|M|DD|D|HH|hh|H|h|mm|m|ss|s|a|A/g;
|
|
24
|
+
var getPartsFormatter = (0, _index2.withCache)((locale, timeZone) => new Intl.DateTimeFormat(_index.Locale.from(locale).isoCode, {
|
|
25
|
+
timeZone,
|
|
26
|
+
year: 'numeric',
|
|
27
|
+
month: 'long',
|
|
28
|
+
day: '2-digit',
|
|
29
|
+
hour: '2-digit',
|
|
30
|
+
minute: '2-digit',
|
|
31
|
+
second: '2-digit',
|
|
32
|
+
hourCycle: 'h23'
|
|
33
|
+
}));
|
|
34
|
+
var getMonthFormatter = (0, _index2.withCache)((locale, timeZone, month) => new Intl.DateTimeFormat(_index.Locale.from(locale).isoCode, {
|
|
35
|
+
timeZone,
|
|
36
|
+
month
|
|
37
|
+
}));
|
|
38
|
+
var get12HourFormatter = (0, _index2.withCache)((locale, timeZone) => new Intl.DateTimeFormat(_index.Locale.from(locale).isoCode, {
|
|
39
|
+
timeZone,
|
|
40
|
+
hour: 'numeric',
|
|
41
|
+
hourCycle: 'h12'
|
|
42
|
+
}));
|
|
43
|
+
function findPart(parts, type) {
|
|
44
|
+
var _parts$find$value, _parts$find;
|
|
45
|
+
return (_parts$find$value = (_parts$find = parts.find(part => part.type === type)) === null || _parts$find === void 0 ? void 0 : _parts$find.value) !== null && _parts$find$value !== void 0 ? _parts$find$value : '';
|
|
46
|
+
}
|
|
47
|
+
function pad(value, length) {
|
|
48
|
+
return value.padStart(length, '0');
|
|
49
|
+
}
|
|
50
|
+
function formatWithMomentPattern(date, pattern, locale, timeZone) {
|
|
51
|
+
var parts = getPartsFormatter(locale, timeZone).formatToParts(date);
|
|
52
|
+
var year = findPart(parts, 'year');
|
|
53
|
+
var dayOfMonth = findPart(parts, 'day');
|
|
54
|
+
var hour24 = findPart(parts, 'hour');
|
|
55
|
+
var minute = findPart(parts, 'minute');
|
|
56
|
+
var second = findPart(parts, 'second');
|
|
57
|
+
return pattern.replace(TOKEN_PATTERN, match => {
|
|
58
|
+
switch (match) {
|
|
59
|
+
case 'YYYY':
|
|
60
|
+
return year;
|
|
61
|
+
case 'YY':
|
|
62
|
+
return year.slice(-2);
|
|
63
|
+
case 'MMMM':
|
|
64
|
+
{
|
|
65
|
+
var monthParts = getMonthFormatter(locale, timeZone, 'long').formatToParts(date);
|
|
66
|
+
return findPart(monthParts, 'month');
|
|
67
|
+
}
|
|
68
|
+
case 'MMM':
|
|
69
|
+
{
|
|
70
|
+
var _monthParts = getMonthFormatter(locale, timeZone, 'short').formatToParts(date);
|
|
71
|
+
return findPart(_monthParts, 'month');
|
|
72
|
+
}
|
|
73
|
+
case 'MM':
|
|
74
|
+
{
|
|
75
|
+
var _monthParts2 = getMonthFormatter(locale, timeZone, '2-digit').formatToParts(date);
|
|
76
|
+
return pad(findPart(_monthParts2, 'month'), 2);
|
|
77
|
+
}
|
|
78
|
+
case 'M':
|
|
79
|
+
{
|
|
80
|
+
var _monthParts3 = getMonthFormatter(locale, timeZone, 'numeric').formatToParts(date);
|
|
81
|
+
return findPart(_monthParts3, 'month');
|
|
82
|
+
}
|
|
83
|
+
case 'DD':
|
|
84
|
+
return pad(dayOfMonth, 2);
|
|
85
|
+
case 'D':
|
|
86
|
+
return String(Number(dayOfMonth));
|
|
87
|
+
case 'HH':
|
|
88
|
+
return pad(hour24, 2);
|
|
89
|
+
case 'H':
|
|
90
|
+
return String(Number(hour24));
|
|
91
|
+
case 'hh':
|
|
92
|
+
case 'h':
|
|
93
|
+
{
|
|
94
|
+
var hourParts = get12HourFormatter(locale, timeZone).formatToParts(date);
|
|
95
|
+
var hour = findPart(hourParts, 'hour');
|
|
96
|
+
return match === 'hh' ? pad(hour, 2) : String(Number(hour));
|
|
97
|
+
}
|
|
98
|
+
case 'mm':
|
|
99
|
+
return pad(minute, 2);
|
|
100
|
+
case 'm':
|
|
101
|
+
return String(Number(minute));
|
|
102
|
+
case 'ss':
|
|
103
|
+
return pad(second, 2);
|
|
104
|
+
case 's':
|
|
105
|
+
return String(Number(second));
|
|
106
|
+
case 'a':
|
|
107
|
+
case 'A':
|
|
108
|
+
{
|
|
109
|
+
var _hour = Number(hour24);
|
|
110
|
+
var isAm = _hour < 12;
|
|
111
|
+
if (match === 'A') return isAm ? 'AM' : 'PM';
|
|
112
|
+
return isAm ? 'am' : 'pm';
|
|
113
|
+
}
|
|
114
|
+
default:
|
|
115
|
+
return match;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format a Date using a moment.js-style format string against a specific locale and timezone.
|
|
3
|
+
*
|
|
4
|
+
* The Prezly backend stores Newsroom date and time formats in moment.js syntax
|
|
5
|
+
* (see Newsroom['date_format'] and Newsroom['time_format']). This formatter
|
|
6
|
+
* supports the subset of moment tokens that show up in those formats and uses
|
|
7
|
+
* `Intl.DateTimeFormat.formatToParts` so the output is correctly localized and
|
|
8
|
+
* timezone-aware without pulling in a heavy dependency.
|
|
9
|
+
*
|
|
10
|
+
* Supported Newsroom date formats: D/M/YY, M/D/YY, YY/M/D, MMM D, YY,
|
|
11
|
+
* MMM D, YYYY, DD/MM/YYYY, MM/DD/YYYY, DD.MM.YYYY.
|
|
12
|
+
* Supported Newsroom time formats: hh:mm a, HH:mm.
|
|
13
|
+
*/
|
|
14
|
+
import { Locale } from '../../locales';
|
|
15
|
+
export declare function formatWithMomentPattern(date: Date, pattern: string, locale: Locale.Code, timeZone: string): string;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format a Date using a moment.js-style format string against a specific locale and timezone.
|
|
3
|
+
*
|
|
4
|
+
* The Prezly backend stores Newsroom date and time formats in moment.js syntax
|
|
5
|
+
* (see Newsroom['date_format'] and Newsroom['time_format']). This formatter
|
|
6
|
+
* supports the subset of moment tokens that show up in those formats and uses
|
|
7
|
+
* `Intl.DateTimeFormat.formatToParts` so the output is correctly localized and
|
|
8
|
+
* timezone-aware without pulling in a heavy dependency.
|
|
9
|
+
*
|
|
10
|
+
* Supported Newsroom date formats: D/M/YY, M/D/YY, YY/M/D, MMM D, YY,
|
|
11
|
+
* MMM D, YYYY, DD/MM/YYYY, MM/DD/YYYY, DD.MM.YYYY.
|
|
12
|
+
* Supported Newsroom time formats: hh:mm a, HH:mm.
|
|
13
|
+
*/
|
|
14
|
+
import { Locale } from "../../locales/index.mjs";
|
|
15
|
+
import { withCache } from "../../utils/index.mjs";
|
|
16
|
+
var TOKEN_PATTERN = /YYYY|YY|MMMM|MMM|MM|M|DD|D|HH|hh|H|h|mm|m|ss|s|a|A/g;
|
|
17
|
+
var getPartsFormatter = withCache((locale, timeZone) => new Intl.DateTimeFormat(Locale.from(locale).isoCode, {
|
|
18
|
+
timeZone,
|
|
19
|
+
year: 'numeric',
|
|
20
|
+
month: 'long',
|
|
21
|
+
day: '2-digit',
|
|
22
|
+
hour: '2-digit',
|
|
23
|
+
minute: '2-digit',
|
|
24
|
+
second: '2-digit',
|
|
25
|
+
hourCycle: 'h23'
|
|
26
|
+
}));
|
|
27
|
+
var getMonthFormatter = withCache((locale, timeZone, month) => new Intl.DateTimeFormat(Locale.from(locale).isoCode, {
|
|
28
|
+
timeZone,
|
|
29
|
+
month
|
|
30
|
+
}));
|
|
31
|
+
var get12HourFormatter = withCache((locale, timeZone) => new Intl.DateTimeFormat(Locale.from(locale).isoCode, {
|
|
32
|
+
timeZone,
|
|
33
|
+
hour: 'numeric',
|
|
34
|
+
hourCycle: 'h12'
|
|
35
|
+
}));
|
|
36
|
+
function findPart(parts, type) {
|
|
37
|
+
var _parts$find$value, _parts$find;
|
|
38
|
+
return (_parts$find$value = (_parts$find = parts.find(part => part.type === type)) === null || _parts$find === void 0 ? void 0 : _parts$find.value) !== null && _parts$find$value !== void 0 ? _parts$find$value : '';
|
|
39
|
+
}
|
|
40
|
+
function pad(value, length) {
|
|
41
|
+
return value.padStart(length, '0');
|
|
42
|
+
}
|
|
43
|
+
export function formatWithMomentPattern(date, pattern, locale, timeZone) {
|
|
44
|
+
var parts = getPartsFormatter(locale, timeZone).formatToParts(date);
|
|
45
|
+
var year = findPart(parts, 'year');
|
|
46
|
+
var dayOfMonth = findPart(parts, 'day');
|
|
47
|
+
var hour24 = findPart(parts, 'hour');
|
|
48
|
+
var minute = findPart(parts, 'minute');
|
|
49
|
+
var second = findPart(parts, 'second');
|
|
50
|
+
return pattern.replace(TOKEN_PATTERN, match => {
|
|
51
|
+
switch (match) {
|
|
52
|
+
case 'YYYY':
|
|
53
|
+
return year;
|
|
54
|
+
case 'YY':
|
|
55
|
+
return year.slice(-2);
|
|
56
|
+
case 'MMMM':
|
|
57
|
+
{
|
|
58
|
+
var monthParts = getMonthFormatter(locale, timeZone, 'long').formatToParts(date);
|
|
59
|
+
return findPart(monthParts, 'month');
|
|
60
|
+
}
|
|
61
|
+
case 'MMM':
|
|
62
|
+
{
|
|
63
|
+
var _monthParts = getMonthFormatter(locale, timeZone, 'short').formatToParts(date);
|
|
64
|
+
return findPart(_monthParts, 'month');
|
|
65
|
+
}
|
|
66
|
+
case 'MM':
|
|
67
|
+
{
|
|
68
|
+
var _monthParts2 = getMonthFormatter(locale, timeZone, '2-digit').formatToParts(date);
|
|
69
|
+
return pad(findPart(_monthParts2, 'month'), 2);
|
|
70
|
+
}
|
|
71
|
+
case 'M':
|
|
72
|
+
{
|
|
73
|
+
var _monthParts3 = getMonthFormatter(locale, timeZone, 'numeric').formatToParts(date);
|
|
74
|
+
return findPart(_monthParts3, 'month');
|
|
75
|
+
}
|
|
76
|
+
case 'DD':
|
|
77
|
+
return pad(dayOfMonth, 2);
|
|
78
|
+
case 'D':
|
|
79
|
+
return String(Number(dayOfMonth));
|
|
80
|
+
case 'HH':
|
|
81
|
+
return pad(hour24, 2);
|
|
82
|
+
case 'H':
|
|
83
|
+
return String(Number(hour24));
|
|
84
|
+
case 'hh':
|
|
85
|
+
case 'h':
|
|
86
|
+
{
|
|
87
|
+
var hourParts = get12HourFormatter(locale, timeZone).formatToParts(date);
|
|
88
|
+
var hour = findPart(hourParts, 'hour');
|
|
89
|
+
return match === 'hh' ? pad(hour, 2) : String(Number(hour));
|
|
90
|
+
}
|
|
91
|
+
case 'mm':
|
|
92
|
+
return pad(minute, 2);
|
|
93
|
+
case 'm':
|
|
94
|
+
return String(Number(minute));
|
|
95
|
+
case 'ss':
|
|
96
|
+
return pad(second, 2);
|
|
97
|
+
case 's':
|
|
98
|
+
return String(Number(second));
|
|
99
|
+
case 'a':
|
|
100
|
+
case 'A':
|
|
101
|
+
{
|
|
102
|
+
var _hour = Number(hour24);
|
|
103
|
+
var isAm = _hour < 12;
|
|
104
|
+
if (match === 'A') return isAm ? 'AM' : 'PM';
|
|
105
|
+
return isAm ? 'am' : 'pm';
|
|
106
|
+
}
|
|
107
|
+
default:
|
|
108
|
+
return match;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _momentFormat = require("./momentFormat.cjs");
|
|
4
|
+
describe('formatWithMomentPattern', () => {
|
|
5
|
+
var date = new Date('2024-03-05T07:30:45.000Z');
|
|
6
|
+
test.each([['D/M/YY', '5/3/24'], ['M/D/YY', '3/5/24'], ['YY/M/D', '24/3/5'], ['DD/MM/YYYY', '05/03/2024'], ['MM/DD/YYYY', '03/05/2024'], ['DD.MM.YYYY', '05.03.2024']])('formats numeric pattern "%s" as "%s"', (pattern, expected) => {
|
|
7
|
+
expect((0, _momentFormat.formatWithMomentPattern)(date, pattern, 'en', 'UTC')).toBe(expected);
|
|
8
|
+
});
|
|
9
|
+
it('formats month abbreviations using the locale', () => {
|
|
10
|
+
expect((0, _momentFormat.formatWithMomentPattern)(date, 'MMM D, YYYY', 'en', 'UTC')).toBe('Mar 5, 2024');
|
|
11
|
+
});
|
|
12
|
+
it('formats time in 24-hour mode', () => {
|
|
13
|
+
expect((0, _momentFormat.formatWithMomentPattern)(date, 'HH:mm', 'en', 'UTC')).toBe('07:30');
|
|
14
|
+
});
|
|
15
|
+
it('formats time in 12-hour mode with am/pm marker', () => {
|
|
16
|
+
expect((0, _momentFormat.formatWithMomentPattern)(date, 'hh:mm a', 'en', 'UTC')).toBe('07:30 am');
|
|
17
|
+
});
|
|
18
|
+
it('respects the provided timezone', () => {
|
|
19
|
+
var lateNight = new Date('2024-03-15T23:30:00.000Z');
|
|
20
|
+
expect((0, _momentFormat.formatWithMomentPattern)(lateNight, 'DD/MM/YYYY', 'en', 'Asia/Tokyo')).toBe('16/03/2024');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { formatWithMomentPattern } from "./momentFormat.mjs";
|
|
2
|
+
describe('formatWithMomentPattern', () => {
|
|
3
|
+
var date = new Date('2024-03-05T07:30:45.000Z');
|
|
4
|
+
test.each([['D/M/YY', '5/3/24'], ['M/D/YY', '3/5/24'], ['YY/M/D', '24/3/5'], ['DD/MM/YYYY', '05/03/2024'], ['MM/DD/YYYY', '03/05/2024'], ['DD.MM.YYYY', '05.03.2024']])('formats numeric pattern "%s" as "%s"', (pattern, expected) => {
|
|
5
|
+
expect(formatWithMomentPattern(date, pattern, 'en', 'UTC')).toBe(expected);
|
|
6
|
+
});
|
|
7
|
+
it('formats month abbreviations using the locale', () => {
|
|
8
|
+
expect(formatWithMomentPattern(date, 'MMM D, YYYY', 'en', 'UTC')).toBe('Mar 5, 2024');
|
|
9
|
+
});
|
|
10
|
+
it('formats time in 24-hour mode', () => {
|
|
11
|
+
expect(formatWithMomentPattern(date, 'HH:mm', 'en', 'UTC')).toBe('07:30');
|
|
12
|
+
});
|
|
13
|
+
it('formats time in 12-hour mode with am/pm marker', () => {
|
|
14
|
+
expect(formatWithMomentPattern(date, 'hh:mm a', 'en', 'UTC')).toBe('07:30 am');
|
|
15
|
+
});
|
|
16
|
+
it('respects the provided timezone', () => {
|
|
17
|
+
var lateNight = new Date('2024-03-15T23:30:00.000Z');
|
|
18
|
+
expect(formatWithMomentPattern(lateNight, 'DD/MM/YYYY', 'en', 'Asia/Tokyo')).toBe('16/03/2024');
|
|
19
|
+
});
|
|
20
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prezly/theme-kit-intl",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.8.0-alpha.0",
|
|
4
4
|
"description": "Translations for Prezly themes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.mjs",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"test:build": "pnpm test:build:i18n && pnpm test:build:i18n:dist && pnpm test:build:cjs && pnpm test:build:esm && pnpm test:build:types",
|
|
46
46
|
"test:build:types": "tsc --noEmit build/index.d.ts",
|
|
47
47
|
"test:build:i18n": "node tests/test-i18n.mjs",
|
|
48
|
-
"test:build:i18n:dist": "echo 'Checking if the build/i18n directory contains
|
|
48
|
+
"test:build:i18n:dist": "echo 'Checking if the build/i18n directory contains 55 files' && ls build/i18n/*.json | cut -d\\/ -f3 | cut -d. -f1 | sort | wc -l | grep -q 55 || (echo 'Fail' && false) && echo 'OK'",
|
|
49
49
|
"test:build:cjs": "node tests/test-build.cjs",
|
|
50
50
|
"test:build:esm": "node tests/test-build.mjs",
|
|
51
51
|
"intl:extract": "formatjs extract 'src/messages/**/*.ts' --out-file i18n/en.json --format crowdin --id-interpolation-pattern '[sha512:contenthash:base64:6]'",
|
|
@@ -74,5 +74,5 @@
|
|
|
74
74
|
"publishConfig": {
|
|
75
75
|
"access": "public"
|
|
76
76
|
},
|
|
77
|
-
"gitHead": "
|
|
77
|
+
"gitHead": "d056a9b0e4ca7ef2adc14ef502c9a2eafb7dcf96"
|
|
78
78
|
}
|