UNPKG

jalaliday

Version:

Persian (Jalali, Khorshidi) Plugin for Day.js

325 lines (296 loc) 8.81 kB
import type { PluginFunc } from 'dayjs' import fa from 'dayjs/locale/fa' import jdate from '../calendar' import * as C from '../constant' const plugin: PluginFunc = (o, Dayjs, dayjs) => { const proto = Dayjs.prototype const U = proto.$utils() const $isJalali = v => v.$C === 'jalali' const $prettyUnit = U.prettyUnit || U.p const $isUndefined = U.isUndefined || U.u const $padStart = U.padStart || U.s const $monthDiff = U.monthDiff || U.m const $absFloor = U.absFloor || U.a const wrapperOfTruth = action => function (...args) { const unsure = action.bind(this)(...args) unsure.$C = this.$C if (unsure.isJalali()) { unsure.InitJalali() } return unsure } // keep calendar on date manipulation proto.startOf = wrapperOfTruth(proto.startOf) proto.endOf = wrapperOfTruth(proto.endOf) proto.add = wrapperOfTruth(proto.add) proto.subtract = wrapperOfTruth(proto.subtract) proto.set = wrapperOfTruth(proto.set) const oldParse = proto.parse const oldInit = proto.init const oldStartOf = proto.startOf const old$Set = proto.$set const oldAdd = proto.add const oldFormat = proto.format const oldDiff = proto.diff const oldYear = proto.year const oldMonth = proto.month const oldDate = proto.date const oldDaysInMonth = proto.daysInMonth const oldToArray = proto.toArray dayjs.$C = 'gregory' // First Day Of Week dayjs.$fdow = 6 // 0: sunday, ... dayjs.calendar = function (calendar) { dayjs.$C = calendar return dayjs } proto.calendar = function (calendar) { const that = this.clone() that.$C = calendar if (that.isJalali()) { that.InitJalali() } return that } proto.isJalali = function () { return $isJalali(this) } dayjs.en.jmonths = 'Farvardin_Ordibehesht_Khordaad_Tir_Mordaad_Shahrivar_Mehr_Aabaan_Aazar_Dey_Bahman_Esfand'.split( '_', ) dayjs.locale('fa', { ...fa, ...C.fa }, true) const wrapper = function (date, instance) { return dayjs(date, { locale: instance.$L, utc: instance.$u, calendar: instance.$C, }) } proto.init = function (cfg = {}) { oldInit.bind(this)(cfg) if (this.isJalali()) { this.InitJalali() } } proto.parse = function (cfg) { this.$C = cfg.calendar || this.$C || dayjs.$C if ( cfg.jalali && typeof cfg.date === 'string' && /.*[^Z]$/i.test(cfg.date) // looking for a better way ) { const reg = cfg.date.match(C.REGEX_PARSE) if (reg) { // 1397-08-08 or 13970808 const [y, m, d] = jdate.G( Number.parseInt(reg[1], 10), Number.parseInt(reg[2], 10), Number.parseInt(reg[3] || 1, 10), ) cfg.date = `${y}-${m}-${d}${reg[4] || ''}` } } return oldParse.bind(this)(cfg) } proto.InitJalali = function () { const [jy, jm, jd] = jdate.J(this.$y, this.$M + 1, this.$D) this.$jy = jy this.$jM = jm - 1 this.$jD = jd } proto.startOf = function (units, startOf) { // startOf -> endOf if (!$isJalali(this)) { return oldStartOf.bind(this)(units, startOf) } const isStartOf = !$isUndefined(startOf) ? startOf : true const unit = $prettyUnit(units) const instanceFactory = (d, m, y = this.$jy) => { const [gy, gm, gd] = jdate.G(y, m + 1, d) const ins = wrapper(new Date(gy, gm - 1, gd), this) return (isStartOf ? ins : ins.endOf(C.D)) .$set('hour', 1) // prevent daylight saving issue in safari } const WModifier = (this.$W + (7 - dayjs.$fdow)) % 7 switch (unit) { case C.Y: return isStartOf ? instanceFactory(1, 0) : instanceFactory(0, 0, this.$jy + 1) case C.M: return isStartOf ? instanceFactory(1, this.$jM) : instanceFactory( 0, (this.$jM + 1) % 12, this.$jy + Math.floor((this.$jM + 1) / 12), ) case C.W: return isStartOf ? instanceFactory(this.$jD - WModifier, this.$jM) : instanceFactory(this.$jD + (6 - WModifier), this.$jM) default: return oldStartOf.bind(this)(units, startOf) } } proto.$set = function (units, int) { if (!$isJalali(this)) { return old$Set.bind(this)(units, int) } const unit = $prettyUnit(units) const instanceFactory = (d, m, y = this.$jy) => { const [gy, gm, gd] = jdate.G(y, m + 1, d) this.$d.setFullYear(gy) this.$d.setMonth(gm - 1) this.$d.setDate(gd) return this } switch (unit) { case C.DATE: case C.D: instanceFactory(int, this.$jM) break case C.M: instanceFactory(this.$jD, int) break case C.Y: instanceFactory(this.$jD, this.$jM, int) break default: return old$Set.bind(this)(units, int) } this.init() return this } proto.add = function (number, units) { if (!$isJalali(this)) { return oldAdd.bind(this)(number, units) } number = Number(number) // units === 'ms' hard code here, will update in next release const unit = units && (units.length === 1 || units === 'ms') ? units : $prettyUnit(units) const instanceFactory = (u, n) => { const date = this.set(C.DATE, 1).set(u, n + number) return date.set(C.DATE, Math.min(this.$jD, date.daysInMonth())) } if (['M', C.M].includes(unit)) { const n = this.$jM + number const y = n < 0 ? -Math.ceil(-n / 12) : Math.floor(n / 12) const d = this.$jD const x = this.set(C.D, 1) .add(y, C.Y) .set(C.M, n - y * 12) return x.set(C.D, Math.min(x.daysInMonth(), d)) } if (['y', C.Y].includes(unit)) { return instanceFactory(C.Y, this.$jy) } if (['d', C.D].includes(unit)) { const date = new Date(this.$d) date.setDate(date.getDate() + number) return wrapper(date, this) } if (['w', C.W].includes(unit)) { const date = new Date(this.$d) date.setDate(date.getDate() + number * 7) return wrapper(date, this) } return oldAdd.bind(this)(number, units) } proto.format = function (formatStr, localeObject) { if (!$isJalali(this)) { return oldFormat.bind(this)(formatStr, localeObject) } const str = formatStr || C.FORMAT_DEFAULT const locale = localeObject || this.$locale() const { jmonths } = locale return str.replace(C.REGEX_FORMAT, (match) => { if (match.includes('[')) return match.replace(/\[|\]/g, '') switch (match) { case 'YY': return String(this.$jy).slice(-2) case 'YYYY': return String(this.$jy) case 'M': return String(this.$jM + 1) case 'MM': return $padStart(this.$jM + 1, 2, '0') case 'MMM': return jmonths[this.$jM].slice(0, 3) case 'MMMM': return jmonths[this.$jM] case 'D': return String(this.$jD) case 'DD': return $padStart(this.$jD, 2, '0') default: return oldFormat.bind(this)(match, localeObject) } }) } proto.diff = function (input, units, float) { if (!$isJalali(this)) { return oldDiff.bind(this)(input, units, float) } const unit = $prettyUnit(units) const that = dayjs(input) let result = $monthDiff(this, that) switch (unit) { case C.Y: result /= 12 break case C.M: break default: // milliseconds return oldDiff.bind(this)(input, units, float) } return float ? result : $absFloor(result) } proto.$g = function (input, get, set) { if ($isUndefined(input)) return this[get] return this.set(set, input) } proto.year = function (input) { if (!$isJalali(this)) { return oldYear.bind(this)(input) } return this.$g(input, '$jy', C.Y) } proto.month = function (input) { if (!$isJalali(this)) { return oldMonth.bind(this)(input) } return this.$g(input, '$jM', C.M) } proto.date = function (input) { if (!$isJalali(this)) { return oldDate.bind(this)(input) } return this.$g(input, '$jD', C.D) } proto.daysInMonth = function () { if (!$isJalali(this)) { return oldDaysInMonth.bind(this)() } return this.endOf(C.M).$jD } /** * toArray function moved to official plugin * Check function existence before override */ if (oldToArray) { proto.toArray = function () { if (!$isJalali(this)) { return oldToArray.bind(this)() } return [this.$jy, this.$jM, this.$jD, this.$H, this.$m, this.$s, this.$ms] } } proto.clone = function () { return wrapper(this.toDate(), this) } } export default plugin