عنوان:

‫شمسی سازی Date-Picker توکار Angular Material 6x


نویسنده: وحید نصیری
تاریخ: ۱۳۹۷/۰۴/۲۸ ۱۹:۳۵
آدرس: www.dntips.ir
Angular Material به همراه یک کامپوننت Date-Picker بسیار شکیل و حرفه‌ای است اما ... از تقویم شمسی پشتیبانی نمی‌کند. در این مطلب می‌خواهیم با تدارک یک DateAdapter سفارشی، این مشکل را برطرف کنیم تا در نهایت به یک چنین Date-Picker شمسی برسیم:



تاریخچه‌ی تغییرات کامپوننت Date-Picker

اخیرا تیم Angular Material، امکان تعریف تقویم‌های دیگری را بجز تقویم میلادی، با تدارک کلاس پایه DateAdapter فراهم کرده‌است. در این بین توسعه دهندگان ایرانی پیگیر نیز یک DateAdapter شمسی را بر این مبنا تهیه کرده‌اند که در ادامه نحوه‌ی افزودن و استفاده‌ی از آن‌را بررسی خواهیم کرد.
 
نصب پیشنیاز تبدیل تاریخ میلادی به شمسی و بر عکس

DateAdapter شمسی تهیه شده از کتابخانه‌ی jalali-moment برای تبدیل تاریخ‌ها استفاده می‌کند. بنابراین ابتدا نیاز است این وابستگی را نصب کرد:
 npm install jalali-moment --save


افزودن DateAdapter شمسی به پروژه

برای افزودن DateAdapter شمسی تهیه شده، فایل جدید app\shared\material.persian-date.adapter.ts را به برنامه اضافه کرده و به صورت زیر تکمیل کنید:
import { DateAdapter } from "@angular/material";
import * as jalaliMoment from "jalali-moment";

export const PERSIAN_DATE_FORMATS = {
  parse: {
    dateInput: "jYYYY/jMM/jDD"
  },
  display: {
    dateInput: "jYYYY/jMM/jDD",
    monthYearLabel: "jYYYY jMMMM",
    dateA11yLabel: "jYYYY/jMM/jDD",
    monthYearA11yLabel: "jYYYY jMMMM"
  }
};

export class MaterialPersianDateAdapter extends DateAdapter<jalaliMoment.Moment> {

  constructor() {
    super();
    super.setLocale("fa");
  }

  getYear(date: jalaliMoment.Moment): number {
    return this.clone(date).jYear();
  }

  getMonth(date: jalaliMoment.Moment): number {
    return this.clone(date).jMonth();
  }

  getDate(date: jalaliMoment.Moment): number {
    return this.clone(date).jDate();
  }

  getDayOfWeek(date: jalaliMoment.Moment): number {
    return this.clone(date).day();
  }

  getMonthNames(style: "long" | "short" | "narrow"): string[] {
    switch (style) {
      case "long":
      case "short":
        return jalaliMoment.localeData("fa").jMonths().slice(0);
      case "narrow":
        return jalaliMoment.localeData("fa").jMonthsShort().slice(0);
    }
  }

  getDateNames(): string[] {
    const valuesArray = Array(31);
    for (let i = 0; i < 31; i++) {
      valuesArray[i] = String(i + 1);
    }
    return valuesArray;
  }

  getDayOfWeekNames(style: "long" | "short" | "narrow"): string[] {
    switch (style) {
      case "long":
        return jalaliMoment.localeData("fa").weekdays().slice(0);
      case "short":
        return jalaliMoment.localeData("fa").weekdaysShort().slice(0);
      case "narrow":
        return ["ی", "د", "س", "چ", "پ", "ج", "ش"];
    }
  }

  getYearName(date: jalaliMoment.Moment): string {
    return this.clone(date).jYear().toString();
  }

  getFirstDayOfWeek(): number {
    return jalaliMoment.localeData("fa").firstDayOfWeek();
  }

  getNumDaysInMonth(date: jalaliMoment.Moment): number {
    return this.clone(date).jDaysInMonth();
  }

  clone(date: jalaliMoment.Moment): jalaliMoment.Moment {
    return date.clone().locale("fa");
  }

  createDate(year: number, month: number, date: number): jalaliMoment.Moment {
    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 result = jalaliMoment()
      .jYear(year).jMonth(month).jDate(date)
      .hours(0).minutes(0).seconds(0).milliseconds(0)
      .locale("fa");

    if (this.getMonth(result) !== month) {
      throw Error(`Invalid date ${date} for month with index ${month}.`);
    }
    if (!result.isValid()) {
      throw Error(`Invalid date "${date}" for month with index "${month}".`);
    }
    return result;
  }

  today(): jalaliMoment.Moment {
    return jalaliMoment().locale("fa");
  }

  parse(value: any, parseFormat: string | string[]): jalaliMoment.Moment | null {
    if (value && typeof value === "string") {
      return jalaliMoment(value, parseFormat, "fa");
    }
    return value ? jalaliMoment(value).locale("fa") : null;
  }

  format(date: jalaliMoment.Moment, displayFormat: string): string {
    date = this.clone(date);
    if (!this.isValid(date)) {
      throw Error("JalaliMomentDateAdapter: Cannot format invalid date.");
    }
    return date.format(displayFormat);
  }

  addCalendarYears(date: jalaliMoment.Moment, years: number): jalaliMoment.Moment {
    return this.clone(date).add(years, "jYear");
  }

  addCalendarMonths(date: jalaliMoment.Moment, months: number): jalaliMoment.Moment {
    return this.clone(date).add(months, "jmonth");
  }

  addCalendarDays(date: jalaliMoment.Moment, days: number): jalaliMoment.Moment {
    return this.clone(date).add(days, "jDay");
  }

  toIso8601(date: jalaliMoment.Moment): string {
    return this.clone(date).format();
  }

  isDateInstance(obj: any): boolean {
    return jalaliMoment.isMoment(obj);
  }

  isValid(date: jalaliMoment.Moment): boolean {
    return this.clone(date).isValid();
  }

  invalid(): jalaliMoment.Moment {
    return jalaliMoment.invalid();
  }

  deserialize(value: any): jalaliMoment.Moment | null {
    let date;
    if (value instanceof Date) {
      date = jalaliMoment(value);
    }
    if (typeof value === "string") {
      if (!value) {
        return null;
      }
      date = jalaliMoment(value).locale("fa");
    }
    if (date && this.isValid(date)) {
      return date;
    }
    return super.deserialize(value);
  }
}
کار این Adapter و یا «وفق دهنده» این است که مشخص می‌کند، هفته‌ی ایرانی از چه روزی شروع می‌شود. نام روزهای هفته‌ی ایرانی چیست؟ برچسب‌های نام ماه‌های ایرانی چگونه باید تامین شوند و در کل جهت وفق دادن تقویم میلادی اصلی با تقویم شمسی، چه اجزایی باید به سیستم معرفی شوند تا این تقویم توکار بدون مشکل مانند قبل کار کند.
 
معرفی وفق دهنده‌ی شمسی به پروژه

پس از تعریف MaterialPersianDateAdapter و همچنین PERSIAN_DATE_FORMATS، برای معرفی آن‌ها به برنامه، فایل app\shared\material.module.ts را گشوده و به صورت زیر تغییر دهید:
import { NgModule } from "@angular/core";
import {  DateAdapter,  MAT_DATE_FORMATS,  MAT_DATE_LOCALE } from "@angular/material";

import { MaterialPersianDateAdapter, PERSIAN_DATE_FORMATS } from "./material.persian-date.adapter";

@NgModule({
  providers: [
    { provide: DateAdapter, useClass: MaterialPersianDateAdapter, deps: [MAT_DATE_LOCALE] },
    { provide: MAT_DATE_FORMATS, useValue: PERSIAN_DATE_FORMATS }
  ]
})
export class MaterialModule {
}
کار این تعاریف، تعویض DateAdapter اصلی میلادی، با نمونه‌ی شمسی است. همچنین فرمت نمایشی برچسب‌ها را نیز جایگزین می‌کند.

پس از آن اگر mat-datepicker را به نحو متداولی به صفحه اضافه کنیم:
<mat-form-field>
    <input matInput [matDatepicker]="picker6" placeholder="json gregorian input" [(ngModel)]="dateControl">
    <mat-datepicker-toggle matSuffix [for]="picker6"></mat-datepicker-toggle>
    <mat-datepicker #picker6></mat-datepicker>
</mat-form-field>
یک چنین خروجی حاصل خواهد شد:




چند مثال تکمیلی از کاربردهای کامپوننت mat-datepicker

1) استفاده از تاریخ میلادی رسیده‌ی از سمت سرور و نمایش آن
<mat-form-field>
    <input matInput [matDatepicker]="picker6" placeholder="json gregorian input" [(ngModel)]="dateControl">
    <mat-datepicker-toggle matSuffix [for]="picker6"></mat-datepicker-toggle>
    <mat-datepicker #picker6></mat-datepicker>
</mat-form-field>
با این کدها:
@Component()
export class PersianDatepickerComponent {

  jsonDate = "2018-01-08T20:21:29.4674496";
  dateControl = this.jsonDate;
}
در اینجا jsonDate همان رشته‌ی تاریخی است که از سمت سرور دریافت شده و میلادی است. با انتساب آن به ngModel، به صورت خودکار شمسی نمایش داده خواهد شد:




2) تعیین تاریخ آغاز تقویم و نمایش آن در حالت انتخاب سال
<mat-form-field>
    <input matInput [matDatepicker]="picker2" placeholder="startAt 2017-01-01 and startView=year">
    <mat-datepicker-toggle matSuffix [for]="picker2"></mat-datepicker-toggle>
    <mat-datepicker #picker2 startView="year" [startAt]="startDate"></mat-datepicker>
</mat-form-field>
با این کدها:
import * as moment from "jalali-moment";

@Component()
export class PersianDatepickerComponent  {

  startDate = moment("2017-01-01", "YYYY-MM-DD"); // = moment.from("2017-01-01", "en");
  
}
در این مثال خاصیت startAt را به یک تاریخ میلادی متصل کرده‌ایم و همچنین خاصیت startView به year تنظیم شده‌است که یک چنین خروجی را در بار اول نمایش تقویم ایجاد می‌کند:




3) تعیین باز‌ه‌ی تاریخی قابل انتخاب توسط کاربر
<mat-form-field>
    <input matInput [matDatepicker]="picker3" [min]="minDate" [max]="maxDate" placeholder="min: 2017-10-02 and max: 1396-07-29">
    <mat-datepicker-toggle matSuffix [for]="picker3"></mat-datepicker-toggle>
    <mat-datepicker #picker3></mat-datepicker>
</mat-form-field>
با این کدها:
import * as moment from "jalali-moment";

@Component()
export class PersianDatepickerComponent  {

  minDate = moment.from("2017-10-02", "en"); // = moment('2017-10-02', 'YYYY-MM-DD');
  maxDate = moment.from("1396-07-29", "fa"); // = moment('1396-07-29', 'jYYYY-jMM-jDD');
}
همانطور که ملاحظه می‌کنید کتابخانه‌ی jalali-moment می‌تواند تاریخ شمسی و یا میلادی را توسط متد from آن دریافت کند و هر دو حالت در اینجا پس از انتساب به خواص min و max تقویم، به خوبی کار کرده و سبب محدود ساختن بازه‌ی قابل انتخاب توسط کاربر می‌شوند.



در این تصویر روزهای خاکستری، قابل انتخاب نیستند و غیرفعال شده‌اند (چون min به 10 مهر و max به 29 مهر تنظیم شده‌است).


4) غیرفعال کردن روزهای قابل انتخاب بر اساس یک منطق سفارشی
<mat-form-field>
    <input matInput [matDatepicker]="picker4" [matDatepickerFilter]="myFilter" placeholder="Date validation - Datepicker Filter">
    <mat-datepicker-toggle matSuffix [for]="picker4"></mat-datepicker-toggle>
    <mat-datepicker #picker4></mat-datepicker>
</mat-form-field>
با این کدها:
import * as moment from "jalali-moment";

@Component()
export class PersianDatepickerComponent {

  myFilter = (d: moment.Moment): boolean => {
    const day: number = d.day();
    // Prevent Thursday and Friday from being selected.
    return day !== 5 && day !== 4;
  }
}
در اینجا روزهای پنج‌شنبه و جمعه در تقویم نمایش داده شده، بر اساس تعریف matDatepickerFilter سفارشی، دیگر قابل انتخاب نیستند:



5) کار با رخ‌دادهای تقویم
<mat-form-field>
    <input matInput [matDatepicker]="picker5" (dateInput)="onInput($event)" (dateChange)="onChange($event)"
        placeholder="dateInput and dateChange events">
    <mat-datepicker-toggle matSuffix [for]="picker5"></mat-datepicker-toggle>
    <mat-datepicker #picker5></mat-datepicker>
</mat-form-field>
با این کدها:
import { MatDatepickerInputEvent } from "@angular/material";
import * as moment from "jalali-moment";

@Component()
export class PersianDatepickerComponent {

  onInput(event: MatDatepickerInputEvent<moment.Moment>) {
    console.log("OnInput: ", event.value);
  }

  onChange(event: MatDatepickerInputEvent<moment.Moment>) {
    const x = moment(event.value).format("jYYYY/jMM/jDD");
    console.log("OnChange: ", x);
  }
}
در اینجا نحوه‌ی واکنش نشان دادن به رخ‌دادهای dateInput و dateChange کامپوننت mat-datepicker را ملاحظه می‌کنید:


در اینجا، onInput، با ورود دستی اطلاعات به textbox کامپوننت، فعال می‌شود و onChange، در صورت انتخاب یک تاریخ از تقویم.


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید.

نظرات

  • یونس بقائی در ۱۳۹۷/۰۷/۱۶ ۱۲:۲۱
    سلام؛ من از DateAdapter ای که گفتید استفاده کردم و در سمت کلاینت به درستی کار می‌کنه؛ اما زمانیکه مدل رو به سمت سرور Post می‌کنم تاریخ شروع و پایان سمت سرور که property هایی از جنس datetime هستند، یک روز کمتر از تاریخ انتخاب شده است!

    این هم قبل ارسال به سرور

    این هم سمت سرور

    • وحید نصیری در ۱۳۹۷/۰۷/۱۶ ۱۴:۴۵
      تاریخ را با این فرمت به سمت سرور ارسال کنید:
      const date = moment.from(this.dateControl, "en").utc(true).toJSON();
      یک مثال
      • سعید نیک سیرت در ۱۳۹۸/۰۵/۱۳ ۱۵:۴۷
        سلام . من اگه نخوام تاریخی که واسه م از سرور ارسال میشه رو تبدیل نکنه چیکار کنم ؟ چون من الان از سرور تاریخ رو دارم شمسی میگیرم ولی وقتی میخوام تو برنامه نشون بدم چون داره تبدیل میکنه به شمسی داره تاریخ رو اشتباه نشون میده . اینو چیکار کنم که خود تقویم که میخوایم تاریخ انتخاب کنیم فارسی باشه ولی اون تاریخی که از سرور میاد رو تبدیل نکنه؟ 
        • وحید نصیری در ۱۳۹۸/۰۵/۱۳ ۱۶:۲
          بهتر است با امکانات «کتابخانه‌ی moment-jalaali» بیشتر آشنا شوید و برای تبدیل حالات مختلف (شمسی به میلادی و برعکس) از آن استفاده کنید. مثال‌های بیشتر آن‌را در اینجا پیگیری کنید (مطابق نگارشی که در این مطلب استفاده شده).
    • علیرضا عسکری در ۱۳۹۸/۱۲/۲۶ ۲۲:۴۳
      باید خروجیهای date تمامی متدهای فایل material.persian-date.adapter.ts رو مجهز به متد utc با پارامتر true کنید. با جواب بالا اونوقت هر وقت که بخواهید تاریخ رو به سمت سرور post کنید مجبورید value رو اصلاح کنید. ولی با روشی که گفتم فقط یکبار تغیرات اساسی لازمه ...

      اصلاح شده فایل material.persian-date.adapter.ts رو در کد زیر میتونید ببینید:

      import { DateAdapter } from "@angular/material/core";
      import * as jalaliMoment from "jalali-moment";
      
      export const PERSIAN_DATE_FORMATS = {
        parse: {
          dateInput: "jYYYY/jMM/jDD"
        },
        display: {
          dateInput: "jYYYY/jMM/jDD",
          monthYearLabel: "jYYYY jMMMM",
          dateA11yLabel: "jYYYY/jMM/jDD",
          monthYearA11yLabel: "jYYYY jMMMM"
        },
      };
      
      export class MaterialPersianDateAdapter extends DateAdapter<jalaliMoment.Moment> {
      
        constructor() {
          super();
          super.setLocale("fa");
        }
      
        getYear(date: jalaliMoment.Moment): number {
          return this.clone(date).utc(true).jYear();
        }
      
        getMonth(date: jalaliMoment.Moment): number {
          return this.clone(date).utc(true).jMonth();
        }
      
        getDate(date: jalaliMoment.Moment): number {
          return this.clone(date).utc(true).jDate();
        }
      
        getDayOfWeek(date: jalaliMoment.Moment): number {
          return this.clone(date).utc(true).day();
        }
      
        getMonthNames(style: "long" | "short" | "narrow"): string[] {
          switch (style) {
            case "long":
            case "short":
              return jalaliMoment.localeData("fa").jMonths().slice(0);
            case "narrow":
              return jalaliMoment.localeData("fa").jMonthsShort().slice(0);
          }
        }
      
        getDateNames(): string[] {
          const valuesArray = Array(31);
          for (let i = 0; i < 31; i++) {
            valuesArray[i] = String(i + 1);
          }
          return valuesArray;
        }
      
        getDayOfWeekNames(style: "long" | "short" | "narrow"): string[] {
          switch (style) {
            case "long":
              return jalaliMoment.localeData("fa").weekdays().slice(0);
            case "short":
              return jalaliMoment.localeData("fa").weekdaysShort().slice(0);
            case "narrow":
              return ["ی", "د", "س", "چ", "پ", "ج", "ش"];
          }
        }
      
        getYearName(date: jalaliMoment.Moment): string {
          return this.clone(date).utc(true).jYear().toString();
        }
      
        getFirstDayOfWeek(): number {
          return jalaliMoment.localeData("fa").firstDayOfWeek();
        }
      
        getNumDaysInMonth(date: jalaliMoment.Moment): number {
          return this.clone(date).utc(true).jDaysInMonth();
        }
      
        clone(date: jalaliMoment.Moment): jalaliMoment.Moment {
          return date.clone().utc(true).locale("fa");
        }
      
        createDate(year: number, month: number, date: number): jalaliMoment.Moment {
          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 result = jalaliMoment()
            .jYear(year).jMonth(month).jDate(date)
            .hours(0).minutes(0).seconds(0).milliseconds(0)
            .locale("fa").utc(true);
      
          if (this.getMonth(result) !== month) {
            throw Error(`Invalid date ${date} for month with index ${month}.`);
          }
          if (!result.isValid()) {
            throw Error(`Invalid date "${date}" for month with index "${month}".`);
          }
          return result;
        }
      
        today(): jalaliMoment.Moment {
          return jalaliMoment().locale("fa").utc(true);;
        }
      
        parse(value: any, parseFormat: string | string[]): jalaliMoment.Moment | null {
          if (value && typeof value === "string") {
            return jalaliMoment(value, parseFormat, "fa").utc(true);
          }
          return value ? jalaliMoment(value).locale("fa").utc(true) : null;
        }
      
        format(date: jalaliMoment.Moment, displayFormat: string): string {
          date = this.clone(date).utc(true);
          if (!this.isValid(date)) {
            throw Error("JalaliMomentDateAdapter: Cannot format invalid date.");
          }
          return date.format(displayFormat);
        }
      
        addCalendarYears(date: jalaliMoment.Moment, years: number): jalaliMoment.Moment {
          return this.clone(date).utc(true).add(years, "jYear");
        }
      
        addCalendarMonths(date: jalaliMoment.Moment, months: number): jalaliMoment.Moment {
          return this.clone(date).utc(true).add(months, "jmonth");
        }
      
        addCalendarDays(date: jalaliMoment.Moment, days: number): jalaliMoment.Moment {
          return this.clone(date).utc(true).add(days, "jDay");
        }
      
        toIso8601(date: jalaliMoment.Moment): string {
          return this.clone(date).utc(true).format();
        }
      
        isDateInstance(obj: any): boolean {
          return jalaliMoment.isMoment(obj);
        }
      
        isValid(date: jalaliMoment.Moment): boolean {
          return this.clone(date).utc(true).isValid();
        }
      
        invalid(): jalaliMoment.Moment {
          return jalaliMoment.invalid();
        }
      
        deserialize(value: any): jalaliMoment.Moment | null {
          let date;
          if (value instanceof Date) {
            date = jalaliMoment(value).utc(true);
          }
          if (typeof value === "string") {
            if (!value) {
              return null;
            }
            date = jalaliMoment(value).utc(true).locale("fa");
          }
          if (date && this.isValid(date)) {
            return date;
          }
          return super.deserialize(value);
        }
      }

  • یونس بقائی در ۱۳۹۸/۰۲/۳۱ ۱۲:۳۰
    سلام 
    آیا این امکان وجود دارد تا در زمان اجرا datepicker  را شمسی یا میلادی کنیم؟ 
    در مثال جاری با تعویض dateAdapter امکان نمایش تاریخ میلادی وجود ندارد، مگر اینکه تنظیمات provider را برداریم.
  • وحید نصیری در ۱۳۹۸/۰۵/۲۴ ۱۹:۱
    یک نکته‌ی تکمیلی
    برای پشتیبانی چند تقویم با هم این پروژه را هم مدنظر داشته باشید « JDNConvertibleCalendarDateAdapter ».
  • محسن عباس آباد در ۱۳۹۹/۰۳/۱۰ ۸:۳۴
    با سلام؛ من دقیقا طبق DateAdapter که فرمودید انجام دادم. اما وقتی تاریخ رو از سرور دریافت میکنم در datapicker  به صورت میلادی نمایش میدهد. نحوه مقدار دهی تاریخ به طور مثال:

     نحوه نمایش datapicker در صورت مقدار دهی اولیه:

    • وحید نصیری در ۱۳۹۹/۰۳/۱۰ ۲۲:۲۹
      - این پروژه به Angular Material 9x ارتقاء داده شده؛ جزئیات بیشتر
      - کتابخانه‌ی jalali-moment متاسفانه باگ زیاد دارد. اگرتنظیم jalaliMoment.locale("fa") در آن وجود نداشته باشد، مدام خطای null reference می‌دهد. همچنین روش معرفی تاریخ میلادی به آن اینبار باید به اینصورت باشد (عدم سازگاری با نگارش‌های قبلی آن).
      • محسن عباس آباد در ۱۳۹۹/۰۵/۰۶ ۱۳:۱۹
        با سلام متاسفانه  روی  angular 10  تقویم فارسی نمی‌شود
         من همه کار‌های مورد نیاز رو انجام دادم ولی باز هم فارسی نشد 

         
  • سیما عباسی در ۱۳۹۹/۰۶/۱۱ ۱۰:۵۸
    سلام، برای اینکه روزهای تقویم به صورت اعداد فارسی نشان داده بشوند چه کار باید بکنیم؟
  • امیر نوروزیان در ۱۴۰۰/۰۹/۱۳ ۲۲:۳۹
    با سلام برای ورژن‌های بالاتر مشکل داره؟ ورژن 13
    mat-datepicker' is not a known element:
    1. If 'mat-datepicker' is an Angular component, then verify that it is part    
    • وحید نصیری در ۱۴۰۰/۰۹/۱۳ ۲۳:۳۸
      این پروژه را پیگیری کنید: « MaterialJalaliMomentAdapter » 
  • بهرامی علی اصغر در ۱۴۰۱/۰۸/۲۱ ۱۰:۱۰
    من این کار رو توی انگیولار 14 انجام دادم و مشکلی نداشتم فقط چندتا نکته دیدم که بهتر دونستم اینجا اعلام کنم شاید به درد کسی بخوره.
    اول اینکه توی ورژن‌های جدید انگیولار متریال، DateAdapter  به فولدر angular/material/core  منتقل شده
    نسخه قدیمی
    import { DateAdapter } from "@angular/material";
    نسخه جدید
    import { DateAdapter } from "@angular/material/core";

    مابقی هم همینطور
    import {  DateAdapter,  MAT_DATE_FORMATS,  MAT_DATE_LOCALE } from "@angular/material/core";

    یه مشکلی هم که من داشتم این بود که تقویم چپ چین بود و من میخواستم راستچین باشه که با "DIR="RTL حل شد
     <mat-datepicker #picker2 dir="rtl"></mat-datepicker>