Feature request
Please provide a MatLuxonDateModule that functions similar to MatNativeDateModule and MatMomentDateModule, but supports the Luxon DateTime type.
Luxon details are available at https://moment.github.io/luxon/
I haven't worked out quite everything yet, but the following is a decent start towards such an adapter:
import { Inject, Injectable, Optional } from '@angular/core';
import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material';
import { DateTime, Info } from 'luxon';
/** Creates an array and fills it with values.
//copied from NativeDateAdapter
*/
function range<T>(length: number, valueFunction: (index: number) => T): T[] {
const valuesArray = Array(length);
for (let i = 0; i < length; i++) {
valuesArray[i] = valueFunction(i);
}
return valuesArray;
}
// TODO(mmalerba): Remove when we no longer support safari 9.
/** Whether the browser supports the Intl API. */
const SUPPORTS_INTL_API = typeof Intl != 'undefined';//copied from NativeDateAdapter
/** The default date names to use if Intl API is not available. */
const DEFAULT_DATE_NAMES = range(31, i => String(i + 1));//copied from NativeDateAdapter
export class LuxonDateAdapter extends DateAdapter<DateTime> {
getYear(date: DateTime): number {
return date.year;
}
getMonth(date: DateTime): number {
return date.month - 1;//The Datepicker uses this to index into the 0 indexed getMonthNames array so far as I can tell. Because Luxon uses 1-12 for months we need to subtract one.
}
getDate(date: DateTime): number {
return date.day;
}
getDayOfWeek(date: DateTime): number {
return date.weekday;
}
getMonthNames(style: "long" | "short" | "narrow"): string[] {
return Info.months(style);
}
getDateNames(): string[] {
if (SUPPORTS_INTL_API) {
let dtf = new Intl.DateTimeFormat(this.locale, { day: 'numeric' });
return range(31, i => this._stripDirectionalityCharacters(
dtf.format(new Date(2017, 0, i + 1))));
}
return DEFAULT_DATE_NAMES;
}
getDayOfWeekNames(style: "long" | "short" | "narrow"): string[] {
return Info.weekdays(style);
}
getYearName(date: DateTime): string {
if (SUPPORTS_INTL_API) {
let dtf = new Intl.DateTimeFormat(this.locale, { year: 'numeric' });
let valueOfDate = date.valueOf();
return this._stripDirectionalityCharacters(dtf.format(valueOfDate));
}
return String(this.getYear(date));
}
getFirstDayOfWeek(): number {
return 0;//assume Sunday.
}
getNumDaysInMonth(date: DateTime): number {
return date.daysInMonth;
}
clone(date: DateTime): DateTime {
return date;//there is no point in cloning a Luxon DateTime since they are immutable.
}
createDate(year: number, month: number, date: number): DateTime {
month += 1;//luxon utc uses 1-12 for dates, but datepicker passes in 0-11.
let aDate = DateTime.utc(year, month, date);
return aDate;
}
today(): DateTime {
return DateTime.local();
}
format(date: DateTime, displayFormat: any): string {
return date.toFormat(displayFormat);
}
addCalendarYears(date: DateTime, years: number): DateTime {
return date.plus({ years: years });
}
addCalendarMonths(date: DateTime, months: number): DateTime {
return date.plus({ months: months });
}
addCalendarDays(date: DateTime, days: number): DateTime {
return date.plus({ days: days });
}
toIso8601(date: DateTime): string {
return date.toISO();
}
isDateInstance(obj: any): boolean {
return (obj instanceof DateTime);
}
isValid(date: DateTime): boolean {
return date.isValid;
}
invalid(): DateTime {
return DateTime.invalid("Invalid set via luxon-date-adapter.");
}
parse(value: any, parseFormat: any): DateTime | null {
if (value && typeof value == 'string') {
//first try to parse an ISO date
let aDateTime = DateTime.fromISO(value);
if (aDateTime.isValid == true) {
return aDateTime;
}
//otherwise try to parse according to specified format (useful for user entered values?).
return DateTime.fromFormat(value, parseFormat);
}
return value;
}
//copied from NativeDateAdapter
/**
* Strip out unicode LTR and RTL characters. Edge and IE insert these into formatted dates while
* other browsers do not. We remove them to make output consistent and because they interfere with
* date parsing.
* @param str The string to strip direction characters from.
* @returns The stripped string.
*/
private _stripDirectionalityCharacters(str: string) {
return str.replace(/[\u200e\u200f]/g, '');
}
deserialize(value: any): DateTime | null {
let date;
if (value instanceof Date) {
date = DateTime.fromJSDate(value);
}
if (typeof value === 'string') {
if (!value) {
return null;
}
date = DateTime.fromISO(value);
}
if (date && this.isValid(date)) {
return date;
}
return super.deserialize(value);
}
}
Combined with this format data the above works for me.
import { MatDateFormats } from '@angular/material';
export const MAT_LUXON_DATE_FORMATS: MatDateFormats = {
parse: {
dateInput: 'LL/dd/yyyy',
},
display: {
dateInput: 'LL/dd/yyyy',
monthYearLabel: 'LLL yyyy',
dateA11yLabel: 'LL',
monthYearA11yLabel: 'LLL yyyy'
},
};
Note I haven't tested this with TIMES, only with Dates. For me that means dates that come from the server as ISO 8601 dates "2018-03-15" for example. As these are dates these should not ever change based upon time zones. "2018-03-15" shouldn't ever become "2018-03-14" as happens with the NativeDateAdapter for - timezones.
With this luxon adaptor, some date will be created in UTC (createDate) and some in the local TimeZone of the user (parse).
That seems like a possibility as I am only using this with ISO 8601 dates and not DateTimes so in my use case I don't ever have a timezone in my serialized format so I would not have noticed this in my testing.
Feature request opened for more than a year and still no MatLuxonDateModule in the Angular Material.
Anything new on the horizon? Should we wait or fall back to MatMomentDateModule?
Is this feature still on the table taking in consideration that Moment.js is legacy now?
Tracking this in #20599 going forward
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
_This action has been performed automatically by a bot._
Most helpful comment
Feature request opened for more than a year and still no MatLuxonDateModule in the Angular Material.
Anything new on the horizon? Should we wait or fall back to MatMomentDateModule?