smellyshovel
smellyshovel

Reputation: 223

Can I get rid of `.000Z` when using `Date.prototype.toISOString`?

I've got some data represented as an object of class X. Some fields of that object contain dates - instances of the standard built-in Date constructor.

The thing is that I need to pass that object of class X as a JSON string using Axios. And when the fields representing dates are converted to strings, the Date.prototype.toISOString is used on them. However, it converts the strings to the following format: 2017-11-14T06:22:43.000Z, - while I need them to be almost the same, but without this .000Z at the end (the server seems to dislike that).

I see 2 options how can I achieve that:

  1. Modify the object manually
    Though it doesn't sound that bad, it has some drawbacks: first of all, there is actually not a single object of class X I have, but a lot of them. Possibly, hundrends. So it might become a pretty expensive operation in terms of performance. The second thing is that I'm pretty much sure that there should be a more elegant solution.
  2. Augment the Date.prototype.toISOString method
    I've tried to do so, but don't know if it's a good decision (1) and I don't know how can I make it (2) the way that the method first calls itself (to perform what it has to) and then I modify its output with substr: there occurs an endless recursion. Look:

Date.prototype.toISOString = function(...args) {
  const res = Date.prototype.toISOString(...args);
  return res.substr(0, 19);
};

const date = new Date();
document.write(date.toISOString());

The following way it doesn't work as well:

const originalMethod = Date.prototype.toISOString;

Date.prototype.toISOString = function(...args) {
  const res = originalMethod(...args);
  return res.substr(0, 19);
};

const date = new Date();
document.write(date.toISOString());
See the console. It outputs "Method Date.prototype.toISOString called on incompatible receiver undefined"

So, why doesn't that work? And, more importantly, what would be the best way of solving my problem?

Upvotes: 0

Views: 1111

Answers (2)

VLAZ
VLAZ

Reputation: 29010

You can create your own replacer function to be used with JSON.stringify(). You can add special handling for any dates but leave any other value intact:

function customReplacer(key, value) {
  if (key === "") //initial object
    return value;
  
  if (this[key] instanceof Date) //any date
    return value.slice(0, 19);
  
  return value; //anything else
}

const obj = {
  a: "hello world",
  b: {
    c: 42
  },
  d: [1, 2, 3],
  e: new Date(),
  f: {
    g: true,
    h: new Date(),
    i: null
  }
}

console.log(JSON.stringify(obj, customReplacer, 4))
.as-console-wrapper {
  max-height: 100% !important;
}

Note that value is going to be the serialised value, not a Date object. Hence, to get the initial non-serialised object, you need to get this[key] - the function is going to be called with the object that's currently serialised as this.

Upvotes: 1

Bergi
Bergi

Reputation: 664599

why doesn't that work?

Because you're calling the originalMethod without a this value. A working code would be

const originalMethod = Date.prototype.toISOString;

Date.prototype.toISOString = function(...args) {
  const res = originalMethod.apply(this, args);
  return res.substr(0, 19);
};

I don't know if it's a good decision

No, definitely not. Do not mess with builtins - other parts of your code (or its dependencies) might rely on their correct functioning.

What would be the best way of solving my problem?

Don't modify the toJSON behaviour of all Date instances, modify the serialisation of your X objects only:

class X {
  …
  toJSON() {
    return {
      ...this,
      // TODO: fix backend to acccept standard timestamp formats
      date: this.date.toISOString().replace(/(?:\.\d{1,3})?Z$/, ''),
      …
    };
  }
}

Upvotes: 2

Related Questions