date Date Parameter

Complete Guide to ICU Date Formatting - Locale-Aware Date Display

Overview

The date parameter type formats date values according to locale conventions and supports multiple format styles. It also supports custom format patterns and timezone configuration via zoneId.

Basic Example

Content: "Event on @{eventDate}"

Params:

{
  "paramName": "eventDate",
  "type": "date",
  "style": "medium",
  "zoneId": "America/New_York"
}

Expected Output:

Format Options

The date parameter supports three standard formats plus custom format patterns:

Style Format Example Output
short Date only 2024-06-02
medium Date + Time (minutes) 2024-06-02 15:30
long Date + Time (seconds) 2024-06-02 15:30:00
custom User-defined pattern June 2, 2024

Custom Format Patterns

When style is not specified, you can provide a custom format pattern using these tokens:

Token Description Example
yyyy 4-digit year 2024
MM 2-digit month 06
dd 2-digit day 02
HH 2-digit hour (24h) 15
mm 2-digit minute 30
ss 2-digit second 00
MMMM Full month name June
MMM Abbreviated month name Jun
EEEE Full day of week Sunday
EEE Abbreviated day of week Sun

⚠️ Token Mapping

In the generator code, cccc and ccc are automatically converted to EEEE and EEE respectively for day of week formatting.

JSON Structure

The date parameter is defined in the params array of your translation definition:

Date Parameter Definition

{
  "paramName": "eventDate",
  "type": "date",
  "style": "medium",
  "zoneId": "America/New_York"
}

Available style values: short, medium, long, or any custom format string

Optional zoneId: IANA timezone identifier (e.g., "America/New_York", "Asia/Shanghai")

Timezone Support

The zoneId parameter allows you to format dates in a specific timezone:

Timezone Example

{
  "paramName": "meetingTime",
  "type": "date",
  "style": "long",
  "zoneId": "America/New_York"
}

Common timezone identifiers:

Language/Locale Differences

US English Format

United States date formatting:

European Format

European date formatting uses similar patterns but may vary by country:

Asian Locales

Most Asian locales use the same ISO-like format:

⚠️ Important: Always Use Locale Parameter

Date formatting relies on the locale parameter passed to the formatting function. Always pass the appropriate locale when formatting dates to ensure correct month names, day names, and formatting conventions.

Language Implementation Comparison

Different programming languages handle date formatting with varying levels of ICU support.

Language Style Support Custom Format Timezone Notes
TypeScript Full ICU date formatting via Intl.DateTimeFormat
JavaScript Full ICU date formatting via Intl.DateTimeFormat
Java Uses DateTimeFormatter with locale
Kotlin Uses DateTimeFormatter with locale
Swift Uses DateFormatter with locale
Python Uses datetime with locale via Babel
Go Uses time.Time with LoadLocation
Rust Uses chrono with locale support
Dart Uses DateFormat from intl package

Generated Code Implementation

TypeScript/JavaScript Implementation

The generated code uses Intl.DateTimeFormat for locale-aware date formatting with caching:

function formatDate(value: Date, p: any, locale: string): string {
    const date = value instanceof Date ? value : new Date(value);
    const style = p?.style || "";
    const zoneId = p?.zoneId;
    const cacheKey = locale + "_" + style + "_" + (zoneId || "");

    if (!(_dateFormatCache as any)[cacheKey]) {
        const intlOptions: Intl.DateTimeFormatOptions = {};
        if (zoneId) { intlOptions.timeZone = zoneId; }

        if (style === "short") {
            (_dateFormatCache as any)[cacheKey] = new Intl.DateTimeFormat(locale, {
                year: "numeric", month: "2-digit", day: "2-digit", ...intlOptions
            });
        } else if (style === "medium") {
            (_dateFormatCache as any)[cacheKey] = new Intl.DateTimeFormat(locale, {
                year: "numeric", month: "2-digit", day: "2-digit",
                hour: "2-digit", minute: "2-digit", ...intlOptions
            });
        } else if (style === "long") {
            (_dateFormatCache as any)[cacheKey] = new Intl.DateTimeFormat(locale, {
                year: "numeric", month: "long", day: "numeric",
                hour: "2-digit", minute: "2-digit", second: "2-digit", ...intlOptions
            });
        } else {
            (_dateFormatCache as any)[cacheKey] = null;
        }
    }

    if ((_dateFormatCache as any)[cacheKey]) {
        return (_dateFormatCache as any)[cacheKey].format(date);
    }
    return _formatDateNative(date, p?.format || "yyyy-MM-dd", locale, zoneId);
}

Swift Implementation

The Swift implementation handles style-based formatting and custom format patterns:

static func formatDate(_ value: Any, _ p: I18NParam) -> String {
    var format = ""
    let style = p.style ?? ""
    let zoneId = p.zoneId

    if !style.isEmpty {
        switch style {
        case "short": format = "yyyy-MM-dd"
        case "medium": format = "yyyy-MM-dd HH:mm"
        case "long": format = "yyyy-MM-dd HH:mm:ss"
        default: format = style
        }
    } else {
        format = p.format ?? ""
    }

    if format.isEmpty { format = "yyyy-MM-dd" }
    format = format.replacingOccurrences(of: "cccc", with: "EEEE")
                 .replacingOccurrences(of: "ccc", with: "EEE")

    if let date = value as? Date {
        return getDateFormatter(format, zoneId).string(from: date)
    }
    return "\(value)"
}

Java Implementation

public static String formatDate(Object value, I18NParam p, String locale) {
    String style = p.style != null ? p.style : "";
    String zoneId = p.zoneId;
    String format;

    if (!style.isEmpty()) {
        switch (style) {
            case "short": format = "yyyy-MM-dd"; break;
            case "medium": format = "yyyy-MM-dd HH:mm"; break;
            case "long": format = "yyyy-MM-dd HH:mm:ss"; break;
            default: format = style;
        }
    } else {
        format = p.format != null ? p.format : "";
    }

    if (format.isEmpty()) { format = "yyyy-MM-dd"; }
    format = format.replace("cccc", "EEEE").replace("ccc", "EEE");

    DateTimeFormatter formatter;
    if (zoneId != null && !zoneId.isEmpty()) {
        formatter = DateTimeFormatter.ofPattern(format)
            .withZone(ZoneId.of(zoneId))
            .withLocale(Locale.forLanguageTag(locale));
    } else {
        formatter = DateTimeFormatter.ofPattern(format)
            .withLocale(Locale.forLanguageTag(locale));
    }

    if (value instanceof Date) {
        return formatter.format(((Date) value).toInstant());
    }
    return value.toString();
}

Best Practices

✅ Do: Match Format to Context

Choose the appropriate date format based on the use case:

❌ Don't: Hardcode Date Format

Never hardcode a specific date format like "yyyy-MM-dd" or "MM/dd/yyyy". Always use the style parameter or custom format patterns and let the locale determine the actual display pattern.

✅ Do: Use Timezone for International Contexts

When displaying dates across timezones:

Complete Import Example

{
  "strings": [
    {
      "key": "event.schedule",
      "translations": {
        "en-US": {
          "content": "Event on @{eventDate}",
          "params": [
            {
              "paramName": "eventDate",
              "type": "date",
              "style": "medium",
              "zoneId": "America/New_York"
            }
          ]
        },
        "de-DE": {
          "content": "Veranstaltung am @{eventDate}",
          "params": [
            {
              "paramName": "eventDate",
              "type": "date",
              "style": "medium",
              "zoneId": "Europe/Berlin"
            }
          ]
        },
        "ja-JP": {
          "content": "イベント日程: @{eventDate}",
          "params": [
            {
              "paramName": "eventDate",
              "type": "date",
              "style": "medium",
              "zoneId": "Asia/Tokyo"
            }
          ]
        }
      }
    }
  ]
}