Skip to content

Commit 9bf4076

Browse files
authored
Merge af75753 into 00c223b
2 parents 00c223b + af75753 commit 9bf4076

File tree

10 files changed

+438
-93
lines changed

10 files changed

+438
-93
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## [1.11.7](https://github.com/iamkun/dayjs/compare/v1.11.6...v1.11.7) (2022-12-06)
2+
3+
4+
### Bug Fixes
5+
6+
* Add locale (zh-tw) meridiem ([#2149](https://github.com/iamkun/dayjs/issues/2149)) ([1e9ba76](https://github.com/iamkun/dayjs/commit/1e9ba761ff4e3f2759106dfe1aa9054d5826451c))
7+
* update fa locale ([#2151](https://github.com/iamkun/dayjs/issues/2151)) ([1c26732](https://github.com/iamkun/dayjs/commit/1c267321a1a01b4947e1482bac67d67ebc7c3dfa))
8+
19
## [1.11.6](https://github.com/iamkun/dayjs/compare/v1.11.5...v1.11.6) (2022-10-21)
210

311

src/index.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,15 @@ const dayjs = function (date, c) {
3838
return date.clone()
3939
}
4040
// eslint-disable-next-line no-nested-ternary
41-
const cfg = typeof c === 'object' ? c : {}
41+
let cfg = {}
42+
if ((typeof c === 'object') && !Array.isArray(c)) {
43+
// clone c (contains cfg object)
44+
cfg = JSON.parse(JSON.stringify(c))
45+
}
4246
cfg.date = date
43-
cfg.args = arguments// eslint-disable-line prefer-rest-params
47+
if (cfg.args === undefined) {
48+
cfg.args = Array.prototype.slice.call(arguments) // eslint-disable-line prefer-rest-params
49+
}
4450
return new Dayjs(cfg) // eslint-disable-line no-use-before-define
4551
}
4652

src/plugin/customParseFormat/index.js

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { u } from '../localizedFormat/utils'
2+
import { daysInMonth } from './utils'
23

34
const formattingTokens = /(\[[^[]*\])|([-_:/.,()\s]+)|(A|a|YYYY|YY?|MM?M?M?|Do|DD?|hh?|HH?|mm?|ss?|S{1,3}|z|ZZ?)/g
45

@@ -138,6 +139,30 @@ function correctHours(time) {
138139
}
139140
}
140141

142+
function checkOverflow(year, month, day, hours, minutes, seconds, milliseconds) {
143+
let overflow = false
144+
if (month) {
145+
overflow = overflow || (month < 0 || month > 12)
146+
}
147+
if ((day !== undefined) && (day !== null)) {
148+
overflow = overflow || (day < 1 || day > daysInMonth(year, month))
149+
}
150+
if (hours) {
151+
overflow = overflow || (hours < 0 || hours > 24 ||
152+
(hours === 24 && (minutes !== 0 || seconds !== 0 || milliseconds !== 0)))
153+
}
154+
if (minutes) {
155+
overflow = overflow || (minutes < 0 || minutes > 59)
156+
}
157+
if (seconds) {
158+
overflow = overflow || (seconds < 0 || seconds > 59)
159+
}
160+
if (milliseconds) {
161+
overflow = overflow || (milliseconds < 0 || milliseconds > 999)
162+
}
163+
return overflow
164+
}
165+
141166
function makeParser(format) {
142167
format = u(format, locale && locale.formats)
143168
const array = format.match(formattingTokens)
@@ -154,32 +179,48 @@ function makeParser(format) {
154179
}
155180
}
156181
return function (input) {
157-
const time = {}
158-
for (let i = 0, start = 0; i < length; i += 1) {
182+
const time = { strictMatch: true }
183+
let start = 0
184+
for (let i = 0; i < length; i += 1) {
159185
const token = array[i]
160186
if (typeof token === 'string') {
187+
const separator = input.slice(start, start + token.length)
188+
time.strictMatch = time.strictMatch && (separator === token)
161189
start += token.length
162190
} else {
163191
const { regex, parser } = token
164192
const part = input.slice(start)
165193
const match = regex.exec(part)
166-
const value = match[0]
167-
parser.call(time, value)
168-
input = input.replace(value, '')
194+
if (match !== null) {
195+
const value = match[0] // eslint-disable-line prefer-destructuring
196+
parser.call(time, value)
197+
input = input.replace(value, '')
198+
if (match.index !== 0) {
199+
time.strictMatch = false
200+
start += match.index
201+
}
202+
} else {
203+
time.strictMatch = false
204+
break
205+
}
169206
}
170207
}
208+
if (start < input.length) {
209+
time.strictMatch = false
210+
}
171211
correctHours(time)
172212
return time
173213
}
174214
}
175215

176-
const parseFormattedInput = (input, format, utc) => {
216+
const parseFormattedInput = (input, format, utc, strict) => {
177217
try {
178218
if (['x', 'X'].indexOf(format) > -1) return new Date((format === 'X' ? 1000 : 1) * input)
179219
const parser = makeParser(format)
180220
const {
181-
year, month, day, hours, minutes, seconds, milliseconds, zone
221+
year, month, day, hours, minutes, seconds, milliseconds, zone, strictMatch
182222
} = parser(input)
223+
const isOverflow = checkOverflow(year, month, day, hours, minutes, seconds, milliseconds)
183224
const now = new Date()
184225
const d = day || ((!year && !month) ? now.getDate() : 1)
185226
const y = year || now.getFullYear()
@@ -191,19 +232,22 @@ const parseFormattedInput = (input, format, utc) => {
191232
const m = minutes || 0
192233
const s = seconds || 0
193234
const ms = milliseconds || 0
194-
if (zone) {
195-
return new Date(Date.UTC(y, M, d, h, m, s, ms + (zone.offset * 60 * 1000)))
196-
}
197-
if (utc) {
198-
return new Date(Date.UTC(y, M, d, h, m, s, ms))
235+
let parsedDate = new Date('') // Invalid Date
236+
if (!strict || (strict && strictMatch && !isOverflow)) {
237+
if (zone) {
238+
parsedDate = new Date(Date.UTC(y, M, d, h, m, s, ms + (zone.offset * 60 * 1000)))
239+
} else if (utc) {
240+
parsedDate = new Date(Date.UTC(y, M, d, h, m, s, ms))
241+
} else {
242+
parsedDate = new Date(y, M, d, h, m, s, ms)
243+
}
199244
}
200-
return new Date(y, M, d, h, m, s, ms)
245+
return parsedDate
201246
} catch (e) {
202247
return new Date('') // Invalid Date
203248
}
204249
}
205250

206-
207251
export default (o, C, d) => {
208252
d.p.customParseFormat = true
209253
if (o && o.parseTwoDigitYear) {
@@ -229,22 +273,16 @@ export default (o, C, d) => {
229273
if (!isStrictWithoutLocale && pl) {
230274
locale = d.Ls[pl]
231275
}
232-
this.$d = parseFormattedInput(date, format, utc)
276+
this.$d = parseFormattedInput(date, format, utc, isStrict)
233277
this.init()
234278
if (pl && pl !== true) this.$L = this.locale(pl).$L
235-
// use != to treat
236-
// input number 1410715640579 and format string '1410715640579' equal
237-
// eslint-disable-next-line eqeqeq
238-
if (isStrict && date != this.format(format)) {
239-
this.$d = new Date('')
240-
}
241279
// reset global locale to make parallel unit test
242280
locale = {}
243281
} else if (format instanceof Array) {
244282
const len = format.length
245283
for (let i = 1; i <= len; i += 1) {
246284
args[1] = format[i - 1]
247-
const result = d.apply(this, args)
285+
const result = d.apply(this, [date, cfg])
248286
if (result.isValid()) {
249287
this.$d = result.$d
250288
this.$L = result.$L

src/plugin/customParseFormat/utils.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// handle negative values when overflowing month (handling positive and negative values)
2+
function modMonth(n, x) {
3+
return ((n % x) + x) % x
4+
}
5+
6+
// code duplication as we cannot use 'isLeapYear' plugin here;
7+
// 'isLeapYear' is only working with Dayjs objects
8+
function isLeapYear(year) {
9+
return ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)
10+
}
11+
12+
// eslint-disable-next-line import/prefer-default-export
13+
export const daysInMonth = (year, month) => {
14+
if (Number.isNaN(year) || Number.isNaN(month)) {
15+
return NaN
16+
}
17+
const monthAsIndex = month - 1
18+
const monthInYear = modMonth(monthAsIndex, 12)
19+
year += (monthAsIndex - monthInYear) / 12
20+
// eslint-disable-next-line no-nested-ternary
21+
return monthInYear === 1
22+
? isLeapYear(year)
23+
? 29
24+
: 28
25+
: 31 - ((monthInYear % 7) % 2)
26+
}

src/plugin/utc/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ function offsetFromString(value = '') {
2424
export default (option, Dayjs, dayjs) => {
2525
const proto = Dayjs.prototype
2626
dayjs.utc = function (date) {
27-
const cfg = { date, utc: true, args: arguments } // eslint-disable-line prefer-rest-params
27+
// eslint-disable-next-line prefer-rest-params
28+
const cfg = { date, utc: true, args: Array.prototype.slice.call(arguments) }
2829
return new Dayjs(cfg) // eslint-disable-line no-use-before-define
2930
}
3031

test/comparison.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ test('is after invalid', () => {
347347

348348
// isBefore()
349349

350-
test('is after without units', () => {
350+
test('is before without units', () => {
351351
const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
352352
const mCopy = dayjs(m)
353353
expect(m.isBefore(dayjs(new Date(2012, 3, 2, 3, 5, 5, 10)))).toBe(true, 'year is later')

0 commit comments

Comments
 (0)