角度材料日期选择器中的日期不正确

Posted

技术标签:

【中文标题】角度材料日期选择器中的日期不正确【英文标题】:day incorrect in angular material datepicker 【发布时间】:2018-07-20 11:21:18 【问题描述】:

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

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

此处复制代码:https://stackblitz.com/edit/angular-material-moment-adapter-example-kdk9nk?file=app%2Fapp.module.ts

在 githup 上与此相关的问题:

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

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

【问题讨论】:

我在这里回答了:***.com/questions/37495089/…(有效,但它是原始的) 我在这里简单快捷地回答了它:***.com/questions/37495089/… 【参考方案1】:

两天前,在https://github.com/angular/material2/issues/7167,Silthus 发布了他的替代 MomentJsD​​ataAdapter 的解决方法。我试过了,它在全球任何地方都能起到魅力。

首先他添加了一个扩展 MomentDateAdapter 的 MomentUtcDateAdapter

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 ,
    ...
],

【讨论】:

这里是一个堆栈闪电战解决方案stackblitz.com/edit/… 它对我们有用,但我们还必须重写这三个方法:今天,解析和反序列化。 非常感谢,它消除了我的痛苦,按预期工作 当用户在文本框中手动输入日期时,此方法不起作用。它没有触发该功能。 选择日期时有效,但如果日期已经来自提供者,则延迟一天【参考方案2】:

只需将选项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 CustomModule  

【讨论】:

不错。有效。但是 MatMoment 数据模块必须单独安装,因为它不是材料模块的一部分。下面应该有帮助。 npm install @angular/material-moment-adapter 参考:material.angular.io/components/datepicker/overview 这是正确而简单的答案。有用。但是如果有人能解释为什么会发生这种情况以及如果我们添加 MatMomentDateAdapter 会在幕后发生什么,我们将不胜感激?【参考方案3】:

也许它会对某人有所帮助。这是我从组件日期中删除 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);
  

【讨论】:

【参考方案4】:

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

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

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

【讨论】:

这仍然需要一个小时的时间使用例如 provide: MAT_DATE_LOCALE, useValue: 'en-GB' 【参考方案5】:

@Bruno_Cerecetto 的代码运行良好,但存在小问题。当用户在文本框中键入日期而不是使用 datepicker 选择日期时,他的代码不起作用,它会再次减少一天。要正确使用上述代码,您还需要覆盖 parse 方法。每次用户在文本框中键入时都会调用 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】:

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

模拟了同样的bug,下面是分析。

通过 JSON 打印 | “开始日期”:“2018-02-08T18:30:00.000Z”。

提交前打印>>2018 年 2 月 9 日星期五 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/ 看看这个。

【讨论】:

我正在使用 php,我认为将时区日期转换为时间戳,然后将其再次转换为任何其他所需格式是正确的,但我正在为所有应用程序寻找解决方案,而无需在添加之前转换日期输入它到 mysql 因为我有很多关于日期输入的视图,如果我没有找到 mt2 datepicker 的全局解决方案来处理这个问题,我想我会选择这个。并感谢您的回答:-)【参考方案7】:

Angular 材质日期选择器目前不支持不同的时区,它会为您提供 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 添加到您的应用程序中。

【讨论】:

太好了,我将记录保存为 mysql 中的日期时间,所以每当我更新前一天的日期时,现在将记录保存为字符串解决了我的问题,非常感谢 实际上我尝试使用 MatMomentDateModule 来解决这个问题,但我无法让它按我的意愿工作,如果你看到我在主要问题中复制的代码,你会找到它并且你可以编辑试试吧,如果你能举个例子,我认为这将是真正的解决方案,因为很多开发人员需要将日期保存为“日期时间”以用于数据库查询。 我不确定它是否有效。我没有像往常一样尝试在 Angular 应用程序中不需要 Moment。无论如何,我认为我们可以将生成的日期也保存为数据库中的日期时间。我会在星期一试一试,然后告诉你。 非常感谢@pouyada,我很感谢你的帮助,我会等待你的测试:-) 我尝试将此日期格式插入mysql,但它不接受它作为日期时间,因此您需要转换它,不推荐。关于MomentDateAdapter,旧版本的角材料不支持它。您应该使用较新的版本(推荐)或从HERE安装它【参考方案8】:

如果你想在你的组件中使用它,你可以简单地做

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

现在,您可以简单地使用它的变换方法,这将是

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

【讨论】:

【参考方案9】:

请参阅此处的link。更简单的方法是在下面尝试

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

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

【讨论】:

【参考方案10】:

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

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

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

【讨论】:

谢谢,这对我来说是缺少的(也以部门的形式提供)部分。【参考方案11】:

感谢@ankit-raonka 在这里回答: https://***.com/a/48761312/6423656

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

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

【讨论】:

【参考方案12】:

如果您使用响应式表单并执行 this.form.get("dateField").value,则必须在提交之前将其转换为日期。

会是这样的:

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

【讨论】:

【参考方案13】:

如果日期的小时等于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');

【讨论】:

【参考方案14】:

这对我有用:

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

【讨论】:

【参考方案15】:

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

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

在这种情况下,您不指定时间,因此日期会以字符串的形式发送,格式取决于区域设置约定,例如:'10/30/2020'。

【讨论】:

【参考方案16】:

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

框架:Angular,使用 FormGroup 和 Formcontrol

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

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

【讨论】:

【参考方案17】:

stackblitz 解决方案效果很好,但是如果您没有将时间戳存储在数据库中,那么您需要将日期转换回 UTC 格式,同时从数据库中检索它,然后再分配给日期选择器。

let dbDate = new Date('2018-02-09T00:00:00.000Z');

let updateDate = new Date(
      Date.UTC(dbDate.getFullYear(), dbDate.getMonth(), dbDate.getDate())
    ).toISOString(); // 2018-02-08T00:00:00.000Z

现在将updatedDate 分配给日期选择器,希望对您有所帮助。

【讨论】:

以上是关于角度材料日期选择器中的日期不正确的主要内容,如果未能解决你的问题,请参考以下文章

角度材料日期时间选择器更改显示日期时间的格式

角度 ui 引导日期选择器中的错误

角度材料日期选择器 - 仅选择日期

角度材料日期选择器最小和最大日期验证消息

角度材料日期选择器错误呈现

在材料ui的日期选择器中更改formatDate