UNPKG

shineout

Version:

Shein 前端组件库

449 lines (402 loc) 13.5 kB
import dayjs from 'dayjs' import zonedTimeToUtc from 'date-fns-tz/zonedTimeToUtc' import utcToZonedTime from 'date-fns-tz/utcToZonedTime' import advancedFormat from 'dayjs/plugin/advancedFormat' import quarterOfYear from 'dayjs/plugin/quarterOfYear' import isoWeek from 'dayjs/plugin/isoWeek' import weekday from 'dayjs/plugin/weekday' import weekYear from 'dayjs/plugin/weekYear' import weekOfYear from 'dayjs/plugin/weekOfYear' import customParseFormat from 'dayjs/plugin/customParseFormat' import relativeTime from 'dayjs/plugin/relativeTime' import enLocale from 'dayjs/locale/en' import { DateTimeType } from './Props' import { DateMode } from './Props' interface DateOptions { timeZone?: string weekStartsOn?: number } const en2Locate = { ...enLocale, name: 'en2', weekStart: 1, } dayjs.locale(en2Locate, undefined, true) dayjs.extend(advancedFormat) dayjs.extend(isoWeek) dayjs.extend(relativeTime) dayjs.extend(weekday) dayjs.extend(weekOfYear) dayjs.extend(weekYear) dayjs.extend(customParseFormat) dayjs.extend(quarterOfYear) const TIME_FORMAT = 'HH:mm:ss' export const compatibleFmt = (fmt?: string) => { if (typeof fmt !== 'string') return fmt const trans = { yy: 'YY', d: 'D', a: 'A', t: 'X', T: 'x', RRRR: 'GGGG', I: 'W', } let result = fmt Object.keys(trans).forEach((key: keyof typeof trans) => { result = result.replace(new RegExp(key, 'g'), trans[key]) }) if (result !== fmt) { console.warn(`invalid datepicker format: ${fmt} please use ${result}`) } return result } function getDayJsLocate(options?: DateOptions) { if (options && options.weekStartsOn === 1) return 'en2' return 'en' } function transDateWithZone(dd: Date, options: DateOptions = {}, back = false) { if (options.timeZone) { const timezoneHH = /^([+-]\d{2})$/ // 只放开两位时区 if (timezoneHH.test(options.timeZone)) { const num = +options.timeZone if (num <= 13 && num >= -12) { return back ? zonedTimeToUtc(dd, options.timeZone) : utcToZonedTime(dd, options.timeZone) } } console.error(new Error(`timeZone is not supported: ${options.timeZone}`)) } return new Date(dd) } function addDays(date: Date, offset: number, options: DateOptions) { const zd = transDateWithZone(date, options) const d = dayjs(zd) .add(offset, 'day') .toDate() const ud = transDateWithZone(d, options, true) return ud } function addMonths(date: Date, offset: number, options: DateOptions) { const zd = transDateWithZone(date, options) const d = dayjs(zd) .add(offset, 'month') .toDate() const ud = transDateWithZone(d, options, true) return ud } function addSeconds(date: Date, offset: number, options: DateOptions) { const zd = transDateWithZone(date, options) const d = dayjs(zd) .add(offset, 'seconds') .toDate() const ud = transDateWithZone(d, options, true) return ud } function addYears(date: Date, offset: number, options: DateOptions) { const zd = transDateWithZone(date, options) const d = dayjs(zd) .add(offset, 'year') .toDate() const ud = transDateWithZone(d, options, true) return ud } function changeDate(date: Date, type: DateMode, num: number, options: DateOptions) { const zd = transDateWithZone(date, options) // type is year month ... const d = (dayjs(zd) as any)[type](num).toDate() const ud = transDateWithZone(d, options, true) return ud } function getDateInfo(date: Date, type: DateMode, options: DateOptions): number { const zd = transDateWithZone(date, options) return (dayjs(zd) as any)[type]() } function compareAsc(dateA: DateTimeType, dateB: DateTimeType) { if (!dateA || !dateB) return NaN const a = dayjs(dateA).valueOf() const b = dayjs(dateB).valueOf() if (!a || !b) return NaN if (a === b) return 0 return a > b ? 1 : -1 } function format(date: Date, fmt?: string, options: DateOptions = {}) { if (!date) return 'Invalid Date' const fmt2 = compatibleFmt(fmt) let zd = date if (fmt2 !== 'X' && fmt2 !== 'x') { zd = transDateWithZone(date, options)! } const dd = dayjs(zd).locale(getDayJsLocate(options)) const result = dd.format(fmt2) return result } function isSameMonth(date1: Date, date2: Date, options: DateOptions = {}) { return date1 && date2 && format(date1, 'YYYY-MM', options) === format(date2, 'YYYY-MM', options) } function isSameDay(date1: Date, date2: Date, options: DateOptions) { return date1 && date2 && format(date1, 'YYYY-MM-DD', options) === format(date2, 'YYYY-MM-DD', options) } function isSameWeek(date1: Date, date2: Date, options: DateOptions) { if (!date1 || !date2) return false return date1 && date2 && format(date1, 'gggg-ww', options) === format(date2, 'gggg-ww', options) } function isSameQuarter(date1: Date, date2: Date, options: DateOptions) { if (!date1 || !date2) return false return date1 && date2 && format(date1, 'YYYY Q', options) === format(date2, 'YYYY Q', options) } function isValid(date: DateTimeType) { if (!date) return false if (!(date instanceof Date)) return false return dayjs(date).isValid() } function parse(d: string, fmt?: string, options?: DateOptions) { if (!d) return new Date('') // should clear[xxx] const reg = /[[]([^[^\]]+?)[\]]/g const date = d && typeof d === 'string' && d.replace ? d.replace(reg, ' ') : d const fmt2 = compatibleFmt(fmt)!.replace(reg, ' ') // handle IOS Year Week const index = fmt2.indexOf('GGGG') if (index >= 0 && typeof date === 'string') { const year = date.slice(index, index + 5) const weekIndex = fmt2.indexOf('WW') const week = weekIndex >= 0 ? date.slice(weekIndex, weekIndex + 3) : 1 const result = dayjs(`${year}-06-15`, 'YYYY-MM-DD') .locale(getDayJsLocate(options)) .isoWeek(Number(week)) .toDate() return transDateWithZone(result, options, true) } // handle Quarter const quarterIndex = fmt2.indexOf('Q') if (quarterIndex >= 0 && typeof date === 'string') { const quarter = date.slice(quarterIndex, quarterIndex + 2) const result = dayjs(date, fmt2) .locale(getDayJsLocate(options)) .quarter(Number(quarter)) .toDate() return transDateWithZone(result, options, true) } // dayjs parse stamp with timeZone have bug if (fmt2 === 'x' || fmt2 === 'X') { let stamp = +date if (fmt2 === 'X') stamp *= 1000 return new Date(stamp) } const result = dayjs(date, fmt2, getDayJsLocate(options)).toDate() return transDateWithZone(result, options, true) } function toDate(day: DateTimeType, options?: DateOptions): Date { if (!day) return new Date('') if (day instanceof Date) return dayjs(day).toDate() if (typeof day === 'number') return new Date(day) if (typeof day === 'string') return transDateWithZone(dayjs(day).toDate(), options, true) return dayjs(day).toDate() } function getDaysOfMonth(dirtyDate: DateTimeType, options: DateOptions) { const date = toDate(dirtyDate, options) const temp = dayjs(transDateWithZone(date, options)) let current = dayjs(transDateWithZone(date, options)) .locale(getDayJsLocate(options)) .startOf('month') .startOf('week') .hour(temp.hour()) .minute(temp.minute()) .second(temp.second()) .millisecond(temp.millisecond()) const days = [] let index = 0 while (index < 42) { days.push(transDateWithZone(current.toDate(), options, true)) current = current.add(1, 'day') index += 1 } return days } function isInvalid(date: unknown) { // eslint-disable-next-line return isNaN(date as number) } // function toDateWithFormat(dirtyDate: Date, fmt: string, def: DateTimeType, options: DateOptions): Date // function toDateWithFormat(dirtyDate: DateTimeType, fmt: string, def: DateTimeType, options: DateOptions): DateTimeType function toDateWithFormat(dirtyDate: DateTimeType, fmt?: string, def?: Date, options?: DateOptions) { let date: Date if (typeof dirtyDate === 'string') { date = parse(dirtyDate, fmt, options) const str = format(date, fmt, options) if (str !== dirtyDate) { date = toDate(dirtyDate, options) } } else date = toDate(dirtyDate, options) if (isInvalid(date)) date = def! return date } function compareDay(dateLeft: Date, dateRight: Date, pad = 0, options: DateOptions) { if (!dateLeft || !dateRight) return NaN const left = dayjs(transDateWithZone(dateLeft, options)) .startOf('date') .toDate() const right = dayjs(transDateWithZone(dateRight, options)) .startOf('date') .add(pad, 'day') .toDate() return compareAsc(left, right) } function compareMonth(dateLeft: Date, dateRight: Date, pad = 0, options: DateOptions) { if (!dateLeft || !dateRight) return 0 const left = dayjs(transDateWithZone(dateLeft, options)) .startOf('month') .toDate() const right = dayjs(transDateWithZone(dateRight, options)) .startOf('month') .add(pad, 'month') .toDate() return compareAsc(left, right) } function compareWeek(dateLeft: Date, dateRight: Date, pad = 0, options: DateOptions) { if (!dateLeft || !dateRight) return 0 const left = dayjs(transDateWithZone(dateLeft, options)) .startOf('isoWeek') .toDate() const right = dayjs(transDateWithZone(dateRight, options)) .startOf('isoWeek') .add(pad, 'week') .toDate() return compareAsc(left, right) } function compareYear(dateLeft: Date, dateRight: Date, pad = 0, options: DateOptions) { if (!dateLeft || !dateRight) return 0 const left = dayjs(transDateWithZone(dateLeft, options)) .startOf('year') .toDate() const right = dayjs(transDateWithZone(dateRight, options)) .startOf('year') .add(pad, 'year') .toDate() return compareAsc(left, right) } function compareQuarter(dateLeft: Date, dateRight: Date, pad = 0, options: DateOptions) { if (!dateLeft || !dateRight) return 0 const left = dayjs(transDateWithZone(dateLeft, options)) .startOf('quarter') .toDate() const right = dayjs(transDateWithZone(dateRight, options)) .startOf('quarter') .add(pad, 'quarter') .toDate() return compareAsc(left, right) } function newDate(defaultDate?: Date | DateTimeType, options?: DateOptions) { const date = defaultDate ? toDate(defaultDate, options) : new Date() const zd = transDateWithZone(date, options) const dd = dayjs(zd) .startOf('date') .toDate() const ud = transDateWithZone(dd, options, true) return ud } function setTime(date: Date, old: Date, options?: DateOptions) { const zd = transDateWithZone(date, options) const oldZd = transDateWithZone(old, options) zd.setHours(oldZd.getHours()) zd.setMinutes(oldZd.getMinutes()) zd.setSeconds(oldZd.getSeconds()) zd.setMilliseconds(oldZd.getMilliseconds()) const ud = transDateWithZone(zd, options, true) return ud } function cloneTime(date: Date, old: Date, fmt?: string, options?: DateOptions) { if (!date) return date const oldDate = toDateWithFormat(old, fmt, undefined, options) if (isInvalid(oldDate)) return date return setTime(date, oldDate, options) } function formatDateWithDefaultTime( date: Date, value: Date | undefined, defaultTime: Date | undefined, fmt: string, options: DateOptions ) { if (!date) return date if (value) return setTime(date, value, options) if (!defaultTime) return date const dateHMS = toDateWithFormat(defaultTime, TIME_FORMAT, undefined, options) if (isInvalid(dateHMS)) return date const nDate = cloneTime(date, defaultTime, TIME_FORMAT, options) return format(nDate, fmt, options) } function clearHMS(date: Date, options: DateOptions) { if (!isValid(date)) return date const zd = transDateWithZone(date, options) const dd = dayjs(zd) .startOf('date') .toDate() const ud = transDateWithZone(dd, options, true) return ud } function compareDateArray(arr1: Date[], arr2: Date[], type = 'date', options: DateOptions) { if (!arr1 || !arr2 || arr1.length !== arr2.length) return false return arr1.every((v, i) => { if (!v || !arr2[i]) return false if (type === 'week') return format(v, 'GGGG WW', options) === format(arr2[i], 'GGGG WW', options) return v.getTime() === arr2[i].getTime() }) } function getFormat(fo: string) { let defaultFormat = 'YYYY-MM-DD HH:mm:ss.SSS' ;['H', 'm', 's', 'S', 'h'].map(v => { if (fo.indexOf(v) <= -1) { const reg = new RegExp(`${v}`, 'g') defaultFormat = defaultFormat.replace(reg, '0') } return v }) return defaultFormat } function resetTimeByFormat(value: Date | undefined, fo: string, options: DateOptions) { if (!value) return null const date = toDate(value, options) return toDate(format(date, getFormat(fo), options), options) } function formatted(date: Date, fmt: string | Function, options: DateOptions) { const offsetDate = transDateWithZone(date, options) if (typeof fmt === 'function') return fmt(date, offsetDate) return format(date, fmt, options) } export default { clearHMS, addDays, addMonths, addYears, addSeconds, cloneTime, compareAsc, compareMonth, compareWeek, compareDay, compareQuarter, getDaysOfMonth, format: formatted, isInvalid, isSameDay, isSameMonth, isSameWeek, isSameQuarter, isValid, newDate, setTime, parse, toDate, toDateWithFormat, formatDateWithDefaultTime, compareDateArray, compareYear, TIME_FORMAT, resetTimeByFormat, changeDate, getDateInfo, compatibleFmt, transDateWithZone, }