Numbers, Strings, Dates & Intl
The everyday data: numbers that are always floats (plus BigInt), strings made of UTF-16 code units, template literals and tags, the famously awkward Date and its replacement Temporal, and the Intl APIs that format all of it for any language.
You will learn
- Number precision, safe integers,
Number.EPSILON, parsing and rounding pitfalls. - Strings as UTF-16:
lengthvs characters, and the modern string methods. - Template literals, tagged templates and
String.rawES2015. Datepitfalls, and the Temporal API as the modern alternative.- Locale-aware formatting with
Intl: currency, dates, plurals, lists, sorting.
Numbers
Every number is a 64-bit IEEE-754 float, the same as Java's double. Integers are exact only up to Number.MAX_SAFE_INTEGER (253 β 1). Beyond that, use BigInt.
π‘ Money: store integer cents (1299), not 12.99, and format only for display. That avoids float drift in totals, the same advice as Java's BigDecimal.
Converting strings to numbers
| Expression | Result | Why |
|---|---|---|
Number("12px") | NaN | the whole string must be numeric |
parseInt("12px") | 12 | parses a prefix, stops at the first bad character |
parseFloat("3.14abc") | 3.14 | same, for decimals |
Number("") / Number(" 42 ") | 0 / 42 | whitespace is trimmed, and an empty string is 0 β οΈ |
parseInt("ff", 16) | 255 | always pass the radix |
isNaN("abc") vs Number.isNaN("abc") | true vs false | the global one converts first; Number.isNaN ES2015 doesn't |
Rounding
toFixedreturns a string, and rounds the binary value:1.005is really 1.00499β¦, so it gives"1.00".Math.roundrounds halves up (toward +β):-2.5becomes-2.Math.trunc,Math.sign,Math.cbrtandMath.hypotare ES2015 additions.
BigInt ES2020
BigInt division truncates. You can't mix BigInt and Number in arithmetic (that's a TypeError); convert explicitly with BigInt(n) or Number(b). JSON.stringify can't serialize BigInts.
Strings
A string is an immutable sequence of UTF-16 code units. Most characters are one unit, but emoji and other characters outside the Basic Multilingual Plane are two (a surrogate pair). length counts units, not what a reader sees as characters.
Spreading or forβ¦of iterates by code point. For "what the user sees as one character" (for example ππ½, which is two code points), use Intl.Segmenter (see the Intl section below).
The modern string methods
| Method | Example β result | Since |
|---|---|---|
includes, startsWith, endsWith | "invoice.pdf".endsWith(".pdf") β true | ES2015 |
repeat | "-".repeat(3) β "---" | ES2015 |
padStart, padEnd | "7".padStart(3, "0") β "007" | ES2017 |
trimStart, trimEnd | " a ".trimStart() β "a " | ES2019 |
replaceAll | "a-b-c".replaceAll("-", "/") β "a/b/c" | ES2021 |
at | "Dune".at(-1) β "e" | ES2022 |
isWellFormed, toWellFormed | detect and repair lone surrogates | ES2024 |
slice, split, indexOf, toUpperCase, localeCompare | the classics | ES5 |
β οΈ Pitfall: "a-b-c".replace("-", "/") replaces only the first match. Use replaceAll, or a regex with the g flag (Chapter 7).
Template literals ES2015
Backtick strings can span several lines and embed any expression with ${β¦}.
Tagged templates
A tag is a function placed before a template literal. It receives the literal text pieces and the evaluated values separately, so it can escape, translate or format the values before joining them. Libraries use this for SQL, HTML and CSS.
Dates
Date stores a single moment as milliseconds since 1 January 1970 UTC. It's a leftover from 1995 Java, and it has well-known traps:
| Trap | Example |
|---|---|
| Months are 0-based (days are not) | new Date(2026, 9, 30) is 30 October |
| Date-only ISO strings are UTC; date-time strings without a zone are local | new Date("2026-10-30") vs new Date("2026-10-30T00:00") |
| Dates are mutable | d.setDate(d.getDate() + 1) changes d for everyone holding it |
| Overflow rolls over silently | new Date(2026, 0, 32) is 1 February |
| Only local time and UTC; no other time zones | use Intl to display in another zone |
Temporal: the modern replacement
Temporal is a new global API, available in Node 26 and current browsers, that fixes Date's design. It has separate immutable types for different jobs: PlainDate (a calendar date), PlainTime, ZonedDateTime (a moment in a real time zone), Instant and Duration. Months are 1-based, and arithmetic is calendar-aware.
π Status: Temporal is new. Check your target browsers or Node version before relying on it, or use the @js-temporal/polyfill package. Date isn't going anywhere, so you'll keep reading it in existing code.
Internationalization with Intl
The Intl namespace formats numbers, dates, lists and plurals, and compares strings, according to a locale such as "de-DE" or "en-IN". Create a formatter once and reuse it; formatters are relatively expensive to build.
β οΈ Pitfall: formatted output may contain special spaces, such as a no-break space before β¬ or a narrow no-break space before AM. Never compare formatted strings in tests against strings you typed by hand; compare the numbers, or use formatToParts().
More examples
1. Summing money safely
2. Masking a card number
3. A receipt line aligned with padEnd/padStart
4. Truncating text by graphemes, not code units
5. Next business day with Temporal
Recap
- Numbers are doubles: compare floats with a tolerance, store money as integer cents, and use BigInt beyond 253.
- Prefer
Number()plusNumber.isNaN; giveparseInta radix.toFixedreturns a string. - Strings are UTF-16:
lengthcounts code units, spread counts code points, andIntl.Segmentercounts what readers see. - Template literals interpolate and span lines. Tags receive the literal parts and values separately.
Datehas 0-based months and mutable objects; Temporal fixes both. UseIntlfor all user-facing formatting.
Exercises
Exercise 1 π’ easy predict the output
Evaluate: Number("42px"), parseInt("42px"), Number(""), parseInt("")
Show solution
NaN, 42, 0, NaN. Number needs the whole string to be numeric, but treats an empty string as 0. parseInt needs at least one digit at the start.
Exercise 2 π’ easy build it
Format an order number 7 as ORD-00007 using one string method.
Show solution
padStart is a string method, so convert the number first.
Exercise 3 π’ easy fix the bug
The month shows as November. Fix it.
Show solution
Date months run from 0 to 11. Temporal's month is 1-based, which avoids this bug.
Exercise 4 π’ easy predict the output
What are "cafΓ©".length, "π©".length and [..."π©π©"].length?
Show solution
4, 2, 2. "Γ©" written as one code point is one unit; the emoji needs a surrogate pair (2 units) but is one code point.
Exercise 5 π‘ medium build it
Write a tag function upper that upper-cases only the interpolated values: upper`Hi ${"ana"}, your book ${"dune"} shipped` β "Hi ANA, your book DUNE shipped".
Show solution
strings always has one more element than values, which is why the last step adds nothing after the final string.
Exercise 6 π‘ medium explain why
Why does (1.005).toFixed(2) give "1.00" and not "1.01"? How would you round money correctly?
Show solution
1.005 can't be stored exactly. The nearest double is 1.00499999999999989β¦, which correctly rounds down. Work in integer cents (Math.round(x * 100) at input time), or use a decimal library for exact decimal rounding.
Exercise 7 π‘ medium build it Β· playground
Create playground/format.js exporting formatPrice(cents, locale, currency), with formatters cached per locale + currency in a Map. Print 1299 cents in en-US/USD, de-DE/EUR and ja-JP/JPY (yen has no minor unit, so 1299 means Β₯1,299).
Show solution
resolvedOptions() reveals how many decimals the currency uses, so the same "minor units" integer works for every currency. Caching avoids rebuilding formatters.
Exercise 8 π‘ medium predict the output
What does ["b", "a", "C", "Γ‘"].sort() return? And with new Intl.Collator("en").compare?
Show solution
['C', 'a', 'b', 'Γ‘'] (by UTF-16 code unit, so uppercase letters come first and accents last), and ['a', 'Γ‘', 'b', 'C'] (the human dictionary order for English).
Exercise 9 π΄ hard build it
Using Temporal, compute the delivery date for an order placed on "2026-12-23" with 3 business days of shipping, skipping weekends and the holidays 2026-12-25 and 2026-12-26.
Show solution
Walking forward from the 23rd: the 24th (Thursday) counts as day 1; the 25th (Friday) is a holiday; the 26th and 27th are a weekend (the 26th is also a holiday); the 28th (Monday) is day 2 and the 29th is day 3. So delivery is 2026-12-29. dayOfWeek uses ISO numbering: Monday = 1 through Sunday = 7.
Exercise 10 π΄ hard mini-project Β· playground
Create playground/receipt.js. Given a locale, a currency and line items { title, cents, qty }, print a receipt: aligned columns, an Intl currency for each line and the total, a pluralised item count ("1 item" / "3 items" via Intl.PluralRules), and a date line formatted with Intl.DateTimeFormat in UTC.
Show solution
It uses integer cents, template literals inside padEnd/padStart, three different Intl formatters, and destructured options with defaults.