This repository has been archived by the owner on Mar 22, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
/
index.ts
144 lines (125 loc) · 6.02 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import toDate from '../toDate/index.ts'
import formatters from '../_lib/format/lightFormatters/index.ts'
import getTimezoneOffsetInMilliseconds from '../_lib/getTimezoneOffsetInMilliseconds/index.js'
import isValid from '../isValid/index.js'
import subMilliseconds from '../subMilliseconds/index.ts'
import requiredArgs from '../_lib/requiredArgs/index.ts'
// This RegExp consists of three parts separated by `|`:
// - (\w)\1* matches any sequences of the same letter
// - '' matches two quote characters in a row
// - '(''|[^'])+('|$) matches anything surrounded by two quote characters ('),
// except a single quote symbol, which ends the sequence.
// Two quote characters do not end the sequence.
// If there is no matching single quote
// then the sequence will continue until the end of the string.
// - . matches any single character unmatched by previous parts of the RegExps
const formattingTokensRegExp = /(\w)\1*|''|'(''|[^'])+('|$)|./g
const escapedStringRegExp = /^'([^]*?)'?$/
const doubleQuoteRegExp = /''/g
const unescapedLatinCharacterRegExp = /[a-zA-Z]/
/**
* @name lightFormat
* @category Common Helpers
* @summary Format the date.
*
* @description
* Return the formatted date string in the given format. Unlike `format`,
* `lightFormat` doesn't use locales and outputs date using the most popular tokens.
*
* > ⚠️ Please note that the `lightFormat` tokens differ from Moment.js and other libraries.
* > See: https://git.io/fxCyr
*
* The characters wrapped between two single quotes characters (') are escaped.
* Two single quotes in a row, whether inside or outside a quoted sequence, represent a 'real' single quote.
*
* Format of the string is based on Unicode Technical Standard #35:
* https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
*
* Accepted patterns:
* | Unit | Pattern | Result examples |
* |---------------------------------|---------|-----------------------------------|
* | AM, PM | a..aaa | AM, PM |
* | | aaaa | a.m., p.m. |
* | | aaaaa | a, p |
* | Calendar year | y | 44, 1, 1900, 2017 |
* | | yy | 44, 01, 00, 17 |
* | | yyy | 044, 001, 000, 017 |
* | | yyyy | 0044, 0001, 1900, 2017 |
* | Month (formatting) | M | 1, 2, ..., 12 |
* | | MM | 01, 02, ..., 12 |
* | Day of month | d | 1, 2, ..., 31 |
* | | dd | 01, 02, ..., 31 |
* | Hour [1-12] | h | 1, 2, ..., 11, 12 |
* | | hh | 01, 02, ..., 11, 12 |
* | Hour [0-23] | H | 0, 1, 2, ..., 23 |
* | | HH | 00, 01, 02, ..., 23 |
* | Minute | m | 0, 1, ..., 59 |
* | | mm | 00, 01, ..., 59 |
* | Second | s | 0, 1, ..., 59 |
* | | ss | 00, 01, ..., 59 |
* | Fraction of second | S | 0, 1, ..., 9 |
* | | SS | 00, 01, ..., 99 |
* | | SSS | 000, 0001, ..., 999 |
* | | SSSS | ... |
*
* @param {Date|Number} date - the original date
* @param {String} format - the string of tokens
* @returns {String} the formatted date string
* @throws {TypeError} 2 arguments required
* @throws {RangeError} format string contains an unescaped latin alphabet character
*
* @example
* const result = lightFormat(new Date(2014, 1, 11), 'yyyy-MM-dd')
* //=> '2014-02-11'
*/
type Token = keyof typeof formatters
export default function lightFormat(
dirtyDate: Date | number,
formatStr: string
): string {
requiredArgs(2, arguments)
const originalDate = toDate(dirtyDate)
if (!isValid(originalDate)) {
throw new RangeError('Invalid time value')
}
// Convert the date in system timezone to the same date in UTC+00:00 timezone.
// This ensures that when UTC functions will be implemented, locales will be compatible with them.
// See an issue about UTC functions: https://github.com/date-fns/date-fns/issues/376
const timezoneOffset = getTimezoneOffsetInMilliseconds(originalDate)
const utcDate = subMilliseconds(originalDate, timezoneOffset)
const tokens = formatStr.match(formattingTokensRegExp)
// The only case when formattingTokensRegExp doesn't match the string is when it's empty
if (!tokens) return ''
const result = tokens
.map((substring) => {
// Replace two single quote characters with one single quote character
if (substring === "''") {
return "'"
}
const firstCharacter = substring[0]
if (firstCharacter === "'") {
return cleanEscapedString(substring)
}
const formatter = formatters[firstCharacter as Token]
if (formatter) {
return formatter(utcDate, substring)
}
if (firstCharacter.match(unescapedLatinCharacterRegExp)) {
throw new RangeError(
'Format string contains an unescaped latin alphabet character `' +
firstCharacter +
'`'
)
}
return substring
})
.join('')
return result
}
function cleanEscapedString(input: string) {
const matches = input.match(escapedStringRegExp)
if (!matches) {
return input
}
return matches[1].replace(doubleQuoteRegExp, "'")
}