Complete Guide to ICU Date Formatting - Locale-Aware Date Display
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.
Content: "Event on @{eventDate}"
Params:
{
"paramName": "eventDate",
"type": "date",
"style": "medium",
"zoneId": "America/New_York"
}
Expected Output:
en-US → "Event on 6/2/2024 3:30 PM"de-DE → "Event on 02.06.2024 15:30"ja-JP → "Event on 2024/06/02 15:30"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 |
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 |
In the generator code, cccc and ccc are automatically converted to EEEE and EEE respectively for day of week formatting.
The date parameter is defined in the params array of your translation 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")
The zoneId parameter allows you to format dates in a specific timezone:
{
"paramName": "meetingTime",
"type": "date",
"style": "long",
"zoneId": "America/New_York"
}
Common timezone identifiers:
America/New_York - US Eastern TimeAmerica/Los_Angeles - US Pacific TimeEurope/London - UK TimeEurope/Paris - Central European TimeAsia/Shanghai - China Standard TimeAsia/Tokyo - Japan Standard TimeUTC - Coordinated Universal TimeUnited States date formatting:
European date formatting uses similar patterns but may vary by country:
Most Asian locales use the same ISO-like format:
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.
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 |
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);
}
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)"
}
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();
}
Choose the appropriate date format based on the use case:
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.
When displaying dates across timezones:
zoneIdmedium or long style when timezone awareness is important{
"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"
}
]
}
}
}
]
}