mrsan22
mrsan22

Reputation: 747

Angular Material 2 (MatDatepickerModule) - The date selected in the date picker is off by 1 day

I am working on an Angular application (v4.4.3) with Angular Material Beta 11 version. With beta 11, the datepicker is accepting date in ISOformat string. However, in my app the date in the date picker module is off by 1 day, as shown in below image:

enter image description here

The flow of date in my app is follows, the app receives a date in Unix Epoch time from Mysql database and I convert that Epoch time date into ISOString string format and return that ISO date to matDatePicker.

/**
 * This method takes the key, which is a epoch time in string format and convert into JS Date object.
 * @param key {string} - the epoch time
 * @return {string} - Date Object.
 */
private static dateHelper(key: string): any {
    const dateObj = new Date(+key * 1000);
    // Need to return ISOString format date as accepted by Material DatePicker component
    return dateObj.toISOString().substring(0, 10);

}

I also have Date Picker Module, to display the date in customized format:

import {NgModule} from '@angular/core';
import {MdDatepickerModule, MdNativeDateModule, NativeDateAdapter, DateAdapter, MD_DATE_FORMATS} from '@angular/material';

// extend NativeDateAdapter's format method to specify the date format.
export class CustomDateAdapter extends NativeDateAdapter {
    format(date: Date, displayFormat: Object): string {
        if (displayFormat === 'input') {
            const day = date.getUTCDate();
            const month = date.getUTCMonth() + 1;
            const year = date.getFullYear();
            return `${year}-${month}-${day}`;
        } else {
            return date.toDateString();
        }
    }
}

const MY_DATE_FORMATS = {
    parse: {
        dateInput: {month: 'short', year: 'numeric', day: 'numeric'}
    },
    display: {
        dateInput: 'input',
        monthYearLabel: {year: 'numeric', month: 'short'},
        dateA11yLabel: {year: 'numeric', month: 'long', day: 'numeric'},
        monthYearA11yLabel: {year: 'numeric', month: 'long'},
    }
};

@NgModule({
    declarations: [],
    imports: [],
    exports: [MdDatepickerModule, MdNativeDateModule],
    providers: [
        {
            provide: DateAdapter, useClass: CustomDateAdapter
        },
        {
            provide: MD_DATE_FORMATS, useValue: MY_DATE_FORMATS
        }
    ]
})

export class DatePickerModule {

}

The DatePicker:

    <mat-form-field>
            <input matInput
                   [matDatepicker]="myDate"
                   placeholder="Start Date"                       
                   [(ngModel)]="date"
                   name="start_date" required>
            <mat-datepicker-toggle matSuffix [for]="myDate"></mat-datepicker-toggle>
            <mat-datepicker #myDate></mat-datepicker>
            <mat-error>This field is required!</mat-error>
        </mat-form-field>

I did Google about it and there were some posts with this issue but I was not able to follow them completely. Any input of this would be highly appreciated. Thanks!

Upvotes: 1

Views: 6605

Answers (4)

mrsan22
mrsan22

Reputation: 747

Got it resolved. Thanks Everyone for their inputs.

So after having some discussion here and going through the Angular Material 2 Github issues and posts, I got some ideas and ways to assign the Date to DatePicker component. With Material beta 11, the DatePicker component can take date as Date or ISOString format. I was having the issue when I was using the ISOString format. I just updated it to normal Date format as shown below.

The updated code snippets from my Question:

 /**
 * This method takes the key, which is a epoch time in string format and 
 * convert into JS Date object.
 * @param key {string} - the epoch time
 * @return {string} - Date Object.
 */
private static dateHelper(key: string): any {
    const dateObj = new Date(+key * 1000);
    // Need to return Date object/ISOString format date as accepted by Material DatePicker component
    return new Date(dateObj.getFullYear(), dateObj.getUTCMonth(), dateObj.getUTCDate());;

}

This was the only change I did to match the date in mdInput and the calendar. I am not still sure the reason for the issue when I was using ISOString format. Also, I realized I do not need to use any CustomDateAdapter as it's mainly for appearance purpose.

Upvotes: 1

amal
amal

Reputation: 3170

I am not quite sure of this, but my suspicion would be on the epoch time to Date conversion and the Time Zone influence on it. Try this approach to convert the DB time.

Most likely in this line,

const dateObj = new Date(+key * 1000);

the Date constructor might initialize the date based on your local (system) TZ and that might offset things from the UTC time.

EDIT

Okay, digged a little deeper. I think I have ball parked the reason for this issue. You may have to customize the parse() method as well in your CustomDateAdapter as it actually converts the given date (UTC) to that of a local time (zone) date as per this inbuilt parse() method.

Try adding something to the effect of this in your CustomDateAdapter,

parse(value: any): Date | null {
 if (typeof value == 'number') {
  return new Date(value); // not likely to enter here as your date is in 'string' format
 }
 return value ? new Date(Date.parse(value + ' GMT')) : null; //forces Time Zone to GMT/UTC and converts it
}

See if it fixes your issue.

Upvotes: 0

Fetrarij
Fetrarij

Reputation: 7326

It should work if you use date.getDate() and date.getMonth(), not date.getUTCDate() and date.getUTCMonth() , in the format method in your custom date adapter.

Try and let us know:

export class CustomDateAdapter extends NativeDateAdapter {
    format(date: Date, displayFormat: Object): string {
           if (displayFormat == "input") {
               let day = date.getDate();
               let month = date.getMonth() + 1;
               let year = date.getFullYear();
               return `${year}-${month}-${day}`
           } else {
               return date.toDateString();
           }
       }
}

Upvotes: 1

ttugates
ttugates

Reputation: 6271

With out debugging in a console, a date somewhere is being represented with a timezone most likely. And the difference between the represented time zone, and UTC is 1 day.

It may be the date.toDataString().

See this.

Upvotes: 0

Related Questions