license import { Platform } from '@angular/cdk/platform' ; import { Inject , Injectable , Optional } from '@angular/core' ; import { DateAdapter , MAT_DATE_LOCALE } from './date-adapter' ; /** Whether the browser supports the Intl API. */ const SUPPORTS_INTL_API = typeof Intl != 'undefined' ; /** The default month names to use if Intl API is not available. */ 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' ] } ; /** The default date names to use if Intl API is not available. */ const DEFAULT_DATE_NAMES = range ( 31 , i => String ( i + 1 ) ) ; /** The default day of the week names to use if Intl API is not available. */ 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' ] } ; /**
 * Matches strings that have the form of a valid RFC 3339 string
 * (https://tools.ietf.org/html/rfc3339). Note that the string may not actually be a valid date
 * because the regex will match strings an with out of bounds month, date, etc.
 */ const ISO_8601_REGEX = /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|(?:(?:\+|-)\d{2}:\d{2}))?)?$/ ; /** Creates an array and fills it with values. */ function range < T > ( length , valueFunction ) { const valuesArray = Array ( length ) ; for ( let i = 0 ; i < length ; i ++ ) { valuesArray [ i ] = valueFunction ( i ) ; } return valuesArray ; } /** Adapts the native JS Date for use with cdk-based components that work with dates. */ @ Injectable ( ) export class NativeDateAdapter extends DateAdapter < Date > { /** Whether to clamp the date between 1 and 9999 to avoid IE and Edge errors. */ private readonly _clampDate : boolean ; /**
     * Whether to use `timeZone: 'utc'` with `Intl.DateTimeFormat` when formatting dates.
     * Without this `Intl.DateTimeFormat` sometimes chooses the wrong timeZone, which can throw off
     * the result. (e.g. in the en-US locale `new Date(1800, 7, 14).toLocaleDateString()`
     * will produce `'8/13/1800'`.
     *
     * TODO(mmalerba): drop this variable. It's not being used in the code right now. We're now
     * getting the string representation of a Date object from it's utc representation. We're keeping
     * it here for sometime, just for precaution, in case we decide to revert some of these changes
     * though.
     */ useUtcForDisplay : boolean = true ; constructor ( @ Optional ( ) @ Inject ( MAT_DATE_LOCALE ) matDateLocale , platform ) { super ( ) ; super . setLocale ( matDateLocale ) ; this . useUtcForDisplay = ! platform . TRIDENT ; this . _clampDate = platform . TRIDENT || platform . EDGE ; } getYear ( date ) { return date . getFullYear ( ) ; } getMonth ( date ) { return date . getMonth ( ) ; } getDate ( date ) { return date . getDate ( ) ; } getDayOfWeek ( date ) { return date . getDay ( ) ; } getMonthNames ( style ) { if ( SUPPORTS_INTL_API ) { const dtf = new Intl . DateTimeFormat ( this . locale , { month : style , timeZone : 'utc' } ) ; return range ( 12 , i => this . _stripDirectionalityCharacters ( this . _format ( dtf , new Date ( 2017 , i , 1 ) ) ) ) ; } return DEFAULT_MONTH_NAMES [ style ] ; } getDateNames ( ) { if ( SUPPORTS_INTL_API ) { const dtf = new Intl . DateTimeFormat ( this . locale , { day : 'numeric' , timeZone : 'utc' } ) ; return range ( 31 , i => this . _stripDirectionalityCharacters ( this . _format ( dtf , new Date ( 2017 , 0 , i + 1 ) ) ) ) ; } return DEFAULT_DATE_NAMES ; } getDayOfWeekNames ( style ) { if ( SUPPORTS_INTL_API ) { const dtf = new Intl . DateTimeFormat ( this . locale , { weekday : style , timeZone : 'utc' } ) ; return range ( 7 , i => this . _stripDirectionalityCharacters ( this . _format ( dtf , new Date ( 2017 , 0 , i + 1 ) ) ) ) ; } return DEFAULT_DAY_OF_WEEK_NAMES [ style ] ; } getYearName ( date ) { if ( SUPPORTS_INTL_API ) { const dtf = new Intl . DateTimeFormat ( this . locale , { year : 'numeric' , timeZone : 'utc' } ) ; return this . _stripDirectionalityCharacters ( this . _format ( dtf , date ) ) ; } return String ( this . getYear ( date ) ) ; } getFirstDayOfWeek ( ) { return 0 ; } getNumDaysInMonth ( date ) { return this . getDate ( this . _createDateWithOverflow ( this . getYear ( date ) , this . getMonth ( date ) + 1 , 0 ) ) ; } clone ( date ) { return this . createDate ( this . getYear ( date ) , this . getMonth ( date ) , this . getDate ( date ) ) ; } createDate ( year , month , date ) { if ( month < 0 || month > 11 ) { throw Error ( `Invalid month index "${ month }". Month index has to be between 0 and 11.` ) ; } if ( date < 1 ) { throw Error ( `Invalid date "${ date }". Date has to be greater than 0.` ) ; } let result = this . _createDateWithOverflow ( year , month , date ) ; if ( result . getMonth ( ) != month ) { throw Error ( `Invalid date "${ date }" for month with index "${ month }".` ) ; } return result ; } today ( ) { return new Date ( ) ; } parse ( value ) { if ( typeof value == 'number' ) { return new Date ( value ) ; } return value ? new Date ( Date . parse ( value ) ) : null ; } format ( date , displayFormat ) { if ( ! this . isValid ( date ) ) { throw Error ( 'NativeDateAdapter: Cannot format invalid date.' ) ; } if ( SUPPORTS_INTL_API ) { if ( this . _clampDate && ( date . getFullYear ( ) < 1 || date . getFullYear ( ) > 9999 ) ) { date = this . clone ( date ) ; date . setFullYear ( Math . max ( 1 , Math . min ( 9999 , date . getFullYear ( ) ) ) ) ; } displayFormat = { ... displayFormat , timeZone : 'utc' } ; const dtf = new Intl . DateTimeFormat ( this . locale , displayFormat ) ; return this . _stripDirectionalityCharacters ( this . _format ( dtf , date ) ) ; } return this . _stripDirectionalityCharacters ( date . toDateString ( ) ) ; } addCalendarYears ( date , years ) { return this . addCalendarMonths ( date , years * 12 ) ; } addCalendarMonths ( date , months ) { let newDate = this . _createDateWithOverflow ( this . getYear ( date ) , this . getMonth ( date ) + months , this . getDate ( date ) ) ; if ( this . getMonth ( newDate ) != ( ( this . getMonth ( date ) + months ) % 12 + 12 ) % 12 ) { newDate = this . _createDateWithOverflow ( this . getYear ( newDate ) , this . getMonth ( newDate ) , 0 ) ; } return newDate ; } addCalendarDays ( date , days ) { return this . _createDateWithOverflow ( this . getYear ( date ) , this . getMonth ( date ) , this . getDate ( date ) + days ) ; } toIso8601 ( date ) { return [ date . getUTCFullYear ( ) , this . _2digit ( date . getUTCMonth ( ) + 1 ) , this . _2digit ( date . getUTCDate ( ) ) ] . join ( '-' ) ; } /**
     * Returns the given value if given a valid Date or null. Deserializes valid ISO 8601 strings
     * (https://www.ietf.org/rfc/rfc3339.txt) into valid Dates and empty string into null. Returns an
     * invalid date for all other values.
     */ deserialize ( value ) { if ( typeof value === 'string' ) { if ( ! value ) { return null ; } if ( ISO_8601_REGEX . test ( value ) ) { let date = new Date ( value ) ; if ( this . isValid ( date ) ) { return date ; } } } return super . deserialize ( value ) ; } isDateInstance ( obj ) { return obj instanceof Date ; } isValid ( date ) { return ! isNaN ( date . getTime ( ) ) ; } invalid ( ) { return new Date ( NaN ) ; } /** Creates a date but allows the month and date to overflow. */ private _createDateWithOverflow ( year , month , date ) { const result = new Date ( year , month , date ) ; if ( year >= 0 && year < 100 ) { result . setFullYear ( this . getYear ( result ) - 1900 ) ; } return result ; } param n returns private _2digit ( n ) { return ( '00' + n ) . slice ( - 2 ) ; } param str returns private _stripDirectionalityCharacters ( str ) { return str . replace ( /[\u200e\u200f]/g , '' ) ; } param dtf param date returns private _format ( dtf , date ) { const d = new Date ( Date . UTC ( date . getFullYear ( ) , date . getMonth ( ) , date . getDate ( ) , date . getHours ( ) , date . getMinutes ( ) , date . getSeconds ( ) , date . getMilliseconds ( ) ) ) ; return dtf . format ( d ) ; } }