Dates in JavaScript
table of contents
While migrating my personal website to Astro I added a tiny JavaScript function to calculate my age in the about section. Just because I wanted to, itās a relatively free world and was something I wanted to do while using Jekyll but for some reason or another found it too tedious to mix JavaScript and markdown files.
I realized I never got the real hang of how Date
really works. Always had to google more than I prefer to admit and read the docs almost from scratch every time.
Enough was enough, so I wrote this for my better understanding and in the hope of helping others that struggled or struggle as I did with dates.
As a sidenote, UTC stands for Coordinated Universal Time. A standard used for determining time worldwide.
The Date object
It is represented internally by a single number, a timestamp.
Which is in turn the milliseconds since the epoch, January 1st 1970 at 00:00:00.
From this object we could get:
- The UTC time
- The local time
- The timestamp
The timezone is determined by the userās device, it is not stored in the
Date
object. This is talked about at the end of the post.
For example, if we do const myDate = new Date()
we could get them like this:
myDate.toUTCString(); // UTC time
myDate.toLocaleString(); // local time
myDate.getTime(); // timestamp
Using the constructor without parameters will result in the current date and time.
If we want a specific date we have 4 options using parameters:
1. Timestamp number
const dateFromUnixTimestamp = new Date(683164800000);
// 'Sun Aug 25 1991'... Linux operating system is announced
2. A valid date string
A universally supported format in this case is:
YYYY-MM-DDTHH:mm:ss.sssZ
which stems from a simplification of the ISO 8601 calendar date extended format.
Luckily, many components can be omitted. So, for example, the following are valid:
const dateFromDateString = new Date("1994-01-13T13:01:12.123Z");
const dateFromASimplerDateString = new Date("1994-01");
However new Date("01-1994")
or new Date("12:32")
wouldnāt work.
Why?
As long as the order of the universally supported format specified before is respected and we add the date components in a decreasing magnitude manner, anything goes.
In other words: if you want to specify the month, you must specify the year; if you specify the day, then you should specify the month and the year, and so on.
Thatās why ā01-1994ā is not a valid date but ā1994-01ā is. Month must be after the year.
ā12-01ā doesnāt work either but ā1993-12-01ā does because year must be included if we want month and day.
You may need to format dates on another format like āAugust 19, 1975 23:15:30 GMT+07:00ā, which is valid as well, for example. Use the ECMAScript Date Time String Format for more details regarding all the possible formats. But I recommend staying with the described above for creation.
3. Another Date
object
The only use case I could find is for copying dates:
let originalDate = new Date(); // Date Sat Jun 29 2024 HH:mm:ss GMT-nn00 ({your country} Standard Time)
let copiedDate = new Date(originalDate); // same date
4. Individual date and time components as arguments
Similar to using a valid date string as described before, but including the year and the month as a minimum, in order to differentiate from the first case where we used a Unix timestamp:
let date = new Date(1994, 01);
If we had done new Date(1994)
we wouldāve got the date after 1994 milliseconds passed on January 1st 1970, not January 1st 1994.
A more detailed example:
let dateWithManyParameters = new Date(1989, 5, 12, 12, 25, 21);
// Mon Jun 12 1989 12:25:21 GMT-nnnn ({your country} Standard Time)
Days and months start from zero thatās why 5 -> June
What can we do with the Date object?
There are many methods associated with it as well as formatting that are detailed in an interactive table below so I prefer to detail compound cases from using those methods, soā¦
What can we do with more than one?
Subtraction
With the -
operator between two Date
objects we get the difference in milliseconds between the timestamps of each one:
const diff = new Date(2010, 2, 15, 16, 32, 10) - new Date(2000, 1, 9, 6, 10, 5); // 315619200000
// 315619200000 / 1000 / 60 / 60 / 24 / 365.25 ā 10 years approximately
And with this we can get the difference in years, months, days, hours, minutes and seconds.
Subtraction leads us to
Calculating someoneās age
Simplest way, just with the years:
const now = new Date();
const birthDate = new Date(1994, 0, 6);
console.log(now.getFullYear() - birthDate.getFullYear()); // 30 (or more depending on when you are reading this)
But what if we want to go one step further and verify if the month and day have passed?
const now = new Date();
const birthDate = new Date(1994, 0, 6);
let age = now.getFullYear() - birthDate.getFullYear();
const currentMonth = now.getMonth();
const bdayMonth = birthDate.getMonth();
const birthdayNotHappenedYet =
(currentMonth === bdayMonth && now.getDate() < birthDate.getDate()) ||
bdayMonth > currentMonth;
if (birthdayNotHappenedYet) {
age--;
}
console.log(age); // age +-1 depending on whether the birthday has passed or not
Sum
With the +
operator between two Date
objects, they get converted to strings and concatenated:
new Date(2010, 0) + new Date(2000, 0);
// "Fri Jan 01 2010 00:00:00 GMT-0300 (Argentina Standard Time)Sat Jan 01 2000 00:00:00 GMT-0300 (Argentina Summer Time)"
Not very useful.
So what if I want to add X days/months/years?
The Date
object has several setX()
methods where X is Minutes, Month, Seconds, etc. For example, to add one year:
const date = new Date(2002, 01, 03); // Sun Feb 03 2002...
date.setFullYear(date.getFullYear() + 1); // returns the new timestamp
console.log(date); // Mon Feb 03 2003...
Adding many days:
const now = new Date();
const futureDate = new Date(now);
futureDate.setDate(now.getDate() + 7); // Add 7 days
Multiplication and division
Same as with subtraction. Timestamps are multiplied/divided.
Compare
Using comparison operators we can see if a date is later than another, following the last example:
futureDate > now; // true
Timezones
If you need to specify the timezone of a date, pass a string to the constructor as described before:
// GMT +05:30
new Date("2024-07-04T12:00:00+05:30");
Why is date one day off?
If you create a date like this:
new Date('2024-06-05')
and you are in GMT < 0 such as I am in argentina (GMT -3) you will get Date Tue Jun 04 2024 21:00:00 GMT-0300
. One day off due to the timezone difference and JavaScript interpreting the date in ISO 8601 format as described earlier when discussing constructor parameters.
But if you do month-day-year:
new Date('06-05-2024')
you get Date Wed Jun 05 2024 00:00:00 GMT-0300
ā¦ the expected date since JavaScript no longer interprets is as UTC, it uses local timezone instead.
So if you are only interested in the date, and you have control on what goes in the Date
object is best to use YYYY-MM-DDT00:00:00 and be sure it uses UTC and the exact date no matter the timezone.
If you are storing user entered dates and times you will most likely need to store the timezone as well. For example, in a calendar app where users are in different timezones, the meeting time is scheduled by someone in Tokio but it must be correctly displayed for someone in the United States. Knowing the timezone of who scheduled it helps us with that.
Though donāt miss this on why, whenever you can, avoid timezones.
After finishing this post I got a better understanding on why such libraries like dayjs or date-fns exist and I would definitely check them out if I had to do heavy-lifting work with dates.
And also a considerable admiration towards anyone implementing web calendars used worldwide. Thank you.
Playground & cheatsheet
Write a valid date or pick one with the inputs:
Timestamp
method | output | description |
---|---|---|
getTime() | 1731376663846 | milliseconds since 1970-01-01, negative if before that date |
Local Time Methods
method | output | description |
---|---|---|
getDate() | 12 | day of the month |
getDay() | 2 | day of the week (0-6) where 0 is sunday |
getFullYear() | 2024 | four-digit year |
getMonth() | 10 | month (0-11) |
getHours() | 1 | hour (0-23) |
getMinutes() | 57 | minutes (0-59) |
getSeconds() | 43 | seconds (0-59) |
getMilliseconds() | 846 | milliseconds (0-999) |
getTimezoneOffset() | 0 | difference between UTC timezone and local time in minutes |
UTC Methods
method | output | description |
---|---|---|
getUTCDate() | 12 | UTC day of the month |
getUTCDay() | 2 | UTC day of the week (0-6) |
getUTCFullYear() | 2024 | UTC four-digit year |
getUTCHours() | 1 | UTC hour (0-23) |
getUTCMinutes() | 57 | UTC minutes (0-59) |
getUTCMonth() | 10 | UTC month (0-11) |
getUTCSeconds() | 43 | UTC seconds (0-59) |
Formatting
method | output | description |
---|---|---|
toDateString() | Tue Nov 12 2024 | |
toISOString() | 2024-11-12T01:57:43.846Z | simplified format based on ISO 8601 |
toLocaleString() | 11/12/2024, 1:57:43 AM | language-sensitive representation of the date in the local timezone |
toLocaleDateString() | 11/12/2024 | same as before only date |
toLocaleTimeString() | 1:57:43 AM | same as before only time |
toTimeString() | 01:57:43 GMT+0000 (Coordinated Universal Time) | time portion of the date interpreted in the local timezone |
toUTCString() | Tue, 12 Nov 2024 01:57:43 GMT | RFC 7231 format |