Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preparse postformat + implementation in ar locale + tests #1255

Merged
merged 6 commits into from
Jan 2, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/locale/ar.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,31 @@
import dayjs from 'dayjs'

const months = 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_')
const symbolMap = {
1: '١',
2: '٢',
3: '٣',
4: '٤',
5: '٥',
6: '٦',
7: '٧',
8: '٨',
9: '٩',
0: '٠'
}

const numberMap = {
'١': '1',
'٢': '2',
'٣': '3',
'٤': '4',
'٥': '5',
'٦': '6',
'٧': '7',
'٨': '8',
'٩': '9',
'٠': '0'
}

const locale = {
name: 'ar',
Expand All @@ -26,6 +51,19 @@ const locale = {
y: 'عام واحد',
yy: '%d أعوام'
},
preparse(string) {
return string
.replace(
/[١٢٣٤٥٦٧٨٩٠]/g,
match => numberMap[match]
)
.replace(/،/g, ',')
},
postformat(string) {
return string
.replace(/\d/g, match => symbolMap[match])
.replace(/,/g, '،')
},
ordinal: n => n,
formats: {
LT: 'HH:mm',
Expand Down
53 changes: 53 additions & 0 deletions src/plugin/preParsePostFormat/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import localeData from '../localeData'

// Plugin template from https://day.js.org/docs/en/plugin/plugin
export default (option, dayjsClass, dayjsFactory) => {
// This plugin depends on other plugins - so I will import them here
// equivalent to dayjsClass.extend(duration)
localeData(option, dayjsClass, dayjsFactory)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't have to import other plugins like this.

Just listing the dependent plugin in the doc.


const oldParse = dayjsClass.prototype.parse
dayjsClass.prototype.parse = function (cfg) {
if (typeof cfg.date === 'string') {
const locale = this.$locale()
cfg.date =
locale && locale.preparse ? locale.preparse(cfg.date) : cfg.date
}
// original parse result
return oldParse.bind(this)(cfg)
}

// // overriding existing API
// // e.g. extend dayjs().format()
const oldFormat = dayjsClass.prototype.format
dayjsClass.prototype.format = function (...args) {
// original format result
const result = oldFormat.call(this, ...args)
// return modified result
const locale = this.$locale()
return locale.postformat ? locale.postformat(result) : result
}

const oldFromTo = dayjsClass.prototype.fromToBase

if (oldFromTo) {
dayjsClass.prototype.fromToBase = function (
input,
withoutSuffix,
instance,
isFrom
) {
const locale = this.$locale() || instance.$locale()

// original format result
return oldFromTo.call(
this,
input,
withoutSuffix,
instance,
isFrom,
locale.postformat
)
}
}
}
12 changes: 10 additions & 2 deletions src/plugin/relativeTime/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default (o, c, d) => {
yy: '%d years'
}
d.en.relativeTime = relObj
const fromTo = (input, withoutSuffix, instance, isFrom) => {
proto.fromToBase = (input, withoutSuffix, instance, isFrom, postFormat) => {
const loc = instance.$locale().relativeTime || relObj
const T = o.thresholds || [
{ l: 's', r: 44, d: C.S },
Expand All @@ -46,11 +46,14 @@ export default (o, c, d) => {
? d(input).diff(instance, t.d, true)
: instance.diff(input, t.d, true)
}
const abs = (o.rounding || Math.round)(Math.abs(result))
let abs = (o.rounding || Math.round)(Math.abs(result))
isFuture = result > 0
if (abs <= t.r || !t.r) {
if (abs <= 1 && i > 0) t = T[i - 1] // 1 minutes -> a minute, 0 seconds -> 0 second
const format = loc[t.l]
if (postFormat) {
abs = postFormat(`${abs}`)
}
if (typeof format === 'string') {
out = format.replace('%d', abs)
} else {
Expand All @@ -66,6 +69,11 @@ export default (o, c, d) => {
}
return pastOrFuture.replace('%s', out)
}

function fromTo(input, withoutSuffix, instance, isFrom) {
return proto.fromToBase(input, withoutSuffix, instance, isFrom)
}

proto.to = function (input, withoutSuffix) {
return fromTo(input, withoutSuffix, this, true)
}
Expand Down
50 changes: 50 additions & 0 deletions test/locale/ar.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import moment from 'moment'
import MockDate from 'mockdate'
import dayjs from '../../src'
import relativeTime from '../../src/plugin/relativeTime'
import preParsePostFormat from '../../src/plugin/preParsePostFormat'
import '../../src/locale/ar'

dayjs.extend(relativeTime)
dayjs.extend(preParsePostFormat)

beforeEach(() => {
MockDate.set(new Date())
})

afterEach(() => {
MockDate.reset()
})

it('Format Month with locale function', () => {
for (let i = 0; i <= 7; i += 1) {
const dayjsAR = dayjs().locale('ar').add(i, 'day')
const momentAR = moment().locale('ar').add(i, 'day')
const testFormat1 = 'DD MMMM YYYY MMM'
const testFormat2 = 'MMMM'
const testFormat3 = 'MMM'
expect(dayjsAR.format(testFormat1)).toEqual(momentAR.format(testFormat1))
expect(dayjsAR.format(testFormat2)).toEqual(momentAR.format(testFormat2))
expect(dayjsAR.format(testFormat3)).toEqual(momentAR.format(testFormat3))
}
})

it('Preparse with locale function', () => {
for (let i = 0; i <= 7; i += 1) {
dayjs.locale('ar')
const momentAR = moment().locale('ar').add(i, 'day')
expect(dayjs(momentAR.format()).format()).toEqual(momentAR.format())
}
})

it('RelativeTime: Time from X gets formatted', () => {
const T = [
[44.4, 'second', 'منذ ثانية واحدة']
]

T.forEach((t) => {
dayjs.locale('ar')
expect(dayjs().from(dayjs().add(t[0], t[1])))
.toBe(t[2])
})
})
167 changes: 167 additions & 0 deletions test/plugin/preParsePostFormat.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import MockDate from 'mockdate'
// import moment from 'moment'
import dayjs from '../../src'
import preParsePostFormat from '../../src/plugin/preParsePostFormat'
import duration from '../../src/plugin/duration'
import calendar from '../../src/plugin/calendar'
import objectSupport from '../../src/plugin/objectSupport'
import customParseFormat from '../../src/plugin/customParseFormat'
import relativeTime from '../../src/plugin/relativeTime'
import utc from '../../src/plugin/utc'
import arraySupport from '../../src/plugin/arraySupport'
import en from '../../src/locale/en'

dayjs.extend(utc)
dayjs.extend(customParseFormat)
dayjs.extend(arraySupport)
dayjs.extend(objectSupport)
dayjs.extend(calendar)
dayjs.extend(duration)
dayjs.extend(relativeTime)
dayjs.extend(preParsePostFormat)

const symbolMap = {
1: '!',
2: '@',
3: '#',
4: '$',
5: '%',
6: '^',
7: '&',
8: '*',
9: '(',
0: ')'
}
const numberMap = {
'!': '1',
'@': '2',
'#': '3',
$: '4',
'%': '5',
'^': '6',
'&': '7',
'*': '8',
'(': '9',
')': '0'
}

const localeCustomizations = {
...en,
preparse(string) {
if (typeof string !== 'string') {
// console.error('preparse - Expected string, got', {
// string
// })
throw new Error(`preparse - Expected string, got ${typeof string}`)
}
try {
const res = string.replace(/[!@#$%^&*()]/g, match => numberMap[match])
// console.log('Called custom preparse', { string, res })
return res
} catch (error) {
const errorMsg = `Unexpected error during preparse of '${string}' - ${error}`
// console.error(errorMsg)
throw new Error(errorMsg)
}
},
postformat(string) {
if (typeof string !== 'string') {
// console.error('postformat - Expected string, got', {
// string
// })
throw new Error(`postformat - Expected string, got ${typeof string}`)
}
try {
const res = string.replace(/\d/g, match => symbolMap[match])
// console.log('Called custom postformat', { string, res })
return res
} catch (error) {
const errorMsg = `Unexpected error during postFormat of '${string}' - ${error}`
// console.error(errorMsg)
throw new Error(errorMsg)
}
}
}

beforeEach(() => {
MockDate.set(new Date())
dayjs.locale('symbol', localeCustomizations)
})

afterEach(() => {
MockDate.reset()
dayjs.locale('symbol', null)
})

describe('preparse and postformat', () => {
describe('transform', () => {
const TEST_DATE = '@)!@-)*-@&'
const TEST_NUM = 1346025600
it('preparse string + format', () =>
expect(dayjs.utc(TEST_DATE, 'YYYY-MM-DD').unix()).toBe(TEST_NUM))
it('preparse ISO8601 string', () =>
expect(dayjs.utc(TEST_DATE).unix()).toBe(TEST_NUM))
it('postformat', () =>
expect(dayjs
.unix(TEST_NUM)
.utc()
.format('YYYY-MM-DD'))
.toBe(TEST_DATE))
})

describe('transform from', () => {
dayjs.locale('symbol', localeCustomizations)
const start = dayjs([2007, 1, 28])

const t1 = dayjs([2007, 1, 28]).add({ s: 90 })
it('postformat should work on dayjs.fn.from', () =>
expect(start.from(t1, true)).toBe('@ minutes'))

const t2 = dayjs().add(6, 'd')
it('postformat should work on dayjs.fn.fromNow', () =>
expect(t2.fromNow(true)).toBe('^ days'))

it('postformat should work on dayjs.duration.fn.humanize', () =>
expect(dayjs.duration(10, 'h').humanize()).toBe('!) hours'))
})
})

describe('calendar day', () => {
const a = dayjs()
.hour(12)
.minute(0)
.second(0)

it('today at the same time', () =>
expect(dayjs(a).calendar()).toBe('Today at !@:)) PM'))

it('Now plus 25 min', () =>
expect(dayjs(a)
.add({ m: 25 })
.calendar())
.toBe('Today at !@:@% PM'))

it('Now plus 1 hour', () =>
expect(dayjs(a)
.add({ h: 1 })
.calendar())
.toBe('Today at !:)) PM'))

it('tomorrow at the same time', () =>
expect(dayjs(a)
.add({ d: 1 })
.calendar())
.toBe('Tomorrow at !@:)) PM'))

it('Now minus 1 hour', () =>
expect(dayjs(a)
.subtract({ h: 1 })
.calendar())
.toBe('Today at !!:)) AM'))

it('yesterday at the same time', () =>
expect(dayjs(a)
.subtract({ d: 1 })
.calendar())
.toBe('Yesterday at !@:)) PM'))
})
4 changes: 4 additions & 0 deletions types/plugin/preParsePostFormat.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { PluginFunc } from 'dayjs'

declare const plugin: PluginFunc
export = plugin