angulartypescriptdatemat-datepickerangular-material-components

How to format Time zone for angular-material-components/datetime-picker?


I'm using the angular-material-components/datetime-picker as described here in their doc/demo site - https://h2qutc.github.io/angular-material-components/#/datetimepicker.

How can I get the time zone (non-UTC) in the picked date time value? Preferred value have the format: Feb 10, 2024, 06:00 PM CST.

If I go the NgxMatNativeDateModule I specify the following format ->

const INTL_DATE_INPUT_FORMAT = {
  year: 'numeric',
  month: 'short',
  day: 'numeric',
  hour: '2-digit',
  minute: '2-digit',
  timeZoneName: 'short'
};

const MAT_DATE_FORMATS: NgxMatDateFormats = {
  parse: {
    dateInput: INTL_DATE_INPUT_FORMAT,
  },
  display: {
    dateInput: INTL_DATE_INPUT_FORMAT,
    monthYearLabel: { year: 'numeric', month: 'short' },
    dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },
    monthYearA11yLabel: { year: 'numeric', month: 'long' },
  },

This produces the following in the date picker input but it's in UTC rather than the local time zone which is what I'm looking for. I'm not sure how to get around that.

Native Date Module

Otherwise I can use the NgxMatMomentModule and specify a date format like

export const MOMENT_DATETIME_WITH_SECONDS_FORMAT = 'MM/d/y, h:mm A ZZ';

const CUSTOM_MOMENT_FORMATS: NgxMatDateFormats = {
  parse: {
    dateInput: MOMENT_DATETIME_WITH_SECONDS_FORMAT,
  },
  display: {
    dateInput: MOMENT_DATETIME_WITH_SECONDS_FORMAT,
    monthYearLabel: 'MMM YYYY',
    dateA11yLabel: 'LL',
    monthYearA11yLabel: 'MMMM YYYY',
  },
};

That produces the following and displays the time zone as an hour offset but I prefer the string like 'CST'. Is there a way to do that? Do I need to make a custom adapter to do that potentially?

Moment Date Module


Solution

  • Here is a copy of my custom Date adapter:

    import { DatePipe } from '@angular/common';
    import { NativeDateAdapter } from '@angular/material/core';
    
    const USE_TIMEZONE = 'UTC';
    
    function range<T>(length: number, valueFunction: (index: number) => T): T[] {
      return Array.from({ length }, (_, i) => valueFunction(i));
    }
    
    function createDtf(locale: string, options: Intl.DateTimeFormatOptions) {
      return new Intl.DateTimeFormat(locale, {
        ...options,
        timeZone: USE_TIMEZONE,
      });
    }
    
    export class AppDateAdapter extends NativeDateAdapter {
      private formatDate(dtf: Intl.DateTimeFormat, date: Date): string {
        const d = new Date(
          Date.UTC(
            date.getFullYear(),
            date.getMonth(),
            date.getDate(),
            date.getHours(),
            date.getMinutes(),
            date.getSeconds(),
            date.getMilliseconds()
          )
        );
        return dtf.format(d);
      }
    
      override getFirstDayOfWeek(): number {
        return 1;
      }
    
      override getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[] {
        const dtf = createDtf(this.locale, { weekday: 'short' });
        return range(7, (i) => this.formatDate(dtf, new Date(2017, 0, i + 1)));
      }
    
      override getDateNames(): string[] {
        const dtf = createDtf(this.locale, { day: '2-digit' });
        return range(31, (i) => this.formatDate(dtf, new Date(2017, 0, i + 1)));
      }
    
      override format(date: any, displayFormat: any): any {
        return new DatePipe(this.locale).transform(date, displayFormat);
      }
    }
    
    
    import { DEFAULT_CURRENCY_CODE, LOCALE_ID, Provider } from '@angular/core';
    import {
      DateAdapter,
      MAT_DATE_FORMATS,
      MAT_DATE_LOCALE,
    } from '@angular/material/core';
    
    // German
    import localeDe from '@angular/common/locales/de';
    import localeDeExtra from '@angular/common/locales/extra/de';
    
    import { registerLocaleData } from '@angular/common';
    registerLocaleData(localeDe, localeDeExtra);
    
    import { AppDateAdapter } from './locale.adapter';
    
    export function provideLocaleConfig(): Provider[] {
      return [
        {
          provide: LOCALE_ID,
          useValue: 'de-DE',
        },
    
        {
          provide: MAT_DATE_FORMATS,
          useValue: {
            parse: { dateInput: 'mediumDate' },
            display: {
              dateInput: 'mediumDate',
              monthLabel: 'MMMM',
              monthYearLabel: 'MMM YYYY',
              dateA11yLabel: 'LL',
              monthYearA11yLabel: 'MMMM YYYY',
            },
          },
        },
        {
          provide: DEFAULT_CURRENCY_CODE,
          useValue: '€',
        },
        {
          provide: DateAdapter,
          useClass: AppDateAdapter ,
          deps: [MAT_DATE_LOCALE],
        },
      ];
    }
    
    // app.config.ts in angular v17+
    
    export const appConfig: ApplicationConfig = {
      providers: [
        //...
        provideLocaleConfig()
        //...
      ],
    };
    

    For older version you can just provide it in same way in app.module:

    providers: [
        //...
        provideLocaleConfig()
        //...
      ],