1

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!

mrsan22
  • 727
  • 2
  • 11
  • 28

4 Answers4

1

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();
           }
       }
}
Fetrarij
  • 7,176
  • 3
  • 27
  • 35
  • Hey Fetra...Thanks for your input. If I do that then the date from custom adapter differs(1 day off) from the ISO format date that I am getting from Epoch time in another function. So to match them..I am using UTC date. – mrsan22 Sep 27 '17 at 03:14
  • @mrsan22 the format method is for display the date after selecting the date in datepicker, so I think the best for you is to use that iso format in the another function you mention? – Fetrarij Sep 27 '17 at 03:19
  • Not sure If I got your point. When I receive the date (in Epoch) format from db, I am using `toISOformat` method to get a date (it matches the date in db)that is compatible with DatePicker and I can populate the DatePicker with this ISO format date. The date in the input is shown correctly and in a format described in the `format` method but in the calendar it's off by 1 day. – mrsan22 Sep 27 '17 at 03:37
1

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.

mrsan22
  • 727
  • 2
  • 11
  • 28
0

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.

ttugates
  • 5,818
  • 3
  • 44
  • 54
0

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.

amal
  • 3,140
  • 1
  • 13
  • 20
  • Hey @amal..Thanks for your input. I tried the approach you mentioned, however, I still have the same issue. I have read few related issues and people have mentioned about TimeZone but I am not sure..how and where Time Zone influence is occurring. – mrsan22 Sep 26 '17 at 21:59
  • Updated my answer, see if that points out anything for you. At least try to console.log both cases and see if they differ. Try one with `new Date(+key * 1000);` and other with `new Date(Date.UTC(+key * 1000));` – amal Sep 26 '17 at 22:02
  • Hey Amal..I will try this and let you know. – mrsan22 Sep 27 '17 at 02:36
  • `Date.UTC()` does not take Epoch time. Also the `format` method of DateAdapter is giving the correct date as the date in the mdInput is populated correctly, it's the the calendar that is off by 1 day. – mrsan22 Sep 27 '17 at 04:15
  • Could you please also add the corresponding component code. – amal Sep 27 '17 at 04:21
  • This is part of very complex form and hence the component code has lot of other functions. However in simple terms and as far as date is concerned, Once I convert the Epoch to ISOformat, I simply assign it to `date` in `ngModel` of Date Picker. Let me know if that helps. – mrsan22 Sep 27 '17 at 04:36
  • Okay. Please help me understand this (have never used material-datepicker before). From the docs, it looks like you don't have the `` in your template (or is it simply not shown in OP?). Without the right template reference to `myDate` how does the `[matDatepicker]="myDate"` take that as an `@Input` on the `` element field? – amal Sep 27 '17 at 11:05
  • Ohh..I had that..it was just outside the , updated the code in my post. – mrsan22 Sep 27 '17 at 15:17
  • What is the corresponding component property that shows the right date up top in your example image given? Is it `date`? – amal Sep 27 '17 at 15:50
  • Yes.. it's the `date`. – mrsan22 Sep 27 '17 at 15:54
  • @mrsan22 see my updated answer. Let me know if it helped (or changed anything in your view) – amal Sep 27 '17 at 17:21
  • Thanks @Amal for your inputs.. Got it resolved by not using `ISOString` format. Check my Post. – mrsan22 Sep 27 '17 at 20:55