Sponsored

Friday, November 6, 2020

Best Practices of Working with Date in JavaScript/TypeScript

Date is a built-in type in JavaScript/TypeScript (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) and provides most of the basic needs for manipulating with Date and Time values in the code. Being one of the most well-defined and often used basic types of the language it still generates a lot of confusion, questions and erroneously working code among a wide developers' community. Let's try and figure out some of the best practices that should be applied to the JavaScript/TypeScript code when dealing with the Date type.

The very first thing every JavaScript/TypeScript developer must remember all the time is the Date type is a Reference type. It may be especially important for developers coming from the server-side strongly type languages like C# in which the DateTime (perhaps the closest analogue) is a Value type. Not in JavaScript! The implication of that is that any JavaScript object of Date type is passed around as a reference not a value, and any variable of Date type holds a reference to a Date object, not its value. Not remembering that will ultimately create a lot of buggy code and headaches and wasted debugging time. Here is an example.

let d1 = Date.now();
let d2 = new Date('03/01/2020');
// expect a current date and 03/01/2020
console.log(d1.toLocaleDateString(), d2.toLocaleDateString());
d2 = d1;
d2.setFullYear(2020,09,30);
// expect both dates to be 09/30/2020
console.log(d1.toLocaleDateString(), d2.toLocaleDateString());

Now, that we understand this baseline let's take a look at a few scenarios in which the Date needs to be passed around or shared across code boundaries and how it can be done in a safe and reliable way.

Dates swap

Say, we implement a logic where we need to keep the current date and a previous value. When we switch to the next current date we need to swap the values between the current and the previous dates. This is how it's done safely.

swap(newDate: Date) {
...
// the getTime/setTime methods use a number value and
// do not expose/modify object references
  prevDate.setTime(currDate.getTime());
  currDate.setTime(newDate.getTime());
...
}

Receiving a Date value

In this scenario in our code we receive a date value from the outside, something like a user interface control. We don't want to introduce a leakage from inside of our code and need to accept the value safely.

onDateChange(date: string | Date) {
// incoming Date value could be either a string or a Date
// either scenario is protected from a reference leakage
// if myDate was not initialized before it's straight-forward
  this.myDate = new Date(date);
// if we wanted to keep an instance
  const newDate = new Date(date);
  this.myDate.setTime(newDate.getTime());
}

Sharing a Date value

Our code produces some datetime stamp that is shared to an external code, say, via an event. We need to make sure that we don't compromise the internal state of our code when external code deals with the shared value.

nextDayEvent() {
  // move one day ahead
  this.eventStamp.setDate(this.eventStamp.getDate() + 1);
  // wrap into another Date object to isolate the reference
  this.dateEmitter.emit(new Date(this.eventStamp));
  // or share a string
  this.stringEmitter.emit(this.eventStamp.toUTCstring());
}

Conclusion

Hope, the examples above illustrate the importance of following the best practices of using a Date type in JavaScript/TypeScript and will save you unnecessary headaches when trying to figure out why the code does not work as expected.

No comments:

Post a Comment