Created
September 12, 2017 19:04
-
-
Save another-guy/adc2d9d731af7693fdbfab21b1e227a9 to your computer and use it in GitHub Desktop.
ISO8601 string based Date Adapter for angular material
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 { 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