Lesson 18 โ€ข Intermediate

    Dates & Internationalization ๐Ÿ“…

    By the end of this lesson you'll create, read, and format dates correctly โ€” and show times, currencies, and "3 hours ago" the way each user's country expects.

    What You'll Learn in This Lesson

    • โœ“Create and parse dates safely with the Date object
    • โœ“Read and change parts (year, month, day, hour)
    • โœ“Use timestamps to do bug-free date arithmetic
    • โœ“Format dates per locale with Intl.DateTimeFormat
    • โœ“Format currency with Intl.NumberFormat
    • โœ“Show relative time and handle time zones

    ๐Ÿ“š Prerequisites: You should be comfortable with variables and functions. Knowing how objects and methods work will help, since a date is an object with methods.

    ๐Ÿ’ก Running Code Locally: While this online editor runs real JavaScript, some advanced examples may have limitations. For the best experience:

    • Download Node.js to run JavaScript on your computer
    • Use your browser's Developer Console (Press F12) to test code snippets
    • Create a .html file with <script> tags and open it in your browser

    ๐Ÿ•ฐ๏ธ Real-World Analogy: A Date is like the single counter on a stopwatch that started ticking at midnight on Jan 1, 1970 (UTC):

    • โ€ข Inside, it only stores one number โ€” how many milliseconds have ticked by
    • โ€ข The clock on your wall (local time) is just that number shown in your zone
    • โ€ข Two people in different countries read the same instant as different wall-clock times
    • โ€ข Intl is the translator that prints that instant the way each country writes it

    1๏ธโƒฃ The Date Object: One Number Underneath

    A Date is JavaScript's built-in type for a moment in time. It does not store a time zone. It stores a single number: the milliseconds since the epoch (midnight, Jan 1 1970, UTC). Every other thing you see โ€” local time, ISO strings, formatted text โ€” is calculated from that number.

    You create "now" with new Date(), read the raw number with getTime(), and get a universal string with toISOString().

    What a Date Really Stores

    See the single number behind every Date

    Try it Yourself ยป
    JavaScript
    // new Date() with no arguments = right now
    const now = new Date();
    
    console.log(now.getTime());      // โœ… Expected output: a big number, e.g. 1750000000000 (ms since 1970)
    console.log(typeof now.getTime()); // โœ… Expected output: number
    
    // toISOString() always prints in UTC with a trailing "Z"
    console.log(new Date(0).toISOString()); // โœ… Expected output: 1970-01-01T00:00:00.000Z
    
    // The same instant, two views:
    const launch = new Date("2025-06-16T12:00:00Z");
    console.log(launch.getTime());   //
    ...

    2๏ธโƒฃ Creating & Parsing Dates Safely

    There are three reliable ways to build a date. The safest take the guesswork out of which time zone JavaScript should assume. Notice the month is zero-indexed in the numeric constructor โ€” 0 is January, 11 is December.

    Three Ways to Create a Date

    Explicit, ISO, and numeric constructors

    Try it Yourself ยป
    JavaScript
    // 1) ISO string with "Z" = unambiguous UTC (recommended for stored data)
    const iso = new Date("2025-01-01T09:30:00Z");
    console.log(iso.toISOString()); // โœ… Expected output: 2025-01-01T09:30:00.000Z
    
    // 2) Date.UTC(year, monthIndex, day, ...) returns a timestamp in UTC
    const utc = new Date(Date.UTC(2025, 0, 1)); // month 0 = January!
    console.log(utc.toISOString()); // โœ… Expected output: 2025-01-01T00:00:00.000Z
    
    // 3) Numeric constructor uses LOCAL time (handy, but zone-dependent)
    const local = 
    ...

    3๏ธโƒฃ Reading & Changing Parts of a Date

    Each piece has a get method and a matching set method: getFullYear(), getMonth(), getDate() (day of month), getDay() (day of week, 0 = Sunday), getHours(), and so on. The set methods mutate the date in place and conveniently roll over โ€” adding a day to Jan 31 lands on Feb 1.

    Get and Set Date Parts

    Read fields and let set methods roll over

    Try it Yourself ยป
    JavaScript
    const d = new Date("2025-01-31T00:00:00Z");
    
    console.log(d.getUTCFullYear()); // โœ… Expected output: 2025
    console.log(d.getUTCMonth());    // โœ… Expected output: 0  (January is 0)
    console.log(d.getUTCDate());     // โœ… Expected output: 31
    
    // set methods MUTATE and roll over automatically
    d.setUTCDate(d.getUTCDate() + 1); // 31 + 1 spills into the next month
    console.log(d.toISOString());     // โœ… Expected output: 2025-02-01T00:00:00.000Z

    ๐ŸŽฏ Your Turn #1 โ€” Build a Birthday

    Remember: the month in Date.UTC() is zero-indexed. Fill in the blanks.

    ๐ŸŽฏ YOUR TURN โ€” Make a date

    Fill in the blanks marked with ___

    Try it Yourself ยป
    JavaScript
    // ๐ŸŽฏ YOUR TURN โ€” fill in the blanks marked with ___
    
    // 1) Build December 25, 2025 at midnight UTC.
    //    Months are zero-indexed, so December is 11.
    const xmas = new Date(Date.UTC(2025, ___, 25)); // ๐Ÿ‘‰ replace ___ with the month index for December
    
    // 2) Read the day of the month back out.
    const day = xmas.getUTCDate(); // ๐Ÿ‘‰ nothing to change here, just observe
    
    console.log(xmas.toISOString()); // โœ… Expected output: 2025-12-25T00:00:00.000Z
    console.log(day);                // โœ… Expected outp
    ...

    4๏ธโƒฃ Timestamps: Bug-Free Date Math

    A timestamp is just that millisecond number. Because it's plain arithmetic, adding fixed durations (minutes, hours) is reliable. Date.now() gives the current timestamp directly. To add calendar units like days or months, prefer the set methods, which respect month lengths and daylight saving.

    Math with Timestamps

    Add durations and measure gaps

    Try it Yourself ยป
    JavaScript
    const start = new Date("2025-06-16T12:00:00Z");
    
    // Add 90 minutes by working in milliseconds
    const later = new Date(start.getTime() + 90 * 60 * 1000);
    console.log(later.toISOString()); // โœ… Expected output: 2025-06-16T13:30:00.000Z
    
    // Difference between two dates, in days
    const a = new Date("2025-01-01T00:00:00Z");
    const b = new Date("2025-01-08T00:00:00Z");
    const days = (b.getTime() - a.getTime()) / (1000 * 60 * 60 * 24);
    console.log(days); // โœ… Expected output: 7
    
    // Add 3 calendar months sa
    ...

    5๏ธโƒฃ Formatting with Intl.DateTimeFormat

    Never build a display string by hand. Intl.DateTimeFormat takes a locale (like "en-US" or "en-GB") and an options object, then prints the date the way that country writes it โ€” correct order, month names, and 12- vs 24-hour clock. Pass timeZone to control which zone it renders in.

    Locale-Aware Date Formatting

    Same instant, formatted for different countries

    Try it Yourself ยป
    JavaScript
    const d = new Date("2025-06-16T18:05:00Z");
    
    // US puts the month first; UK puts the day first
    console.log(new Intl.DateTimeFormat("en-US").format(d)); // โœ… Expected output: 6/16/2025
    console.log(new Intl.DateTimeFormat("en-GB").format(d)); // โœ… Expected output: 16/06/2025
    
    // Spell it out with options + a fixed time zone
    const long = new Intl.DateTimeFormat("en-GB", {
      weekday: "long",
      day: "numeric",
      month: "long",
      year: "numeric",
      timeZone: "UTC",
    }).format(d);
    console.log(long); // 
    ...

    Intl.NumberFormat โ€” Money & Numbers

    The same family formats numbers. With style: "currency" and a currency code, Intl.NumberFormat adds the right symbol, decimals, and grouping for the locale. This is how you show prices correctly to a global audience.

    Currency and Number Formatting

    Format money for different locales

    Try it Yourself ยป
    JavaScript
    const price = 1234.5;
    
    // US dollars
    console.log(new Intl.NumberFormat("en-US", {
      style: "currency", currency: "USD",
    }).format(price)); // โœ… Expected output: $1,234.50
    
    // British pounds
    console.log(new Intl.NumberFormat("en-GB", {
      style: "currency", currency: "GBP",
    }).format(price)); // โœ… Expected output: ยฃ1,234.50
    
    // Plain grouped number
    console.log(new Intl.NumberFormat("en-US").format(1000000)); // โœ… Expected output: 1,000,000

    7๏ธโƒฃ Intl.RelativeTimeFormat โ€” "3 hours ago"

    For feeds and notifications, show relative time. Negative numbers are the past, positive are the future. With numeric: "auto" it even says "yesterday" and "tomorrow" where a language has those words.

    Relative Time

    Human-friendly time differences

    Try it Yourself ยป
    JavaScript
    const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
    
    console.log(rtf.format(-3, "hour")); // โœ… Expected output: 3 hours ago
    console.log(rtf.format(2, "day"));   // โœ… Expected output: in 2 days
    console.log(rtf.format(-1, "day"));  // โœ… Expected output: yesterday
    console.log(rtf.format(1, "week"));  // โœ… Expected output: next week

    ๐ŸŽฏ Your Turn #2 โ€” Price Tag

    Use Intl.NumberFormat to print a euro price for a French audience.

    ๐ŸŽฏ YOUR TURN โ€” Format currency

    Fill in the blanks marked with ___

    Try it Yourself ยป
    JavaScript
    // ๐ŸŽฏ YOUR TURN โ€” fill in the blanks marked with ___
    
    const amount = 49.99;
    
    // 1) Choose the currency code for euros (3 letters, uppercase).
    const euros = new Intl.NumberFormat("de-DE", {
      style: "currency",
      currency: "___", // ๐Ÿ‘‰ replace ___ with the ISO code for the euro
    }).format(amount);
    
    console.log(euros); // โœ… Expected output: 49,99 โ‚ฌ  (German formats euro after the number)

    ๐Ÿงฉ Mini-Challenge โ€” Event Countdown

    Now with the scaffolding removed. Work from the comment outline only.

    ๐Ÿงฉ MINI-CHALLENGE โ€” Days until an event

    Only a comment outline โ€” you write the code

    Try it Yourself ยป
    JavaScript
    // ๐Ÿงฉ MINI-CHALLENGE: Days until New Year 2026
    // 1. Create a Date for the event: 2026-01-01 at midnight UTC (use an ISO string with "Z").
    // 2. Create a Date for a fixed "today": 2025-12-16 at midnight UTC.
    // 3. Subtract their getTime() values and divide by the ms in one day (1000 * 60 * 60 * 24).
    // 4. Log the whole number of days using Math.round(...).
    //
    // โœ… Expected output: 16
    
    // your code here

    Time Zones in Practice

    Store the instant in UTC; convert only when you display. Pass an IANA zone name (like "America/New_York") to timeZone and the formatter handles the offset and daylight saving for you.

    One Instant, Many Clocks

    Render a UTC moment in several zones

    Try it Yourself ยป
    JavaScript
    const instant = new Date("2025-06-16T16:00:00Z");
    
    const show = (zone) =>
      new Intl.DateTimeFormat("en-GB", {
        hour: "2-digit",
        minute: "2-digit",
        timeZone: zone,
      }).format(instant);
    
    console.log("London:", show("Europe/London"));     // โœ… Expected output: London: 17:00 (BST in summer)
    console.log("New York:", show("America/New_York")); // โœ… Expected output: New York: 12:00
    console.log("Tokyo:", show("Asia/Tokyo"));           // โœ… Expected output: Tokyo: 01:00
    
    // Discover the visi
    ...

    ๐Ÿ“ฆ A Note on Date Libraries: The built-in Date is fine for the basics, but it is mutable and quirky. Two things worth knowing:

    • โ€ข date-fns โ€” a popular toolkit of small, pure functions (addDays, format, differenceInDays) that never mutate your dates.
    • โ€ข Temporal โ€” the new standard built into JavaScript, designed to replace Date with immutable, time-zone-aware types. It's rolling out now; learn it when it lands in your runtime.

    Common Errors & Fixes

    1. 1๏ธโƒฃ Months are off by one
      new Date(2025, 12, 1); // ๐Ÿ‘ˆ month 12 rolls into JANUARY 2026!
      new Date(2025, 11, 1); // โœ” December: months are 0-indexed (0โ€“11)
    2. 2๏ธโƒฃ "One day off" from an ambiguous string
      new Date("2025-01-01");        // parsed as UTC midnight, shown locally โ†’ may read Dec 31
      new Date("2025-01-01T00:00:00"); // โœ” add a time, or use Date.UTC(2025, 0, 1)
    3. 3๏ธโƒฃ set methods mutate the original
      const start = new Date("2025-01-01T00:00:00Z");
      const end = start;          // ๐Ÿ‘ˆ same object, not a copy!
      end.setUTCDate(10);         // this also changes "start"
      const copy = new Date(start); // โœ” clone first, then mutate the copy
    4. 4๏ธโƒฃ Invalid input gives "Invalid Date", not an error
      const d = new Date("not a date");
      console.log(isNaN(d.getTime())); // โœ” true โ†’ check this before trusting the date
    5. 5๏ธโƒฃ Forgetting the time zone when formatting
      // Output depends on the server/visitor's zone โ€” flaky in tests
      new Intl.DateTimeFormat("en-GB").format(d);
      // โœ” pin it: pass { timeZone: "UTC" } (or the zone you mean)

    Quick Reference โ€” Dates & Intl

    TaskCode
    Nownew Date() / Date.now()
    From ISO (UTC)new Date("2025-06-16T12:00:00Z")
    From parts (UTC)new Date(Date.UTC(2025, 0, 1))
    Raw timestampd.getTime()
    Universal stringd.toISOString()
    Read partd.getUTCFullYear(), getUTCMonth() (0โ€“11)
    Format datenew Intl.DateTimeFormat("en-GB", {โ€ฆ}).format(d)
    Format currencynew Intl.NumberFormat("en-US", { style: "currency", currency: "USD" })
    Relative timenew Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(-3, "hour")

    Frequently Asked Questions

    Why is the month 11 for December?

    JavaScript months are zero-indexed: January is 0 and December is 11. Always subtract 1 when you build a date from a human month number, and add 1 when you read getMonth() back for display.

    Why does new Date('2025-01-01') sometimes show December 31?

    A date-only string like '2025-01-01' is parsed as midnight UTC, then displayed in your local time. If you are behind UTC (e.g. New York), midnight UTC is the evening of the day before, so it looks 'one day off'. Add an explicit time and zone, or use Date.UTC().

    What's the difference between getTime() and Date.now()?

    Both return milliseconds since the Unix epoch (Jan 1 1970 UTC). Date.now() is a static method that gives the current time without creating a Date object; getTime() is called on an existing Date instance.

    How do I format money for different countries?

    Use Intl.NumberFormat with style: 'currency' and a currency code like 'USD' or 'GBP'. It picks the right symbol, decimal separators, and digit grouping for the locale you pass.

    Should I use a date library like date-fns instead?

    For simple formatting and arithmetic the built-in Date plus Intl is enough. For heavy parsing, time-zone math, or durations, a library such as date-fns is safer. The upcoming Temporal API aims to replace Date entirely with an immutable, time-zone-aware design.

    Lesson Complete โ€” Dates & Internationalization!

    • โœ… A Date is one number: milliseconds since the 1970 UTC epoch
    • โœ… Months are zero-indexed; prefer ISO strings with Z or Date.UTC()
    • โœ… Do math with timestamps; use set methods for calendar units
    • โœ… Format with Intl.DateTimeFormat, Intl.NumberFormat, and Intl.RelativeTimeFormat
    • โœ… Store in UTC, display in the user's time zone

    You can now handle dates the way production apps do โ€” accurately, and in any language. ๐ŸŒ

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous