← Back to Blog

Flutter DateTime Localization: Format Dates and Times for Every Locale

flutterdatetimelocalizationintldate-formattime-format

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:

  1. Use intl package - DateFormat provides locale-aware formatting
  2. Choose appropriate patterns - Use predefined patterns when possible
  3. Handle time zones - Convert to local time before displaying
  4. Test RTL languages - Arabic, Hebrew have special requirements
  5. Create utilities - Build reusable extension methods

With proper DateTime localization, your app will feel native to users regardless of their location or language preference.