Muhammad Ali
Muhammad Ali

Reputation: 97

Date value changes when sent from angular and received in ASP.NET server

I am sending an object containing two dates from Angular 6 to a ASP.NET Core Web API method. The dates are correct on the Angular side, but when they are received in the Web API method, they are one day behind from what they were went being sent from the Angular application.

startSprint(id: number, SprintDates: any): Observable<any> {
    this.sprintId = id.toString();
    return this.http.patch<any>(this.baseUrl + 'setSprintDates/' + this.sprintId,
                                SprintDates);
}

The above method sends dates object to the Web API method. The method below is the Web API method that receives that object, but when I debug and hover the cursor over the sprintDatesDto object, the values received are one day behind what they were when sent from Angular.

[HttpPatch("setSprintDates/{sprintId}")]
public async Task<IActionResult> SetSprintDates(string sprintId, SprintDatesDto sprintDatesDto)
{
    // Business login related code
    return StatusCode(200);
}

This is the SprintDatesDto class:

namespace AgileFootPrints.API.Dtos
{
    public class SprintDatesDto
    {
        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }
    }
}

This is the object i created in angular Typescript file:

sprinDates = {
startDate: Date,
endDate: Date
  };

Then below, in Html file i have used ngModel to bind to sprintDates object properties:

<div class="row">
  <div class="col-md-7 w-100 ">
<mat-form-field class="example-full-width">
  <input
    matInput
    [(ngModel)]="sprinDates.startDate"
    [min]="minDate"
    [max]="maxDate"
    [matDatepicker]="startsAt"
    placeholder="Starts At"
  />
  <mat-datepicker-toggle matSuffix [for]="startsAt"></mat-datepicker-toggle>
  <mat-datepicker #startsAt></mat-datepicker>
</mat-form-field>
  </div>
  <br />
</div>
<div class="row">
  <div class="col-md-7 w-100">
<mat-form-field class="example-full-width">
  <input
    [disabled]="sprinDates.startDate === undefined"
    matInput
    [(ngModel)]="sprinDates.endDate"
    [min]="sprinDates.startDate"
    [max]="maxDate"
    [matDatepicker]="endsAt"
    placeholder="Ends At"
  />
  <mat-datepicker-toggle matSuffix [for]="endsAt"></mat-datepicker-toggle>
  <mat-datepicker #endsAt></mat-datepicker>
</mat-form-field>
  </div>
</div>

Upvotes: 3

Views: 6081

Answers (2)

gourav
gourav

Reputation: 94

A likely culprit is the time zone offset between the client and the server.

When JavaScript dates are sent via the Angular HTTP Client, the data is converted to a JSON string using the JSON.stingify() method. When the date is converted to this string, the numerical time value is actually modified to reflect no timezone offset, instead of the timezone offset of the client. This is to normalize actual time in case the server is in a different timezone.

To resolve this issue you need to convert datetime to a string format that the server can interpret. For this there are two approaches. You can use the DatePipe from angular or you can simply convert the date to a string using the Date functions available.

import { DatePipe } from '@angular/common';

In constructor:

private datePipe: DatePipe

Convert to whichever date time format you need.

this.sprinDates.StartDate = this.datePipe.transform(this.sprinDates.StartDate, 'MM/dd/yyyy');

The other approach like I mentioned is to convert using date functions.

this.sprinDates.StartDate = this.sprinDates.StartDate ? this.sprinDates.StartDate.toLocaleString() : null;

Upvotes: 3

Tiago Silva
Tiago Silva

Reputation: 2349

The problem its the way your date is being parsed by Json and by default in angular it will add the local time compared to UTC (example: if you are Gmt+1 at midnight it will convert to 11 pm of yesterday UTC), what you need to do is create a file that will overwrite the MomentDateAdapter to ignore this conversion and create the selected time in UTC like so :

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

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

Next just import the following provider and this new file into the imports to either your app.module or angular material module if you have one

@NgModule({
  providers: [
    { provide: MAT_DATE_LOCALE, useValue: 'en-GB' },
      { provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS },
      { provide: DateAdapter, useClass: MomentUtcDateAdapter },
  ],
  imports: [
   MomentUtcDateAdapter,
   ... rest of your imports]

Upvotes: 0

Related Questions