Skip to content

Commit

Permalink
Merge pull request #1184 from iamkun/dev
Browse files Browse the repository at this point in the history
D2M
  • Loading branch information
iamkun authored Nov 5, 2020
2 parents e21aa17 + a7f858b commit 0c67c66
Show file tree
Hide file tree
Showing 15 changed files with 115 additions and 47 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ English | [简体中文](./docs/zh-cn/README.zh-CN.md) | [日本語](./docs/ja/R
src="https://user-images.githubusercontent.com/17680888/39081119-3057bbe2-456e-11e8-862c-646133ad4b43.png"
alt="Day.js"></a></p>
<p align="center">Fast <b>2kB</b> alternative to Moment.js with the same modern API</p>
<br>
<p align="center">
<a href="https://www.duohui.cn?utm_source=dayjs" title="多会 - 专业活动管理系统" target="_blank"><img height="120px" alt="多会" src="https://user-images.githubusercontent.com/17680888/97983132-c433bd80-1e0f-11eb-8dde-9f216e05ee8a.png"/></a></p>
<p align="center">
<a href="https://unpkg.com/dayjs/dayjs.min.js"><img
src="http://img.badgesize.io/https://unpkg.com/dayjs/dayjs.min.js?compression=gzip&style=flat-square"
alt="Gzip Size"></a>
<a href="https://www.npmjs.com/package/dayjs"><img src="https://img.shields.io/npm/v/dayjs.svg?style=flat-square&colorB=51C838"
alt="NPM Version"></a>
<a href="https://travis-ci.org/iamkun/dayjs"><img
<a href="https://travis-ci.com/iamkun/dayjs"><img
src="https://img.shields.io/travis/iamkun/dayjs/master.svg?style=flat-square" alt="Build Status"></a>
<a href="https://codecov.io/gh/iamkun/dayjs"><img
src="https://img.shields.io/codecov/c/github/iamkun/dayjs/master.svg?style=flat-square" alt="Codecov"></a>
Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const parseDate = (cfg) => {

class Dayjs {
constructor(cfg) {
this.$L = this.$L || parseLocale(cfg.locale, null, true)
this.$L = parseLocale(cfg.locale, null, true)
this.parse(cfg) // for plugin
}

Expand Down
30 changes: 23 additions & 7 deletions src/plugin/customParseFormat/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { u } from '../localizedFormat/utils'

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

const match1 = /\d/ // 0 - 9
const match2 = /\d\d/ // 00 - 99
const match3 = /\d{3}/ // 000 - 999
const match4 = /\d{4}/ // 0000 - 9999
const match1to2 = /\d\d?/ // 0 - 99
const matchUpperCaseAMPM = /[AP]M/
const matchLowerCaseAMPM = /[ap]m/
const matchSigned = /[+-]?\d+/ // -inf - inf
const matchOffset = /[+-]\d\d:?\d\d/ // +00:00 -00:00 +0000 or -0000
const matchWord = /\d*[^\s\d-:/()]+/ // Word
Expand Down Expand Up @@ -36,13 +36,28 @@ const getLocalePart = (name) => {
part.indexOf ? part : part.s.concat(part.f)
)
}

const meridiemMatch = (input, isLowerCase) => {
let isAfternoon
const { meridiem } = locale
if (!meridiem) {
isAfternoon = input === (isLowerCase ? 'pm' : 'PM')
} else {
for (let i = 1; i <= 24; i += 1) {
// todo: fix input === meridiem(i, 0, isLowerCase)
if (input.indexOf(meridiem(i, 0, isLowerCase)) > -1) {
isAfternoon = i > 12
break
}
}
}
return isAfternoon
}
const expressions = {
A: [matchUpperCaseAMPM, function (input) {
this.afternoon = input === 'PM'
A: [matchWord, function (input) {
this.afternoon = meridiemMatch(input, false)
}],
a: [matchLowerCaseAMPM, function (input) {
this.afternoon = input === 'pm'
a: [matchWord, function (input) {
this.afternoon = meridiemMatch(input, true)
}],
S: [match1, function (input) {
this.milliseconds = +input * 100
Expand Down Expand Up @@ -118,6 +133,7 @@ function correctHours(time) {
}

function makeParser(format) {
format = u(format, locale.formats)
const array = format.match(formattingTokens)
const { length } = array
for (let i = 0; i < length; i += 1) {
Expand Down
2 changes: 1 addition & 1 deletion src/plugin/devHelper/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-console */
export default (o, c, d) => {
if (process.env.NODE_ENV !== 'production') {
if (!process || process.env.NODE_ENV !== 'production') {
const proto = c.prototype
const oldParse = proto.parse
proto.parse = function (cfg) {
Expand Down
7 changes: 4 additions & 3 deletions src/plugin/localeData/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export default (o, c, dayjs) => { // locale needed later
(instance ? instance.format('dd') : getShort(this, 'weekdaysMin', 'weekdays', 2)),
weekdaysShort: instance =>
(instance ? instance.format('ddd') : getShort(this, 'weekdaysShort', 'weekdays', 3)),
longDateFormat: format => getLongDateFormat(this.$locale(), format)

longDateFormat: format => getLongDateFormat(this.$locale(), format),
meridiem: this.$locale().meridiem
}
}
proto.localeData = function () {
Expand All @@ -45,7 +45,8 @@ export default (o, c, dayjs) => { // locale needed later
weekdaysMin: () => dayjs.weekdaysMin(),
months: () => dayjs.months(),
monthsShort: () => dayjs.monthsShort(),
longDateFormat: format => getLongDateFormat(localeObject, format)
longDateFormat: format => getLongDateFormat(localeObject, format),
meridiem: localeObject.meridiem
}
}

Expand Down
16 changes: 3 additions & 13 deletions src/plugin/localizedFormat/index.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
import { FORMAT_DEFAULT } from '../../constant'
import { t } from './utils'
import { u, englishFormats } from './utils'

export default (o, c, d) => {
const proto = c.prototype
const oldFormat = proto.format
const englishFormats = {
LTS: 'h:mm:ss A',
LT: 'h:mm A',
L: 'MM/DD/YYYY',
LL: 'MMMM D, YYYY',
LLL: 'MMMM D, YYYY h:mm A',
LLLL: 'dddd, MMMM D, YYYY h:mm A'
}

d.en.formats = englishFormats
proto.format = function (formatStr = FORMAT_DEFAULT) {
const { formats = {} } = this.$locale()
const result = formatStr.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g, (_, a, b) => {
const B = b && b.toUpperCase()
return a || formats[b] || englishFormats[b] || t(formats[B])
})
const result = u(formatStr, formats)
return oldFormat.call(this, result)
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/plugin/localizedFormat/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,16 @@
export const t = format =>
format.replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g, (_, a, b) => a || b.slice(1))

export const englishFormats = {
LTS: 'h:mm:ss A',
LT: 'h:mm A',
L: 'MM/DD/YYYY',
LL: 'MMMM D, YYYY',
LLL: 'MMMM D, YYYY h:mm A',
LLLL: 'dddd, MMMM D, YYYY h:mm A'
}

export const u = (formatStr, formats) => formatStr.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g, (_, a, b) => {
const B = b && b.toUpperCase()
return a || formats[b] || englishFormats[b] || t(formats[B])
})
5 changes: 2 additions & 3 deletions src/plugin/timezone/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,11 @@ export default (o, c, d) => {
const parseFormat = arg2 && arg1
const timezone = arg2 || arg1 || defaultTimezone
const previousOffset = tzOffset(+d(), timezone)
let localTs
if (typeof input !== 'string') {
// timestamp number || js Date || Day.js
localTs = d(input) + (previousOffset * 60 * 1000)
return d(input).tz(timezone)
}
localTs = localTs || d.utc(input, parseFormat).valueOf()
const localTs = d.utc(input, parseFormat).valueOf()
const [targetTs, targetOffset] = fixOffset(localTs, previousOffset, timezone)
const ins = d(targetTs).utcOffset(targetOffset)
ins.$x.$timezone = timezone
Expand Down
3 changes: 3 additions & 0 deletions src/plugin/utc/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ export default (option, Dayjs, dayjs) => {
}
const oldDiff = proto.diff
proto.diff = function (input, units, float) {
if (this.$u === input.$u) {
return oldDiff.call(this, input, units, float)
}
const localThis = this.local()
const localInput = dayjs(input).local()
return oldDiff.call(localThis, localInput, units, float)
Expand Down
36 changes: 34 additions & 2 deletions test/plugin/customParseFormat.test.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import MockDate from 'mockdate'
import moment from 'moment'
import dayjs from '../../src'
import customParseFormat from '../../src/plugin/customParseFormat'
import '../../src/locale/ru'
import uk from '../../src/locale/uk'
import '../../src/locale/zh-cn'
import '../../src/locale/ru'
import customParseFormat from '../../src/plugin/customParseFormat'
import localizedFormats from '../../src/plugin/localizedFormat'

dayjs.extend(customParseFormat)
dayjs.extend(localizedFormats)

beforeEach(() => {
MockDate.set(new Date())
Expand Down Expand Up @@ -72,6 +74,21 @@ it('recognizes noon in small letters', () => {
expect(dayjs(input, format).valueOf()).toBe(moment(input, format).valueOf())
})

describe('parse localizedFormats', () => {
['zh-cn', 'ru', 'uk', 'en'].forEach((lo) => {
it(`Locale: ${lo}`, () => {
const input = '2018-05-02 01:02:03.004'
dayjs.locale(lo)
moment.locale(lo)
const longDateFormats = ['LT', 'LTS', 'L', 'LL', 'l', 'll', 'lll', 'l LT', 'LL [l] LTS'] // TODO: fix LLL, LLLL and llll
longDateFormats.forEach((f) => {
const localizedInput = moment(input).format(f)
expect(dayjs(localizedInput, f).valueOf()).toBe(moment(localizedInput, f).valueOf())
})
})
})
})

it('leaves non-token parts of the format intact', () => {
const input = '2018-05-02 12:00 +0000 S:/-.() SS h '
const format = 'YYYY-MM-DD HH:mm ZZ [S]:/-.()[ SS h ]'
Expand Down Expand Up @@ -288,3 +305,18 @@ describe('Array format support', () => {
expect(dayjs(input, format, 'zh-cn', true).format('YYYY MMMM DD')).toBe(input)
})
})

describe('meridiem locale', () => {
const format = 'YYYY年M月D日Ah点mm分ss秒'
const format2 = 'YYYY-MM-DD HH:mm:ss'
it('AM', () => {
const input = '2018-05-02 01:02:03'
const date = dayjs(input).locale('zh-cn').format(format)
expect(dayjs(date, format, 'zh-cn').format(format2)).toBe(input)
})
it('PM', () => {
const input = '2018-05-02 20:02:03'
const date = dayjs(input).locale('zh-cn').format(format)
expect(dayjs(date, format, 'zh-cn').format(format2)).toBe(input)
})
})
7 changes: 7 additions & 0 deletions test/plugin/localeData.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,10 @@ it('Locale order', () => {
moment.locale('en')
expect(dayjs.weekdays(true)).toEqual(moment.weekdays(true))
})

it('meridiem', () => {
dayjs.locale('zh-cn')
expect(typeof dayjs.localeData().meridiem).toEqual('function')
expect(typeof dayjs().localeData().meridiem).toEqual('function')
dayjs.locale('en')
})
22 changes: 9 additions & 13 deletions test/plugin/timezone.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,24 +185,20 @@ describe('DST, a time that never existed Fall Back', () => {
expect(d.valueOf()).toBe(1352005199000)
})
})
it('2012-11-04 01:00:00', () => {
const s = '2012-11-04 01:00:00';
[dayjs, moment].forEach((_) => {
const d = _.tz(s, NY)
expect(d.format()).toBe('2012-11-04T01:00:00-04:00')
expect(d.utcOffset()).toBe(-240)
expect(d.valueOf()).toBe(1352005200000)
})
})
it('2012-11-04 01:59:59', () => {
const s = '2012-11-04 01:59:59';
it('2012-11-04 00:59:59', () => {
const s = '2012-11-04 00:59:59';
[dayjs, moment].forEach((_) => {
const d = _.tz(s, NY)
expect(d.format()).toBe('2012-11-04T01:59:59-04:00')
expect(d.format()).toBe('2012-11-04T00:59:59-04:00')
expect(d.utcOffset()).toBe(-240)
expect(d.valueOf()).toBe(1352008799000)
expect(d.valueOf()).toBe(1352005199000)
})
})

// there's no sense to test "2012-11-04 01:59:59 America/New_York"
// cause it's an invalid date and never exist
// and dayjs result it as "2012-11-04T01:59:00-05:00"

it('2012-11-04 02:00:00', () => {
const s = '2012-11-04 02:00:00';
[dayjs, moment].forEach((_) => {
Expand Down
8 changes: 8 additions & 0 deletions test/timezone.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,11 @@ it('UTC and utcOffset', () => {
expect(moment.utc(test2).utcOffset(-60).format())
.toBe(dayjs.utc(test2).utcOffset(-60).format())
})

it('UTC diff in DST', () => {
// DST till 2020-10-25
const day1 = dayjs.utc('20201023') // in DST
const day2 = dayjs.utc('20201026')
expect(day1.diff(day2, 'd'))
.toBe(-3)
})
4 changes: 2 additions & 2 deletions types/plugin/isoWeek.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { PluginFunc, UnitType, ConfigType } from 'dayjs'
import { PluginFunc, OpUnitType, ConfigType } from 'dayjs'

declare const plugin: PluginFunc
export = plugin

type ISOUnitType = UnitType | 'isoWeek';
type ISOUnitType = OpUnitType | 'isoWeek';

declare module 'dayjs' {
interface Dayjs {
Expand Down
2 changes: 2 additions & 0 deletions types/plugin/localeData.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ declare module 'dayjs' {
months(instance?: Dayjs): MonthNames;
monthsShort(instance?: Dayjs): MonthNames;
longDateFormat(format: string): string;
meridiem(hour?: number, minute?: number, isLower?: boolean): string;
}

interface GlobalLocaleDataReturn {
Expand All @@ -25,6 +26,7 @@ declare module 'dayjs' {
months(): MonthNames;
monthsShort(): MonthNames;
longDateFormat(format: string): string;
meridiem(hour?: number, minute?: number, isLower?: boolean): string;
}

interface Dayjs {
Expand Down

0 comments on commit 0c67c66

Please sign in to comment.