timezone_country 1.2.1
timezone_country: ^1.2.1 copied to clipboard
Convert IANA timezones to ISO 3166-1 country codes and back: country names, flag emoji, alpha-2/alpha-3, Windows timezone IDs. Pure Dart, zero dependencies.
Timezone Country #
Bidirectional mapping between IANA timezone identifiers and ISO 3166-1 country codes (alpha-2 and alpha-3).
Pure Dart. Zero dependencies. O(1) identifier lookups with compile-time const maps.
No platform channel, no plugin, no network call — a plain Dart library, so it behaves the same in a server, a CLI, a Flutter app and a release web build.
Features #
- Timezone → Country: Get the country code for any IANA timezone
- Country → Timezones: Get all timezones for a country
- Alpha-2 & Alpha-3: Full ISO 3166-1 support (
US↔USA) - Country-specific Zones:
Europe/Oslo→'NO',Europe/Copenhagen→'DK' - Legacy Resolution: Deprecated names are resolved on lookup (
US/Eastern→'US',Asia/Calcutta→'IN') - Multi-country Timezones:
Europe/Brussels→['BE', 'LU', 'NL'] - Country Names:
'JP'→'Japan'(from Debian iso-codes) - Flag Emoji:
'JP'→'🇯🇵'(pure Unicode arithmetic, zero data) - Windows Timezones:
'Tokyo Standard Time'↔'Asia/Tokyo'(from CLDR) - Zone Display Names:
'America/Denver'→'Mountain Standard Time'(from CLDR) - Coordinates:
'Asia/Tokyo'→ latitude and longitude, plus a nearest-city search - String Extensions: Fluent API on
Stringfor quick conversions - Validated Types:
CountryCodeandTimezoneIdextension types — parse once, no null checks after - Validation: Check if a timezone or country code is valid
- Enumeration: List all known timezones or country codes
Installation #
dependencies:
timezone_country: ^1.2.0
dart pub add timezone_country
# or for Flutter projects:
flutter pub add timezone_country
Quick Start #
import 'package:timezone_country/timezone_country.dart';
// Timezone → Country
TimezoneConvert.timezoneToCountryCode('Asia/Tokyo'); // 'JP'
TimezoneConvert.timezoneToCountryCode('Asia/Tokyo',
format: CountryCodeFormat.alpha3); // 'JPN'
// Country → Timezones
TimezoneConvert.countryToTimezones('US'); // ['America/New_York', ...]
TimezoneConvert.countryToTimezones('USA'); // same — auto-detects format
// Legacy resolution
TimezoneConvert.resolveTimezone('US/Eastern'); // 'America/New_York'
// Alpha-2 ↔ Alpha-3
TimezoneConvert.alpha2ToAlpha3('JP'); // 'JPN'
TimezoneConvert.alpha3ToAlpha2('JPN'); // 'JP'
// Country name & flag
TimezoneConvert.countryName('JP'); // 'Japan'
TimezoneConvert.countryFlag('JP'); // '🇯🇵'
// Windows timezones
TimezoneConvert.timezoneToWindows('Asia/Tokyo'); // 'Tokyo Standard Time'
TimezoneConvert.windowsToTimezone('Romance Standard Time'); // 'Europe/Paris'
TimezoneConvert.windowsToTimezone('Romance Standard Time',
countryCode: 'BE'); // 'Europe/Brussels'
// Zone display names
TimezoneConvert.timezoneGenericName('America/Denver'); // 'Mountain Time'
TimezoneConvert.timezoneStandardName('America/Denver'); // 'Mountain Standard Time'
TimezoneConvert.timezoneDaylightName('America/Denver'); // 'Mountain Daylight Time'
TimezoneConvert.timezoneCity('Asia/Ho_Chi_Minh'); // 'Ho Chi Minh City'
TimezoneConvert.primaryTimezone('DE'); // 'Europe/Berlin'
// Fixed-offset zones
TimezoneConvert.isKnownTimezone('Etc/UTC'); // true
TimezoneConvert.timezoneFixedOffset('Etc/GMT-1'); // Duration(hours: 1)
// Location
TimezoneConvert.timezoneCoordinates('Asia/Tokyo'); // ≈ (35.65, 139.74)
TimezoneConvert.timezoneComment('America/New_York'); // IANA's description
TimezoneConvert.nearestTimezone(35.68, 139.76); // 'Asia/Tokyo'
// String extensions
'Asia/Tokyo'.toCountryCode; // 'JP'
'US'.toTimezones; // ['America/New_York', ...]
'US/Eastern'.toCanonicalTimezone; // 'America/New_York'
'JP'.toCountryName; // 'Japan'
'JP'.toFlag; // '🇯🇵'
Validated types #
Validate a code once and the accessors stop returning null. Both are
extension types over String — no wrapper object exists at runtime.
final country = CountryCode.tryParse('usa'); // null if not ISO 3166-1
country?.alpha2; // 'US'
country?.alpha3; // 'USA'
country?.name; // 'United States'
country?.flag; // '🇺🇸'
country?.timezones; // [America/New_York, ...]
final zone = TimezoneId.tryParse('US/Eastern'); // null if not a known zone
zone?.id; // 'America/New_York' — canonical
zone?.country.name; // 'United States'
zone?.coordinates; // ≈ (40.71, -74.01)
zone?.comment; // IANA's description
zone?.windowsId; // 'Eastern Standard Time'
Usage with Flutter #
This is a pure Dart package — it works in any Dart project including Flutter.
Dart's DateTime.now().timeZoneName returns abbreviations like 'EST' or 'CET',
not IANA identifiers like 'America/New_York'. To get the IANA timezone ID from the
device, use flutter_timezone:
dependencies:
timezone_country: ^1.2.0
flutter_timezone: ^5.1.0
import 'package:flutter_timezone/flutter_timezone.dart';
import 'package:timezone_country/timezone_country.dart';
// Get the device timezone and convert to country code
final timezone = await FlutterTimezone.getLocalTimezone(); // e.g. 'Asia/Tokyo'
final country = TimezoneConvert.timezoneToCountryCode(timezone); // 'JP'
// Or get the alpha-3 code directly
final country3 = TimezoneConvert.timezoneToCountryCode(
timezone,
format: CountryCodeFormat.alpha3,
); // 'JPN'
// Get all timezones available on the device
final available = await FlutterTimezone.getAvailableTimezones();
for (final tz in available) {
print('$tz → ${TimezoneConvert.timezoneToCountryCode(tz)}');
}
On the web, without a plugin #
Browsers expose the IANA identifier through Intl, so no platform channel is
involved and nothing has to be awaited:
import 'dart:js_interop';
@JS('Intl.DateTimeFormat')
extension type _DateTimeFormat._(JSObject _) implements JSObject {
external factory _DateTimeFormat();
external _ResolvedOptions resolvedOptions();
}
extension type _ResolvedOptions._(JSObject _) implements JSObject {
external String get timeZone;
}
String? localCountryCode() =>
TimezoneConvert.timezoneToCountryCode(_DateTimeFormat().resolvedOptions().timeZone);
Older browsers report a deprecated identifier here — Asia/Calcutta rather
than Asia/Kolkata, Europe/Kiev rather than Europe/Kyiv. Both forms
resolve, so no normalisation step is needed.
Bundle size #
Tree shaking works per data file: an app pays only for the maps it reaches.
Measured with dart run tool/measure_size.dart, as the difference against an
identical program that does not import the package.
| What the app calls | dart2js | dart2wasm | AOT |
|---|---|---|---|
timezoneToCountryCode |
7.7 KB | 11.1 KB | 18.1 KB |
countryName, countryFlag |
6.3 KB | 10.9 KB | 17.6 KB |
timezoneToWindows |
10.3 KB | 15.2 KB | 21.0 KB |
timezoneCoordinates |
13.3 KB | 19.1 KB | 28.1 KB |
| the entire public API | 55.9 KB | 55.1 KB | 93.5 KB |
Gzipped, which is what a browser downloads. Re-run the tool to check these against the current data rather than trusting the table.
API Reference #
TimezoneConvert #
| Method | Description |
|---|---|
timezoneToCountryCode(tz, {format}) |
Primary country for a timezone |
timezoneToCountryCodes(tz, {format}) |
All countries for a timezone |
countryToTimezones(code) |
All timezones for a country (accepts alpha-2 or alpha-3) |
resolveTimezone(tz) |
Resolve legacy name to canonical |
alpha2ToAlpha3(code) |
Convert 'US' → 'USA' |
alpha3ToAlpha2(code) |
Convert 'USA' → 'US' |
countryNumericCode(code) |
Convert 'US' → '840' |
numericToCountryCode(num, {format}) |
Convert '840' → 'US' |
countryName(code) |
English name for a country (accepts alpha-2 or alpha-3) |
countryFlag(code) |
Flag emoji for a country (accepts alpha-2 or alpha-3) |
countryCodeFromFlag(flag, {format}) |
Flag emoji → country code |
timezoneToWindows(tz) |
IANA → Windows timezone identifier |
windowsToTimezone(id, {countryCode}) |
Windows timezone identifier → IANA |
timezoneCity(tz) |
The zone's exemplar city in English |
timezoneGenericName(tz) |
Location-independent name, e.g. 'Mountain Time' |
timezoneStandardName(tz) |
Standard-time name, e.g. 'Mountain Standard Time' |
timezoneDaylightName(tz) |
Daylight-time name, null where the zone has none |
metazone(tz) |
CLDR metazone the zone currently belongs to |
primaryTimezone(code) |
The country's principal zone, null when unranked |
isKnownTimezone(tz) |
Known identifier, Etc/* and aliases included |
timezoneFixedOffset(tz) |
Fixed offset of an Etc/* zone, null otherwise |
windowsToTimezones(id, {countryCode}) |
Every IANA zone CLDR groups under a Windows ID |
windowsZonesVersion |
CLDR's version stamp for the Windows mapping |
windowsZonesIanaVersion |
IANA release that mapping was aligned to |
timezoneCoordinates(tz) |
Latitude and longitude of the zone's city |
timezoneComment(tz) |
IANA's English description of the zone |
nearestTimezone(lat, lon, {countryCode}) |
Zone with the closest city |
isValidTimezone(tz) |
Check if timezone exists (legacy names included) |
isValidCountryCode(code) |
Check if a country code has at least one timezone |
isKnownCountryCode(code) |
Check if a country code is in ISO 3166-1 |
allTimezones |
Sorted list of all timezone IDs |
allCountryCodes |
Sorted list of all alpha-2 codes |
allCountryCodesAlpha3 |
Sorted list of all alpha-3 codes |
ianaVersion |
IANA Time Zone Database version used |
String Extensions #
| Extension | Description |
|---|---|
'Asia/Tokyo'.toCountryCode |
Timezone → alpha-2 |
'Asia/Tokyo'.toCountryCodeAlpha3 |
Timezone → alpha-3 |
'Asia/Tokyo'.toCountryCodes |
Timezone → all alpha-2 codes |
'Asia/Tokyo'.toCountryCodesAlpha3 |
Timezone → all alpha-3 codes |
'JP'.toTimezones |
Country → timezones |
'US/Eastern'.toCanonicalTimezone |
Legacy → canonical |
'US'.toAlpha3 |
Alpha-2 → alpha-3 |
'USA'.toAlpha2 |
Alpha-3 → alpha-2 |
'JP'.toCountryName |
Country code → English name |
'JP'.toFlag |
Country code → flag emoji |
'🇯🇵'.fromFlag |
Flag emoji → country code |
'JP'.toNumericCode |
Country code → ISO 3166-1 numeric |
'392'.fromNumericCode |
ISO 3166-1 numeric → country code |
The String getters cover the country-code mappings only. Windows identifiers
and coordinates are reachable through TimezoneConvert or the validated types.
CountryCode and TimezoneId #
| Member | Description |
|---|---|
CountryCode.tryParse(code) |
Alpha-2 or alpha-3, any case → CountryCode? |
.alpha2 .alpha3 .numeric |
The three ISO 3166-1 forms |
.name .flag |
English name, flag emoji |
.timezones |
List<TimezoneId>, empty if the country has none |
TimezoneId.tryParse(tz) |
Known identifier → TimezoneId?, canonicalised |
.id |
The canonical IANA identifier |
.country .countries |
Primary country, all countries |
.coordinates .comment |
Location, IANA's description |
.windowsId |
Windows timezone identifier, null if CLDR omits the zone |
Both are extension types over String: they cost nothing at runtime and print
as the underlying code, but they are distinct static types. Pass .alpha2 or
.id where a String is expected.
Raw Data Access #
For advanced use cases, raw const maps are available:
TimezoneConvert.timezoneToCountryMap; // Map<String, String>
TimezoneConvert.timezoneToCountriesMap; // Map<String, List<String>>
TimezoneConvert.countryToTimezonesMap; // Map<String, List<String>>
TimezoneConvert.legacyTimezoneAliases; // Map<String, String>
TimezoneConvert.alpha2ToAlpha3Map; // Map<String, String>
TimezoneConvert.alpha3ToAlpha2Map; // Map<String, String>
TimezoneConvert.countryNamesMap; // Map<String, String>
TimezoneConvert.alpha2ToNumericMap; // Map<String, String>
TimezoneConvert.numericToAlpha2Map; // Map<String, String>
TimezoneConvert.timezoneCommentsMap; // Map<String, String>
TimezoneConvert.timezoneCoordinatesMap; // Map<String, (double, double)>
TimezoneConvert.timezoneToWindowsMap; // Map<String, String>
TimezoneConvert.windowsToTimezoneMap; // Map<String, Map<String, String>>
These maps and the lists returned by the API are unmodifiable. Mutating them
throws UnsupportedError; copy first.
Data Sources #
- IANA Time Zone Database (
zone1970.tab,zone.tab,backward) — timezone ↔ country mappings and legacy aliases. CheckTimezoneConvert.ianaVersionfor the exact version. Both zone tables are used:zone1970.tabsupplies the multi-country lists,zone.tabsupplies the country-specific zones thatzone1970.tabmerges away (Europe/Oslo,Europe/Copenhagen, and others). Column 2 of the zone tables supplies the coordinates, column 4 the English descriptions. - ISO 3166-1 via Debian iso-codes — alpha-2 ↔ alpha-3 ↔ numeric country code mappings and English country names
- CLDR
windowsZones.xml— Windows ↔ IANA timezone identifiers. Released on its own schedule, independent of the IANA version, and pinned to a release tag rather than the development branch. The mapping states the IANA release it was itself aligned to; read it fromwindowsZonesIanaVersion, and expect it to trailianaVersionby years. - CLDR
metaZones.xmlandcommon/main/en.xml— English zone display names, exemplar cities, and the primary zone of a country. - CLDR
common/bcp47/timezone.xml— which IANA zone each CLDR spelling stands for.backwardmay only link to a canonical zone, so it sendsAtlantic/Jan_MayentoEurope/BerlinandPacific/YaptoPacific/Port_Moresby; this table names the zone in the right country. - IANA
etcetera— theEtc/*fixed-offset zones. These carry no country, so they are reachable throughisKnownTimezoneandtimezoneFixedOffsetbut stay out ofallTimezones.
All data is embedded as compile-time const maps. No network requests, no file I/O, no runtime parsing.
Notes #
Etc/*zones (Etc/UTC,Etc/GMT) have no country association in the IANA database.isValidTimezone('Etc/UTC')returnsfalseandtimezoneToCountryCode('Etc/UTC')returnsnull. UseisKnownTimezoneto tell an unrecognised string apart from a real zone that simply has no country — devices do report these.timezoneFixedOffsetgives the offset with its true sign, not the inverted one inside the identifier.- The display names are English only, and every one of them is nullable. A
zone with no current metazone has no generic, standard or daylight name; a
zone that never leaves standard time has no daylight name. Nothing is
synthesised to fill a gap, so a
nullmeans CLDR has no answer. Short abbreviations are deliberately not exposed: CLDR supplies them for only a handful of metazones in English, and they are ambiguous across regions. isValidCountryCodemeans "has at least one IANA timezone", which is narrower than ISO 3166-1 membership. UseisKnownCountryCodefor the latter.nearestTimezonecompares against the coordinates IANA gives each zone — the city the identifier is named after, not a polygon. It answers "which of these zones is closest", not "which zone contains this point", and the two disagree often — including inside a single country, socountryCodenarrows the error without removing it. Treat the result as a guess.- Some zones serve several countries because IANA merged them. Use
timezoneToCountryCodesfor the full list, and prefer the country-specific identifier (Europe/OslooverEurope/Berlin) when you know the country. allTimezoneslists one identifier per zone, with deprecated aliases and theEtc/*zones excluded. It matches the primary identifiers ECMA-402 defines, including keeping two zones apart where they serve different countries despite agreeing on the clock (Europe/OsloandEurope/Berlin). Browsers expose the same set throughIntl.supportedValuesOf('timeZone'), but engines are slow to follow IANA renames and still emit the superseded spelling, so the two lists disagree on the names even where they agree on the zones.
Non-goals #
This package maps identifiers. It does not do:
- Time arithmetic, UTC offsets or DST — use
timezone. An embedded offset table would ship stale: offsets move with DST and on political notice. TheEtc/*zones are the exception, being fixed by definition. - Localized country names — English only. Use
common_locale_dataorcountry_codes. - Dial codes — use
country_codes. - Timezone boundary lookup from a coordinate — that needs shapefiles.
nearestTimezoneis a nearest-city approximation, not a substitute. - Wider country data (currencies, capitals, subdivisions) — use
sealed_countries. - Reading the device timezone — needs a platform channel. Use
flutter_timezoneand pass the result in, as shown above.
License #
MIT