import { DatePipe , TitleCasePipe } from '@angular/common' ; import { Inject , Injectable , LOCALE_ID , Optional } from '@angular/core' ; import { SbbDateAdapter } from './date-adapter' ; import { SBB_DATEPICKER_2DIGIT_YEAR_PIVOT } from './datepicker-token' ; /**
 * 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 ; } @ Injectable ( ) export class SbbNativeDateAdapter extends SbbDateAdapter < Date > { private _datePipe : DatePipe ; private _yearPivot : number ; private _titleCasePipe = new TitleCasePipe ( ) ; constructor ( @ Inject ( LOCALE_ID ) protected override _locale , @ Optional ( ) @ Inject ( SBB_DATEPICKER_2DIGIT_YEAR_PIVOT ) yearPivot ) { super ( ) ; this . _datePipe = new DatePipe ( _locale ) ; this . _yearPivot = typeof yearPivot === 'number' ? yearPivot : new Date ( ) . getFullYear ( ) - 1975 ; } getYear ( date ) { return date . getFullYear ( ) ; } getMonth ( date ) { return date . getMonth ( ) ; } getDate ( date ) { return date . getDate ( ) ; } getDayOfWeek ( date ) { return date . getDay ( ) ; } getMonthName ( date ) { return this . _datePipe . transform ( date , 'LLLL' ) ! ; } getMonthNames ( style ) { let format ; switch ( style ) { case 'long' : format = 'LLLL' ; break ; case 'narrow' : format = 'LLLLL' ; break ; default : format = 'LLL' ; break ; } return range ( 12 , ( i ) => this . _datePipe . transform ( new Date ( 2017 , i , 1 ) , format ) ! ) ; } getDateNames ( ) { return range ( 31 , ( i ) => this . _datePipe . transform ( new Date ( 2017 , 0 , i + 1 ) , 'd' ) ! ) ; } getDayOfWeekNames ( style ) { let format ; switch ( style ) { case 'long' : format = 'EEEE' ; break ; case 'narrow' : format = 'EEEEE' ; break ; default : format = 'EEE' ; break ; } return range ( 7 , ( i ) => this . _datePipe . transform ( new Date ( 2017 , 0 , i + 1 ) , format ) ! ) ; } getYearName ( date ) { return this . _datePipe . transform ( date , 'yyyy' ) ! ; } getFirstDayOfWeek ( ) { return 1 ; } getNumDaysInMonth ( date ) { const lastDayOfMonth = new Date ( 0 ) ; lastDayOfMonth . setFullYear ( date . getFullYear ( ) , date . getMonth ( ) + 1 , 0 ) ; lastDayOfMonth . setHours ( 0 , 0 , 0 , 0 ) ; return lastDayOfMonth . getDate ( ) ; } clone ( date ) { return new Date ( date . valueOf ( ) ) ; } 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.` ) ; } const result = this . _createDateWithOverflow ( year , month , date ) ; if ( result . getMonth ( ) !== month ) { throw Error ( `Invalid date "${ date }" for month with index "${ month }".` ) ; } return result ; } today ( ) { const date = new Date ( ) ; date . setHours ( 0 , 0 , 0 , 0 ) ; return date ; } parse ( value ) { if ( typeof value === 'number' ) { return new Date ( value ) ; } else if ( typeof value === 'string' ) { return this . _parseStringDate ( value ) ; } return null ; } format ( date , displayFormat ) { if ( ! this . isValid ( date ) ) { throw Error ( 'NativeDateAdapter: Cannot format invalid date.' ) ; } return this . _titleCasePipe . transform ( this . _datePipe . transform ( date , displayFormat ) ! ) ; } addCalendarYears ( date , years ) { return this . addCalendarMonths ( date , years * 12 ) ; } addCalendarMonths ( date , months ) { const targetMonth = date . getMonth ( ) + months ; const dateWithCorrectMonth = new Date ( 0 ) ; dateWithCorrectMonth . setFullYear ( date . getFullYear ( ) , targetMonth , 1 ) ; dateWithCorrectMonth . setHours ( 0 , 0 , 0 , 0 ) ; const daysInMonth = this . getNumDaysInMonth ( dateWithCorrectMonth ) ; const newDate = this . clone ( date ) ; newDate . setMonth ( targetMonth , Math . min ( daysInMonth , date . getDate ( ) ) ) ; return newDate ; } addCalendarDays ( date , days ) { const newDate = this . clone ( date ) ; newDate . setDate ( newDate . getDate ( ) + days ) ; return newDate ; } toIso8601 ( date ) { return this . _datePipe . transform ( date , 'yyyy-MM-dd' ) ! ; } /**
     * 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.
     */ override deserialize ( value ) { if ( typeof value === 'string' ) { if ( ! value ) { return null ; } if ( ISO_8601_REGEX . test ( value ) ) { const 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 . valueOf ( ) ) ; } invalid ( ) { return new Date ( NaN ) ; } protected _parseStringDate ( value ) { const match = /^(\w+,[ ]?)?(\d+)\.(\d+)\.(\d+)$/ . exec ( value ) ; if ( ! match ) { return null ; } const date = this . _createDateWithOverflow ( + match [ 4 ] , + match [ 3 ] - 1 , + match [ 2 ] ) ; return this . _normalizeYear ( date ) ; } protected _normalizeYear ( date ) { if ( date . getFullYear ( ) < 0 || date . getFullYear ( ) >= 100 ) { return date ; } const yearOffset = date . getFullYear ( ) <= this . _yearPivot ? 2000 : 1900 ; return new Date ( date . getFullYear ( ) + yearOffset , date . getMonth ( ) , date . getDate ( ) ) ; } /** Creates a date but allows the month and date to overflow. */ protected _createDateWithOverflow ( year , month , date ) { const result = new Date ( year , month , date ) ; if ( year >= 0 && year < 100 ) { result . setFullYear ( this . getYear ( result ) - 1900 ) ; } return result ; } }