角度材料datepicker中的日期不正确

时间:2018-02-09 16:22:37

标签: angular datepicker angular-material2

当我选择日期时,我在字段中看到了正确的日期,但是,当我保存时,日期选择器发送我选择的日期前一天(3小时偏移) 我正在使用角度反应形式和MatMomentDateModule作为日期选择器。

问题与时区有关,但我只想保存用户输入数据库的相同日期。

此处代码被重新删除:https://stackblitz.com/edit/angular-material-moment-adapter-example-kdk9nk?file=app%2Fapp.module.ts

issue on stackblitz

与githup相关的问题:

https://github.com/angular/material2/issues/7167

感谢任何帮助,我认为很多开发人员需要一个解决方案。

17 个答案:

答案 0 :(得分:16)

两天前,在https://github.com/angular/material2/issues/7167,Silthus发布了他的解决方法,覆盖了MomentJsD​​ataAdapter。我尝试过它,它可以作为世界各地的魅力。

首先,他添加了MomentUtcDateAdapter,扩展了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;
  }
}

然后在AppModule组件中,你必须这样做:

providers: [
    ...
    { provide: MAT_DATE_LOCALE, useValue: 'en-GB' },
    { provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS },
    { provide: DateAdapter, useClass: MomentUtcDateAdapter },
    ...
],

答案 1 :(得分:1)

Angular material date picker目前不支持不同的时区,它为您提供UTC时间偏移。但对我来说这不是一个问题,因为我将输出字符串保存到我的数据库,当我将它返回给用户时,浏览器显示正确的日期时间对象。 在您的控制台中尝试此操作(在+01区域中),您将看到所选日期:

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

另一种解决方案是在保存到数据库之前修改日期对象,这不是最佳做法。

最后,如果您在应用中使用Moment.js,可以尝试MomentDateAdapter,看看它是否对您有所帮助。只需按照here所述,将MatMomentDateModule添加到您的应用中。

答案 2 :(得分:1)

选项1: 有相同的问题,并在支持(Java)中解决它你可以在你的后端代码中解决这个问题 如果有人需要相同的帮助,请提供java代码。其他服务器端技术的概念也应该类似。

模拟相同的错误,以下是分析。

通过JSON打印| “startdate”:“2018-02-08T18:30:00.000Z”。

在提交前打印&gt;&gt; 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);
    }

选项2 :(前端解决方案) 我没有尝试过这个选项,但文档似乎很清楚 https://maggiepint.com/2016/05/14/moment-js-shows-the-wrong-date/ 看看这个。

答案 3 :(得分:1)

https://github.com/angular/material2/issues/7167#issuecomment-402061126

  

您可以更改默认行为,以通过以下方式将日期解析为UTC:   提供MAT_MOMENT_DATA_ADAPTER_OPTIONS并将其设置为   useUtc:是的。

@NgModule({ 
    imports: [MatDatepickerModule, MatMomentDateModule], 
    providers: [ 
        { provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } } 
    ] 
})

答案 4 :(得分:1)

也许对某人会有帮助。这是我从示例日期中擦除TimeZone OffSet的示例方法

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

答案 5 :(得分:1)

@Bruno_Cerecetto的代码可以正常工作,但是存在小问题。当用户在文本框中输入日期而不是使用datepicker选择日期时,他的代码将不起作用,这又将减少一天的时间。为了正确使用上述代码,您还需要重写parse方法。用户每次在文本框中键入内容时都会调用的解析方法。这是对我有用的代码。我认为这可能会对某人有所帮助。

parse(value: any, parseFormat: string | string[]): Moment | null {
    console.log(value)
    if (value && typeof value === 'string') {
      return moment.utc(value, parseFormat, this.locale, true);
    }
    return value ? moment.utc(value).locale(this.locale) : null;
  }

答案 6 :(得分:0)

以上所有解决方案都很棒,但没有一个是单行解决方案 如果您只想获取您选择的本地日期并将该数据作为日期对象发布到后端,请尝试以下操作:

框架:Angular,使用 FormGroup 和 Formcontrol

let newDate = new Date(moment(this.date.value).format('YYYY-MM-DD'))

这不会将您的本地日期修改为字符串,它将保留为 Date 对象,并允许您以一致的方式使用数据类型,并且它很容易修复。

答案 7 :(得分:0)

我知道这是一个很老的问题,但由于我没有找到简单的答案,我将提供对我有用的方法:

[ { provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS,
    useValue: { useUtc: true } },
  { provide: DateAdapter, useClass: MomentDateAdapter,
    deps: [MAT_DATE_LOCALE, MAT_MOMENT_DATE_ADAPTER_OPTIONS] } ]

您还必须在 depsdateAdapter 中指定适配器选项。这就是它对我有用的原因。

答案 8 :(得分:0)

您可以在发送到服务器之前修改日期格式:

public saveYourDate(date: Date): Observable<any> {
        const formattedDate = date.toLocaleDateString();
        return this.http.post(`${this.yourPathVariable}/date`, formattedDate);
    }

在这种情况下,您无需指定时间,因此日期将以字符串形式发送,具体格式取决于本地语言约定,例如:“ 10/30/2020”。

答案 9 :(得分:0)

在此处参考link。更简单的方法是尝试以下

var d = new Date('yourDate');
d.setMinutes( d.getMinutes() + d.getTimezoneOffset() );

d现在应该是正确的日期。

答案 10 :(得分:0)

这对我有用:

const date = new Date(dateFromDatepicker);
date.toLocaleString();   // displays the correct date that was chosen in the datepicker

我正在使用反应形式。 dateFromDatepicker是我从Angular材质datepicker formControl中获得的值。

来源: https://github.com/angular-ui/ui-date/issues/88

答案 11 :(得分:0)

如果要在组件中使用它,只需执行

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

现在,您只需使用其转换方法即可,

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

答案 12 :(得分:0)

如果日期中的小时等于0,则表示我们处于同一时区,您无需执行任何操作。

如果日期中的小时数大于12,则表示该日期是昨天,您需要添加一天。

如果日期中的小时数小于12,则表示该日期是明天的一天,您需要对其进行更正。

以下是一个可能对您有所帮助的小功能

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

答案 13 :(得分:0)

如果您使用“反应式表单”并执行this.form.get(“ dateField”)。value,则必须在提交前将其转换为Date。

会是这样的:

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

答案 14 :(得分:0)

只需为useUtc: true使用选项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 { }

答案 15 :(得分:0)

解决方案here很好用,但是当您将日期值设置为datepicker时,它仍默认将其转换为本地时间戳,为解决此问题,我创建了一个函数来删除本地时间戳并分配将日期转换为日期选择器;

returnFormattedDate(dateObj) {
    let codDate = '';
    if (dateObj) {
      codDate =
        new Date(dateObj).getFullYear() +
        '-' +
        ('0' + (new Date(dateObj).getMonth() + 1)).slice(-2) +
        '-' +
        ('0' + new Date(dateObj).getDate()).slice(-2) +
        'T00:00:00.000Z';
    } else {
      codDate =
        new Date().getFullYear() +
        '-' +
        ('0' + (new Date().getMonth() + 1)).slice(-2) +
        '-' +
        ('0' + new Date().getDate()).slice(-2) +
        'T00:00:00.000Z';
    }
    return codDate;
  }

datePickerDate = returnFormattedDate(startDate);

答案 16 :(得分:0)

感谢@ ankit-raonka的回答: https://stackoverflow.com/a/48761312/6423656

使用ControlValueAccessor解决了我的问题,这是一个有效的例子: https://stackblitz.com/edit/angular-dlxnmx?file=app%2Fcva-date.component.ts

感谢每一个人的答案,我认为这个问题也可以解决使用时刻日期适配器的解析方法,但我喜欢ControlValueAccessor尝试,因为我控制所有输入方面+减少我希望在我使用它时写的代码html。

相关问题