Parse date with Angular 4.3 HttpClient Parse date with Angular 4.3 HttpClient angular angular

Parse date with Angular 4.3 HttpClient


This works for me:

import { Injectable } from '@angular/core';import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse, HttpResponse } from '@angular/common/http';import { Observable } from 'rxjs/Observable';@Injectable()export class ApiInterceptor implements HttpInterceptor {  private dateRegex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)$/;  private utcDateRegex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/;  constructor() { }  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {    return next.handle(request)      .do((event: HttpEvent<any>) => {        if (event instanceof HttpResponse) {          this.convertDates(event.body);        }      });  }  private convertDates(object: Object) {    if (!object || !(object instanceof Object)) {      return;    }    if (object instanceof Array) {      for (const item of object) {        this.convertDates(item);      }    }    for (const key of Object.keys(object)) {      const value = object[key];      if (value instanceof Array) {        for (const item of value) {          this.convertDates(item);        }      }      if (value instanceof Object) {        this.convertDates(value);      }      if (typeof value === 'string' && this.dateRegex.test(value)) {        object[key] = new Date(value);      }    }  }}

The advantage of this over the answer of bygrace is that you don't need to parse to json yourself, you just convert the dates after angular is done with the parsing.

This also works with arrays and nested objects.I modified this it should support arrays.


Unfortunately there doesn't seem to be a way to pass a reviver to the JSON.parse method that is used within the Angular HttpClient. Here is the source code for where they call JSON.parse:https://github.com/angular/angular/blob/20e1cc049fa632e88dfeb00476455a41b7f42338/packages/common/http/src/xhr.ts#L189

Angular will only parse the response if the response type is set to "json" (you can see this on line 183). If you don't specify the response type then Angular defaults to "json" (https://github.com/angular/angular/blob/20e1cc049fa632e88dfeb00476455a41b7f42338/packages/common/http/src/request.ts#L112).

So you could use a response type of "text" and parse the json yourself. Or you could just be lazy and serialize and then deserialize the response (JSON.parse(JSON.stringify(res.body), reviver)).

Rather than modifying every call you could create an interceptor like the following:

json-interceptor.ts

import { Injectable } from '@angular/core';import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpResponse, HttpErrorResponse } from '@angular/common/http';import { Observable } from 'rxjs/Rx';import 'rxjs/add/operator/map';// https://github.com/angular/angular/blob/master/packages/common/http/src/xhr.ts#L18const XSSI_PREFIX = /^\)\]\}',?\n/;/** * Provide custom json parsing capabilities for api requests. * @export * @class JsonInterceptor */@Injectable()export class JsonInterceptor implements HttpInterceptor {  /**   * Custom http request interceptor   * @public   * @param {HttpRequest<any>} req   * @param {HttpHandler} next   * @returns {Observable<HttpEvent<any>>}   * @memberof JsonInterceptor   */  public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {    if (req.responseType !== 'json') {      return next.handle(req);    }    // convert to responseType of text to skip angular parsing    req = req.clone({      responseType: 'text'    });    return next.handle(req).map(event => {      // Pass through everything except for the final response.      if (!(event instanceof HttpResponse)) {        return event;      }      return this.processJsonResponse(event);    });  }  /**   * Parse the json body using custom revivers.   * @private   * @param {HttpResponse<string>} res   * @returns{HttpResponse<any>}   * @memberof JsonInterceptor   */  private processJsonResponse(res: HttpResponse<string>): HttpResponse<any> {      let body = res.body;      if (typeof body === 'string') {        const originalBody = body;        body = body.replace(XSSI_PREFIX, '');        try {          body = body !== '' ? JSON.parse(body, (key: any, value: any) => this.reviveUtcDate(key, value)) : null;        } catch (error) {          // match https://github.com/angular/angular/blob/master/packages/common/http/src/xhr.ts#L221          throw new HttpErrorResponse({            error: { error, text: originalBody },            headers: res.headers,            status: res.status,            statusText: res.statusText,            url: res.url || undefined,          });        }      }      return res.clone({ body });  }  /**   * Detect a date string and convert it to a date object.   * @private   * @param {*} key json property key.   * @param {*} value json property value.   * @returns {*} original value or the parsed date.   * @memberof JsonInterceptor   */  private reviveUtcDate(key: any, value: any): any {      if (typeof value !== 'string') {          return value;      }      if (value === '0001-01-01T00:00:00') {          return null;      }      const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);      if (!match) {          return value;      }      return new Date(value);  }}

Then you have to provide it in your module:

*.module.ts

import { HTTP_INTERCEPTORS } from '@angular/common/http';import { JsonInterceptor } from '...';@NgModule({    providers: [        {            provide: HTTP_INTERCEPTORS,            useClass: JsonInterceptor,            multi: true        }    ]})...

In the example I tried to mimic how angular was doing the parsing as much as possible. They don't seem to export HttpJsonParseError so I couldn't cast the error to that type. It probably isn't perfect but I hope it gets the idea across.

Here is a running example (look in the console to see the date parsed): https://stackblitz.com/edit/json-interceptor

I created a feature request here:https://github.com/angular/angular/issues/21079


Similar to Jonas Stensved's answer, but using pipes:

import { map } from "rxjs/operators";this.http.get(url)  .pipe(    map(response => {      response.mydate = new Date(response.mydate);      return response;    })

Note the different import syntax of the map operator.

Pipes were introduced in RxJS 5.5. They facilitate import handling, code readability, and reduce bundle size. See Understanding Operator Imports.