Skip to main content

Chapter 19 - Keeping Time, Scheduling Tasks, and Launching Programs (JavaScript)

Here's a JavaScript-flavoured version of the same concepts, with small JS/Node examples for each idea.


Time basics

Epoch timestamp with Date.now and human time

  • Unix epoch: same midnight Jan 1, 1970 UTC.
  • Date.now() → milliseconds since epoch (not seconds like Python).
  • new Date().toString() → human-readable string.
const nowMs = Date.now();
console.log(nowMs); // e.g. 1773813875350
console.log(nowMs / 1000); // seconds (like Python's time.time())
console.log(new Date().toString()); // 'Tue Mar 17 2026 23:05:38 GMT-0700'
console.log(new Date(nowMs).toString()); // same time

Profiling code with Date.now or performance.now

Measure elapsed time by subtracting timestamps. performance.now() gives sub-millisecond precision.

const { performance } = require("perf_hooks");

function calculateProduct() {
let product = 1n; // BigInt for huge numbers
for (let i = 1n; i <= 100000n; i++) {
product *= i;
}
return product;
}

const startTime = performance.now();
const result = calculateProduct();
const endTime = performance.now();
console.log(`It took ${(endTime - startTime) / 1000} seconds to calculate.`);

For deeper profiling, Node has --prof and the built-in inspector module.

Pausing with setTimeout and sleep helpers

JavaScript is async by nature — there's no blocking sleep(). Use setTimeout with a promise:

function sleep(seconds) {
return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
}

async function main() {
for (let i = 0; i < 3; i++) {
console.log("Tick");
await sleep(1);
console.log("Tock");
await sleep(1);
}

await sleep(5); // extra pause
}

main();

For synchronous blocking (not recommended in production), use Atomics.wait:

function sleepSync(seconds) {
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, seconds * 1000);
}

Project 14: Super Stopwatch

Same goal: track lap times with ENTER, exit with CTRL‑C.

const readline = require("readline");

const rl = readline.createInterface({ input: process.stdin });

console.log("Press ENTER to begin and to mark laps. Ctrl-C quits.");

let startTime, lastTime, lapNumber;

rl.on("line", () => {
if (!startTime) {
console.log("Started.");
startTime = Date.now();
lastTime = startTime;
lapNumber = 1;
return;
}

const now = Date.now();
const lapTime = ((now - lastTime) / 1000).toFixed(2);
const totalTime = ((now - startTime) / 1000).toFixed(2);
console.log(`Lap #${lapNumber}: ${totalTime} (${lapTime})`);
lapNumber++;
lastTime = now;
});

process.on("SIGINT", () => {
console.log("\nDone.");
process.exit();
});

Date objects (datetime equivalent)

Date objects: specific moments

new Date() → current date/time; new Date(year, month, ...) → specific moment. Months are 0-based in JavaScript (0 = January).

const now = new Date();
console.log(now); // 2026-02-27T19:10:49.727Z

const dt = new Date(2026, 9, 21, 16, 29, 0); // month 9 = October
console.log(dt.getFullYear(), dt.getMonth() + 1, dt.getDate()); // 2026 10 21
console.log(dt.getHours(), dt.getMinutes(), dt.getSeconds()); // 16 29 0

Convert epoch timestamp to Date:

console.log(new Date(1000000 * 1000));   // 1970-01-12T13:46:40.000Z
console.log(new Date(Date.now())); // current moment

Compare dates (compare timestamps, not objects directly):

const halloween2026 = new Date(2026, 9, 31);
const newYears2027 = new Date(2027, 0, 1);
const oct312026 = new Date(2026, 9, 31);

console.log(halloween2026.getTime() === oct312026.getTime()); // true
console.log(halloween2026 > newYears2027); // false
console.log(newYears2027 > halloween2026); // true

Durations (timedelta equivalent)

JavaScript has no built-in timedelta. Work with millisecond arithmetic instead:

const MS_PER_DAY = 24 * 60 * 60 * 1000;
const MS_PER_HOUR = 60 * 60 * 1000;
const MS_PER_MINUTE = 60 * 1000;

// 11 days, 10 hours, 9 minutes, 8 seconds
const deltaMs =
11 * MS_PER_DAY + 10 * MS_PER_HOUR + 9 * MS_PER_MINUTE + 8 * 1000;
console.log(deltaMs / 1000); // 986948 total seconds

// Date arithmetic
const now = new Date();
const thousandDays = 1000 * MS_PER_DAY;
console.log(new Date(now.getTime() + thousandDays)); // 1000 days from now

More examples:

const oct21st = new Date(2026, 9, 21, 16, 29, 0);
const aboutThirtyYears = 365 * 30 * MS_PER_DAY;

console.log(new Date(oct21st.getTime() - aboutThirtyYears)); // ~1996-10-28
console.log(new Date(oct21st.getTime() - 2 * aboutThirtyYears)); // ~1966-11-05

Pausing until a specific date

function sleep(seconds) {
return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
}

async function waitUntil() {
const halloween2039 = new Date(2039, 9, 31);
while (new Date() < halloween2039) {
await sleep(1);
}
// continue after Halloween 2039
}

Formatting dates

JavaScript has toLocaleDateString and toLocaleString with options, or manual formatting:

const oct21st = new Date(2026, 9, 21, 16, 29, 0);

// Using toLocaleString with options
console.log(
oct21st.toLocaleString("en-US", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
})
);
// '10/21/2026, 16:29:00'

console.log(
oct21st.toLocaleString("en-US", { hour: "2-digit", minute: "2-digit" })
);
// '04:29 PM'

console.log(
oct21st.toLocaleString("en-US", { month: "long", year: "2-digit" })
);
// "October 26"

For Python-style strftime format strings, use the date-fns package:

npm install date-fns
const { format } = require("date-fns");

const oct21st = new Date(2026, 9, 21, 16, 29, 0);
console.log(format(oct21st, "yyyy/MM/dd HH:mm:ss")); // '2026/10/21 16:29:00'
console.log(format(oct21st, "hh:mm a")); // '04:29 PM'
console.log(format(oct21st, "MMMM 'of' ''yy")); // "October of '26"

Parsing date strings

Built-in new Date(string) handles ISO 8601 and common formats:

console.log(new Date("October 21, 2026")); // 2026-10-21T...
console.log(new Date("2026/10/21 16:29:00")); // 2026-10-21T...

For custom format parsing, use date-fns:

const { parse } = require("date-fns");

console.log(parse("October 21, 2026", "MMMM dd, yyyy", new Date()));
// 2026-10-21T00:00:00

console.log(parse("2026/10/21 16:29:00", "yyyy/MM/dd HH:mm:ss", new Date()));
// 2026-10-21T16:29:00

Review of JS time functions

Summary of types:

  • Epoch timestamp: milliseconds since 1970-01-01 (Date.now()).
  • Date object: specific moment (year, month, day, hour, minute, second, millisecond).
  • No built-in duration type — use millisecond arithmetic.

Key functions: Date.now, new Date(...), date.getTime(), date.getFullYear/getMonth/getDate/getHours/getMinutes/getSeconds, toLocaleString, date-fns for format/parse.


Launching programs with child_process

execSync and spawnSync (blocking — like subprocess.run)

const { execSync, spawnSync } = require("child_process");

// macOS
spawnSync("open", ["/System/Applications/Calculator.app"]);

// Linux
// spawnSync('/usr/bin/gnome-calculator');

// Windows
// spawnSync('C:\\Windows\\System32\\calc.exe');

spawnSync returns an object with .status (exit code) and .stdout/.stderr buffers.

spawn (non-blocking — like subprocess.Popen)

spawn starts a process and returns immediately; your script continues.

const { spawn } = require("child_process");

const calcProc = spawn("open", ["/System/Applications/Calculator.app"]);

calcProc.on("exit", (code) => {
console.log(`Exited with code ${code}`);
});

// Kill a process
// calcProc.kill();

Passing command line arguments

Arguments go in the array after the command:

const { spawnSync } = require("child_process");

// macOS: open a file in default app
spawnSync("open", ["/Users/Al/hello.txt"]);

// Windows equivalent:
// spawnSync('C:\\Windows\\notepad.exe', ['C:\\Users\\Al\\hello.txt']);

Capturing output from commands

Use execSync with encoding, or spawnSync:

const { execSync } = require("child_process");

// macOS/Linux
const output = execSync("ping -c 4 nostarch.com", { encoding: "utf-8" });
console.log(output);

// Windows: ping -n 4 nostarch.com

Or with spawnSync:

const { spawnSync } = require("child_process");

const proc = spawnSync("ping", ["-c", "4", "nostarch.com"], {
encoding: "utf-8",
});
console.log(proc.stdout);

Scheduling with OS tools

Same OS schedulers apply:

  • Windows: Task Scheduler.
  • macOS: launchd.
  • Linux: cron.

In Node, you can also use packages like node-cron for in-process scheduling:

npm install node-cron
const cron = require("node-cron");

cron.schedule("0 9 * * *", () => {
console.log("Running task at 9:00 AM every day");
});

Opening files with default applications

const { execSync } = require("child_process");
const fs = require("fs");

fs.writeFileSync("hello.txt", "Hello, world!", "utf-8");

// macOS
execSync("open hello.txt");

// Windows
// execSync('start hello.txt', { shell: true });

// Linux
// execSync('xdg-open hello.txt');

The open package provides a cross-platform alternative:

npm install open
const open = require("open");
await open("hello.txt");

Project 15: Simple Countdown

Step 1: Countdown loop

function sleep(seconds) {
return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
}

async function countdown() {
let timeLeft = 60;
while (timeLeft > 0) {
console.log(timeLeft);
await sleep(1);
timeLeft--;
}
// TODO: play sound/open file
}

countdown();

Step 2: Play sound or open something

const { execSync } = require("child_process");

// macOS:
// execSync('open alarm.wav');

// Windows:
// execSync('start alarm.wav', { shell: true });

// Or use the open package:
// const open = require('open');
// await open('alarm.wav');

You can instead open a website with the open package or use child_process to launch a browser.


Overall idea of the chapter

Chapter 19 in JavaScript: time handling with Date.now() (milliseconds, not seconds), Date objects for moments, millisecond arithmetic for durations (no built-in timedelta), formatting with toLocaleString or date-fns, async sleep with setTimeout promises, and launching programs with child_process (spawnSync/execSync for blocking, spawn for non-blocking). OS schedulers (Task Scheduler, launchd, cron) or node-cron handle recurring tasks.