day incorrect in angular material datepicker

倾然丶 夕夏残阳落幕 提交于 2019-12-03 07:08:17

问题


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 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 .


回答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 },
    ...
],



回答2:


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 } } 
    ] 
})



回答3:


OPTION 1: Had same issue and solved it in backed (Java) Can you solve this in your backend code.
Presenting java code in case if anyone needs the same help. concept should be similar for other server side technologies too.

Simulated the same bug and following are the analysis.

PRINT Via JSON | "startdate": "2018-02-08T18:30:00.000Z" .

PRINT BEFORE SUBMIT >>Fri Feb 09 2018 00:00:00 GMT+0530 (IST)

public static String dateConversion(String dt) {

        Instant timestamp = Instant.parse(dt);
        ZonedDateTime isttime = timestamp.atZone(ZoneId.of("Asia/Kolkata"));
        System.out.println(isttime);

        System.out.println(DateTimeFormatter.ofPattern("dd-MM-yyyy").format(isttime));
        return DateTimeFormatter.ofPattern("dd-MM-yyyy").format(isttime);
    }

OPTION 2: (solution in frontend) I have not tried this option but the documentation seems very clear.
https://maggiepint.com/2016/05/14/moment-js-shows-the-wrong-date/ Check this out.




回答4:


Angular material date picker does not support different time zones at the moment and it gives you an UTC time offset. But to me it is not a problem as i save the output string to my database and when i return it back to user the browser shows the correct date time object. Try this in your console(im in +01 zone) and you will see the selected date:

new Date('2018-02-08T23:00:00.000Z')
Fri Feb 09 2018 00:00:00 GMT+0100 (CET)

Another solution is to modify your date object before saving to database which is not the best practice.

Lastly, If you are using Moment.js in your app you could give the MomentDateAdapter a try and see if it helps you. Just add the MatMomentDateModule to your application as described here.




回答5:


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);
  }



回答6:


thanks to @ankit-raonka answer here : https://stackoverflow.com/a/48761312/6423656

using ControlValueAccessor solved my issue and here is a working example : https://stackblitz.com/edit/angular-dlxnmx?file=app%2Fcva-date.component.ts

Thanks for every one for answers , i think this issue could also resolved using parse method of moment date adapter but i like the ControlValueAccessor try as i control all the input aspects + reduce the code written when i want to use it in my html .




回答7:


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 MaterialModule { }



回答8:


If You using Reactive Forms and do this.form.get("dateField").value you must cast it as a Date before submit.

It'll be something like:

save() {
  let obj = new MyObj();
  obj.myDate = new Date(this.form.get("mydateField").value);
  this.service.save(obj);
}



回答9:


If the hour of the date is equal to 0, it means we are in the same timezone and you don't need to do something.

If the hour of the date is greater than 12, it means the date is the day of yesterday and you need to add a day.

If the hour of the date is lower than 12, it means the date is the day tomorrow and you need to correct it.

Below, a small function that may helps you as it helps me

let date = new Date(dateReceived);
//We use dayCorrector to remove the timezone. We want brut date without any timezone
let dayCorrector = (date.getHours()>12) ? (1) : (date.getHours()<=12) ? (-1) : (0);
date.setDate(date.getDate()+dayCorrector);
let dateFinal = (String(date.getFullYear()) +'-'+ String(date.getMonth()+1) +'-'+ String(date.getDate())+' 00:00:00Z');



回答10:


If you want to use it in your component, you can simply do

pipe = new DatePipe('en-US'); // Use your own locale

Now, you can simply use its transform method, which will be

const now = Date.now();
const myFormattedDate = this.pipe.transform(now, 'short');



回答11:


The solution stackblitz works great, but if you are not storing the timestamp in Database, then you need to convert back the date to UTC format while retrieving it from database before assigning to the datepicker.

const dbDate = getDbDate(); // '2018-02-09T00:00:00.000Z'

const updateDate = new Date(
      Date.UTC(dbDate.getFullYear(), dbDate.getMonth(), dbDate.getDate())
    ).toISOString();

now assign the updatedDate to the datepicker, hope this helps.



来源:https://stackoverflow.com/questions/48710053/day-incorrect-in-angular-material-datepicker

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!