Skip to content

Commit

Permalink
feat(useDateFormat): support meridiem format
Browse files Browse the repository at this point in the history
  • Loading branch information
huynl-96 committed Aug 1, 2022
1 parent 8b21ca2 commit da218b0
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 7 deletions.
6 changes: 6 additions & 0 deletions packages/shared/useDateFormat/index.md
Expand Up @@ -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
Expand Down
37 changes: 37 additions & 0 deletions packages/shared/useDateFormat/index.test.ts
Expand Up @@ -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)
})
})
})
32 changes: 25 additions & 7 deletions packages/shared/useDateFormat/index.ts
Expand Up @@ -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()
Expand All @@ -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, () => string | number> = {
YY: () => String(years).slice(-2),
YYYY: () => years,
Expand All @@ -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]())
}
Expand All @@ -66,7 +84,7 @@ export const normalizeDate = (date: DateLike) => {
}
}

return new Date(date!)
return new Date(date)
}

/**
Expand All @@ -79,7 +97,7 @@ export const normalizeDate = (date: DateLike) => {
*/

export function useDateFormat(date: MaybeComputedRef<DateLike>, formatStr: MaybeComputedRef<string> = '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<typeof useDateFormat>

0 comments on commit da218b0

Please sign in to comment.