@d.blizen/datepicker-nash-travel
Version:
A vue component of datepicker, includes DatePicker, DatetimePicker, DateRangePicker, TimePicker
530 lines (454 loc) • 14 kB
text/typescript
import {
compareDates,
DateInfoBase,
DateInfoBase1,
DateStr,
fillTo,
getMonthByStep,
getMonthLen,
parseDate,
parseTime,
TimeInfo,
TimeStr,
} from '@livelybone/date-generator'
export const dateReg = /^((\d{4})-?(\d{1,2})?-?(\d{1,2})?)/
export const timeReg = /^((\d{1,2}):?(\d{1,2})?:?(\d{1,2})?)/
export const AllTypes = ['year', 'month', 'date', 'time'] as const
export const AllTimeTypes = ['hour', 'minute', 'second'] as const
export type TAllTypes = typeof AllTypes extends Readonly<(infer A)[]>
? A
: never
export type TAllTimeTypes = typeof AllTimeTypes extends Readonly<(infer A)[]>
? A
: never
export function sliceUtilEqual<T extends any>(arr: T[], val: T) {
const index = Object.keys(arr).find(i => arr[+i] === val)
if (!index) return arr
return arr.slice(0, +index + 1)
}
// flag:
// -1 - 如果 d1 小于等于 d2,返回 true
// 1 - 如果 d1 大于等于 d2,返回 true
// 0 - 如果 d1 等于 d2,返回 true
export function dateCompare<
T extends Partial<DateInfoBase1>,
U extends Partial<DateInfoBase1>
>(
date: T | DateStr | undefined | null,
targetDate: U | DateStr | undefined | null,
flag = 1,
type: Exclude<TAllTypes, 'time'> = 'date',
) {
const $date =
typeof date === 'string' ? parseDate(date) : date ? { ...date } : undefined
const $targetDate =
typeof targetDate === 'string'
? parseDate(targetDate)
: targetDate
? { ...targetDate }
: undefined
if (!$date) return false
if (!$targetDate) return true
if (type === 'year') {
$date.month = '01'
$date.date = '01'
$targetDate.month = '01'
$targetDate.date = '01'
} else if (type === 'month') {
$date.date = '01'
$targetDate.date = '01'
}
const compare = compareDates($date as any, $targetDate as any)
return flag === 0 ? compare === 0 : compare * flag >= 0
}
// flag:
// -1 - 如果 t1 小于等于 t2,返回 true
// 1 - 如果 t1 大于等于 t2,返回 true
// 0 - 如果 t1 等于 t2,返回 true
export function timeCompare(
time?: TimeInfo | string,
targetTime?: TimeInfo | string,
flag = 1,
type: TAllTimeTypes = 'second',
) {
const $time =
typeof time === 'string' ? parseTime(time) : time ? { ...time } : undefined
const $targetTime =
typeof targetTime === 'string'
? parseTime(targetTime)
: targetTime
? { ...targetTime }
: undefined
if (!$time) return false
if (!$targetTime) return true
if (type === 'hour') {
$time.minute = '00'
$time.second = '00'
$targetTime.minute = '00'
$targetTime.second = '00'
} else if (type === 'minute') {
$time.second = '00'
$targetTime.second = '00'
}
const get = (t: TimeInfo) =>
+`${fillTo(2, t.hour)}${fillTo(2, t.minute)}${fillTo(2, t.second)}`
const compare = get($time) - get($targetTime)
return flag === 0 ? compare === 0 : compare * flag >= 0
}
export function datetimeCompare(
datetime: (DateInfoBase & Partial<TimeInfo>) | undefined,
targetDatetime: (DateInfoBase & TimeInfo) | undefined,
flag = 1,
type: TAllTimeTypes = 'second',
) {
if (!datetime) return false
if (!targetDatetime) return true
if (flag === 0)
return (
dateCompare(datetime, targetDatetime, flag) &&
(datetime.hour === undefined ||
timeCompare(datetime as TimeInfo, targetDatetime, flag))
)
const compare = dateCompare(datetime, targetDatetime, flag)
if (!compare) return false
const compareReverse = dateCompare(datetime, targetDatetime, -flag)
if (compareReverse)
return (
datetime.hour === undefined ||
timeCompare(datetime as TimeInfo, targetDatetime, flag, type)
)
return true
}
function $fillTo(width: number, num?: string | number) {
return num !== undefined ? fillTo(width, num) : ''
}
export function getTenYears(val: DateInfoBase) {
if (!val) return ''
const tenYear = Math.floor(+val.year / 10 - 0.5)
return `${tenYear * 10 + 1} - ${(tenYear + 1) * 10}`
}
export function createEmptyTimeObj() {
return { year: '', month: '', date: '', hour: '', minute: '', second: '' }
}
export function createNowTimeObj() {
const now = new Date()
return {
year: now.getFullYear(),
month: now.getMonth() + 1,
date: now.getDate(),
hour: now.getHours(),
minute: now.getMinutes(),
second: now.getSeconds(),
}
}
export function formatDateObj(val: DateInfoBase1) {
return {
year: (val.year && fillTo(4, val.year)) || '',
month: (val.month && fillTo(2, val.month)) || '',
date: (val.date && fillTo(2, val.date)) || '',
}
}
export function formatTimeObj(
val: { [key in keyof TimeInfo]: number | string },
) {
return {
hour: (val.hour && fillTo(2, val.hour)) || '',
minute: (val.minute && fillTo(2, val.minute)) || '',
second: (val.second && fillTo(2, val.second)) || '',
}
}
export function formatDate(d: DateInfoBase, type: TAllTypes = 'date') {
if (!d) return ''
const arr = []
const year = d.year && $fillTo(4, d.year)
if (year) {
arr.push(year)
const month = d.month && $fillTo(2, d.month)
if (month) {
arr.push(month)
const date = d.date && $fillTo(2, d.date)
if (date) arr.push(date)
}
}
return arr.slice(0, sliceUtilEqual(AllTypes as any, type).length).join('-')
}
export function formatTime(t: TimeInfo, type: TAllTimeTypes = 'second') {
if (!t) return ''
const arr = []
const hour = t.hour && $fillTo(2, t.hour)
if (hour) {
arr.push(hour)
const minute = t.minute && $fillTo(2, t.minute)
if (minute) {
arr.push(minute)
const second = t.second && $fillTo(2, t.second)
if (second) arr.push(second)
}
}
return arr
.slice(0, sliceUtilEqual(AllTimeTypes as any, type).length)
.join(':')
}
function mergePropExceptEmpty(o1: any, o2: any) {
return Object.keys({ ...o1, ...o2 }).reduce(
(pre, k) => ({ ...pre, [k]: (o2 && o2[k]) || (o1 && o1[k]) }),
o1,
)
}
export function dealDateLimit(
min: DateStr | undefined,
max: DateStr | undefined,
) {
const error: string[] = []
const minDate = (min && parseDate(min)) || undefined
if (min && !minDate) error.push(`Prop min(${min}) is invalid`)
const maxDate = (max && parseDate(max)) || undefined
if (max && !maxDate) error.push(`Prop max(${max}) is invalid`)
return {
minDate: formatDateObj(
mergePropExceptEmpty({ year: '0000', month: '01', date: '01' }, minDate),
),
maxDate: formatDateObj(
mergePropExceptEmpty({ year: '9999', month: '12', date: '31' }, maxDate),
),
error: error.join(';'),
}
}
export function dealTimeLimit(
min: TimeStr | undefined,
max: TimeStr | undefined,
) {
const error: string[] = []
const minTime = (min && parseTime(min)) || undefined
if (min && !minTime) error.push(`Prop min(${min}) is invalid`)
const maxTime = (max && parseTime(max)) || undefined
if (max && !maxTime) error.push(`Prop max(${max}) is invalid`)
return {
minTime: formatTimeObj(
mergePropExceptEmpty({ hour: '00', minute: '00', second: '00' }, minTime),
),
maxTime: formatTimeObj(
mergePropExceptEmpty({ hour: '23', minute: '59', second: '59' }, maxTime),
),
error: error.join(';'),
}
}
export function dealDatetimeLimit(
min: string | undefined,
max: string | undefined,
selectedDate?: DateInfoBase,
) {
const minArr = (min || '').trim().split(/\s+/)
const maxArr = (max || '').trim().split(/\s+/)
const { minDate, maxDate } = dealDateLimit(minArr[0], maxArr[0])
const compare = {
toMin: minDate && selectedDate ? compareDates(selectedDate, minDate) : 1,
toMax: maxDate && selectedDate ? compareDates(selectedDate, maxDate) : -1,
}
const { minTime: $minTime, maxTime: $maxTime } = dealTimeLimit(
minArr[1],
maxArr[1],
)
const error: string[] = []
const minDatetime = minDate ? { ...minDate, ...$minTime } : undefined
if (min && !minDatetime) error.push(`Prop min(${min}) is invalid`)
const maxDatetime = maxDate ? { ...maxDate, ...$maxTime } : undefined
if (max && !maxDatetime) error.push(`Prop min(${max}) is invalid`)
const minTime = (() => {
if (compare.toMin === 0) return $minTime
if (compare.toMin < 0) return { hour: '23', minute: '59', second: '59' }
return { hour: '00', minute: '00', second: '00' }
})()
const maxTime = (() => {
if (compare.toMax === 0) return $maxTime
if (compare.toMax > 0) return { hour: '00', minute: '00', second: '00' }
return { hour: '23', minute: '59', second: '59' }
})()
return {
minArr,
maxArr,
minDate,
maxDate,
minTime,
maxTime,
minDatetime,
maxDatetime,
error: error.join(';'),
}
}
export function getNextMonthFirstDate(
dateInfo?: DateInfoBase1,
): typeof dateInfo extends undefined ? undefined : DateInfoBase {
if (!dateInfo) return undefined as any
const month = getMonthByStep(dateInfo, 1)
return { ...month, date: fillTo(2, 1) }
}
export function getPrevMonthLastDate(
dateInfo?: DateInfoBase1,
): typeof dateInfo extends undefined ? undefined : DateInfoBase {
if (!dateInfo) return undefined as any
const month = getMonthByStep(dateInfo, -1)
return { ...month, date: fillTo(2, getMonthLen(month.year, month.month)) }
}
export function getCurrMonthLastDate(
dateInfo?: DateInfoBase1,
): typeof dateInfo extends undefined ? undefined : DateInfoBase {
if (!dateInfo) return undefined as any
return formatDateObj({
...dateInfo,
date: fillTo(2, getMonthLen(dateInfo.year, dateInfo.month)),
})
}
export function getPrevYearLastDate(year: string | number) {
if (!year) return undefined
return { year: fillTo(4, +year - 1), month: '12', date: '31' }
}
export function getCurrYearLastDate(year: string | number) {
if (!year) return undefined
return { year: fillTo(4, +year), month: '12', date: '31' }
}
export function getPrevTenYearLastDate(year: string | number) {
if (!year) return undefined
return {
year: fillTo(4, (Math.ceil(+year / 10) - 1) * 10),
month: '12',
date: '31',
}
}
export function getCurrTenYearLastDate(year: string | number) {
if (!year) return undefined
return {
year: fillTo(4, Math.ceil(+year / 10) * 10),
month: '12',
date: '31',
}
}
export function getNextYearFirstDate(year: string | number) {
if (!year) return undefined
return { year: fillTo(4, +year + 1), month: '01', date: '01' }
}
export function getNextTenYearFirstDate(year: string | number) {
if (!year) return undefined
return {
year: fillTo(4, Math.ceil(+year / 10) * 10 + 1),
month: '01',
date: '01',
}
}
export function getFirstMaxDate(
val: DateInfoBase1,
type: Exclude<TAllTypes, 'time'> = 'date',
) {
if (!val) return undefined
if (type === 'year') return getPrevTenYearLastDate(val.year)
if (type === 'month') return getPrevYearLastDate(val.year)
return getPrevMonthLastDate(val)
}
export function getLastMinDate(
val: DateInfoBase1,
type: Exclude<TAllTypes, 'time'> = 'date',
) {
if (!val) return undefined
if (type === 'year') return getNextTenYearFirstDate(val.year)
if (type === 'month') return getNextYearFirstDate(val.year)
return getNextMonthFirstDate(val)
}
export function dateValidator(
val?: string | DateInfoBase1,
type: Exclude<TAllTypes, 'time'> = 'date',
min: any = '',
max: any = '',
) {
if (!val) return ''
const date = typeof val === 'string' ? parseDate(val) : val
const checkProps = sliceUtilEqual(AllTypes as any, type)
if (!date || !checkProps.every(k => date[k]))
return `Value(${val}) is invalid`
if (
!(
(!min || dateCompare(date, min, 1, type)) &&
(!max || dateCompare(date, max, -1, type))
)
) {
return `Value${val} is out of range`
}
return ''
}
export function timeValidator(
val?: string | TimeInfo,
type: TAllTimeTypes = 'second',
min: any = '',
max: any = '',
) {
if (!val) return ''
const time = typeof val === 'string' ? parseTime(val) : val
const checkProps = sliceUtilEqual(AllTimeTypes as any, type)
if (!time || !checkProps.every(k => time[k]))
return `Value(${val}) is invalid`
if (
!(
(!min || timeCompare(time, min, 1, type)) &&
(!max || timeCompare(time, max, -1, type))
)
) {
return `Value${val} is out of range`
}
return ''
}
export function parseDatetime(val: string) {
const $val = val || ''
if (!$val.trim()) return null
const arr = $val.trim().split(/\s+/)
return {
...parseDate(arr[0]),
...parseTime(arr[1]),
}
}
export function datetimeValidator(
val?: any,
type: TAllTimeTypes = 'second',
min: any = '',
max: any = '',
) {
if (!val) return ''
const time = typeof val === 'string' ? parseDatetime(val) : val
const checkProps = AllTypes.slice(0, 3).concat(
sliceUtilEqual(AllTimeTypes as any, type) as any[],
)
if (!time || !checkProps.every(k => time[k]))
return `Value(${val}) is invalid`
if (
!(
(!min || datetimeCompare(time, min, 1, type)) &&
(!max || datetimeCompare(time, max, -1, type))
)
) {
return `Value${val} is out of range`
}
return ''
}
export function isInSameTenYear(v1: DateInfoBase1, v2: DateInfoBase1) {
return dateCompare(v1, v2, 0, 'year')
}
export function isInSameYear(v1: DateInfoBase1, v2: DateInfoBase1) {
return dateCompare(v1, v2, 0, 'year')
}
export function isInSameMonth(v1: DateInfoBase1, v2: DateInfoBase1) {
return dateCompare(v1, v2, 0, 'month')
}
export function isInSamePage(
[v1, v2]: [DateInfoBase1, DateInfoBase1],
type?: Exclude<TAllTypes, 'time'>,
) {
if (type === 'year') return isInSameTenYear(v1, v2)
if (type === 'month') return isInSameYear(v1, v2)
return isInSameMonth(v1, v2)
}
export function formatDatetime(val: any, type?: TAllTimeTypes) {
let str = ''
const d = formatDate(val)
str += d
if (!str) return ''
const t = formatTime(val, type)
return t ? `${str} ${t}` : ''
}