Skip to content

Instantly share code, notes, and snippets.

@remy90
Last active March 27, 2024 15:11
Show Gist options
  • Save remy90/46920c3496def037992ffd1d1d70eaa2 to your computer and use it in GitHub Desktop.
Save remy90/46920c3496def037992ffd1d1d70eaa2 to your computer and use it in GitHub Desktop.
WIP: Temporal date adapter implementation of MuiPickersAdapter<TDate>
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