From da218b0620088a208296679056063d01dfe3d52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Levi=20=28Nguy=E1=BB=85n=20L=C6=B0=C6=A1ng=20Huy=29?= Date: Thu, 28 Jul 2022 23:18:53 +0700 Subject: [PATCH 1/5] feat(useDateFormat): support meridiem format --- packages/shared/useDateFormat/index.md | 6 ++++ packages/shared/useDateFormat/index.test.ts | 37 +++++++++++++++++++++ packages/shared/useDateFormat/index.ts | 32 ++++++++++++++---- 3 files changed, 68 insertions(+), 7 deletions(-) diff --git a/packages/shared/useDateFormat/index.md b/packages/shared/useDateFormat/index.md index bc2c75f8852..77816de21de 100644 --- a/packages/shared/useDateFormat/index.md +++ b/packages/shared/useDateFormat/index.md @@ -25,11 +25,17 @@ Get the formatted date according to the string of tokens passed in, inspired by | `s` | 0-59 | The second | | `ss` | 00-59 | The second, 2-digits | | `SSS` | 000-999 | The millisecond, 3-digits | +| `A` | AM PM | The meridiem | +| `AA` | A.M. P.M. | The meridiem, periods | +| `a` | am pm | The meridiem, lowercase | +| `aa` | a.m. p.m. | The meridiem, lowercase and periods | | `d` | 0-6 | The day of the week, with Sunday as 0 | | `dd` | S-S | The min name of the day of the week | | `ddd` | Sun-Sat | The short name of the day of the week | | `dddd` | Sunday-Saturday | The name of the day of the week | +- Meridiem is customizable by defining `customMeridiem` in `options`. + ## Usage ### Basic diff --git a/packages/shared/useDateFormat/index.test.ts b/packages/shared/useDateFormat/index.test.ts index f63a787a04e..ef9ac890d62 100644 --- a/packages/shared/useDateFormat/index.test.ts +++ b/packages/shared/useDateFormat/index.test.ts @@ -37,4 +37,41 @@ describe('useDateFormat', () => { it('should work with YYYY/MM/DD dddd', () => { expect(useDateFormat(new Date('2022-01-01 15:05:05'), 'YYYY/MM/DD dddd', { locales: 'en-US' }).value).toBe('2022/01/01 Saturday') }) + + describe('meridiem', () => { + it.each([ + // AM + { dateStr: '2022-01-01 03:05:05', formatStr: 'hh:mm:ss A', expected: '03:05:05 AM' }, + { dateStr: '2022-01-01 03:05:05', formatStr: 'hh:mm:ss AA', expected: '03:05:05 A.M.' }, + { dateStr: '2022-01-01 03:05:05', formatStr: 'hh:mm:ss a', expected: '03:05:05 am' }, + { dateStr: '2022-01-01 03:05:05', formatStr: 'hh:mm:ss aa', expected: '03:05:05 a.m.' }, + // PM + { dateStr: '2022-01-01 15:05:05', formatStr: 'hh:mm:ss A', expected: '03:05:05 PM' }, + { dateStr: '2022-01-01 15:05:05', formatStr: 'hh:mm:ss AA', expected: '03:05:05 P.M.' }, + { dateStr: '2022-01-01 15:05:05', formatStr: 'hh:mm:ss a', expected: '03:05:05 pm' }, + { dateStr: '2022-01-01 15:05:05', formatStr: 'hh:mm:ss aa', expected: '03:05:05 p.m.' }, + ])('should work with $formatStr', ({ dateStr, formatStr, expected }) => { + expect(useDateFormat(new Date(dateStr), formatStr).value).toBe(expected) + }) + + const customMeridiem = (hours: number, minutes: number, isLowercase?: boolean, hasPeriod?: boolean) => { + const m = hours > 11 ? (isLowercase ? 'μμ' : 'ΜΜ') : (isLowercase ? 'πμ' : 'ΠΜ') + return hasPeriod ? m.split('').reduce((acc, curr) => acc += `${curr}.`, '') : m + } + + it.each([ + // AM + { dateStr: '2022-01-01 03:05:05', formatStr: 'hh:mm:ss A', expected: '03:05:05 ΠΜ' }, + { dateStr: '2022-01-01 03:05:05', formatStr: 'hh:mm:ss AA', expected: '03:05:05 Π.Μ.' }, + { dateStr: '2022-01-01 03:05:05', formatStr: 'hh:mm:ss a', expected: '03:05:05 πμ' }, + { dateStr: '2022-01-01 03:05:05', formatStr: 'hh:mm:ss aa', expected: '03:05:05 π.μ.' }, + // PM + { dateStr: '2022-01-01 15:05:05', formatStr: 'hh:mm:ss A', expected: '03:05:05 ΜΜ' }, + { dateStr: '2022-01-01 15:05:05', formatStr: 'hh:mm:ss AA', expected: '03:05:05 Μ.Μ.' }, + { dateStr: '2022-01-01 15:05:05', formatStr: 'hh:mm:ss a', expected: '03:05:05 μμ' }, + { dateStr: '2022-01-01 15:05:05', formatStr: 'hh:mm:ss aa', expected: '03:05:05 μ.μ.' }, + ])('should work with custom meridiem with $formatStr', ({ dateStr, formatStr, expected }) => { + expect(useDateFormat(new Date(dateStr), formatStr, { customMeridiem }).value).toBe(expected) + }) + }) }) diff --git a/packages/shared/useDateFormat/index.ts b/packages/shared/useDateFormat/index.ts index 9fa89cdfb3a..c0e4cd78970 100644 --- a/packages/shared/useDateFormat/index.ts +++ b/packages/shared/useDateFormat/index.ts @@ -11,12 +11,25 @@ export interface UseDateFormatOptions { * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locales_argument). */ locales?: Intl.LocalesArgument + + /** + * A custom function to re-modify the way to display meridiem + * + */ + customMeridiem?: (hours: number, minutes: number, isLowercase?: boolean, hasPeriod?: boolean) => string } const REGEX_PARSE = /* #__PURE__ */ /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/ -const REGEX_FORMAT = /* #__PURE__ */ /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g +const REGEX_FORMAT = /* #__PURE__ */ /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a{1,2}|A{1,2}|m{1,2}|s{1,2}|Z{1,2}|SSS/g + +const defaultMeridiem = (hours: number, minutes: number, isLowercase?: boolean, hasPeriod?: boolean) => { + let m = (hours < 12 ? 'AM' : 'PM') + if (hasPeriod) + m = m.split('').reduce((acc, curr) => acc += `${curr}.`, '') + return isLowercase ? m.toLowerCase() : m +} -export const formatDate = (date: Date, formatStr: string, locales?: Intl.LocalesArgument) => { +export const formatDate = (date: Date, formatStr: string, options: UseDateFormatOptions) => { const years = date.getFullYear() const month = date.getMonth() const days = date.getDate() @@ -25,6 +38,7 @@ export const formatDate = (date: Date, formatStr: string, locales?: Intl.Locales const seconds = date.getSeconds() const milliseconds = date.getMilliseconds() const day = date.getDay() + const meridiem = options.customMeridiem ?? defaultMeridiem const matches: Record string | number> = { YY: () => String(years).slice(-2), YYYY: () => years, @@ -42,9 +56,13 @@ export const formatDate = (date: Date, formatStr: string, locales?: Intl.Locales ss: () => `${seconds}`.padStart(2, '0'), SSS: () => `${milliseconds}`.padStart(3, '0'), d: () => day, - dd: () => date.toLocaleDateString(locales, { weekday: 'narrow' }), - ddd: () => date.toLocaleDateString(locales, { weekday: 'short' }), - dddd: () => date.toLocaleDateString(locales, { weekday: 'long' }), + dd: () => date.toLocaleDateString(options.locales, { weekday: 'narrow' }), + ddd: () => date.toLocaleDateString(options.locales, { weekday: 'short' }), + dddd: () => date.toLocaleDateString(options.locales, { weekday: 'long' }), + A: () => meridiem(hours, minutes), + AA: () => meridiem(hours, minutes, false, true), + a: () => meridiem(hours, minutes, true), + aa: () => meridiem(hours, minutes, true, true), } return formatStr.replace(REGEX_FORMAT, (match, $1) => $1 || matches[match]()) } @@ -66,7 +84,7 @@ export const normalizeDate = (date: DateLike) => { } } - return new Date(date!) + return new Date(date) } /** @@ -79,7 +97,7 @@ export const normalizeDate = (date: DateLike) => { */ export function useDateFormat(date: MaybeComputedRef, formatStr: MaybeComputedRef = 'HH:mm:ss', options: UseDateFormatOptions = {}) { - return computed(() => formatDate(normalizeDate(resolveUnref(date)), resolveUnref(formatStr), options?.locales)) + return computed(() => formatDate(normalizeDate(resolveUnref(date)), resolveUnref(formatStr), options)) } export type UseDateFormatReturn = ReturnType From f9e0b882d5c5cdcd370dc5a9d6d10f667984ce04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Levi=20=28Nguy=E1=BB=85n=20L=C6=B0=C6=A1ng=20Huy=29?= Date: Wed, 21 Sep 2022 13:10:52 +0700 Subject: [PATCH 2/5] fix: resolve conflicts --- packages/shared/useDateFormat/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared/useDateFormat/index.ts b/packages/shared/useDateFormat/index.ts index 0045529fac0..931dac47543 100644 --- a/packages/shared/useDateFormat/index.ts +++ b/packages/shared/useDateFormat/index.ts @@ -44,8 +44,8 @@ export const formatDate = (date: Date, formatStr: string, options: UseDateFormat YYYY: () => years, M: () => month + 1, MM: () => `${month + 1}`.padStart(2, '0'), - MMM: () => date.toLocaleDateString(locales, { month: 'short' }), - MMMM: () => date.toLocaleDateString(locales, { month: 'long' }), + MMM: () => date.toLocaleDateString(options.locales, { month: 'short' }), + MMMM: () => date.toLocaleDateString(options.locales, { month: 'long' }), D: () => String(days), DD: () => `${days}`.padStart(2, '0'), H: () => String(hours), From 11af8ba89396762c828408af66c2187d3a691d26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Levi=20=28Nguy=E1=BB=85n=20L=C6=B0=C6=A1ng=20Huy=29?= Date: Wed, 21 Sep 2022 13:16:56 +0700 Subject: [PATCH 3/5] fix: linting --- packages/shared/useDateFormat/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/useDateFormat/index.test.ts b/packages/shared/useDateFormat/index.test.ts index efc56339644..cbc60120457 100644 --- a/packages/shared/useDateFormat/index.test.ts +++ b/packages/shared/useDateFormat/index.test.ts @@ -43,7 +43,7 @@ describe('useDateFormat', () => { it('should work with MMMM DD YYYY', () => { expect(useDateFormat(new Date('2022-01-01 15:05:05'), 'MMMM DD YYYY', { locales: 'en-US' }).value).toBe('January 01 2022') }) - + describe('meridiem', () => { it.each([ // AM From b975834ca3a62f2585a84cc63ebe04f75e0ef592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Levi=20=28Nguy=E1=BB=85n=20L=C6=B0=C6=A1ng=20Huy=29?= Date: Fri, 23 Sep 2022 08:38:50 +0700 Subject: [PATCH 4/5] chore: improve JSDoc --- packages/shared/useDateFormat/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/shared/useDateFormat/index.ts b/packages/shared/useDateFormat/index.ts index 931dac47543..128be7e0e2b 100644 --- a/packages/shared/useDateFormat/index.ts +++ b/packages/shared/useDateFormat/index.ts @@ -93,9 +93,9 @@ export const normalizeDate = (date: DateLike) => { * Get the formatted date according to the string of tokens passed in. * * @see https://vueuse.org/useDateFormat - * @param date - * @param formatStr - * @param options + * @param date - The date to format, can either be a `Date` object, a timestamp, or a string + * @param formatStr - The combination of tokens to format the date + * @param options - UseDateFormatOptions */ export function useDateFormat(date: MaybeComputedRef, formatStr: MaybeComputedRef = 'HH:mm:ss', options: UseDateFormatOptions = {}) { From ec3706a1eab1c6575b949e6c3d6879d4f4d9b40a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Levi=20=28Nguy=E1=BB=85n=20L=C6=B0=C6=A1ng=20Huy=29?= Date: Fri, 23 Sep 2022 08:39:46 +0700 Subject: [PATCH 5/5] docs: add usage guide for custom meridiem --- packages/shared/useDateFormat/index.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/shared/useDateFormat/index.md b/packages/shared/useDateFormat/index.md index 861c28c0fbc..2c523052685 100644 --- a/packages/shared/useDateFormat/index.md +++ b/packages/shared/useDateFormat/index.md @@ -57,7 +57,7 @@ const formatted = useDateFormat(useNow(), 'YYYY-MM-DD HH:mm:ss') ``` -### Use with locale +### Use with locales ```html