Is there any way to set the time zone on the datepicker from angular material 2?
Component:
<mat-form-field>
<input matInput [matDatepicker]="picker" placeholder="Choose a date">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
To set the timezone on the datepicker you need to create a custom DateAdapter as mentioned by @lee-richardson.
The following implementation is working on angular 11.0.0.
https://stackblitz.com/edit/angular-zbmhw5-n955ki
How I created this stackblitz:
I forked the stackblitz provided by the Angular Material documentation illustrating the basic datepicker
I Installed the moment-timezone dependency
I Created a custom-moment-date-adapter.ts and inherit from MomentDateAdapter provided by Angular/Component. Then I have overrided createDate
, deserialize
, parse
and today
methods.
For the exemple I hardcoded the timezone but you can adapt the code to get the timezone from where you like.
import { Inject, Injectable, Optional } from "@angular/core";
import { MomentDateAdapter } from "@angular/material-moment-adapter";
import { MAT_DATE_LOCALE } from "@angular/material/core";
import moment from "moment-timezone";
@Injectable()
export class CustomMomentDateAdapter extends MomentDateAdapter {
constructor(@Optional() @Inject(MAT_DATE_LOCALE) dateLocale: string) {
super(dateLocale);
}
static TIMEZONE = "Asia/Sakhalin";
createDate(year: number, month: number, date: number): moment.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.`);
}
const monthString = ("0" + (month + 1)).slice(-2);
const yearSting = ("0" + date).slice(-2);
const dateString = `${year}-${monthString}-${yearSting} 00:00`;
const result = moment.tz(dateString, CustomMomentDateAdapter.TIMEZONE);
// 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;
}
/**
* Returns the given value if given a valid Moment or null. Deserializes valid ISO 8601 strings
* (https://www.ietf.org/rfc/rfc3339.txt) and valid Date objects into valid Moments and empty
* string into null. Returns an invalid date for all other values.
*/
deserialize(value: any): moment.Moment | null {
let date;
if (value instanceof Date) {
date = this._createMoment2(value).locale(this.locale);
} else if (this.isDateInstance(value)) {
// Note: assumes that cloning also sets the correct locale.
return this.clone(value);
}
if (typeof value === "string") {
if (!value) {
return null;
}
date = this._createMoment2(value, moment.ISO_8601).locale(this.locale);
}
if (date && this.isValid(date)) {
return this._createMoment2(date).locale(this.locale);
}
return super.deserialize(value);
}
parse(value: any, parseFormat: string | string[]): moment.Moment | null {
if (value && typeof value === "string") {
return this._createMoment2(value, parseFormat, this.locale);
}
return value ? this._createMoment2(value).locale(this.locale) : null;
}
today(): moment.Moment {
return moment()
.utc()
.tz(CustomMomentDateAdapter.TIMEZONE)
.local(this.locale);
}
/** Creates a Moment instance while respecting the current UTC settings. */
private _createMoment2(
date: moment.MomentInput,
format?: moment.MomentFormatSpecification,
locale?: string
): moment.Moment {
const date2 = moment(date, format, locale).format("YYYY-MM-DD");
return moment.tz(date2, CustomMomentDateAdapter.TIMEZONE);
}
}
MatMomentDateModule
2.2) I Injected the newly created CustomMomentDateAdapter
providers: [{ provide: DateAdapter, useClass: CustomMomentDateAdapter }]
datepicker-overview-example
component for the purpose of this exemple.Other references I used to find this solution:
Technically I think the correct answer to your question is to create a custom DateAdapter
This is accomplished by subclassing DateAdapter and providing your subclass as the DateAdapter implementation.
However, if the underlying problem is that you're using the MomentDateAdapter and seeing odd time's in your date like 2019-05-01 23:00:00
when you actually clicked May 2, 2019 (which I see when I switch my timezone to London), then the solution is to set the moment date adapter to use UTC like:
{ provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } }
Then you should see May 2, 2019 go through as 2019-05-02 00:00:00
, as you would expect.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With