Flutter DateTime Localization: Format Dates and Times for Every Locale
Master date and time formatting in Flutter for international audiences. Learn to display dates, times, and relative timestamps that feel native to users in every country.
Why DateTime Localization Matters
Date formats vary dramatically across countries:
| Country | Date Format | Example |
|---|---|---|
| USA | MM/DD/YYYY | 12/07/2025 |
| UK | DD/MM/YYYY | 07/12/2025 |
| Germany | DD.MM.YYYY | 07.12.2025 |
| Japan | YYYY年MM月DD日 | 2025年12月07日 |
| China | YYYY-MM-DD | 2025-12-07 |
Using the wrong format confuses users and can cause critical errors (is 03/04/2025 March 4th or April 3rd?).
Setting Up DateTime Localization
Add Required Dependencies
# pubspec.yaml
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: ^0.19.0
Configure Localization Delegates
import 'package:flutter_localizations/flutter_localizations.dart';
MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
Locale('en', 'US'),
Locale('en', 'GB'),
Locale('de', 'DE'),
Locale('fr', 'FR'),
Locale('ja', 'JP'),
Locale('ar', 'SA'),
],
)
Using DateFormat from intl Package
Basic Date Formatting
import 'package:intl/intl.dart';
final now = DateTime.now();
// Locale-aware formatting
final dateFormat = DateFormat.yMd('en_US');
print(dateFormat.format(now)); // 12/7/2025
final dateFormatDE = DateFormat.yMd('de_DE');
print(dateFormatDE.format(now)); // 7.12.2025
Using Context Locale
class DateDisplay extends StatelessWidget {
final DateTime date;
DateDisplay({required this.date});
@override
Widget build(BuildContext context) {
final locale = Localizations.localeOf(context).toString();
final formatted = DateFormat.yMMMd(locale).format(date);
return Text(formatted);
// en_US: Dec 7, 2025
// de_DE: 7. Dez. 2025
// ja_JP: 2025年12月7日
}
}
DateFormat Patterns Reference
Predefined Patterns
The intl package provides these predefined constructors:
final date = DateTime(2025, 12, 7, 14, 30, 45);
final locale = 'en_US';
DateFormat.d(locale).format(date); // 7
DateFormat.E(locale).format(date); // Sun
DateFormat.EEEE(locale).format(date); // Sunday
DateFormat.LLL(locale).format(date); // Dec
DateFormat.LLLL(locale).format(date); // December
DateFormat.M(locale).format(date); // 12
DateFormat.Md(locale).format(date); // 12/7
DateFormat.MEd(locale).format(date); // Sun, 12/7
DateFormat.MMM(locale).format(date); // Dec
DateFormat.MMMd(locale).format(date); // Dec 7
DateFormat.MMMEd(locale).format(date); // Sun, Dec 7
DateFormat.MMMM(locale).format(date); // December
DateFormat.MMMMd(locale).format(date); // December 7
DateFormat.MMMMEEEEd(locale).format(date); // Sunday, December 7
DateFormat.QQQ(locale).format(date); // Q4
DateFormat.QQQQ(locale).format(date); // 4th quarter
DateFormat.y(locale).format(date); // 2025
DateFormat.yM(locale).format(date); // 12/2025
DateFormat.yMd(locale).format(date); // 12/7/2025
DateFormat.yMEd(locale).format(date); // Sun, 12/7/2025
DateFormat.yMMM(locale).format(date); // Dec 2025
DateFormat.yMMMd(locale).format(date); // Dec 7, 2025
DateFormat.yMMMEd(locale).format(date); // Sun, Dec 7, 2025
DateFormat.yMMMM(locale).format(date); // December 2025
DateFormat.yMMMMd(locale).format(date); // December 7, 2025
DateFormat.yMMMMEEEEd(locale).format(date); // Sunday, December 7, 2025
DateFormat.yQQQ(locale).format(date); // Q4 2025
DateFormat.yQQQQ(locale).format(date); // 4th quarter 2025
Time Patterns
DateFormat.Hm(locale).format(date); // 14:30 (24-hour)
DateFormat.Hms(locale).format(date); // 14:30:45
DateFormat.j(locale).format(date); // 2 PM (locale-appropriate)
DateFormat.jm(locale).format(date); // 2:30 PM
DateFormat.jms(locale).format(date); // 2:30:45 PM
Combining Date and Time
// Combined patterns
DateFormat.yMd(locale).add_jm().format(date);
// 12/7/2025, 2:30 PM
DateFormat.yMMMMEEEEd(locale).add_Hm().format(date);
// Sunday, December 7, 2025, 14:30
Custom Format Patterns
Pattern Symbols
| Symbol | Meaning | Example |
|---|---|---|
| G | Era | AD |
| y | Year | 2025 |
| M | Month in year | 12 or Dec |
| d | Day in month | 7 |
| E | Day of week | Sun |
| H | Hour (0-23) | 14 |
| h | Hour (1-12) | 2 |
| m | Minute | 30 |
| s | Second | 45 |
| a | AM/PM | PM |
| z | Time zone | PST |
Custom Pattern Examples
final date = DateTime(2025, 12, 7, 14, 30, 45);
// Custom patterns
DateFormat('yyyy-MM-dd').format(date); // 2025-12-07
DateFormat('dd/MM/yyyy').format(date); // 07/12/2025
DateFormat('EEEE, MMMM d, y').format(date); // Sunday, December 7, 2025
DateFormat('hh:mm a').format(date); // 02:30 PM
DateFormat("MMMM d 'at' h:mm a").format(date); // December 7 at 2:30 PM
DateTime in ARB Files
Basic DateTime Placeholder
{
"eventDate": "Event on {date}",
"@eventDate": {
"placeholders": {
"date": {
"type": "DateTime",
"format": "yMMMd"
}
}
}
}
Multiple DateTime Formats
{
"scheduleInfo": "From {startDate} to {endDate} at {time}",
"@scheduleInfo": {
"description": "Shows event schedule with dates and time",
"placeholders": {
"startDate": {
"type": "DateTime",
"format": "MMMd",
"example": "2025-12-07"
},
"endDate": {
"type": "DateTime",
"format": "MMMd",
"example": "2025-12-14"
},
"time": {
"type": "DateTime",
"format": "jm",
"example": "2025-12-07T14:30:00"
}
}
}
}
Usage:
final start = DateTime(2025, 12, 7);
final end = DateTime(2025, 12, 14);
final time = DateTime(2025, 12, 7, 14, 30);
Text(AppLocalizations.of(context)!.scheduleInfo(start, end, time));
// "From Dec 7 to Dec 14 at 2:30 PM"
Relative Time Formatting
Simple Relative Time
String getRelativeTime(DateTime dateTime, String locale) {
final now = DateTime.now();
final difference = now.difference(dateTime);
if (difference.inDays > 365) {
return DateFormat.yMMMd(locale).format(dateTime);
} else if (difference.inDays > 30) {
return DateFormat.MMMd(locale).format(dateTime);
} else if (difference.inDays > 7) {
final weeks = (difference.inDays / 7).floor();
return '$weeks weeks ago'; // Localize this!
} else if (difference.inDays > 0) {
return '${difference.inDays} days ago';
} else if (difference.inHours > 0) {
return '${difference.inHours} hours ago';
} else if (difference.inMinutes > 0) {
return '${difference.inMinutes} minutes ago';
} else {
return 'Just now';
}
}
Localized Relative Time with ARB
{
"timeAgo_justNow": "Just now",
"timeAgo_minutes": "{count, plural, =1{1 minute ago} other{{count} minutes ago}}",
"timeAgo_hours": "{count, plural, =1{1 hour ago} other{{count} hours ago}}",
"timeAgo_days": "{count, plural, =1{Yesterday} other{{count} days ago}}",
"timeAgo_weeks": "{count, plural, =1{1 week ago} other{{count} weeks ago}}",
"@timeAgo_minutes": {
"placeholders": {
"count": {"type": "int"}
}
},
"@timeAgo_hours": {
"placeholders": {
"count": {"type": "int"}
}
},
"@timeAgo_days": {
"placeholders": {
"count": {"type": "int"}
}
},
"@timeAgo_weeks": {
"placeholders": {
"count": {"type": "int"}
}
}
}
String getLocalizedRelativeTime(BuildContext context, DateTime dateTime) {
final l10n = AppLocalizations.of(context)!;
final now = DateTime.now();
final difference = now.difference(dateTime);
if (difference.inMinutes < 1) {
return l10n.timeAgo_justNow;
} else if (difference.inHours < 1) {
return l10n.timeAgo_minutes(difference.inMinutes);
} else if (difference.inDays < 1) {
return l10n.timeAgo_hours(difference.inHours);
} else if (difference.inDays < 7) {
return l10n.timeAgo_days(difference.inDays);
} else {
return l10n.timeAgo_weeks((difference.inDays / 7).floor());
}
}
Time Zone Handling
Displaying Local Time
// Convert UTC to local time before formatting
final utcTime = DateTime.parse('2025-12-07T14:30:00Z');
final localTime = utcTime.toLocal();
final formatted = DateFormat.jm().format(localTime);
Showing Time Zone Info
String formatWithTimezone(DateTime dateTime, String locale) {
final formatted = DateFormat.jm(locale).format(dateTime);
final timezone = dateTime.timeZoneName;
return '$formatted ($timezone)';
// "2:30 PM (PST)"
}
User Timezone Preference
class TimezoneService {
String? _userTimezone;
DateTime toUserTimezone(DateTime utcTime) {
if (_userTimezone == null) {
return utcTime.toLocal();
}
// Use timezone package for specific zones
// return tz.TZDateTime.from(utcTime, tz.getLocation(_userTimezone!));
return utcTime.toLocal();
}
}
Date Picker Localization
Material Date Picker
Future<DateTime?> showLocalizedDatePicker(BuildContext context) async {
return showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2020),
lastDate: DateTime(2030),
locale: Localizations.localeOf(context),
);
}
Cupertino Date Picker
void showCupertinoDatePicker(BuildContext context) {
showCupertinoModalPopup(
context: context,
builder: (context) => Container(
height: 300,
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.date,
onDateTimeChanged: (date) {
// Handle date change
},
),
),
);
}
Common DateTime Localization Patterns
Event Scheduling
{
"eventSchedule": "{date} at {time}",
"@eventSchedule": {
"placeholders": {
"date": {
"type": "DateTime",
"format": "yMMMEd"
},
"time": {
"type": "DateTime",
"format": "jm"
}
}
}
}
Date Ranges
{
"dateRange": "{start} - {end}",
"@dateRange": {
"description": "Shows a date range for bookings, events, etc.",
"placeholders": {
"start": {
"type": "DateTime",
"format": "MMMd"
},
"end": {
"type": "DateTime",
"format": "MMMd"
}
}
}
}
Countdown/Duration
{
"countdown_days": "{count, plural, =1{1 day left} other{{count} days left}}",
"countdown_hours": "{count, plural, =1{1 hour left} other{{count} hours left}}",
"@countdown_days": {
"placeholders": {"count": {"type": "int"}}
},
"@countdown_hours": {
"placeholders": {"count": {"type": "int"}}
}
}
RTL Languages and Dates
Arabic Date Handling
Arabic uses Eastern Arabic numerals and different month names:
final date = DateTime(2025, 12, 7);
final arFormat = DateFormat.yMMMd('ar');
print(arFormat.format(date));
// ٧ ديسمبر ٢٠٢٥
Hebrew Date Handling
final date = DateTime(2025, 12, 7);
final heFormat = DateFormat.yMMMd('he');
print(heFormat.format(date));
// 7 בדצמ׳ 2025
Layout Considerations
Widget buildDateRow(BuildContext context, DateTime date) {
final isRtl = Directionality.of(context) == TextDirection.rtl;
final locale = Localizations.localeOf(context).toString();
return Row(
textDirection: isRtl ? TextDirection.rtl : TextDirection.ltr,
children: [
Icon(Icons.calendar_today),
SizedBox(width: 8),
Text(DateFormat.yMMMd(locale).format(date)),
],
);
}
Best Practices
1. Always Use Locale Parameter
// Good
DateFormat.yMd(Localizations.localeOf(context).toString())
// Avoid - uses system default
DateFormat.yMd()
2. Create Reusable Date Utilities
extension DateTimeLocalization on DateTime {
String toLocalizedDate(BuildContext context) {
final locale = Localizations.localeOf(context).toString();
return DateFormat.yMMMd(locale).format(this);
}
String toLocalizedDateTime(BuildContext context) {
final locale = Localizations.localeOf(context).toString();
return DateFormat.yMMMd(locale).add_jm().format(this);
}
String toLocalizedTime(BuildContext context) {
final locale = Localizations.localeOf(context).toString();
return DateFormat.jm(locale).format(this);
}
}
// Usage
Text(appointment.toLocalizedDateTime(context))
3. Test Multiple Locales
void main() {
group('DateTime localization', () {
final testDate = DateTime(2025, 12, 7, 14, 30);
test('formats correctly for US locale', () {
expect(DateFormat.yMd('en_US').format(testDate), '12/7/2025');
});
test('formats correctly for UK locale', () {
expect(DateFormat.yMd('en_GB').format(testDate), '07/12/2025');
});
test('formats correctly for German locale', () {
expect(DateFormat.yMd('de_DE').format(testDate), '7.12.2025');
});
});
}
Simplify DateTime Localization with FlutterLocalisation
Managing date formats across languages gets complex quickly. FlutterLocalisation.com helps you:
- Preview date formats - See how dates appear in each language
- AI-powered translations - Properly handle relative time phrases
- Format validation - Ensure DateTime placeholders are configured correctly
- Visual editing - No more editing raw JSON for date patterns
Stop guessing how dates will appear. Try FlutterLocalisation free and deliver perfectly formatted dates in every language.
Summary
Flutter DateTime localization requires:
- Use intl package - DateFormat provides locale-aware formatting
- Choose appropriate patterns - Use predefined patterns when possible
- Handle time zones - Convert to local time before displaying
- Test RTL languages - Arabic, Hebrew have special requirements
- Create utilities - Build reusable extension methods
With proper DateTime localization, your app will feel native to users regardless of their location or language preference.