Angular 5, HttpClient changes Date fields to UTC
Asked Answered
U

4

17

I Have an object which has a Date type property on client side. When I try to send object via HttpClient.post to server, property's value changes to UTC timezone.

On client side value is Sun Nov 26 2017 00:00:00 GMT+0300 (Turkey Standard Time) but when it goes to server, changes to : 25.11.2017 21:00:00

How can I control This?

This is my class.

export interface IBill {
   BillID : number;
   SubscriptionID:number;
   SiteID : number;
   SubscriptionName: string;
   Amount: number;
   Date: Date;
   PaymentDeadline: Date;
   Virtual: boolean;
   Portioned: boolean;
   Issuanced: boolean;
   FinancialTransactionComment : string;}

I create an object of this while filling a ng-form, then call following Http.post :

let bill = this.formData;
this.http.post(this.configuration.ServerWithApiUrl + url, bill , { headers: headers, withCredentials: true, observe: "response", responseType: 'text' })
        .map((response) => {
            return this.parseResponse(response);
        }).catch(
        (err) =>
            this.handleError(err));
Unreel answered 26/11, 2017 at 12:3 Comment(8)
can you please write here the POST service ?Parquet
This is quite a good thing anyway. A Date represents a precise moment in time. It doesn't have a timezone. UTC is the best way to represent that moment. If the server needs to know the timezone, then format the date in an ISO format including the timezone, or send the timezone as part of a different field.Yerxa
@Parquet : I have wrote a backend service which controles all of my connection server. public httpPostTypetxt(url: string, object: any, headers: HttpHeaders): Observable<any> { headers = headers.set('Content-Type', 'application/json'); return this.http.post(this.configuration.ServerWithApiUrl + url, object, { headers: headers, withCredentials: true, observe: "response", responseType: 'text' }) .map((response) => { return this.parseResponse(response); }).catch((err) => this.handleError(err, willBlock, loadingBar)); }Unreel
you can convert it and send it to the server as a stringRiggle
@Unreel post the relevant code in your question. It's unreadable in comments. And the code you posted doesn't have any Date involved.Yerxa
@JBNizet Code has been added to question.Unreel
@FatehMohamed I will have this kind of Date object all over project. It is not logical to change and send to server. I need a complete solution for this.Unreel
I really dislike this behavior. In my opinion the httpClient should only take the data, serialize them and post to the destination. I expect it to be an "identity" function - if I send something and receive it back I could get the same. But in this case I send some data and without any obvious data processing I get something different. This should be a optional behavior indeed. Are you aware this timezone issue? You can use a interceptor if you want. Now I need around 30 lines of code and extra config to implement "nothing".Cecilia
U
12

I changed Type of Date, PaymentDeadline to string.

export interface IBill {
   BillID : number;
   SubscriptionID:number;
   SiteID : number;
   SubscriptionName: string;
   Amount: number;
   Date: string;
   PaymentDeadline: string;
   Virtual: boolean;
   Portioned: boolean;
   Issuanced: boolean;
   FinancialTransactionComment : string; }

and before sending to service rewrite them.

let bill = this.formData;
bill.Date = this.formData.Date.toLocaleString();
bill.PaymentDeadline = this.formData.PaymentDeadline.toLocaleString();

in this case time will sent as string ("11/10/2017, 12:00:00 AM") and no change will be done for UTC time Zone

Unreel answered 28/11, 2017 at 12:42 Comment(1)
The date format you use to transfer dates is in a local timezone. You could use the date.toJSON() which will output the 1975-08-19T23:15:30.000Z. To reverse back to a Date instance use new Date(jsonDate). More Details: developer.mozilla.org/en-US/docs/web/javascript/reference/…Cushitic
B
28

Use an HttpInterceptor for modifying request body on put/post requests. Recursively update all date fields by creating corrected Date objects.

Sample code:

@Injectable() export class UpdateDateHttpInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    this.startLoading();
    if (req.method === 'POST' || req.method === 'PUT') {
        this.shiftDates(req.body);
    }
  }

  shiftDates(body) {
    if (body === null || body === undefined) {
        return body;
    }

    if (typeof body !== 'object') {
        return body;
    }

    for (const key of Object.keys(body)) {
        const value = body[key];
        if (value instanceof Date) {
            body[key] = new Date(Date.UTC(value.getFullYear(), value.getMonth(), value.getDate(), value.getHours(), value.getMinutes()
                , value.getSeconds()));
        } else if (typeof value === 'object') {
            this.shiftDates(value);
        }
    }
  }
Boccherini answered 5/10, 2018 at 10:57 Comment(3)
Perfect answer.Singhal
Just another reason I'm considering migrating from Angular to Blazor. Dates are just way too difficult in Angular. We you need to pull in a library, e.g. moment, to do the simplest things.Remount
This interceptor approach is just brilliant.. So far using it for adding authentication token to my request and redirecting to login if token expires. Now this! ThanksSurgery
U
12

I changed Type of Date, PaymentDeadline to string.

export interface IBill {
   BillID : number;
   SubscriptionID:number;
   SiteID : number;
   SubscriptionName: string;
   Amount: number;
   Date: string;
   PaymentDeadline: string;
   Virtual: boolean;
   Portioned: boolean;
   Issuanced: boolean;
   FinancialTransactionComment : string; }

and before sending to service rewrite them.

let bill = this.formData;
bill.Date = this.formData.Date.toLocaleString();
bill.PaymentDeadline = this.formData.PaymentDeadline.toLocaleString();

in this case time will sent as string ("11/10/2017, 12:00:00 AM") and no change will be done for UTC time Zone

Unreel answered 28/11, 2017 at 12:42 Comment(1)
The date format you use to transfer dates is in a local timezone. You could use the date.toJSON() which will output the 1975-08-19T23:15:30.000Z. To reverse back to a Date instance use new Date(jsonDate). More Details: developer.mozilla.org/en-US/docs/web/javascript/reference/…Cushitic
W
4

A little late but, Angular internally uses JSON.stringify to serialize HttpRequest body (typeof object). JSON.stringify converts JavaScript Date to ISO string. To overcome this either override Date.prototype.toJSON using some third party libraries like moment.js format function.

Date.prototype.toJSON = function() {
    return  moment(this).format();
}

The problem with this approach is it will alter the global Date object. Alternatively you can create a function that creates a proxy object that is sent to server.

export function prepareRequestBody(body: any): any {
    if (typeof body === 'object') {
        return new Proxy(body, {
            get(target, prop, receiver) {
                if (target[prop] instanceof Date) {
                    return moment(target[prop]).format();
                }
                return target[prop]
            }
        })
    }

    return body
}

  
Waters answered 11/4, 2022 at 4:38 Comment(0)
A
0

You can update the date using the following method before your request

setHours(date: Date) {
    const hours = (date.getTimezoneOffset() / 60) * -1;
    const minutes = date.getMinutes();
    const seconds = date.getSeconds();
    const milliseconds = date.getMilliseconds();
    date = new Date(date.getFullYear(), date.getMonth(), date.getDate(), hours, minutes, seconds, milliseconds);
    
    return date;
}

Before using HttpClient.post you can update the date like this

{
...

date = setHours(date);

...
}
Anselme answered 1/6, 2023 at 17:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.