Warm-up · Activity 1 of 7
Warm-up from lesson I5.2. What does this print?
const [, year, month] = "2026-10-05".match(/(\d{4})-(\d{2})/);
console.log(Number(month));// I5.3 · ~35 min · Intermediate
After this lesson you create a date without the off-by-one month, turn it into a timestamp or an ISO string and back, and calculate and compare dates safely.
Warm-up · Activity 1 of 7
const [, year, month] = "2026-10-05".match(/(\d{4})-(\d{2})/);
console.log(Number(month));Predict · Activity 2 of 7
const d = new Date(Date.UTC(2026, 10, 5));
console.log(d.toISOString().slice(0, 10));Practice · Activity 3 of 7
const ms = Date.UTC(2026, 9, 6) - Date.UTC(2026, 9, 5);
console.log(ms / ____);Practice · Activity 4 of 7
Practice · Activity 5 of 7
const d = new Date("2026-10-05");
console.log(d.getUTCHours(), d.toISOString());Brain teaser · Activity 6 of 7
const d = new Date("2026-10-05T00:00");
console.log(d.toISOString());Apply · Activity 7 of 7
Check your work against this list
Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.
Worked example
A meeting starts on 5 October 2026 at 14:30 UTC, built with Date.UTC and month 9. The program prints it as an ISO string, as its month number and as a timestamp, then parses two ISO strings. It adds 90 minutes and counts the days to 24 December in milliseconds, shows that 31 February carries over, compares dates, and recognises an Invalid Date. Every line uses UTC, so the output is the same on every computer.
main.js
// 1. Months count from 0: 9 is October.
const start = new Date(Date.UTC(2026, 9, 5, 14, 30));
console.log("start:", start.toISOString());
console.log("getUTCMonth:", start.getUTCMonth(), "-> month number", start.getUTCMonth() + 1);
// 2. A Date is a timestamp: milliseconds since 1970-01-01 UTC.
console.log("timestamp:", start.getTime());
console.log("timestamp 0:", new Date(0).toISOString());
// 3. Parsing ISO strings: date-only means UTC; Z or +02:00 say it exactly.
console.log(new Date("2026-10-05").toISOString());
console.log(new Date("2026-10-05T14:30:00+02:00").toISOString());
// 4. Arithmetic in milliseconds.
const MINUTE = 60 * 1000;
const DAY = 24 * 60 * MINUTE;
const end = new Date(start.getTime() + 90 * MINUTE);
console.log("90 minutes later:", end.toISOString());
console.log("days to 24 December:", (Date.UTC(2026, 11, 24) - Date.UTC(2026, 9, 5)) / DAY);
// 5. Overflow carries over: 31 February becomes 3 March.
console.log(new Date(Date.UTC(2026, 1, 31)).toISOString().slice(0, 10));
// 6. Comparing: < works on timestamps, === compares objects.
const a = new Date("2026-10-05");
const b = new Date("2026-10-05");
console.log(a < end, a === b, a.getTime() === b.getTime());
// 7. A string that is not a date gives an Invalid Date.
const bad = new Date("2026-13-01");
console.log("invalid:", Number.isNaN(bad.getTime()));
Run it with
node main.jsOutput
start: 2026-10-05T14:30:00.000Z
getUTCMonth: 9 -> month number 10
timestamp: 1791210600000
timestamp 0: 1970-01-01T00:00:00.000Z
2026-10-05T00:00:00.000Z
2026-10-05T12:30:00.000Z
90 minutes later: 2026-10-05T16:00:00.000Z
days to 24 December: 80
2026-03-03
true false true
invalid: trueTab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.
Exercise 1 of 2
addDays(isoDate, days) gets a date-only string such as "2026-10-05" and returns the date that many days later, as "YYYY-MM-DD". Negative days go back. The starter passes the month from the string straight to Date.UTC, so every result lands a month too late. Fix it.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.
Run the tests: 2026-10-05 plus 7 gives a date in November. Which number is off?
The string says 10 for October, but Date.UTC counts months from 0.
new Date(Date.UTC(year, month - 1, day + days))
One way to solve it. Yours can look different and still pass the checks.
export function addDays(isoDate, days) {
const [year, month, day] = isoDate.split("-").map(Number);
const date = new Date(Date.UTC(year, month - 1, day + days));
return date.toISOString().slice(0, 10);
}
Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.js
export function addDays(isoDate, days) {
const [year, month, day] = isoDate.split("-").map(Number);
const date = new Date(Date.UTC(year, month, day + days));
return date.toISOString().slice(0, 10);
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {addDays} from './main.js';
test('2026-10-05 plus 7 days is 2026-10-12', () => {
const got = addDays('2026-10-05', 7);
assert.equal(got, '2026-10-12', `addDays('2026-10-05', 7) gave ${got}`);
});
test('Adding days crosses the end of a month', () => {
const got = addDays('2026-01-30', 3);
assert.equal(got, '2026-02-02', `addDays('2026-01-30', 3) gave ${got}`);
});
test('Negative days go back', () => {
const got = addDays('2026-03-01', -1);
assert.equal(got, '2026-02-28', `addDays('2026-03-01', -1) gave ${got}`);
});
package.json
{
"type": "module"
}
package.json tells Node.js that the .js files are modules; keep it in the folder.
Run the program:
node main.jsRun the checks (needs learnrun.js in the same folder):
node --testDownload learnrun.jsExercise 2 of 2
sortByTime(events) gets objects like {title: "Call", at: "2026-10-05T14:30:00+02:00"} and returns a new array sorted from the earliest moment to the latest, without changing the input. The starter compares the strings, which is wrong when the offsets differ: 14:30+02:00 is 12:30 UTC. Compare the moments instead.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.
Run the tests: Call comes after Lunch. Is the string "14:30+02:00" later than "13:00Z" as a moment?
new Date(a.at) understands the offset. Subtracting two Dates gives the difference in milliseconds.
return [...events].sort((a, b) => new Date(a.at) - new Date(b.at));
One way to solve it. Yours can look different and still pass the checks.
export function sortByTime(events) {
return [...events].sort((a, b) => new Date(a.at) - new Date(b.at));
}
Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.js
export function sortByTime(events) {
return [...events].sort((a, b) => a.at.localeCompare(b.at));
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {sortByTime} from './main.js';
const events = [
{title: 'Lunch', at: '2026-10-05T13:00:00Z'},
{title: 'Call', at: '2026-10-05T14:30:00+02:00'},
{title: 'Breakfast', at: '2026-10-05T07:00:00Z'}
];
const titles = (list) => list.map((e) => e.title).join(', ');
test('Events with Z are sorted by time', () => {
const got = sortByTime([events[0], events[2]]);
assert.equal(titles(got), 'Breakfast, Lunch', `the order is ${titles(got)}`);
});
test('The offset counts: 14:30+02:00 is before 13:00 UTC', () => {
const got = sortByTime(events);
assert.equal(titles(got), 'Breakfast, Call, Lunch', `the order is ${titles(got)}`);
});
test('The input array is not changed', () => {
const input = [...events];
sortByTime(input);
assert.equal(titles(input), 'Lunch, Call, Breakfast', `the input became ${titles(input)}`);
});
package.json
{
"type": "module"
}
package.json tells Node.js that the .js files are modules; keep it in the folder.
Run the program:
node main.jsRun the checks (needs learnrun.js in the same folder):
node --testDownload learnrun.jsconst due = new Date("31.12.2026");
console.log(due.toISOString());
What Node.js prints
RangeError: Invalid time valueWhy, and the fix
Only the ISO format, such as "2026-12-31", is guaranteed to work. "31.12.2026" gives an Invalid Date, and toISOString() on an Invalid Date throws. Take the parts apart yourself and use Date.UTC(2026, 11, 31), or convert the text to ISO first. Check a parsed date with Number.isNaN(date.getTime()).
const due = Date.UTC(2026, 9, 5);
console.log(due.toISOString());
What Node.js prints
TypeError: due.toISOString is not a functionWhy, and the fix
Date.UTC returns a number, the timestamp, not a Date object, so it has no Date methods. Wrap it: const due = new Date(Date.UTC(2026, 9, 5)). Date.now() is a number too.
const saved = JSON.stringify({ due: new Date(Date.UTC(2026, 9, 5)) });
const task = JSON.parse(saved);
console.log(task.due.getUTCFullYear());
What Node.js prints
TypeError: task.due.getUTCFullYear is not a functionWhy, and the fix
JSON has no date type. JSON.stringify writes a Date as its ISO string, and JSON.parse gives that string back, not a Date. Turn it into a Date again after parsing: new Date(task.due).getUTCFullYear() is 2026.
JavaScript in the browser: your browser’s own engine, in a sandboxed worker. Syntax errors are located with acorn 8.18.0, MIT. Licence and source
5 questions, no hints. Score 80% or more to complete the lesson.
Finish every activity above to unlock the exit ticket.