Skip to content

Instantly share code, notes, and snippets.

@another-guy
Created September 12, 2017 19:04
Show Gist options
  • Save another-guy/adc2d9d731af7693fdbfab21b1e227a9 to your computer and use it in GitHub Desktop.
Save another-guy/adc2d9d731af7693fdbfab21b1e227a9 to your computer and use it in GitHub Desktop.
ISO8601 string based Date Adapter for angular material
import { DateAdapter } from '@angular/material';
import {
addDays,
addMonths,
compareAsc,
format,
getDate,
getDay,
getDaysInMonth,
getMonth,
getYear,
isEqual,
parse,
setDate,
setMonth,
setYear,
} from 'date-fns';
type iso8601DateString = string;
// TODO(mmalerba): Remove when we no longer support safari 9.
/** Whether the browser supports the Intl API. */
const SUPPORTS_INTL_API = typeof Intl != 'undefined';
/**
* This implementation depends on date-fns library.
* Using this implementation for reference: https://github.com/angular/material2/blob/master/src/lib/core/datetime/native-date-adapter.ts
*
* Adapts type `iso8601DateString` (`string`) to be usable as a date by cdk-based components that work with dates.
*/
export class Iso8601DateStringAdapter extends DateAdapter<iso8601DateString> {
/** The locale to use for all dates. */
protected locale: any; // TODO Use this!
/**
* Gets the year component of the given date.
* @param date The date to extract the year from.
* @returns The year component.
*/
getYear(date: iso8601DateString): number {
const result = getYear(date);
console.warn(`getYear(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Gets the month component of the given date.
* @param date The date to extract the month from.
* @returns The month component (0-indexed, 0 = January).
*/
getMonth(date: iso8601DateString): number {
const result = getMonth(date);
console.warn(`getMonth(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Gets the date of the month component of the given date.
* @param date The date to extract the date of the month from.
* @returns The month component (1-indexed, 1 = first of month).
*/
getDate(date: iso8601DateString): number {
const result = getDate(date);
console.warn(`getDate(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Gets the day of the week component of the given date.
* @param date The date to extract the day of the week from.
* @returns The month component (0-indexed, 0 = Sunday).
*/
getDayOfWeek(date: iso8601DateString): number {
const result = getDay(date);
console.warn(`getDayOfWeek(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Gets a list of names for the months.
* @param style The naming style (e.g. long = 'January', short = 'Jan', narrow = 'J').
* @returns An ordered list of all month names, starting with January.
*/
getMonthNames(style: 'long' | 'short' | 'narrow'): string[] {
let result;
if (SUPPORTS_INTL_API) {
const dtf = new Intl.DateTimeFormat(this.locale, {month: style});
result = range(12, i => this._stripDirectionalityCharacters(dtf.format(new Date(2017, i, 1))));
} else {
result = DEFAULT_MONTH_NAMES[style];
}
console.warn(`getMonthNames(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Gets a list of names for the dates of the month.
* @returns An ordered list of all date of the month names, starting with '1'.
*/
getDateNames(): string[] {
let result;
if (SUPPORTS_INTL_API) {
const dtf = new Intl.DateTimeFormat(this.locale, {day: 'numeric'});
result = range(31, i => this._stripDirectionalityCharacters(dtf.format(new Date(2017, 0, i + 1))));
} else {
result = DEFAULT_DATE_NAMES;
}
console.warn(`getDateNames(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Gets a list of names for the days of the week.
* @param style The naming style (e.g. long = 'Sunday', short = 'Sun', narrow = 'S').
* @returns An ordered list of all weekday names, starting with Sunday.
*/
getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[] {
let result;
if (SUPPORTS_INTL_API) {
const dtf = new Intl.DateTimeFormat(this.locale, {weekday: style});
result = range(7, i => this._stripDirectionalityCharacters(dtf.format(new Date(2017, 0, i + 1))));
} else {
result = DEFAULT_DAY_OF_WEEK_NAMES[style];
}
console.warn(`getDayOfWeekNames(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Gets the name for the year of the given date.
* @param date The date to get the year name for.
* @returns The name of the given year (e.g. '2017').
*/
getYearName(date: iso8601DateString): string {
let result;
if (SUPPORTS_INTL_API) {
const dtf = new Intl.DateTimeFormat(this.locale, {year: 'numeric'});
result = this._stripDirectionalityCharacters(dtf.format(parse(date)));
} else {
result = String(this.getYear(date));
}
console.warn(`getYearName(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Gets the first day of the week.
* @returns The first day of the week (0-indexed, 0 = Sunday).
*/
getFirstDayOfWeek(): number {
const result = 0;
console.warn(`getFirstDayOfWeek(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Gets the number of days in the month of the given date.
* @param date The date whose month should be checked.
* @returns The number of days in the month of the given date.
*/
getNumDaysInMonth(date: iso8601DateString): number {
const result = getDaysInMonth(date);
console.warn(`getNumDaysInMonth(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Clones the given date.
* @param date The date to clone
* @returns A new date equal to the given date.
*/
clone(date: iso8601DateString): iso8601DateString {
const result = date;
console.warn(`clone(${JSON.stringify(arguments)}) => ${date}`);
return result;
};
/**
* Creates a date with the given year, month, and date. Does not allow over/under-flow of the month and date.
* @param year The full year of the date. (e.g. 89 means the year 89, not the year 1989).
* @param month The month of the date (0-indexed, 0 = January). Must be an integer 0 - 11.
* @param date The date of month of the date. Must be an integer 1 - length of the given month.
* @returns The new date, or null if invalid.
*/
createDate(year: number, month: number, date: number): iso8601DateString {
const desiredDate = setDate(setMonth(setYear(new Date(), year), month), date);
console.warn(`createDate(${JSON.stringify(arguments)}) => ${desiredDate}`);
return toIsoString(desiredDate);
};
/**
* Gets today's date.
* @returns Today's date.
*/
today(): iso8601DateString {
const today = new Date();
const result = toIsoString(today);
console.warn(`today(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Parses a date from a value.
* @param value The value to parse.
* @param parseFormat The expected format of the value being parsed (type is implementation-dependent).
* @returns The parsed date.
*/
parse(value: any, parseFormat: any): iso8601DateString | null {
const result = value;
console.warn(`parse(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Formats a date as a string.
* @param date The value to format.
* @param displayFormat The format to use to display the date as a string.
* @returns The formatted date string.
*/
format(date: iso8601DateString, displayFormat: any): string {
const format = new Intl.DateTimeFormat(this.locale, displayFormat);
const result = this._stripDirectionalityCharacters(format.format(parse(date)));
console.warn(`format(${JSON.stringify(arguments)}) => ${result}`);
return result;
// TODO
// if (SUPPORTS_INTL_API) {
// if (this.useUtcForDisplay) {
// date = new Date(Date.UTC(
// date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(),
// date.getMinutes(), date.getSeconds(), date.getMilliseconds()));
// displayFormat = extendObject({}, displayFormat, {timeZone: 'utc'});
// }
// const dtf = new Intl.DateTimeFormat(this.locale, displayFormat);
// return this._stripDirectionalityCharacters(dtf.format(date));
// }
// return this._stripDirectionalityCharacters(date.toDateString());
};
/**
* Adds the given number of years to the date. Years are counted as if flipping 12 pages on the
* calendar for each year and then finding the closest date in the new month. For example when
* adding 1 year to Feb 29, 2016, the resulting date will be Feb 28, 2017.
* @param date The date to add years to.
* @param years The number of years to add (may be negative).
* @returns A new date equal to the given one with the specified number of years added.
*/
addCalendarYears(date: iso8601DateString, years: number): iso8601DateString {
const result = addDays(date, years);
console.warn(`addCalendarYears(${JSON.stringify(arguments)}) => ${result}`);
return toIsoString(result);
};
/**
* Adds the given number of months to the date. Months are counted as if flipping a page on the
* calendar for each month and then finding the closest date in the new month. For example when
* adding 1 month to Jan 31, 2017, the resulting date will be Feb 28, 2017.
* @param date The date to add months to.
* @param months The number of months to add (may be negative).
* @returns A new date equal to the given one with the specified number of months added.
*/
addCalendarMonths(date: iso8601DateString, months: number): iso8601DateString {
const result = addMonths(date, months);
console.warn(`addCalendarMonths(${JSON.stringify(arguments)}) => ${result}`);
return toIsoString(result);
};
/**
* Adds the given number of days to the date. Days are counted as if moving one cell on the calendar for each day.
* @param date The date to add days to.
* @param days The number of days to add (may be negative).
* @returns A new date equal to the given one with the specified number of days added.
*/
addCalendarDays(date: iso8601DateString, days: number): iso8601DateString {
const result = addDays(date, days);
console.warn(`addCalendarDays(${JSON.stringify(arguments)}) => ${result}`);
return toIsoString(result);
};
/**
* Gets the RFC 3339 compatible date string (https://tools.ietf.org/html/rfc3339) for the given date.
* @param date The date to get the ISO date string for.
* @returns The ISO date string date string.
*/
getISODateString(date: iso8601DateString): string {
const result = `${toIsoString(date)}T00:00:00Z`;
console.warn(`getISODateString(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Checks whether the given object is considered a date instance by this DateAdapter.
* @param obj The object to check
* @returns Whether the object is a date instance.
*/
isDateInstance(obj: any): boolean {
const result = obj == null ||
(typeof obj === 'string' && (obj.length === 10 || obj.length === 0));
console.warn(`isDateInstance::(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Checks whether the given date is valid.
* @param date The date to check.
* @returns Whether the date is valid.
*/
isValid(date: iso8601DateString): boolean {
const result = true; // TODO Fix this!
console.warn(`isValid(${JSON.stringify(arguments)}) => ${result}`);
return result;
};
/**
* Sets the locale used for all dates.
* @param locale The new locale.
*/
setLocale(locale: any): void {
this.locale = locale;
}
/**
* Compares two dates.
* @param first The first date to compare.
* @param second The second date to compare.
* @returns 0 if the dates are equal, a number less than 0 if the first date is earlier,
* a number greater than 0 if the first date is later.
*/
compareDate(first: iso8601DateString, second: iso8601DateString): number {
const result = compareAsc(first, second);
console.warn(`compareDate(${JSON.stringify(arguments)}) => ${result}`);
return result;
}
/**
* Checks if two dates are equal.
* @param first The first date to check.
* @param second The second date to check.
* @returns {boolean} Whether the two dates are equal.
* Null dates are considered equal to other null dates.
*/
sameDate(first: iso8601DateString | null, second: iso8601DateString | null): boolean {
const result = isEqual(first, second);
console.warn(`sameDate(${JSON.stringify(arguments)}) => ${result}`);
return result;
}
/**
* Clamp the given date between min and max dates.
* @param date The date to clamp.
* @param min The minimum value to allow. If null or omitted no min is enforced.
* @param max The maximum value to allow. If null or omitted no max is enforced.
* @returns `min` if `date` is less than `min`, `max` if date is greater than `max`,
* otherwise `date`.
*/
// clampDate(date: iso8601DateString, min?: iso8601DateString | null, max?: iso8601DateString | null): iso8601DateString;
/**
* 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, '');
}
}
function toIsoString(date: string | Date | number): string {
return format(date, `YYYY-MM-DD`);
}
/** Creates an array and fills it with values. */
function range<T>(length: number, valueFunction: (index: number) => T): T[] {
const valuesArray = Array(length);
for (let index = 0; index < length; index++) {
valuesArray[index] = valueFunction(index);
}
return valuesArray;
}
const DEFAULT_DATE_NAMES = range(31, i => String(i + 1));
const DEFAULT_DAY_OF_WEEK_NAMES = {
'long': ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
'short': ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
'narrow': ['S', 'M', 'T', 'W', 'T', 'F', 'S']
};
const DEFAULT_MONTH_NAMES = {
'long': [
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'
],
'short': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
'narrow': ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D']
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment