day incorrect in angular material datepicker

55,716

Solution 1

Two days ago, at https://github.com/angular/material2/issues/7167, Silthus posted his workaround that overrides the MomentJsDataAdapter. I tried it and it worked as a charm from anywhere in the globe.

First he added a MomentUtcDateAdapter that extends MomentDateAdapter

import { Inject, Injectable, Optional } from '@angular/core';
import { MAT_DATE_LOCALE } from '@angular/material';   
import { MomentDateAdapter } from '@angular/material-moment-adapter';
import { Moment } from 'moment';
import * as moment from 'moment';

@Injectable()
export class MomentUtcDateAdapter extends MomentDateAdapter {

  constructor(@Optional() @Inject(MAT_DATE_LOCALE) dateLocale: string) {
    super(dateLocale);
  }

  createDate(year: number, month: number, date: number): Moment {
    // Moment.js will create an invalid date if any of the components are out of bounds, but we
    // explicitly check each case so we can throw more descriptive errors.
    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 = moment.utc({ year, month, date }).locale(this.locale);

    // If the result isn't valid, the date must have been out of bounds for this month.
    if (!result.isValid()) {
      throw Error(`Invalid date "${date}" for month with index "${month}".`);
    }

    return result;
  }
}

And then in the AppModule component, you have to do this:

providers: [
    ...
    { provide: MAT_DATE_LOCALE, useValue: 'en-GB' },
    { provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS },
    { provide: DateAdapter, useClass: MomentUtcDateAdapter },
    ...
],

Solution 2

Just use option useUtc: true for MatMomentDateAdapter:

import { MatMomentDateModule, MAT_MOMENT_DATE_ADAPTER_OPTIONS } from '@angular/material-moment-adapter';

@NgModule({
  exports: [
    MatMomentDateModule,
    // ...
  ],
  providers: [
    { provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } }
  ],
})
export class CustomModule { }

Solution 3

Maybe it will be helpful for someone. It is my example method in which i erase TimeZone OffSet from component date

  addPriceListPeriod(priceListId: number, periodDateFrom: Date) {

    let UTCDate = Date.UTC(periodDateFrom.getFullYear(), periodDateFrom.getMonth(), periodDateFrom.getDate()) - periodDateFrom.getTimezoneOffset();

    periodDateFrom = new Date(UTCDate);

    const tempObject = {
      priceListId,
      fromDate: periodDateFrom
    }
    return this.httpClient.post('PriceLists/addPriceListPeriod', tempObject);
  }

Solution 4

https://github.com/angular/material2/issues/7167#issuecomment-402061126

You can change the default behaviour to parse dates as UTC by providing the MAT_MOMENT_DATA_ADAPTER_OPTIONS and setting it to useUtc: true.

@NgModule({ 
    imports: [MatDatepickerModule, MatMomentDateModule], 
    providers: [ 
        { provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } } 
    ] 
})

Solution 5

@Bruno_Cerecetto's code works fine but there is small issue. When a user type date in text box instead of choosing date with datepicker then his code does't work it reduce one day again. To work properly with the above code you need to override parse method also. Parse method called every time when a user type in the text box. Here is the code that works for me. I think it may help someone.

parse(value: any, parseFormat: string | string[]): Moment | null {
    console.log(value)
    if (value && typeof value === 'string') {
      return moment.utc(value, parseFormat, this.locale, true);
    }
    return value ? moment.utc(value).locale(this.locale) : null;
  }
Share:
55,716

Related videos on Youtube

Motaz Homsi
Author by

Motaz Homsi

CEO at Webotak

Updated on July 09, 2022

Comments

  • Motaz Homsi
    Motaz Homsi almost 2 years

    When I select a date I see the correct date in the field but, when I save, the datepicker send the day before the date I have selected ( 3 hours offset ) i am using angular reactive form and MatMomentDateModule for date picker .

    the problem is related to timezones but i just want to save the same date that the user enter to database .

    Code repreduced here : https://stackblitz.com/edit/angular-material-moment-adapter-example-kdk9nk?file=app%2Fapp.module.ts

    issue on stackblitz

    issue related to this on githup :

    https://github.com/angular/material2/issues/7167

    Any help is appreciated , and i think a lot of developers need a solution for this .

  • Motaz Homsi
    Motaz Homsi over 6 years
    i am using PHP and i think its right to convert timezoned date to timestamp and then convert it again to any other required format but i am searching for a solution for all app without convert date inputs before i add it to mysql because i have a lot of views with date inputs , i think i will go with this if i did not found a global solution for mt2 datepicker to handle this . and thanks for your answer :-)
  • Motaz Homsi
    Motaz Homsi over 6 years
    Great , i was saving the record as datetime in mysql , so when ever i update the date i get the previous day , now saving the record as string solve my problem , thanks a lot
  • Motaz Homsi
    Motaz Homsi over 6 years
    actually i tried to use MatMomentDateModule to solve this but i can't get it to work as i want , if you see the code that i repreduced in the main question you will find it and you can edit it to try it , i think that would be the real solution if you can give an example because a lot of developers need to save the date as "datetime" for DB quering purposes .
  • pouyada
    pouyada over 6 years
    Im not sure that it works. I didn't try as normally we don't need Moment in an angular app. Anyway i think we can save the produced date also as a datetime in DB. I will try it Monday and let you know.
  • Motaz Homsi
    Motaz Homsi over 6 years
    Thanks a lot @pouyada , i am appreciate your help , i will wait for your test :-)
  • pouyada
    pouyada over 6 years
    I tried to insert this date format to mysql but it does not accept it as a dateTime, so you need to convert it which is not recommended. Regarding to MomentDateAdapter, it is not suppoeted in older versions of angular material. you should use newer version ( recommended ) or install it from HERE
  • Motaz Homsi
    Motaz Homsi over 6 years
    i am using angular material 5.0.3 in my real app , i am including MatMomentDateModule for MomentDateAdapter to be included as the docs explain here . i think i have to use parse method of this adapter to change it using moment js to be in UTC or any other format the mysql accept it as datetime without changing it in backend , but i tried a lot without any success , now i am trying using ControlValueAccessor to change output value of datepicker .
  • Motaz Homsi
    Motaz Homsi about 6 years
    here is a stackblitz for solution stackblitz.com/edit/…
  • D33
    D33 about 6 years
    It worked for us but we had to override also these three methods: today, parse and deserialize.
  • Achyuth Kodali
    Achyuth Kodali almost 5 years
    Silthus's adapter works like a charm, but I have a requirement to set the format for the date. How can I achieve that alongside timezone adapter? my date format: "DD MMM YYYY" // 15 jul 2019
  • Md. Sabbir Ahamed
    Md. Sabbir Ahamed over 4 years
    Thanks a lot, it's remove my pain, working as expected
  • jenson-button-event
    jenson-button-event over 4 years
    this still takes an hour off when using e.g. { provide: MAT_DATE_LOCALE, useValue: 'en-GB' }
  • Johnathan Li
    Johnathan Li about 4 years
    This method is not working when user mannually type the date inside the text box. it didn't trigger the function.
  • Luis Ruiz Figueroa
    Luis Ruiz Figueroa almost 4 years
    It works when you select the date, but if the date already comes from the provider, it delays one day
  • Deitsch
    Deitsch almost 3 years
    Nice one, should be higher up!
  • Deitsch
    Deitsch almost 3 years
    For the issue raised by @JohnathanLi look at the answer of Pias Uddin Jibon
  • Senthilkumar Ramasamy
    Senthilkumar Ramasamy almost 3 years
    Nice. It worked. But the MatMoment Data Module has to be installed separately as this is not a part of material module. The below should help. npm install @angular/material-moment-adapter reference: material.angular.io/components/datepicker/overview
  • Serkan Sipahi
    Serkan Sipahi over 2 years
    Thank you, that was the missing (to provide it also in deps) part for me.
  • PrazSam
    PrazSam over 2 years
    This is the correct and simple answer. It works. But It's much appreciated if someone can explain why this happens and what happens behind the scene if we add MatMomentDateAdapter?
  • Tomek
    Tomek about 2 years
    If you're also providing MomentDateAdapter as DateAdapter then you need to inject that options object there too: { provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE, MAT_MOMENT_DATE_ADAPTER_OPTIONS] }
  • Hello
    Hello about 2 years
    This one is the only correct solution for me. All thew others are not working even using moment and MAT_MOMENT_DATE_ADAPTER_OPTIONS.
  • toxaq
    toxaq about 2 years
    Moment is no longer in active development. Not the ideal ongoing solution.