diff --git a/src/locale/en.js b/src/locale/en.js index 71c2f226e..2913d5772 100644 --- a/src/locale/en.js +++ b/src/locale/en.js @@ -3,5 +3,10 @@ export default { name: 'en', weekdays: 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - months: 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_') + months: 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), + ordinal: (n) => { + const s = ['th', 'st', 'nd', 'rd'] + const v = n % 100 + return `[${n}${(s[(v - 20) % 10] || s[v] || s[0])}]` + } } diff --git a/src/plugin/advancedFormat/index.js b/src/plugin/advancedFormat/index.js index a78296f7a..7794af987 100644 --- a/src/plugin/advancedFormat/index.js +++ b/src/plugin/advancedFormat/index.js @@ -1,14 +1,8 @@ import { FORMAT_DEFAULT } from '../../constant' -export default (o, c, d) => { // locale needed later +export default (o, c) => { // locale needed later const proto = c.prototype const oldFormat = proto.format - d.en.ordinal = (number) => { - const s = ['th', 'st', 'nd', 'rd'] - const v = number % 100 - return `[${number}${(s[(v - 20) % 10] || s[v] || s[0])}]` - } - // extend en locale here proto.format = function (formatStr) { const locale = this.$locale() diff --git a/src/plugin/bigIntSupport/index.js b/src/plugin/bigIntSupport/index.js new file mode 100644 index 000000000..a90bae1fe --- /dev/null +++ b/src/plugin/bigIntSupport/index.js @@ -0,0 +1,25 @@ +// eslint-disable-next-line valid-typeof +const isBigInt = num => typeof num === 'bigint' +export default (o, c, dayjs) => { + const proto = c.prototype + const parseDate = (cfg) => { + const { date } = cfg + if (isBigInt(date)) { + return Number(date) + } + return date + } + + const oldParse = proto.parse + proto.parse = function (cfg) { + cfg.date = parseDate.bind(this)(cfg) + oldParse.bind(this)(cfg) + } + + + const oldUnix = dayjs.unix + dayjs.unix = function (timestamp) { + const ts = isBigInt(timestamp) ? Number(timestamp) : timestamp + return oldUnix(ts) + } +} diff --git a/src/plugin/objectSupport/index.js b/src/plugin/objectSupport/index.js index 4344dc402..650166606 100644 --- a/src/plugin/objectSupport/index.js +++ b/src/plugin/objectSupport/index.js @@ -1,6 +1,7 @@ export default (o, c, dayjs) => { const proto = c.prototype - const isObject = obj => !(obj instanceof Date) && !(obj instanceof Array) && obj instanceof Object + const isObject = obj => !(obj instanceof Date) && !(obj instanceof Array) + && !proto.$utils().u(obj) && (obj.constructor.name === 'Object') const prettyUnit = (u) => { const unit = proto.$utils().p(u) return unit === 'date' ? 'day' : unit @@ -39,29 +40,36 @@ export default (o, c, dayjs) => { const oldSet = proto.set const oldAdd = proto.add + const oldSubtract = proto.subtract const callObject = function (call, argument, string, offset = 1) { - if (argument instanceof Object) { - const keys = Object.keys(argument) - let chain = this - keys.forEach((key) => { - chain = call.bind(chain)(argument[key] * offset, key) - }) - return chain - } - return call.bind(this)(argument * offset, string) + const keys = Object.keys(argument) + let chain = this + keys.forEach((key) => { + chain = call.bind(chain)(argument[key] * offset, key) + }) + return chain } - proto.set = function (string, int) { - int = int === undefined ? string : int - return callObject.bind(this)(function (i, s) { - return oldSet.bind(this)(s, i) - }, int, string) + proto.set = function (unit, value) { + value = value === undefined ? unit : value + if (unit.constructor.name === 'Object') { + return callObject.bind(this)(function (i, s) { + return oldSet.bind(this)(s, i) + }, value, unit) + } + return oldSet.bind(this)(unit, value) } - proto.add = function (number, string) { - return callObject.bind(this)(oldAdd, number, string) + proto.add = function (value, unit) { + if (value.constructor.name === 'Object') { + return callObject.bind(this)(oldAdd, value, unit) + } + return oldAdd.bind(this)(value, unit) } - proto.subtract = function (number, string) { - return callObject.bind(this)(oldAdd, number, string, -1) + proto.subtract = function (value, unit) { + if (value.constructor.name === 'Object') { + return callObject.bind(this)(oldAdd, value, unit, -1) + } + return oldSubtract.bind(this)(value, unit) } } diff --git a/test/issues/issue2027.correct-order.test.js b/test/issues/issue2027.correct-order.test.js new file mode 100644 index 000000000..3d9d6300c --- /dev/null +++ b/test/issues/issue2027.correct-order.test.js @@ -0,0 +1,78 @@ +import MockDate from 'mockdate' +import dayjs from '../../src' +import duration from '../../src/plugin/duration' +import objectSupport from '../../src/plugin/objectSupport' + +dayjs.extend(objectSupport) +dayjs.extend(duration) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +// issue 2027 +describe('issue 2027 - order objectSupport > Duration', () => { + it('add Duration object returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const durationToAdd = dayjs.duration(6, 'hours') + const testDate = baseDate.add(durationToAdd) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 20:01:02.003') + }) + it('subtract Duration object returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const durationToAdd = dayjs.duration(6, 'hours') + const testDate = baseDate.subtract(durationToAdd) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 08:01:02.003') + }) + + it('add number with unit returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.add(6, 'hours') + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 20:01:02.003') + }) + it('subtract number with unit returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.subtract(6, 'hours') + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 08:01:02.003') + }) + + it('parse string returns correct date', () => { + const testDate = dayjs('2022-06-26T14:01:02.003') + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 14:01:02.003') + }) + it('parse object returns correct date', () => { + const testDate = dayjs({ + year: '2022', + month: '05', + day: '26', + hour: '14', + minute: '01', + second: '02', + millisecond: '003' + }) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 14:01:02.003') + }) + + it('set hour with number returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.hour(10) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 10:01:02.003') + }) + it('set hour with object returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.set({ hour: '10' }) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 10:01:02.003') + }) +}) diff --git a/test/issues/issue2027.swapped-order.test.js b/test/issues/issue2027.swapped-order.test.js new file mode 100644 index 000000000..62a969e49 --- /dev/null +++ b/test/issues/issue2027.swapped-order.test.js @@ -0,0 +1,79 @@ +import MockDate from 'mockdate' +import dayjs from '../../src' +import duration from '../../src/plugin/duration' +import objectSupport from '../../src/plugin/objectSupport' + +dayjs.extend(duration) +dayjs.extend(objectSupport) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +// issue 2027 +describe('issue 2027 - order objectSupport > Duration', () => { + it('add Duration object returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const durationToAdd = dayjs.duration(6, 'hours') + const testDate = baseDate.add(durationToAdd) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 20:01:02.003') + }) + + it('subtract Duration object returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const durationToAdd = dayjs.duration(6, 'hours') + const testDate = baseDate.subtract(durationToAdd) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 08:01:02.003') + }) + + it('add number with unit returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.add(6, 'hours') + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 20:01:02.003') + }) + it('subtract number with unit returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.subtract(6, 'hours') + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 08:01:02.003') + }) + + it('parse string returns correct date', () => { + const testDate = dayjs('2022-06-26T14:01:02.003') + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 14:01:02.003') + }) + it('parse object returns correct date', () => { + const testDate = dayjs({ + year: '2022', + month: '05', + day: '26', + hour: '14', + minute: '01', + second: '02', + millisecond: '003' + }) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 14:01:02.003') + }) + + it('set hour with number returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.hour(10) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 10:01:02.003') + }) + it('set hour with object returns correct date', () => { + const baseDate = dayjs('2022-06-26T14:01:02.003') + const testDate = baseDate.set({ hour: '10' }) + + expect(testDate.format('YYYY-MM-DD HH:mm:ss.SSS')).toBe('2022-06-26 10:01:02.003') + }) +}) diff --git a/test/plugin/bigIntSupport.test.js b/test/plugin/bigIntSupport.test.js new file mode 100644 index 000000000..b2e70866f --- /dev/null +++ b/test/plugin/bigIntSupport.test.js @@ -0,0 +1,30 @@ +import MockDate from 'mockdate' +import moment from 'moment' +import dayjs from '../../src' +import bigIntSupport from '../../src/plugin/bigIntSupport' + +dayjs.extend(bigIntSupport) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +/* global BigInt */ + +it('Parse BigInt ts and tsms', () => { + const tsms = 1666310421101 + const tsmsBig = BigInt(tsms) + const ts = 1666311003 + const tsBig = BigInt(ts) + const momentTsms = moment(tsms) + const momentTs = moment.unix(ts) + expect(dayjs(tsms).valueOf()).toBe(momentTsms.valueOf()) + expect(dayjs(tsms).valueOf()).toBe(dayjs(tsmsBig).valueOf()) + expect(dayjs.unix(ts).valueOf()).toBe(momentTs.valueOf()) + expect(dayjs.unix(tsBig).valueOf()).toBe(dayjs.unix(tsBig).valueOf()) +}) + diff --git a/types/plugin/bigIntSupport.d.ts b/types/plugin/bigIntSupport.d.ts new file mode 100644 index 000000000..d9f2f394e --- /dev/null +++ b/types/plugin/bigIntSupport.d.ts @@ -0,0 +1,11 @@ +import { PluginFunc } from 'dayjs' + +declare module 'dayjs' { + interface ConfigTypeMap { + bigIntSupport: BigInt + } + export function unix(t: BigInt): Dayjs +} + +declare const plugin: PluginFunc +export = plugin