# I18n Challenges and Best Practices

# 🕐 | Regional Time Zone Systems

## 🎯 Learning Objectives

- Understand the fundamentals of time zones and UTC
- Handle Daylight Saving Time (DST) transitions correctly
- Store and transmit temporal data properly
- Display times appropriately for user's local context
- Avoid common pitfalls in time zone handling

## 📖 The Fundamentals

### What is UTC?

**Coordinated Universal Time (UTC)**<span style="white-space: pre-wrap;"> is the primary time standard by which the world regulates clocks and time. It is not affected by Daylight Saving Time and serves as the foundation for all time zone calculations.</span>

#### Key Concepts

<table id="bkmrk-termdefinitionexampl" style="width: 100%; border-collapse: collapse;"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr style="background: rgb(248, 249, 250);"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Term

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Definition

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Example

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**UTC Offset**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Time difference from UTC

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">EST: UTC-5, JST: UTC+9

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Time Zone**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Region with uniform standard time

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">America/New\_York, Europe/London

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**DST**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Seasonal clock adjustment

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Spring forward, Fall back

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**IANA TZ Database**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Authoritative time zone data

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">tzdata, Olson database

</td></tr></tbody></table>

### Time Zone Identifiers

<span style="white-space: pre-wrap;">Always use </span>**IANA time zone identifiers**<span style="white-space: pre-wrap;"> (e.g., </span>`<span class="editor-theme-code">America/New_York</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">Asia/Tokyo</span>`) rather than abbreviations like EST or PST. Abbreviations are ambiguous and don't account for DST.

#### ⚠️ Common Mistake: Using Abbreviations

**❌ Don't Do This:**

```
// Ambiguous - which CST?
// Central Standard Time (US)?
// China Standard Time?
// Cuba Standard Time?
timezone = "CST"
```

**✅ Do This Instead:**

```
// Unambiguous IANA identifier
timezone = "America/Chicago"
// or
timezone = "Asia/Shanghai"
// or
timezone = "America/Havana"
```

## 💻 Implementation Guidelines

### The Golden Rule: Store in UTC, Display in Local

#### ✅ Best Practice Pattern

1. **Storage:**<span style="white-space: pre-wrap;"> Always store timestamps in UTC (ISO 8601 format recommended)</span>
2. **Transmission:**<span style="white-space: pre-wrap;"> Send timestamps in UTC between services</span>
3. **Display:**<span style="white-space: pre-wrap;"> Convert to user's local time zone only for presentation</span>
4. **Input:**<span style="white-space: pre-wrap;"> Accept user input in local time, immediately convert to UTC</span>

### Code Examples

#### JavaScript Example

```
// Store: Always use UTC
const eventTime = new Date().toISOString();
// → "2025-11-05T14:30:00.000Z"

// Database storage (example)
await db.events.insert({
  title: "Team Meeting",
  startTime: eventTime,  // UTC timestamp
  timezone: "America/Los_Angeles"  // Store user's timezone separately
});

// Display: Convert to user's local time
const formatter = new Intl.DateTimeFormat('en-US', {
  timeZone: 'America/Los_Angeles',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  hour: '2-digit',
  minute: '2-digit',
  timeZoneName: 'short'
});

console.log(formatter.format(new Date(eventTime)));
// → "November 5, 2025, 06:30 AM PST"
```

#### Python Example

```
from datetime import datetime, timezone
import pytz

# Store: Always use UTC
event_time = datetime.now(timezone.utc)
# Store as ISO 8601: event_time.isoformat()

# Display: Convert to user's timezone
user_tz = pytz.timezone('Europe/London')
local_time = event_time.astimezone(user_tz)

print(local_time.strftime('%Y-%m-%d %H:%M:%S %Z'))
# → "2025-11-05 14:30:00 GMT"

# During BST (British Summer Time):
# → "2025-06-15 15:30:00 BST"
```

#### Java Example

```
import java.time.*;
import java.time.format.DateTimeFormatter;

// Store: Always use UTC
Instant eventTime = Instant.now();
// Store in database as: eventTime.toString()
// → "2025-11-05T14:30:00Z"

// Display: Convert to user's timezone
ZoneId userZone = ZoneId.of("Asia/Tokyo");
ZonedDateTime localTime = eventTime.atZone(userZone);

DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
    "yyyy-MM-dd HH:mm:ss z"
);
System.out.println(localTime.format(formatter));
// → "2025-11-05 23:30:00 JST"
```

## ⚡ Handling Daylight Saving Time

DST transitions create ambiguous and non-existent times. Your code must handle these edge cases gracefully.

#### Spring Forward (Non-existent Hour)

When clocks "spring forward," one hour doesn't exist. For example, on March 10, 2024, in the US, 2:00 AM → 3:00 AM instantly.

**Problem:**<span style="white-space: pre-wrap;"> 2:30 AM doesn't exist on that day!</span>

#### Fall Back (Ambiguous Hour)

When clocks "fall back," one hour occurs twice. On November 3, 2024, 2:00 AM → 1:00 AM, repeating the 1 AM hour.

**Problem:**<span style="white-space: pre-wrap;"> 1:30 AM happens twice!</span>

#### ✅ Solution: Let Libraries Handle It

<span style="white-space: pre-wrap;">Modern date/time libraries (like </span>`<span class="editor-theme-code">Intl</span>`<span style="white-space: pre-wrap;"> in JavaScript, </span>`<span class="editor-theme-code">pytz</span>`<span style="white-space: pre-wrap;"> in Python, </span>`<span class="editor-theme-code">java.time</span>`<span style="white-space: pre-wrap;"> in Java) automatically handle DST transitions. </span>**Never implement your own DST logic!**

```
// JavaScript example: Library handles DST automatically
const date1 = new Date('2024-03-10T02:30:00');  // During "spring forward"
const date2 = new Date('2024-11-03T01:30:00');  // During "fall back"

// The Intl API handles these transitions correctly
const formatter = new Intl.DateTimeFormat('en-US', {
  timeZone: 'America/New_York',
  hour: '2-digit',
  minute: '2-digit',
  timeZoneName: 'short'
});

// Results are handled properly by the system
```

## 🌍 Real-World Scenarios

#### Scenario 1: Scheduling a Meeting Across Time Zones

**Situation:**<span style="white-space: pre-wrap;"> A user in New York schedules a meeting for 3:00 PM their time, inviting participants in London and Tokyo.</span>

<table id="bkmrk-locationtime-zonedis" style="width: 100%; border-collapse: collapse; margin-top: 1rem;"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr style="background: rgb(52, 152, 219); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Location

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Time Zone

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Display Time

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">New York

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">America/New\_York

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">3:00 PM EST

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">London

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Europe/London

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">8:00 PM GMT

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Tokyo

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Asia/Tokyo

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">5:00 AM JST (next day)

</td></tr><tr style="background: rgb(227, 242, 253);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Stored Value**

</td><td colspan="2" style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**2025-11-05T20:00:00Z (UTC)**

</td></tr></tbody></table>

#### Scenario 2: Recurring Events During DST Transition

**Situation:**<span style="white-space: pre-wrap;"> A weekly meeting scheduled for "every Monday at 9:00 AM local time" needs to maintain the local time even when DST changes.</span>

**Solution:**<span style="white-space: pre-wrap;"> Store the time zone identifier along with the local time, not just a UTC offset. This allows the system to recalculate the correct UTC time after DST transitions.</span>

```
{
  "eventTitle": "Weekly Standup",
  "recurrence": "weekly",
  "dayOfWeek": "Monday",
  "localTime": "09:00",
  "timeZone": "America/Los_Angeles",  // Critical: Store TZ, not offset!
  "duration": 30
}

// Before DST (PST, UTC-8): 9:00 AM = 17:00 UTC
// After DST (PDT, UTC-7):  9:00 AM = 16:00 UTC
// Users still see 9:00 AM local time ✓
```

## 🎯 Best Practices Checklist

<table id="bkmrk-practicepriority%E2%9C%85-al" style="width: 100%; border-collapse: collapse;"><colgroup><col style="width: 60%;"></col><col></col></colgroup><tbody><tr style="background: rgb(52, 152, 219); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221); width: 60%;">Practice

</th><th class="align-center" style="padding: 0.75rem; text-align: center; border: 1px solid rgb(221, 221, 221);">Priority

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Always store timestamps in UTC

</td><td class="align-center" style="background: rgb(231, 76, 60); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**CRITICAL**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Use IANA time zone identifiers (America/New\_York, not EST)

</td><td class="align-center" style="background: rgb(231, 76, 60); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**CRITICAL**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Use ISO 8601 format for date/time strings

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Let standard libraries handle DST transitions

</td><td class="align-center" style="background: rgb(231, 76, 60); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**CRITICAL**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Display times in user's local time zone with TZ name

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Test with edge cases (DST transitions, year boundaries)

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Allow users to explicitly set their time zone preference

</td><td class="align-center" style="background: rgb(52, 152, 219); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**MEDIUM**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Keep time zone database updated (IANA releases)

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr></tbody></table>

## 📚 Additional Resources

- **IANA Time Zone Database:**<span style="white-space: pre-wrap;"> </span>[www.iana.org/time-zones](https://www.iana.org/time-zones)
- **ISO 8601:**<span style="white-space: pre-wrap;"> International date/time standard</span>
- **moment-timezone (JavaScript):**<span style="white-space: pre-wrap;"> Comprehensive time zone library</span>
- **pytz (Python):**<span style="white-space: pre-wrap;"> World timezone definitions for Python</span>
- **java.time (Java 8+):**<span style="white-space: pre-wrap;"> Modern date-time API</span>

**Next Topic:**<span style="white-space: pre-wrap;"> Number &amp; Currency Formatting →</span>

# 💰 | Number & Currency Formatting

## 🎯 Learning Objectives

- Understand global variations in number formatting
- Handle decimal separators and digit grouping correctly
- Format currency with proper symbols and placement
- Work with different numbering systems
- Implement locale-aware number parsing and validation

## 🌍 The Global Number Challenge

<span style="white-space: pre-wrap;">The same number can look completely different around the world. What appears as </span>`<span class="editor-theme-code">1,234.56</span>`<span style="white-space: pre-wrap;"> in the US becomes </span>`<span class="editor-theme-code">1.234,56</span>`<span style="white-space: pre-wrap;"> in Germany and </span>`<span class="editor-theme-code">1 234,56</span>`<span style="white-space: pre-wrap;"> in France.</span>

### Same Number, Different Representations

<table id="bkmrk-localenumber-formatc" style="width: 100%; border-collapse: collapse;"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr style="background: rgb(46, 204, 113); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Locale

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Number Format

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Currency Format

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**English (US)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">1,234.56</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">$1,234.56</span>`

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**German (Germany)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">1.234,56</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">1.234,56 €</span>`

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**French (France)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">1 234,56</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">1 234,56 €</span>`

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Hindi (India)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">1,234.56</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">₹1,234.56</span>`

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Arabic (Saudi Arabia)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">١٬٢٣٤٫٥٦</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">١٬٢٣٤٫٥٦ ر.س</span>`

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Japanese (Japan)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">1,234.56</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">¥1,235</span>`

<span style="white-space: pre-wrap;"> (no decimals)</span>

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Swiss (German)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">1'234.56</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">CHF 1'234.56</span>`

</td></tr></tbody></table>

#### ⚠️ Critical Insight

**Never assume a specific number format!**<span style="white-space: pre-wrap;"> Hard-coding formats like </span>`<span class="editor-theme-code">"1,234.56"</span>`<span style="white-space: pre-wrap;"> will confuse or alienate users from other regions. Always use locale-aware formatting functions.</span>

## 🔤 Key Formatting Elements

### Decimal Separator

The character that separates the integer part from the fractional part.

- **Period (.)**<span style="white-space: pre-wrap;"> — US, UK, China, Japan</span>
- **Comma (,)**<span style="white-space: pre-wrap;"> — Most of Europe, Latin America</span>
- **Arabic decimal (٫)**<span style="white-space: pre-wrap;"> — Middle East (Arabic)</span>

### Group Separator

The character that groups digits for readability (typically thousands).

- **Comma (,)**<span style="white-space: pre-wrap;"> — US, UK, China, Japan</span>
- **Period (.)**<span style="white-space: pre-wrap;"> — Germany, Italy, Netherlands</span>
- **Space ( )**<span style="white-space: pre-wrap;"> — France, Sweden, Czech</span>
- **Apostrophe (')**<span style="white-space: pre-wrap;"> — Switzerland</span>

### Grouping Size

How many digits appear in each group.

- **3 digits**<span style="white-space: pre-wrap;"> — Most of the world: 1,234,567</span>
- **Indian system**<span style="white-space: pre-wrap;"> — After first 3, then 2: 12,34,567</span>
- **Chinese**<span style="white-space: pre-wrap;"> — Groups of 4: 1234,5678</span>

### Digit Symbols

Not all locales use Western Arabic numerals (0-9).

- **Western:**<span style="white-space: pre-wrap;"> 0 1 2 3 4 5 6 7 8 9</span>
- **Arabic-Indic:**<span style="white-space: pre-wrap;"> ٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩</span>
- **Devanagari:**<span style="white-space: pre-wrap;"> ० १ २ ३ ४ ५ ६ ७ ८ ९</span>

## 💻 Implementation Guidelines

### Number Formatting

#### JavaScript Example

```
const number = 1234567.89;

// US English
const usFormatter = new Intl.NumberFormat('en-US');
console.log(usFormatter.format(number));
// → "1,234,567.89"

// German
const deFormatter = new Intl.NumberFormat('de-DE');
console.log(deFormatter.format(number));
// → "1.234.567,89"

// French
const frFormatter = new Intl.NumberFormat('fr-FR');
console.log(frFormatter.format(number));
// → "1 234 567,89"

// Arabic (Saudi Arabia) - with Arabic-Indic digits
const arFormatter = new Intl.NumberFormat('ar-SA');
console.log(arFormatter.format(number));
// → "١٬٢٣٤٬٥٦٧٫٨٩"

// With options: 2 decimal places, minimum integer digits
const formatted = new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
  minimumIntegerDigits: 1
}).format(42);
console.log(formatted);  // → "42.00"
```

#### Python Example

```
import locale
from babel.numbers import format_decimal, format_currency

number = 1234567.89

# Using locale module (system-dependent)
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
formatted = locale.format_string('%.2f', number, grouping=True)
print(formatted)  # → "1,234,567.89"

# Better: Using Babel (recommended)
# US English
us_format = format_decimal(number, locale='en_US')
print(us_format)  # → "1,234,567.89"

# German
de_format = format_decimal(number, locale='de_DE')
print(de_format)  # → "1.234.567,89"

# French
fr_format = format_decimal(number, locale='fr_FR')
print(fr_format)  # → "1 234 567,89"

# With custom decimal places
precise = format_decimal(42, locale='en_US', decimal_quantization=False)
print(f"{precise:.2f}")  # → "42.00"
```

#### Java Example

```
import java.text.NumberFormat;
import java.util.Locale;

double number = 1234567.89;

// US English
NumberFormat usFormat = NumberFormat.getInstance(Locale.US);
System.out.println(usFormat.format(number));
// → "1,234,567.89"

// German
NumberFormat deFormat = NumberFormat.getInstance(Locale.GERMANY);
System.out.println(deFormat.format(number));
// → "1.234.567,89"

// French
NumberFormat frFormat = NumberFormat.getInstance(Locale.FRANCE);
System.out.println(frFormat.format(number));
// → "1 234 567,89"

// With custom decimal places
NumberFormat customFormat = NumberFormat.getInstance(Locale.US);
customFormat.setMinimumFractionDigits(2);
customFormat.setMaximumFractionDigits(2);
System.out.println(customFormat.format(42));
// → "42.00"
```

## 💵 Currency Formatting

Currency formatting is more complex than simple numbers. You must consider symbol placement, spacing, negative formatting, and currency-specific decimal places.

### Currency Format Variations

<table id="bkmrk-localepositive-amoun" style="width: 100%; border-collapse: collapse;"><colgroup><col></col><col></col><col></col><col></col></colgroup><tbody><tr style="background: rgb(46, 204, 113); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Locale

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Positive Amount

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Negative Amount

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Notes

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**en-US**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">$1,234.56</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">-$1,234.56</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Symbol before, no space

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**de-DE**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">1.234,56 €</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">-1.234,56 €</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Symbol after, with space

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**fr-FR**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">1 234,56 €</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">-1 234,56 €</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Space group separator

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**ja-JP**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">¥1,235</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">-¥1,235</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">No decimal places for JPY

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**ar-SA**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">١٬٢٣٤٫٥٦ ر.س</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">؜-١٬٢٣٤٫٥٦ ر.س</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Arabic digits, RTL

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**en-GB**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">£1,234.56</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">-£1,234.56</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Symbol before

</td></tr></tbody></table>

### Currency Formatting Code Examples

#### JavaScript Example

```
const amount = 1234.56;

// US Dollar
const usdFormatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
});
console.log(usdFormatter.format(amount));
// → "$1,234.56"

// Euro in Germany
const eurDeFormatter = new Intl.NumberFormat('de-DE', {
  style: 'currency',
  currency: 'EUR'
});
console.log(eurDeFormatter.format(amount));
// → "1.234,56 €"

// Japanese Yen (no decimal places)
const jpyFormatter = new Intl.NumberFormat('ja-JP', {
  style: 'currency',
  currency: 'JPY'
});
console.log(jpyFormatter.format(amount));
// → "¥1,235"  (automatically rounded)

// Display currency code instead of symbol
const codeFormatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencyDisplay: 'code'
});
console.log(codeFormatter.format(amount));
// → "USD 1,234.56"

// Accounting format (parentheses for negative)
const accountingFormatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencySign: 'accounting'
});
console.log(accountingFormatter.format(-amount));
// → "($1,234.56)"
```

#### Python Example

```
from babel.numbers import format_currency

amount = 1234.56

# US Dollar
usd_format = format_currency(amount, 'USD', locale='en_US')
print(usd_format)  # → "$1,234.56"

# Euro in Germany
eur_de_format = format_currency(amount, 'EUR', locale='de_DE')
print(eur_de_format)  # → "1.234,56 €"

# Japanese Yen (automatically handles no decimals)
jpy_format = format_currency(amount, 'JPY', locale='ja_JP')
print(jpy_format)  # → "￥1,235"

# British Pound
gbp_format = format_currency(amount, 'GBP', locale='en_GB')
print(gbp_format)  # → "£1,234.56"

# Display currency code
code_format = format_currency(
    amount, 'USD', locale='en_US', 
    format_type='name'
)
print(code_format)  # → "1,234.56 US dollars"
```

#### ✅ Pro Tip: Currency-Specific Decimal Places

**Not all currencies use 2 decimal places!**<span style="white-space: pre-wrap;"> The formatting library automatically handles this:</span>

- **0 decimals:**<span style="white-space: pre-wrap;"> JPY (¥), KRW (₩), VND (₫)</span>
- **2 decimals:**<span style="white-space: pre-wrap;"> USD ($), EUR (€), GBP (£) — Most common</span>
- **3 decimals:**<span style="white-space: pre-wrap;"> BHD (BD), JOD (JD), KWD (KD), OMR (OR), TND (TD)</span>

Always use locale-aware formatting libraries to handle these variations automatically!

## ⚠️ Common Pitfalls &amp; Solutions

#### ❌ Pitfall 1: String Concatenation for Currency

**Wrong:**

```
// Hard-coded format
const price = "$" + amount.toFixed(2);
// Result: "$1234.56"
// Problems: No thousands separator,
// wrong for most countries
```

**Right:**

```
// Locale-aware formatting
const price = new Intl.NumberFormat(
  userLocale, {
    style: 'currency',
    currency: userCurrency
}).format(amount);
```

#### ❌ Pitfall 2: Parsing User Input Without Locale

<span style="white-space: pre-wrap;">When users enter </span>`<span class="editor-theme-code">"1.234,56"</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">parseFloat()</span>`<span style="white-space: pre-wrap;"> will fail or give wrong results. You need locale-aware parsing.</span>

```
// JavaScript: Use a library like numbro or parser
import { parse } from 'numbro';

const germanInput = "1.234,56";
const parsed = parse(germanInput, { locale: 'de-DE' });
console.log(parsed);  // → 1234.56

// Python: Use locale or babel
from babel.numbers import parse_decimal

german_input = "1.234,56"
parsed = parse_decimal(german_input, locale='de_DE')
print(parsed)  # → Decimal('1234.56')
```

#### ❌ Pitfall 3: Storing Formatted Strings in Database

**Never store formatted numbers or currency in your database!**

**Store:**<span style="white-space: pre-wrap;"> Raw numeric values (preferably as integers for currency)</span>

```
// Store cents/pence to avoid floating-point issues
const priceInCents = 123456;  // $1,234.56
// Or use Decimal type in your database
```

**Format:**<span style="white-space: pre-wrap;"> Only when displaying to users</span>

```
const displayPrice = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
}).format(priceInCents / 100);
```

## 🎯 Best Practices Checklist

<table id="bkmrk-practicepriority%E2%9C%85-us" style="width: 100%; border-collapse: collapse;"><colgroup><col style="width: 60%;"></col><col></col></colgroup><tbody><tr style="background: rgb(46, 204, 113); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221); width: 60%;">Practice

</th><th class="align-center" style="padding: 0.75rem; text-align: center; border: 1px solid rgb(221, 221, 221);">Priority

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Use locale-aware formatting libraries (Intl, Babel, ICU)

</td><td class="align-center" style="background: rgb(231, 76, 60); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**CRITICAL**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Store raw numeric values, format only for display

</td><td class="align-center" style="background: rgb(231, 76, 60); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**CRITICAL**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Use integers for currency (cents/pence) to avoid float errors

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Always specify currency code (USD, EUR) when formatting

</td><td class="align-center" style="background: rgb(231, 76, 60); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**CRITICAL**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Use locale-aware parsing for user input

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Test with multiple locales (US, Germany, Japan, Arabic)

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Handle right-to-left (RTL) for Arabic/Hebrew currencies

</td><td class="align-center" style="background: rgb(52, 152, 219); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**MEDIUM**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Provide clear error messages for invalid number input

</td><td class="align-center" style="background: rgb(52, 152, 219); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**MEDIUM**

</td></tr></tbody></table>

## 📚 Additional Resources

- **Unicode CLDR:**<span style="white-space: pre-wrap;"> Common Locale Data Repository for number patterns</span>
- **ISO 4217:**<span style="white-space: pre-wrap;"> Currency code standard</span>
- **Intl.NumberFormat (JavaScript):**<span style="white-space: pre-wrap;"> MDN documentation</span>
- **Babel (Python):**<span style="white-space: pre-wrap;"> Comprehensive i18n library</span>
- **ICU (C/C++/Java):**<span style="white-space: pre-wrap;"> International Components for Unicode</span>

**Next Topic:**<span style="white-space: pre-wrap;"> Date &amp; Time Formatting →</span>

# 📅 | Date & Time Formatting

## 🎯 Learning Objectives

- Understand date and time format variations across cultures
- Master date/time pattern strings and placeholders
- Handle 12-hour vs 24-hour time formats
- Format dates correctly for user's locale
- Avoid ambiguous date representations

## 🌍 The Date Format Challenge

<span style="white-space: pre-wrap;">The date </span>**"03/04/05"**<span style="white-space: pre-wrap;"> could mean:</span>

- 🇺🇸 March 4, 2005 (US: MM/DD/YY)
- 🇬🇧 April 3, 2005 (UK: DD/MM/YY)
- 🇯🇵 May 3, 2004 (Japan: YY/MM/DD)
- And many more interpretations!

#### ⚠️ Critical Insight

**There is no universal date format that works everywhere.**<span style="white-space: pre-wrap;"> A date written as </span>`<span class="editor-theme-code">12/01/2025</span>`<span style="white-space: pre-wrap;"> is ambiguous and will confuse international users. Always format dates according to the user's locale or use an unambiguous format like ISO 8601.</span>

### Common Date Format Patterns

<table id="bkmrk-region%2Flocaleshort-f" style="width: 100%; border-collapse: collapse;"><colgroup><col></col><col></col><col></col><col></col></colgroup><tbody><tr style="background: rgb(231, 76, 60); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Region/Locale

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Short Format

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Long Format

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Pattern

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**US (en-US)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">11/5/2025</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">November 5, 2025

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">M/D/YYYY

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**UK (en-GB)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">05/11/2025</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">5 November 2025

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">D/M/YYYY

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Japan (ja-JP)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">2025/11/05</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">2025年11月5日

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">YYYY/M/D

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Germany (de-DE)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">05.11.2025</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">5\. November 2025

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">D.M.YYYY

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**China (zh-CN)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">2025/11/5</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">2025年11月5日

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">YYYY/M/D

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Korea (ko-KR)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">2025. 11. 5.</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">2025년 11월 5일

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">YYYY. M. D.

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**ISO 8601**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">2025-11-05</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">2025-11-05

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">YYYY-MM-DD

</td></tr></tbody></table>

## 🕐 Time Format Variations

<span style="white-space: pre-wrap;">Time formatting varies primarily between </span>**12-hour (with AM/PM)**<span style="white-space: pre-wrap;"> and </span>**24-hour**<span style="white-space: pre-wrap;"> formats, but there are also differences in separators and period markers.</span>

### Time Format Examples (2:30 PM)

<table id="bkmrk-region%2Flocaleshort-t" style="width: 100%; border-collapse: collapse;"><colgroup><col></col><col></col><col></col><col></col></colgroup><tbody><tr style="background: rgb(231, 76, 60); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Region/Locale

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Short Time

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Long Time

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Format Type

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**US (en-US)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">2:30 PM</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">2:30:00 PM</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">12-hour

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**UK (en-GB)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">14:30</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">14:30:00</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">24-hour

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Germany (de-DE)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">14:30</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">14:30:00</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">24-hour

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Japan (ja-JP)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">14:30</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">14時30分00秒</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">24-hour

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**India (hi-IN)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">2:30 pm</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">2:30:00 pm</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">12-hour

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**France (fr-FR)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">14:30</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">14:30:00</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">24-hour

</td></tr></tbody></table>

#### 12-Hour Format Regions

- United States
- Canada (English)
- Australia
- Philippines
- India
- Pakistan

#### 24-Hour Format Regions

- Most of Europe
- Latin America
- Asia (China, Japan, Korea)
- Middle East
- Africa

## 💻 Implementation Guidelines

### JavaScript Examples

#### Date Formatting

```
const date = new Date('2025-11-05T14:30:00Z');

// US Format - Short
const usShort = new Intl.DateTimeFormat('en-US').format(date);
console.log(usShort);
// → "11/5/2025"

// US Format - Long
const usLong = new Intl.DateTimeFormat('en-US', {
  year: 'numeric',
  month: 'long',
  day: 'numeric'
}).format(date);
console.log(usLong);
// → "November 5, 2025"

// UK Format
const ukFormat = new Intl.DateTimeFormat('en-GB', {
  year: 'numeric',
  month: 'short',
  day: 'numeric'
}).format(date);
console.log(ukFormat);
// → "5 Nov 2025"

// German Format
const deFormat = new Intl.DateTimeFormat('de-DE', {
  year: 'numeric',
  month: 'long',
  day: 'numeric'
}).format(date);
console.log(deFormat);
// → "5. November 2025"

// Japanese Format
const jpFormat = new Intl.DateTimeFormat('ja-JP', {
  year: 'numeric',
  month: 'long',
  day: 'numeric'
}).format(date);
console.log(jpFormat);
// → "2025年11月5日"

// ISO 8601 (unambiguous, good for APIs)
console.log(date.toISOString());
// → "2025-11-05T14:30:00.000Z"
```

#### Time Formatting

```
const date = new Date('2025-11-05T14:30:00Z');

// US - 12-hour format with AM/PM
const usTime = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: '2-digit',
  timeZoneName: 'short',
  timeZone: 'America/New_York'
}).format(date);
console.log(usTime);
// → "9:30 AM EST"

// UK - 24-hour format
const ukTime = new Intl.DateTimeFormat('en-GB', {
  hour: '2-digit',
  minute: '2-digit',
  timeZone: 'Europe/London'
}).format(date);
console.log(ukTime);
// → "14:30"

// With seconds
const timeWithSeconds = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: '2-digit',
  second: '2-digit',
  hour12: true
}).format(date);
console.log(timeWithSeconds);
// → "9:30:00 AM"

// Force 24-hour format
const force24h = new Intl.DateTimeFormat('en-US', {
  hour: '2-digit',
  minute: '2-digit',
  hour12: false
}).format(date);
console.log(force24h);
// → "09:30"
```

#### Combined Date &amp; Time Formatting

```
const date = new Date('2025-11-05T14:30:00Z');

// Full date and time - US
const usFull = new Intl.DateTimeFormat('en-US', {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  hour: 'numeric',
  minute: '2-digit',
  timeZoneName: 'short',
  timeZone: 'America/Los_Angeles'
}).format(date);
console.log(usFull);
// → "November 5, 2025 at 6:30 AM PST"

// Full date and time - German
const deFull = new Intl.DateTimeFormat('de-DE', {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  hour: '2-digit',
  minute: '2-digit',
  timeZone: 'Europe/Berlin'
}).format(date);
console.log(deFull);
// → "5. November 2025, 15:30"

// Relative time formatting (modern browsers)
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
console.log(rtf.format(-1, 'day'));    // → "yesterday"
console.log(rtf.format(2, 'week'));    // → "in 2 weeks"
console.log(rtf.format(-3, 'month'));  // → "3 months ago"
```

### Python Examples

#### Using Babel for Date/Time Formatting

```
from datetime import datetime
from babel.dates import format_date, format_time, format_datetime
import pytz

dt = datetime(2025, 11, 5, 14, 30, 0, tzinfo=pytz.UTC)

# Date formatting
us_date = format_date(dt, format='long', locale='en_US')
print(us_date)  # → "November 5, 2025"

uk_date = format_date(dt, format='long', locale='en_GB')
print(uk_date)  # → "5 November 2025"

de_date = format_date(dt, format='long', locale='de_DE')
print(de_date)  # → "5. November 2025"

jp_date = format_date(dt, format='long', locale='ja_JP')
print(jp_date)  # → "2025年11月5日"

# Time formatting
us_time = format_time(dt, format='short', locale='en_US')
print(us_time)  # → "2:30 PM"

uk_time = format_time(dt, format='short', locale='en_GB')
print(uk_time)  # → "14:30"

# Combined date and time
us_full = format_datetime(dt, format='full', locale='en_US')
print(us_full)
# → "Wednesday, November 5, 2025 at 2:30:00 PM GMT"

# Custom format patterns
custom = format_datetime(dt, "EEE, MMM d, yyyy 'at' h:mm a", locale='en_US')
print(custom)  # → "Wed, Nov 5, 2025 at 2:30 PM"
```

### Java Examples

#### Using java.time for Date/Time Formatting

```
import java.time.*;
import java.time.format.*;
import java.util.Locale;

ZonedDateTime dt = ZonedDateTime.of(
    2025, 11, 5, 14, 30, 0, 0,
    ZoneId.of("UTC")
);

// US Format
DateTimeFormatter usFormatter = DateTimeFormatter.ofLocalizedDate(
    FormatStyle.LONG
).withLocale(Locale.US);
System.out.println(dt.format(usFormatter));
// → "November 5, 2025"

// German Format
DateTimeFormatter deFormatter = DateTimeFormatter.ofLocalizedDate(
    FormatStyle.LONG
).withLocale(Locale.GERMANY);
System.out.println(dt.format(deFormatter));
// → "5. November 2025"

// Time with US 12-hour format
DateTimeFormatter usTimeFormatter = DateTimeFormatter.ofLocalizedTime(
    FormatStyle.SHORT
).withLocale(Locale.US);
System.out.println(dt.format(usTimeFormatter));
// → "2:30 PM"

// Custom pattern
DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern(
    "EEE, MMM d, yyyy 'at' h:mm a",
    Locale.US
);
System.out.println(dt.format(customFormatter));
// → "Wed, Nov 5, 2025 at 2:30 PM"
```

## 📝 Common Date/Time Format Patterns

When you need custom formats, most libraries use similar pattern strings based on Unicode LDML.

### Pattern Reference

<table id="bkmrk-symbolmeaningexample" style="width: 100%; border-collapse: collapse;"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr style="background: rgb(231, 76, 60); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Symbol

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Meaning

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Example

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">y / yyyy</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Year

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">2025

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">M / MM</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Month (numeric)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">11 / 11

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">MMM / MMMM</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Month (text)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Nov / November

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">d / dd</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Day of month

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">5 / 05

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">E / EEEE</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Day of week

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Wed / Wednesday

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">h / hh</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Hour (12-hour)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">2 / 02

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">H / HH</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Hour (24-hour)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">14 / 14

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">m / mm</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Minute

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">30 / 30

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">s / ss</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Second

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">0 / 00

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">a</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">AM/PM marker

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">PM

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">z / zzzz</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Time zone

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">PST / Pacific Standard Time

</td></tr></tbody></table>

**Example:**<span style="white-space: pre-wrap;"> </span>`<span class="editor-theme-code">"MMM d, yyyy 'at' h:mm a"</span>`<span style="white-space: pre-wrap;"> → </span>`<span class="editor-theme-code">"Nov 5, 2025 at 2:30 PM"</span>`

## ⚠️ Common Pitfalls &amp; Solutions

#### ❌ Pitfall 1: Ambiguous Numeric Dates

**Problem:**<span style="white-space: pre-wrap;"> Dates like </span>`<span class="editor-theme-code">03/04/2025</span>`<span style="white-space: pre-wrap;"> are ambiguous.</span>

**✅ Solution:**<span style="white-space: pre-wrap;"> Use long format with month names (</span>`<span class="editor-theme-code">"March 4, 2025"</span>`<span style="white-space: pre-wrap;"> or </span>`<span class="editor-theme-code">"4 March 2025"</span>`) or ISO 8601 (`<span class="editor-theme-code">2025-03-04</span>`) for unambiguous display.

#### ❌ Pitfall 2: Hard-coded Date Formats

```
// ❌ Wrong: Hard-coded format
const dateStr = `${month}/${day}/${year}`;  // US-only!

// ✅ Right: Locale-aware formatting
const dateStr = new Intl.DateTimeFormat(userLocale, {
  year: 'numeric',
  month: 'numeric',
  day: 'numeric'
}).format(date);
```

#### ❌ Pitfall 3: Ignoring User's Time Zone

**Problem:**<span style="white-space: pre-wrap;"> Displaying </span>`<span class="editor-theme-code">"Meeting at 3:00 PM"</span>`<span style="white-space: pre-wrap;"> without specifying the time zone confuses remote users.</span>

**✅ Solution:**<span style="white-space: pre-wrap;"> Always include the time zone name when displaying times: </span>`<span class="editor-theme-code">"3:00 PM EST"</span>`<span style="white-space: pre-wrap;"> or convert to user's local time.</span>

## 🎯 Best Practices Checklist

<table id="bkmrk-practicepriority%E2%9C%85-us" style="width: 100%; border-collapse: collapse;"><colgroup><col style="width: 60%;"></col><col></col></colgroup><tbody><tr style="background: rgb(231, 76, 60); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221); width: 60%;">Practice

</th><th class="align-center" style="padding: 0.75rem; text-align: center; border: 1px solid rgb(221, 221, 221);">Priority

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Use locale-aware date/time formatting libraries

</td><td class="align-center" style="background: rgb(231, 76, 60); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**CRITICAL**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Store dates in UTC (see Time Zones topic)

</td><td class="align-center" style="background: rgb(231, 76, 60); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**CRITICAL**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Use month names or ISO 8601 to avoid ambiguity

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Always display time zone when showing times

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Respect user's 12-hour vs 24-hour preference

</td><td class="align-center" style="background: rgb(52, 152, 219); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**MEDIUM**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Test with multiple locales (US, UK, German, Japanese)

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Use relative times where appropriate ("2 hours ago")

</td><td class="align-center" style="background: rgb(52, 152, 219); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**MEDIUM**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Provide date pickers that respect locale formats

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr></tbody></table>

## 📚 Additional Resources

- **ISO 8601:**<span style="white-space: pre-wrap;"> International date/time standard</span>
- **Unicode LDML:**<span style="white-space: pre-wrap;"> Locale Data Markup Language for date patterns</span>
- **Intl.DateTimeFormat (JavaScript):**<span style="white-space: pre-wrap;"> MDN documentation</span>
- **Babel (Python):**<span style="white-space: pre-wrap;"> Date and time formatting</span>
- **java.time (Java):**<span style="white-space: pre-wrap;"> Modern date-time API</span>
- **Moment.js / Luxon:**<span style="white-space: pre-wrap;"> Popular JavaScript date libraries</span>

**Next Topic:**<span style="white-space: pre-wrap;"> Language &amp; Region Identifiers →</span>

# 🏷️ | Language & Region Identifiers

## 🎯 Learning Objectives

- Understand locale identifiers and their structure
- Master BCP 47 language tags and IETF standards
- Distinguish between language, region, and script
- Implement proper locale detection and fallback
- Handle special cases and edge scenarios

## 📖 What is a Locale?

<span style="white-space: pre-wrap;">A </span>**locale**<span style="white-space: pre-wrap;"> is a set of parameters that defines the user's language, region, and cultural preferences. It determines how your application formats numbers, dates, currency, and displays text.</span>

### Locale Components

#### Language

The primary language being used (e.g., English, Spanish, Japanese)

`<span class="editor-theme-code">en, es, ja, zh</span>`

#### Region/Territory

The country or region affecting formats and conventions

`<span class="editor-theme-code">US, GB, CN, BR</span>`

#### Script (Optional)

The writing system used for the language

`<span class="editor-theme-code">Latn, Cyrl, Arab, Hans</span>`

#### Variant (Rare)

Specific dialectal or orthographic variations

`<span class="editor-theme-code">valencia, pinyin</span>`

## 🔤 BCP 47 Language Tags

**BCP 47**<span style="white-space: pre-wrap;"> (Best Current Practice 47) is the IETF standard for language tags. It defines how to construct identifiers that specify language, region, script, and variants.</span>

### BCP 47 Tag Structure

**language**-**Script**-**REGION**-**variant**

<span style="white-space: pre-wrap;">Example: </span>`<span class="editor-theme-code">zh-Hans-CN</span>`<span style="white-space: pre-wrap;"> = Chinese (Simplified script) as used in China</span>

<table id="bkmrk-componentformatstand" style="width: 100%; border-collapse: collapse; margin-top: 1rem;"><colgroup><col></col><col></col><col></col><col></col></colgroup><tbody><tr style="background: rgb(243, 156, 18); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Component

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Format

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Standard

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Example

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Language**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">2-3 lowercase letters

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">ISO 639

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">en, es, zh, ar</span>`

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Script**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">4 letters, title case

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">ISO 15924

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">Latn, Cyrl, Arab, Hans</span>`

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Region**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">2 uppercase letters or 3 digits

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">ISO 3166-1

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">US, GB, CN, 001</span>`

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Variant**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">5-8 alphanumeric

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">IANA registry

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">valencia, posix</span>`

</td></tr></tbody></table>

### Common BCP 47 Examples

<table id="bkmrk-bcp-47-tagdescriptio" style="width: 100%; border-collapse: collapse;"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr style="background: rgb(248, 249, 250);"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">BCP 47 Tag

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Description

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Use Case

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">en-US</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">English (United States)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">MM/DD/YYYY, $ before amount

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">en-GB</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">English (United Kingdom)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">DD/MM/YYYY, £ before amount

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">es-ES</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Spanish (Spain)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Uses € and European Spanish

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">es-MX</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Spanish (Mexico)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Uses $ and Mexican Spanish

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">zh-Hans-CN</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Chinese (Simplified, China)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Simplified characters, Mainland

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">zh-Hant-TW</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Chinese (Traditional, Taiwan)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Traditional characters, Taiwan

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">ar-SA</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Arabic (Saudi Arabia)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">RTL, Arabic numerals, Saudi riyal

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">pt-BR</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Portuguese (Brazil)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Brazilian Portuguese, R$ currency

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">pt-PT</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Portuguese (Portugal)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">European Portuguese, € currency

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">fr-CA</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">French (Canada)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Canadian French, $ currency

</td></tr></tbody></table>

#### ✅ Why Both Language AND Region Matter

**Same language, different formats:**

- `<span class="editor-theme-code">en-US</span>`<span style="white-space: pre-wrap;"> vs </span>`<span class="editor-theme-code">en-GB</span>`: "color" vs "colour", $ vs £, MM/DD vs DD/MM
- `<span class="editor-theme-code">es-ES</span>`<span style="white-space: pre-wrap;"> vs </span>`<span class="editor-theme-code">es-MX</span>`: € vs $, "ordenador" vs "computadora"
- `<span class="editor-theme-code">fr-FR</span>`<span style="white-space: pre-wrap;"> vs </span>`<span class="editor-theme-code">fr-CA</span>`: € vs $, some vocabulary differences
- `<span class="editor-theme-code">pt-BR</span>`<span style="white-space: pre-wrap;"> vs </span>`<span class="editor-theme-code">pt-PT</span>`: Significant spelling and vocabulary differences

## 🔍 Locale Detection &amp; Fallback

How do you determine a user's locale? There are multiple strategies, and you should use them in order of priority.

### Locale Detection Priority

1. **User Preference (Explicit Setting):**<span style="white-space: pre-wrap;"> Highest priority — user has explicitly selected their locale in settings</span>
2. **URL Parameter:**<span style="white-space: pre-wrap;"> </span>`<span class="editor-theme-code">?lang=en-GB</span>`<span style="white-space: pre-wrap;"> or </span>`<span class="editor-theme-code">/en-gb/page</span>`<span style="white-space: pre-wrap;"> — useful for switching without login</span>
3. **Cookie/Session:**<span style="white-space: pre-wrap;"> Previously saved preference from this device</span>
4. **Browser Accept-Language Header:**<span style="white-space: pre-wrap;"> </span>`<span class="editor-theme-code">Accept-Language: en-US,en;q=0.9,es;q=0.8</span>`
5. **IP Geolocation:**<span style="white-space: pre-wrap;"> Infer from user's location (least reliable, privacy concerns)</span>
6. **Default Fallback:**<span style="white-space: pre-wrap;"> Your application's default locale (usually </span>`<span class="editor-theme-code">en-US</span>`)

### Locale Detection Code Examples

#### JavaScript (Browser)

```
// Get browser's locale preferences
const userLocales = navigator.languages || [navigator.language];
console.log(userLocales);
// → ["en-US", "en", "es"]

// Get primary locale
const primaryLocale = navigator.language;
console.log(primaryLocale);
// → "en-US"

// Detect and use best available locale
function getBestLocale(supportedLocales, userPreferences) {
  // Try exact match first
  for (const userLocale of userPreferences) {
    if (supportedLocales.includes(userLocale)) {
      return userLocale;
    }
  }
  
  // Try language-only match (en-GB → en-US)
  for (const userLocale of userPreferences) {
    const language = userLocale.split('-')[0];
    const match = supportedLocales.find(l => l.startsWith(language));
    if (match) return match;
  }
  
  // Fallback to default
  return supportedLocales[0];
}

const supported = ['en-US', 'es-ES', 'fr-FR', 'de-DE'];
const userPrefs = ['en-GB', 'en', 'es'];
const bestLocale = getBestLocale(supported, userPrefs);
console.log(bestLocale);  // → "en-US" (language match)
```

#### Python (Server-side)

```
from flask import request
from babel import Locale, negotiate_locale

# Supported locales in your application
SUPPORTED_LOCALES = ['en_US', 'es_ES', 'fr_FR', 'de_DE', 'ja_JP']
DEFAULT_LOCALE = 'en_US'

def get_user_locale():
    # 1. Check user's explicit preference (from database/session)
    user_pref = session.get('locale')
    if user_pref and user_pref in SUPPORTED_LOCALES:
        return user_pref
    
    # 2. Check URL parameter
    url_locale = request.args.get('lang')
    if url_locale and url_locale in SUPPORTED_LOCALES:
        return url_locale
    
    # 3. Negotiate from Accept-Language header
    header_locales = request.accept_languages
    best_match = negotiate_locale(
        [str(l) for l in header_locales],
        SUPPORTED_LOCALES,
        sep='_'
    )
    if best_match:
        return best_match
    
    # 4. Fallback to default
    return DEFAULT_LOCALE

# Usage
locale = get_user_locale()
print(f"Using locale: {locale}")
```

#### ⚠️ Locale Fallback Chain

<span style="white-space: pre-wrap;">Always implement a </span>**fallback chain**<span style="white-space: pre-wrap;">. If you don't have </span>`<span class="editor-theme-code">zh-Hant-HK</span>`<span style="white-space: pre-wrap;"> (Traditional Chinese, Hong Kong), try falling back to:</span>

1. `<span class="editor-theme-code">zh-Hant-HK</span>`<span style="white-space: pre-wrap;"> (exact match) → not available</span>
2. `<span class="editor-theme-code">zh-Hant</span>`<span style="white-space: pre-wrap;"> (language + script) → try this</span>
3. `<span class="editor-theme-code">zh</span>`<span style="white-space: pre-wrap;"> (language only) → then this</span>
4. `<span class="editor-theme-code">en</span>`<span style="white-space: pre-wrap;"> (default language) → final fallback</span>

## 💻 Working with Locales in Code

#### JavaScript - Parsing and Validating Locales

```
// Check if locale is valid
function isValidLocale(locale) {
  try {
    Intl.NumberFormat(locale);
    return true;
  } catch (e) {
    return false;
  }
}

console.log(isValidLocale('en-US'));     // → true
console.log(isValidLocale('invalid'));   // → false

// Get canonical locale
const canonical = Intl.getCanonicalLocales('EN-us')[0];
console.log(canonical);  // → "en-US" (normalized)

// Parse locale components
function parseLocale(tag) {
  const parts = tag.split('-');
  return {
    language: parts[0]?.toLowerCase(),
    script: parts[1]?.length === 4 ? parts[1] : undefined,
    region: parts.find(p => p.length === 2)?.toUpperCase(),
  };
}

console.log(parseLocale('zh-Hans-CN'));
// → { language: 'zh', script: 'Hans', region: 'CN' }

// Using Intl.Locale (modern browsers)
const locale = new Intl.Locale('zh-Hans-CN');
console.log(locale.language);   // → "zh"
console.log(locale.script);     // → "Hans"
console.log(locale.region);     // → "CN"
console.log(locale.baseName);   // → "zh-Hans-CN"
```

#### Python - Working with Babel Locales

```
from babel import Locale, UnknownLocaleError

# Parse locale
try:
    locale = Locale.parse('zh_Hans_CN', sep='_')
    print(f"Language: {locale.language}")      # → zh
    print(f"Script: {locale.script}")          # → Hans
    print(f"Territory: {locale.territory}")    # → CN
    print(f"Display name: {locale.display_name}")  # → Chinese (Simplified, China)
except UnknownLocaleError:
    print("Invalid locale")

# Get English name for locale
locale = Locale.parse('fr_CA')
print(locale.get_display_name('en'))  # → "French (Canada)"
print(locale.get_display_name('fr'))  # → "français (Canada)"

# List all available locales
from babel.localedata import list as list_locales
all_locales = list_locales()
print(f"Available locales: {len(all_locales)}")
# → Available locales: 700+

# Locale negotiation
from babel import negotiate_locale

supported = ['en_US', 'es_ES', 'fr_FR']
user_prefs = ['de_DE', 'en_GB', 'en']
best = negotiate_locale(user_prefs, supported, sep='_')
print(best)  # → "en_US" (language fallback from en)
```

## 🌐 Special Cases &amp; Edge Scenarios

#### 🔤 Script Matters for Some Languages

**Chinese**<span style="white-space: pre-wrap;"> has two writing systems:</span>

- `<span class="editor-theme-code">zh-Hans</span>`<span style="white-space: pre-wrap;"> — Simplified Chinese (Mainland China, Singapore)</span>
- `<span class="editor-theme-code">zh-Hant</span>`<span style="white-space: pre-wrap;"> — Traditional Chinese (Taiwan, Hong Kong, Macau)</span>

<span style="white-space: pre-wrap;">Using just </span>`<span class="editor-theme-code">zh</span>`<span style="white-space: pre-wrap;"> is ambiguous and can lead to displaying the wrong script!</span>

#### 🌍 Language Without Region

<span style="white-space: pre-wrap;">Sometimes you have only </span>`<span class="editor-theme-code">en</span>`<span style="white-space: pre-wrap;"> without a region. What should you do?</span>

- **Option 1:**<span style="white-space: pre-wrap;"> Use a sensible default (e.g., </span>`<span class="editor-theme-code">en</span>`<span style="white-space: pre-wrap;"> → </span>`<span class="editor-theme-code">en-US</span>`)
- **Option 2:**<span style="white-space: pre-wrap;"> Use language-only formatting (may not be culturally appropriate)</span>
- **Option 3:**<span style="white-space: pre-wrap;"> Detect region from IP/browser and complete the locale</span>

#### 🔄 Format Separators: Underscore vs Hyphen

Different systems use different separators:

- **BCP 47 / IETF / JavaScript:**<span style="white-space: pre-wrap;"> </span>`<span class="editor-theme-code">en-US</span>`<span style="white-space: pre-wrap;"> (hyphen)</span>
- **POSIX / Python / Java:**<span style="white-space: pre-wrap;"> </span>`<span class="editor-theme-code">en_US</span>`<span style="white-space: pre-wrap;"> (underscore)</span>

<span style="white-space: pre-wrap;">Be prepared to convert between formats: </span>`<span class="editor-theme-code">en-US</span>`<span style="white-space: pre-wrap;"> ↔ </span>`<span class="editor-theme-code">en_US</span>`

#### ⚠️ Don't Use Locale for Authorization

**Never assume**<span style="white-space: pre-wrap;"> that </span>`<span class="editor-theme-code">locale = "de-DE"</span>`<span style="white-space: pre-wrap;"> means the user is in Germany or should see Germany-specific content. Users can set any locale regardless of location. Use separate mechanisms for:</span>

- **Locale:**<span style="white-space: pre-wrap;"> Formatting preferences (how to display data)</span>
- **Location/Region:**<span style="white-space: pre-wrap;"> What content/features to show (geo-restrictions, pricing)</span>

## 🎯 Best Practices Checklist

<table id="bkmrk-practicepriority%E2%9C%85-us" style="width: 100%; border-collapse: collapse;"><colgroup><col style="width: 60%;"></col><col></col></colgroup><tbody><tr style="background: rgb(243, 156, 18); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221); width: 60%;">Practice

</th><th class="align-center" style="padding: 0.75rem; text-align: center; border: 1px solid rgb(221, 221, 221);">Priority

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Use BCP 47 format for language tags (en-US, not en\_US in APIs)

</td><td class="align-center" style="background: rgb(231, 76, 60); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**CRITICAL**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Always include both language AND region (en-US, not just en)

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Implement locale fallback chain (zh-Hant-HK → zh-Hant → zh → en)

</td><td class="align-center" style="background: rgb(231, 76, 60); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**CRITICAL**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Let users explicitly choose their locale (don't just auto-detect)

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Validate locale codes before using them

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Store user's locale preference in profile/session

</td><td class="align-center" style="background: rgb(52, 152, 219); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**MEDIUM**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Use script subtag for Chinese (zh-Hans vs zh-Hant)

</td><td class="align-center" style="background: rgb(231, 76, 60); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**CRITICAL**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Don't confuse locale with user location/authorization

</td><td class="align-center" style="background: rgb(231, 76, 60); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**CRITICAL**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Test locale detection with various browser/header configurations

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr></tbody></table>

## 📚 Additional Resources

- **BCP 47:**<span style="white-space: pre-wrap;"> </span>[RFC 5646 - Tags for Identifying Languages](https://www.rfc-editor.org/rfc/bcp/bcp47.txt)
- **IANA Language Subtag Registry:**<span style="white-space: pre-wrap;"> Official registry of language codes</span>
- **ISO 639:**<span style="white-space: pre-wrap;"> Language codes standard</span>
- **ISO 3166-1:**<span style="white-space: pre-wrap;"> Country/region codes standard</span>
- **ISO 15924:**<span style="white-space: pre-wrap;"> Script codes standard</span>
- **Unicode CLDR:**<span style="white-space: pre-wrap;"> Common Locale Data Repository</span>
- **Intl.Locale (JavaScript):**<span style="white-space: pre-wrap;"> MDN documentation</span>
- **Babel (Python):**<span style="white-space: pre-wrap;"> Locale handling library</span>

**Next Topic:**<span style="white-space: pre-wrap;"> Start Day of the Week →</span>

# 📆 | Start Day of the Week

## 🎯 Learning Objectives

- Understand global variations in week start days
- Handle calendar displays for different cultures
- Implement locale-aware week calculations
- Work with ISO week dates and week numbering
- Handle calendar systems beyond Gregorian

## 🌍 The Week Start Day Challenge

What day does the week start on? The answer depends on where you are in the world! While it might seem like a simple question, different cultures have different conventions, and getting this wrong in a calendar interface can be confusing for users.

### Week Start Day by Region

#### 🇺🇸 Sunday Start

**Countries:**<span style="white-space: pre-wrap;"> United States, Canada, Australia, Philippines, Japan, South Korea, Mexico, Brazil, Israel (partially)</span>

**Standard:**<span style="white-space: pre-wrap;"> Traditional in Americas and parts of Asia</span>

#### 🇬🇧 Monday Start

**Countries:**<span style="white-space: pre-wrap;"> Most of Europe, China, Russia, India, South Africa, most of Africa, Latin America (some)</span>

**Standard:**<span style="white-space: pre-wrap;"> ISO 8601 international standard</span>

#### 🇸🇦 Saturday Start

**Countries:**<span style="white-space: pre-wrap;"> Saudi Arabia, UAE, Egypt, and other Middle Eastern countries</span>

**Standard:**<span style="white-space: pre-wrap;"> Common in Islamic calendar contexts</span>

<table id="bkmrk-localecountryweek-st" style="width: 100%; border-collapse: collapse; margin-top: 1.5rem;"><colgroup><col></col><col></col><col></col><col></col></colgroup><tbody><tr style="background: rgb(155, 89, 182); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Locale

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Country

</th><th class="align-center" style="padding: 0.75rem; text-align: center; border: 1px solid rgb(221, 221, 221);">Week Starts

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Weekend Days

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">en-US</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">United States

</td><td class="align-center" style="background: rgb(232, 245, 233); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center;">**Sunday**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Saturday, Sunday

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">en-GB</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">United Kingdom

</td><td class="align-center" style="background: rgb(227, 242, 253); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center;">**Monday**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Saturday, Sunday

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">de-DE</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Germany

</td><td class="align-center" style="background: rgb(227, 242, 253); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center;">**Monday**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Saturday, Sunday

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">ar-SA</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Saudi Arabia

</td><td class="align-center" style="background: rgb(255, 243, 224); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center;">**Saturday**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Friday, Saturday

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">he-IL</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Israel

</td><td class="align-center" style="background: rgb(232, 245, 233); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center;">**Sunday**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Friday, Saturday

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">ja-JP</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Japan

</td><td class="align-center" style="background: rgb(232, 245, 233); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center;">**Sunday**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Saturday, Sunday

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">zh-CN</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">China

</td><td class="align-center" style="background: rgb(227, 242, 253); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center;">**Monday**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Saturday, Sunday

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">`<span class="editor-theme-code">pt-BR</span>`

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Brazil

</td><td class="align-center" style="background: rgb(232, 245, 233); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center;">**Sunday**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Saturday, Sunday

</td></tr></tbody></table>

#### ✅ ISO 8601 Standard

<span style="white-space: pre-wrap;">The international standard </span>**ISO 8601**<span style="white-space: pre-wrap;"> defines Monday as the first day of the week. However, many countries (particularly in the Americas and parts of Asia) traditionally use Sunday. Your application should respect the user's locale preference, not enforce a standard.</span>

## 💻 Implementation Guidelines

### Detecting Week Start Day

#### JavaScript Example

```
// Using Intl.Locale (modern browsers, Node.js 12+)
const locale = new Intl.Locale('en-US');
const weekInfo = locale.weekInfo || locale.getWeekInfo?.();

console.log(weekInfo?.firstDay);  // → 7 (Sunday, 1=Monday, 7=Sunday)

// Different locales
const locales = ['en-US', 'en-GB', 'de-DE', 'ar-SA'];
locales.forEach(loc => {
  const l = new Intl.Locale(loc);
  const info = l.weekInfo || l.getWeekInfo?.();
  console.log(`${loc}: Week starts on day ${info?.firstDay}`);
});
// → en-US: Week starts on day 7 (Sunday)
// → en-GB: Week starts on day 1 (Monday)
// → de-DE: Week starts on day 1 (Monday)
// → ar-SA: Week starts on day 6 (Saturday)

// Fallback for older browsers (manual mapping)
const weekStartByLocale = {
  'en-US': 0,  // Sunday
  'en-GB': 1,  // Monday
  'de-DE': 1,
  'fr-FR': 1,
  'ar-SA': 6,  // Saturday
  'he-IL': 0,  // Sunday
  'ja-JP': 0,
  'zh-CN': 1
};

function getWeekStartDay(locale) {
  // Try modern API first
  try {
    const l = new Intl.Locale(locale);
    const info = l.weekInfo || l.getWeekInfo?.();
    if (info?.firstDay) {
      // Convert 1-7 (Mon-Sun) to 0-6 (Sun-Sat) for JavaScript Date
      return info.firstDay === 7 ? 0 : info.firstDay;
    }
  } catch (e) {}
  
  // Fallback to lookup table
  return weekStartByLocale[locale] ?? 0;  // Default to Sunday
}

console.log(getWeekStartDay('en-US'));  // → 0 (Sunday)
console.log(getWeekStartDay('en-GB'));  // → 1 (Monday)
```

#### Python Example

```
from babel import Locale
import calendar

# Using Babel
locale = Locale.parse('en_US')
week_start = locale.first_week_day
print(f"Week starts on day: {week_start}")  # → 6 (Sunday in 0-6 where 0=Monday)

# Different locales
locales = ['en_US', 'en_GB', 'de_DE', 'ar_SA']
day_names = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']

for loc_code in locales:
    loc = Locale.parse(loc_code)
    start_day = loc.first_week_day
    print(f"{loc_code}: Week starts on {day_names[start_day]}")

# → en_US: Week starts on Sun
# → en_GB: Week starts on Mon
# → de_DE: Week starts on Mon
# → ar_SA: Week starts on Sat

# Using Python's calendar module (global setting)
# Set first weekday (0=Monday, 6=Sunday)
calendar.setfirstweekday(calendar.SUNDAY)
print(calendar.firstweekday())  # → 6

# Get month calendar with custom first day
calendar.setfirstweekday(calendar.MONDAY)
cal = calendar.monthcalendar(2025, 11)
print(cal)  # Week starts on Monday
```

### Building Locale-Aware Calendar Displays

#### JavaScript Calendar Grid Example

```
function generateCalendarGrid(year, month, locale) {
  const firstDay = new Date(year, month, 1);
  const lastDay = new Date(year, month + 1, 0);
  
  // Get week start for locale
  const weekStart = getWeekStartDay(locale);
  
  // Get day of week for first day of month
  let firstDayOfWeek = firstDay.getDay();
  
  // Adjust for locale's week start
  firstDayOfWeek = (firstDayOfWeek - weekStart + 7) % 7;
  
  // Build calendar grid
  const grid = [];
  let week = new Array(firstDayOfWeek).fill(null);
  
  for (let day = 1; day <= lastDay.getDate(); day++) {
    week.push(day);
    
    if (week.length === 7) {
      grid.push(week);
      week = [];
    }
  }
  
  // Fill remaining days
  if (week.length > 0) {
    while (week.length < 7) {
      week.push(null);
    }
    grid.push(week);
  }
  
  return grid;
}

// Generate calendar for US (Sunday start)
const usCalendar = generateCalendarGrid(2025, 10, 'en-US');
console.log('US Calendar (November 2025, Sunday start):');
console.log(usCalendar);

// Generate calendar for UK (Monday start)
const ukCalendar = generateCalendarGrid(2025, 10, 'en-GB');
console.log('UK Calendar (November 2025, Monday start):');
console.log(ukCalendar);

// Get localized day names for header
function getWeekdayNames(locale, weekStart) {
  const formatter = new Intl.DateTimeFormat(locale, { weekday: 'short' });
  const names = [];
  
  // Start from locale's first day
  for (let i = 0; i < 7; i++) {
    const day = new Date(2024, 0, weekStart + i); // Jan 2024 starts on Monday
    names.push(formatter.format(day));
  }
  
  return names;
}

console.log('US weekdays:', getWeekdayNames('en-US', 0));
// → ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']

console.log('UK weekdays:', getWeekdayNames('en-GB', 1));
// → ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
```

## 📊 Week Numbering Systems

Different regions also have different conventions for numbering weeks of the year. This affects business reporting, scheduling, and date calculations.

### Week Numbering Systems

<table id="bkmrk-systemdescriptionuse" style="width: 100%; border-collapse: collapse;"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr style="background: rgb(155, 89, 182); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">System

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Description

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Used In

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**ISO 8601**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Week starts Monday. Week 1 contains first Thursday of year.

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Europe, most of world

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**US System**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Week starts Sunday. Week 1 contains January 1st.

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">United States, Canada

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Middle Eastern**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Week starts Saturday. Varies by country.

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Saudi Arabia, UAE, Egypt

</td></tr></tbody></table>

#### JavaScript ISO Week Example

```
// Calculate ISO week number (week starts Monday, week 1 has first Thursday)
function getISOWeek(date) {
  const target = new Date(date.valueOf());
  const dayNum = (date.getDay() + 6) % 7;
  target.setDate(target.getDate() - dayNum + 3);
  const firstThursday = target.valueOf();
  target.setMonth(0, 1);
  if (target.getDay() !== 4) {
    target.setMonth(0, 1 + ((4 - target.getDay()) + 7) % 7);
  }
  return 1 + Math.ceil((firstThursday - target) / 604800000);
}

const date1 = new Date(2025, 0, 1);  // January 1, 2025
console.log(`ISO Week: ${getISOWeek(date1)}`);  // → ISO Week: 1

const date2 = new Date(2025, 10, 5);  // November 5, 2025
console.log(`ISO Week: ${getISOWeek(date2)}`);  // → ISO Week: 45

// Using Intl for week-year formatting (where supported)
const formatter = new Intl.DateTimeFormat('en-GB', {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  weekday: 'long'
});

console.log(formatter.format(date2));
// → "Wednesday, 5 November 2025"
```

## 🌙 Alternative Calendar Systems

While the Gregorian calendar is most widely used, many cultures use alternative calendar systems for religious, cultural, or official purposes.

### Major Calendar Systems

<table id="bkmrk-calendarused-bykey-f" style="width: 100%; border-collapse: collapse;"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr style="background: rgb(155, 89, 182); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Calendar

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Used By

</th><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221);">Key Features

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Gregorian**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Worldwide standard

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Solar, 12 months, year 2025

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Islamic (Hijri)**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Muslim communities

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Lunar, 12 months, year 1447 (2025 CE)

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Hebrew**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Jewish communities

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Lunisolar, year 5786 (2025 CE)

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Chinese**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Chinese, Vietnamese

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Lunisolar, 12-13 months, zodiac years

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Japanese**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Japan (official)

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Era-based, Reiwa 7 (2025 CE)

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Persian**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Iran, Afghanistan

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Solar, year 1404 (2025 CE)

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">**Buddhist**

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Thailand, Sri Lanka

</td><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">Solar, year 2569 (2025 CE)

</td></tr></tbody></table>

#### JavaScript Calendar System Example

```
// Using Intl.DateTimeFormat with different calendars
const date = new Date(2025, 10, 5);  // November 5, 2025

// Gregorian (default)
const gregorian = new Intl.DateTimeFormat('en-US', {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  calendar: 'gregory'
}).format(date);
console.log(`Gregorian: ${gregorian}`);
// → "November 5, 2025"

// Islamic/Hijri calendar
const islamic = new Intl.DateTimeFormat('ar-SA', {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  calendar: 'islamic'
}).format(date);
console.log(`Islamic: ${islamic}`);
// → "جمادى الأولى ٤، ١٤٤٧" (approx)

// Hebrew calendar
const hebrew = new Intl.DateTimeFormat('he-IL', {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  calendar: 'hebrew'
}).format(date);
console.log(`Hebrew: ${hebrew}`);
// → "ד׳ בְּחֶשְׁוָן תשפ״ו" (approx)

// Japanese calendar (with era)
const japanese = new Intl.DateTimeFormat('ja-JP', {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  calendar: 'japanese',
  era: 'long'
}).format(date);
console.log(`Japanese: ${japanese}`);
// → "令和7年11月5日"

// Chinese calendar
const chinese = new Intl.DateTimeFormat('zh-CN', {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  calendar: 'chinese'
}).format(date);
console.log(`Chinese: ${chinese}`);
// → Chinese calendar date
```

#### ⚠️ Important Note on Calendar Systems

**Always store dates in Gregorian calendar internally**<span style="white-space: pre-wrap;"> (as UTC timestamps or ISO 8601 strings). Alternative calendars should only be used for </span>**display purposes**. Converting between calendar systems for storage can lead to data corruption and synchronization issues.

## 🎯 Best Practices Checklist

<table id="bkmrk-practicepriority%E2%9C%85-de" style="width: 100%; border-collapse: collapse;"><colgroup><col style="width: 60%;"></col><col></col></colgroup><tbody><tr style="background: rgb(155, 89, 182); color: white;"><th class="align-left" style="padding: 0.75rem; text-align: left; border: 1px solid rgb(221, 221, 221); width: 60%;">Practice

</th><th class="align-center" style="padding: 0.75rem; text-align: center; border: 1px solid rgb(221, 221, 221);">Priority

</th></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Detect and respect user's locale for week start day

</td><td class="align-center" style="background: rgb(231, 76, 60); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**CRITICAL**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Display calendar grids with correct week start

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Store dates in Gregorian/UTC internally, convert for display only

</td><td class="align-center" style="background: rgb(231, 76, 60); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**CRITICAL**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Allow users to override calendar preferences in settings

</td><td class="align-center" style="background: rgb(52, 152, 219); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**MEDIUM**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Be aware of different weekend days (Fri-Sat vs Sat-Sun)

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Use ISO 8601 for week numbering in international contexts

</td><td class="align-center" style="background: rgb(52, 152, 219); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**MEDIUM**

</td></tr><tr><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Test calendar displays with Sunday, Monday, and Saturday starts

</td><td class="align-center" style="background: rgb(243, 156, 18); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**HIGH**

</td></tr><tr style="background: rgb(248, 249, 250);"><td style="padding: 0.75rem; border: 1px solid rgb(221, 221, 221);">✅ Support alternative calendars for display in relevant locales

</td><td class="align-center" style="background: rgb(52, 152, 219); padding: 0.75rem; border: 1px solid rgb(221, 221, 221); text-align: center; color: white;">**MEDIUM**

</td></tr></tbody></table>

## 📚 Additional Resources

- **ISO 8601:**<span style="white-space: pre-wrap;"> Date and time format standard (includes week numbering)</span>
- **Unicode CLDR:**<span style="white-space: pre-wrap;"> Locale-specific calendar data</span>
- **Intl.Locale.weekInfo:**<span style="white-space: pre-wrap;"> MDN documentation</span>
- **Temporal API (Proposed):**<span style="white-space: pre-wrap;"> Modern JavaScript date/time handling</span>
- **Babel (Python):**<span style="white-space: pre-wrap;"> Calendar and locale support</span>
- **ICU (International Components for Unicode):**<span style="white-space: pre-wrap;"> Comprehensive calendar support</span>

**Next Topic:**<span style="white-space: pre-wrap;"> Conclusion &amp; Resources →</span>