Last active
March 27, 2024 15:11
-
-
Save remy90/46920c3496def037992ffd1d1d70eaa2 to your computer and use it in GitHub Desktop.
WIP: Temporal date adapter implementation of MuiPickersAdapter<TDate>
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { DateIOFormats, Unit } from '@date-io/core/IUtils'; | |
import { MuiPickersAdapter } from '@mui/lab'; | |
import { Temporal, Intl } from '@js-temporal/polyfill'; | |
interface Opts { | |
locale?: string; | |
formats?: Partial<DateIOFormats>; | |
} | |
const defaultFormats: DateIOFormats = { | |
normalDateWithWeekday: 'ddd, MMM D', | |
normalDate: 'D MMMM', | |
shortDate: 'MMM D', | |
monthAndDate: 'MMMM D', | |
dayOfMonth: 'D', | |
year: 'YYYY', | |
month: 'MMMM', | |
monthShort: 'MMM', | |
monthAndYear: 'MMMM YYYY', | |
weekday: 'dddd', | |
weekdayShort: 'ddd', | |
minutes: 'mm', | |
hours12h: 'hh', | |
hours24h: 'HH', | |
seconds: 'ss', | |
fullTime: 'LT', | |
fullTime12h: 'hh:mm A', | |
fullTime24h: 'HH:mm', | |
fullDate: 'll', | |
fullDateWithWeekday: 'dddd, LL', | |
fullDateTime: 'lll', | |
fullDateTime12h: 'll hh:mm A', | |
fullDateTime24h: 'll HH:mm', | |
keyboardDate: 'L', | |
keyboardDateTime: 'L LT', | |
keyboardDateTime12h: 'L hh:mm A', | |
keyboardDateTime24h: 'L HH:mm', | |
}; | |
// References: js-joda, moment & dayjs | |
// https://github.com/dmtrKovalenko/date-io/blob/master/packages/dayjs/src/dayjs-utils.ts | |
class TemporalDateAdapter<TDate extends Temporal.ZonedDateTime> implements MuiPickersAdapter<TDate> { | |
public lib = 'temporal'; | |
public temporal: Temporal.ZonedDateTime; | |
public locale?: string; | |
public formats: DateIOFormats; | |
private compare: (one: string | Temporal.ZonedDateTime | Temporal.ZonedDateTimeLike, two: string | Temporal.ZonedDateTime | Temporal.ZonedDateTimeLike) => Temporal.ComparisonResult; | |
constructor({ locale, formats }: Opts = {}) { | |
this.temporal = new Temporal.ZonedDateTime( | |
BigInt(1234567890000), // epoch nanoseconds | |
Temporal.TimeZone.from('Europe/London'), // timezone | |
Temporal.Calendar.from('iso8601') // default calendar | |
); | |
this.locale = locale; | |
this.formats = Object.assign({}, defaultFormats, formats); | |
this.compare = Temporal.ZonedDateTime.compare; | |
} | |
getFormatHelperText = (format: string) => | |
'dd/mm/yyyy'; // currently only used as datetime placeholder text | |
public is12HourCycleInCurrentLocale = () => { | |
/** | |
* DO NOT USE | |
*/ | |
throw new Error('Method not implemented.'); | |
}; | |
public getCurrentLocaleCode = () => { | |
return this.locale || 'en'; | |
}; | |
public parseISO = (isoString: string) => { | |
throw new Error('temp'); | |
console.log('parseISO function'); | |
Temporal.ZonedDateTime.from(isoString); | |
}; | |
public toISO = (value: Temporal.ZonedDateTime) => | |
value.toString(); | |
public parse = (value: any, format: string) => { | |
if (value === '') { | |
return null; | |
} | |
return Temporal.ZonedDateTime.from(value) as TDate; | |
}; | |
public date = (value?: any) => { | |
if (value === null) { | |
return null; | |
} | |
if (value === undefined) { | |
value = new Date().toISOString(); | |
} | |
let testVal; | |
console.log(`stan type: ${typeof value}`); | |
if(typeof value === 'string') { | |
testVal = value?.includes('Z') ? value : value.concat('Z'); | |
} | |
else if (typeof value === 'object') { | |
testVal = value.toString(); | |
} | |
const res2 = Temporal.Instant.from(testVal).toString(); | |
const isoZonedDateTime = res2.concat('[Europe/London]'); | |
return Temporal.ZonedDateTime.from(isoZonedDateTime) as TDate; | |
}; | |
public toJsDate = (value: Temporal.ZonedDateTime) => | |
new Date(value.epochMilliseconds); | |
public isValid = (value: any) => { | |
if (value instanceof Error) { | |
return false; | |
} | |
if (value === null) { | |
return false; | |
} | |
if (value === undefined) { | |
return true; | |
} | |
if (typeof value === 'string') { | |
return !isNaN(new Date(value).valueOf()); | |
} | |
if (value instanceof Temporal.ZonedDateTime) { | |
return true; | |
} | |
// throw new Error(`Unknown Date value in function isValid(): ${value}`); | |
}; | |
public isNull = (date: TDate | null) => { | |
return date === null; | |
}; | |
public getDiff = (date: TDate, comparing: Temporal.ZonedDateTime | string, units?: Unit) => { | |
return this.compare(date.toString(), comparing.toString()); | |
}; | |
public isAfter = (date: TDate, value: Temporal.ZonedDateTime | string) => { | |
return this.compare(date.toString(), value.toString()) > 0; | |
}; | |
public isBefore = (date: TDate, value: Temporal.ZonedDateTime) => { | |
return this.compare(date, value) < 0; | |
}; | |
public isAfterDay = (date: TDate, value: Temporal.ZonedDateTime) => { | |
return this.compare(date, value) > 0 as boolean; | |
}; | |
public isBeforeDay = (date: TDate, value: Temporal.ZonedDateTime) => { | |
return this.compare(date, value) < 0 as boolean; | |
}; | |
public isBeforeYear = (date: TDate, value: Temporal.ZonedDateTime) => { | |
return date.year < value.year; | |
}; | |
public isAfterYear = (date: TDate, value: Temporal.ZonedDateTime) => { | |
return date.year > value.year; | |
}; | |
public startOfDay = (date: TDate) => { | |
return date.startOfDay() as TDate; | |
}; | |
public endOfDay = (date: TDate) => { | |
return date | |
.startOfDay() | |
.add({ days: 1}) | |
.subtract({ nanoseconds: 1}) as TDate; | |
}; | |
public format = (date: TDate, formatKey: keyof DateIOFormats) => { | |
return this.formatByString(date, this.formats[formatKey]); | |
}; | |
public formatByString = (date: TDate, formatString: string) => { | |
return new Intl.DateTimeFormat('en-GB').format(date); | |
}; | |
public formatNumber = (numberToFormat: string) => { | |
return numberToFormat; | |
}; | |
public getHours = (date: TDate) => { | |
return date.hour; | |
}; | |
public addSeconds = (date: TDate, count: number) => | |
(count > 0 | |
? date.add({ seconds: count }) | |
: date.subtract({ seconds: count})) as TDate; | |
public addMinutes = (date: TDate, count: number) => | |
(count > 0 | |
? date.add({ minutes: count }) | |
: date.subtract({ minutes: count})) as TDate; | |
public addHours = (date: TDate, count: number) => | |
(count > 0 | |
? date.add({ hours: count }) | |
: date.subtract({ hours: count})) as TDate; | |
public addDays = (date: TDate, count: number) => | |
(count > 0 | |
? date.add({ days: count }) | |
: date.subtract({ days: count})) as TDate; | |
public addWeeks = (date: TDate, count: number) => | |
(count > 0 | |
? date.add({ weeks: count }) | |
: date.subtract({ weeks: count})) as TDate; | |
public addMonths = (date: TDate, count: number) => | |
(count > 0 | |
? date.add({ months: count }) | |
: date.subtract({ months: count})) as TDate; | |
public setMonth = (date: TDate, count: number) => | |
date.add({months: -date.month + count}) as TDate; | |
public setHours = (date: TDate, count: number) => | |
date.add({hours: -date.hour + count}) as TDate; | |
public getMinutes = (date: TDate) => | |
date.minute; | |
public setMinutes = (date: TDate, count: number) => | |
date.add({minutes: -date.minute + count}) as TDate; | |
public getSeconds = (date: TDate) => | |
date.second; | |
public setSeconds = (date: TDate, count: number) => | |
date.add({seconds: -date.second + count}) as TDate; | |
public getMonth = (date: TDate) => | |
date.month; | |
public getDaysInMonth = (date: TDate) => | |
date.daysInMonth; | |
public isSameDay = (date: TDate, comparing: Temporal.ZonedDateTime) => | |
date.toPlainDate().equals(comparing.toPlainDate()); | |
public isSameMonth = (date: TDate, comparing: Temporal.ZonedDateTime) => | |
date.toPlainYearMonth() === comparing.toPlainYearMonth(); | |
public isSameYear = (date: TDate, comparing: Temporal.ZonedDateTime) => | |
date.year === comparing.year; | |
public isSameHour = (date: TDate, comparing: Temporal.ZonedDateTime) => | |
date.hour === comparing.hour | |
&& this.isSameDay(date, comparing) | |
&& this.isSameMonth(date, comparing) | |
&& this.isSameYear(date, comparing); | |
public getMeridiemText = (ampm: 'am' | 'pm') => | |
ampm === 'am' ? 'AM' : 'PM'; | |
public startOfMonth = (date: TDate) => | |
this.setMonth(date, 1) as TDate; | |
public endOfMonth = (date: TDate) => | |
this.setMonth(date, date.daysInMonth) as TDate; | |
public startOfWeek = (date: TDate) => | |
date.add({days: -date.day + 1 }) as TDate; | |
public endOfWeek = (date: TDate) => | |
date.add({days: -date.second + 7 }) as TDate; | |
public getNextMonth = (date: TDate) => | |
date.add({ months: 1}) as TDate; | |
public getPreviousMonth = (date: TDate) => | |
date.subtract({ months: 1}) as TDate; | |
public getMonthArray = (date: TDate) => { | |
const firstMonth = this.startOfMonth(date) as TDate; | |
const monthArray = [firstMonth]; | |
while (monthArray.length < 12) { | |
const prevMonth = monthArray[monthArray.length - 1]; | |
monthArray.push(this.getNextMonth(prevMonth)); | |
} | |
return monthArray; | |
}; | |
public getYear = (date: TDate) => | |
date.year; | |
public setYear = (date: TDate, year: number) => | |
date.add({years: -date.year + year}) as TDate; | |
public mergeDateAndTime = (date: TDate, time: Temporal.ZonedDateTime) => | |
date.add({ | |
hours: -date.hour + time.hour, | |
minutes: -date.minute + time.minute, | |
seconds: -date.second + time.second | |
}) as TDate; | |
public getWeekdays = () => | |
['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; | |
public isEqual = (value: any, comparing: any) => | |
(!value && !comparing) | |
? true | |
: this.compare(value, comparing) === 0; | |
public getWeekArray = (date: TDate) => { | |
const monthStart = this.startOfMonth(date); | |
const start = this.startOfWeek(monthStart) as TDate; | |
const monthEnd = this.endOfMonth(date); | |
const end = this.endOfWeek(monthEnd) as TDate; | |
let count = 0; | |
let current = start; | |
const nestedWeeks: TDate[][] = []; | |
while (this.compare(current, end) === -1) { | |
const weekNumber = Math.floor(count / 7); | |
nestedWeeks[weekNumber] = nestedWeeks[weekNumber] || []; | |
nestedWeeks[weekNumber].push(current); | |
current = current.add({days: 1}) as TDate; | |
count += 1; | |
} | |
return nestedWeeks; | |
}; | |
public getYearRange = (start: TDate, end: TDate) => { | |
const startDate = start.add({months: -start.month}) as TDate; | |
const endDate = end.add({months: -end.month + 12}) as TDate; | |
const years: TDate[] = []; | |
let current = startDate; | |
while (this.compare(current, endDate) === -1 ) { | |
years.push(current as TDate); | |
current = current.add({ years: 1}) as TDate; | |
} | |
return years; | |
}; | |
public isWithinRange = (date: TDate, [start, end]: [Temporal.ZonedDateTime, Temporal.ZonedDateTime]) => | |
this.compare(date, start) > -1 && this.compare(date, end) < 1; | |
} | |
export { TemporalDateAdapter }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment