import { IComponentController } from 'angular' import { Component, Input, Output } from '../decorators' import { DateTime } from 'luxon' import { getTimeZone } from './time-zone-city.service' export interface DateTimeZoneCity { dateTime: Date | DateTime | null timeZoneName: string | null } @Component({ selector: 'mflyDateTimeZoneCityPicker', template: require('./date-time-zone-city-picker.html'), }) export default class DateTimeZoneCityPickerController implements IComponentController { @Input('@') format: string @Input('@') inputId: string @Input() isDisabled?: boolean @Input('@') isRequired: boolean @Input() value?: DateTimeZoneCity @Output('&?') onChange: (param: { newValue: DateTimeZoneCity }) => void @Output('&?') onOpen: () => void protected dateLuxon: DateTime | null protected timeLuxon: DateTime | null protected timeZoneName: string | null $onInit(): void { this.initFromValue(this.value) } $onChanges(changes: { value: ng.IChangesObject }): void { if (changes.value) { this.initFromValue(changes.value.currentValue) } } private initFromValue(value: DateTimeZoneCity | undefined): void { let luxon: DateTime | undefined if (!value?.dateTime) { luxon = undefined } else if (value.dateTime instanceof DateTime) { luxon = value.dateTime } else if (value.dateTime instanceof Date) { luxon = DateTime.fromJSDate(value.dateTime) } else if (typeof value.dateTime === 'string') { luxon = DateTime.fromISO(value.dateTime, { setZone: true }) } else { throw new Error(`Invalid value type ${typeof value} (${value})`) } if (!luxon) { this.dateLuxon = null this.timeLuxon = DateTime.fromObject({ hour: 0, minute: 0, second: 0, millisecond: 0 }) } else { this.dateLuxon = DateTime.fromObject({ year: luxon.year, month: luxon.month, day: luxon.day }) this.timeLuxon = DateTime.fromObject({ hour: luxon.hour, minute: luxon.minute, second: 0, millisecond: 0 }) } const timeZone = getTimeZone(value?.timeZoneName) this.timeZoneName = timeZone?.timeZoneName || Intl.DateTimeFormat().resolvedOptions().timeZone } protected updateDate(dateLuxon: DateTime): void { this.dateLuxon = dateLuxon this.updateModel() } protected updateTime(timeLuxon: DateTime): void { this.timeLuxon = timeLuxon this.updateModel() } protected updateTimeZone(timeZoneName: string): void { this.timeZoneName = timeZoneName this.updateModel() } private updateModel(): void { const oldDateTime = this.value?.dateTime const oldTimeZoneName = this.value?.timeZoneName let dateTime: DateTime | undefined if (this.dateLuxon && this.timeLuxon) { const targetZone = this.timeZoneName || 'utc' dateTime = DateTime.fromObject({ year: this.dateLuxon.year, month: this.dateLuxon.month, day: this.dateLuxon.day, hour: this.timeLuxon.hour, minute: this.timeLuxon.minute, second: 0, millisecond: 0, }, { zone: targetZone }) } this.value = { dateTime: dateTime || null, timeZoneName: this.timeZoneName } if (!this.datesMatch(oldDateTime, dateTime) || oldTimeZoneName !== this.timeZoneName) { this.onChange?.({ newValue: this.value }) } } private datesMatch(dateTime1: DateTime | Date | null | undefined, dateTime2: DateTime | Date | null | undefined): boolean { if (dateTime1 instanceof DateTime && dateTime2 instanceof DateTime) { return dateTime1.toISO() === dateTime2.toISO() } else if (dateTime1 instanceof Date && dateTime2 instanceof Date) { return dateTime1.toISOString() === dateTime2.toISOString() } return false } }