npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

country-geo-data

v1.0.0

Published

Country names & ISO codes, calling codes, capitals with coordinates, and IANA timezones with coordinates — one small dependency-free dataset with lookup utilities.

Downloads

146

Readme

country-geo-data

Country names & ISO codes, calling codes, capitals with coordinates, and IANA timezones with coordinates — one small, dependency-free dataset with lookup utilities. Works with both import and require.

Install

npm install country-geo-data

Quick start

import {
  getCountryByAlpha2,
  alpha2ToAlpha3,
  getCapital,
  getCountriesByDialCode,
  getTimezonesByCountry,
  getTimezoneOffset,
  convertTime,
} from 'country-geo-data';

getCountryByAlpha2('BD');
// { name: 'Bangladesh', alpha2: 'BD', alpha3: 'BGD', numeric: '050',
//   dialCodes: ['+880'], capital: 'Dhaka', capitalLatLng: [23.7104, 90.40744],
//   timezones: ['Asia/Dhaka'], official: true }

alpha2ToAlpha3('BD');           // 'BGD'
getCapital('FR');               // { name: 'Paris', latLng: [48.85341, 2.3488] }
getCountriesByDialCode('+1');   // [US, CA, ...every NANP member]
getTimezonesByCountry('US');    // full Timezone objects for all 29 US zones
getTimezoneOffset('Asia/Dhaka'); // '+06:00' (computed live, DST-aware)

// "A meeting at 15:00 in Dhaka — what time is that in New York?"
convertTime({ year: 2024, month: 7, day: 1, hour: 15, minute: 0 }, 'Asia/Dhaka', 'America/New_York');
// { year: 2024, month: 7, day: 1, hour: 5, minute: 0, second: 0, offset: '-04:00' }

CommonJS works identically:

const { getCountryByAlpha2 } = require('country-geo-data');

API

Lookups are O(1) — every exact-key function (alpha2/alpha3/numeric/dial code) reads from a Map built once when the module loads, not a scan over the 250 records. Only genuinely search-shaped functions (searchCountries, fuzzy name match, the two "nearest" geo functions) scan the dataset, and at this size (250 countries, 313 timezones) that's still sub-millisecond.

Official countries vs. territories

This package tracks 250 entries total: the 193 UN member states (official: true) plus ~57 other entries — dependencies, territories, and the 2 UN observer states, Vatican City and Palestine (official: false). General-purpose functions default to official countries only — pass { includeTerritories: true } to widen them. Specific, intentional lookups (by alpha2/alpha3/numeric/exact name) are unaffected by this and always search everything, since asking for "PR" is a deliberate ask regardless of UN status.

| Function | Default scope | With { includeTerritories: true } | |---|---|---| | getAllCountries() | 193 official | 250 (all tracked entries) | | searchCountries(query) | 193 official | 250 | | getCountryByName(name, { fuzzy: true }) | 193 official | 250 | | getCountriesByDialCode(code) | 250 (opt out with { includeTerritories: false }) | — | | getCountryByAlpha2/Alpha3/Numeric, exact getCountryByName | unaffected — always searches all 250 | — |

Countries

| Function | Description | |---|---| | getAllCountries({ includeTerritories }?) | Country records — 193 official by default, 250 with the option. | | getOfficialCountries() | Shorthand for getAllCountries() — 193 UN member states. | | getAllCountriesIncludingTerritories() | Shorthand for getAllCountries({ includeTerritories: true }) — all 250. | | getCountryByAlpha2(code) | Lookup by ISO 3166-1 alpha-2 (case-insensitive). | | getCountryByAlpha3(code) | Lookup by ISO 3166-1 alpha-3 (case-insensitive). | | getCountryByNumeric(code) | Lookup by ISO 3166-1 numeric code. | | getCountryByName(name, { fuzzy, includeTerritories }?) | Lookup by common name; fuzzy: true also matches substrings. | | searchCountries(query, { includeTerritories }?) | All countries whose name contains query — for autocomplete. | | getCountriesByDialCode(code, { includeTerritories }?) | All countries sharing a calling code (e.g. +1 → every NANP member, territories included by default). | | getCapital(alpha2) | { name, latLng } for a country's capital. | | getCountriesByTimezone(tzId) | All countries observing a given IANA timezone. | | isValidAlpha2(code) / isValidAlpha3(code) | Guard helpers. | | isOfficialCountry(alpha2) | True if the code is a UN member state, not a territory/dependency. | | alpha2ToAlpha3(code) / alpha3ToAlpha2(code) | Direct code conversion, returns the bare code string. | | getCountryByCoordinates(lat, lng) | Nearest-capital heuristic — see Accuracy notes. |

Timezones

| Function | Description | |---|---| | getAllTimezones() | All 313 IANA timezones observed by at least one country. | | getTimezoneById(tzId) | Lookup by IANA id, e.g. "Asia/Dhaka" (case-sensitive, as IANA ids are). | | getCountriesForTimezone(tzId) | Alpha-2 codes of countries observing a timezone. | | getTimezonesByCountry(alpha2) | Full Timezone objects for a country (a country can span many). | | getTimezoneOffset(tzId, date?) | Current UTC offset (e.g. "+06:00"), computed live so DST is always correct. | | getWallTimeInTimezone(date, tzId) | Formats a real instant as its wall-clock reading in tzId. | | convertTime(wallTime, fromTzId, toTzId) | Converts a wall-clock reading in one timezone to another — see below. | | findNearestTimezone(lat, lng) | Nearest timezone by distance — see Accuracy notes. |

A note on convertTime: it takes a wall-clock reading, not a UTC instant — { year, month, day, hour, minute, second? } (month is 1-12), or anything new Date(...) accepts (only its calendar/clock digits are used, its own timezone is ignored). This matches how people actually think about it: "3pm in Dhaka" converts to "5am in New York" regardless of what timezone the server computing this happens to be in. fromTzId/toTzId must be an id from getAllTimezones() — generic aliases like "UTC" aren't included since the dataset (IANA's zone1970.tab) only lists zones tied to a country; use a real zero-offset zone like "Africa/Abidjan" if you need a UTC-like reference point.

Geo

| Function | Description | |---|---| | distanceBetween([lat1,lng1], [lat2,lng2]) | Great-circle distance in kilometers (haversine). |

Full type definitions ship in types/index.d.ts for both JS (via editor intellisense) and TypeScript consumers.

Data shape

data/countries.json — array, one entry per country:

{
  name: string;
  alpha2: string;          // ISO 3166-1 alpha-2, e.g. "BD"
  alpha3: string;          // ISO 3166-1 alpha-3, e.g. "BGD"
  numeric: string | null;  // ISO 3166-1 numeric, e.g. "050"
  dialCodes: string[];     // e.g. ["+880"]; ["+1"] shared by all NANP members
  capital: string | null;
  capitalLatLng: [number, number] | null;
  timezones: string[];     // IANA ids, ref timezones.json
  official: boolean;       // true for the 193 UN member states
}

data/timezones.json — object keyed by IANA timezone id:

{
  "Asia/Dhaka": {
    id: "Asia/Dhaka";
    latLng: [23.716667, 90.416667];
    countries: ["BD"];
  }
}

Both files are also importable directly if you don't need the utility functions:

import countries from 'country-geo-data/data/countries.json' with { type: 'json' };

Data sources & licensing

This package deliberately does not hand-type ~250 countries × 6 fields, but every field is traceable to a primary/authoritative source, fetched by the scripts in scripts/fetch/ and merged by scripts/build.cjs:

| Field(s) | Source | License | |---|---|---| | Name, alpha2/alpha3/numeric codes, dial code, capital name | mledoze/countries (itself sourced from ISO 3166-1, ITU-T E.164, and UN data) | MIT | | IANA timezone ids, per-timezone lat/lng, country↔timezone mapping | IANA Time Zone Database, zone1970.tab | Public domain | | Capital city coordinates | GeoNames, cities1000.zip | CC BY 4.0 — per GeoNames' terms, this project attributes GeoNames as a data source |

Run npm run refresh-data to re-fetch everything from source and rebuild data/*.json. This is a maintainer-only step — it is not run on npm install for consumers.

Known data gaps

A handful of edge cases are intentionally left as null/empty rather than guessed, and are treated as expected in scripts/validate.cjs and the test suite:

  • No capital coordinates: AQ (Antarctica), BV (Bouvet Island), HM (Heard & McDonald Islands), UM (US Minor Outlying Islands) — no permanent population, no capital exists. EH (Western Sahara), IO (British Indian Ocean Territory) — the capital name is known but GeoNames records it under a name/spelling this build doesn't currently resolve to coordinates. TK (Tokelau) — has no single fixed capital in reality.
  • No timezone mapped: BV, HM (uninhabited, as above), and XK (Kosovo) — Kosovo has no officially assigned ISO 3166 code, so it's absent from IANA's own country-code list; in practice it observes Europe/Belgrade.
  • South Africa and other countries with multiple constitutional capitals report only the administrative capital (Pretoria), matching what GeoNames flags as the primary seat of government.
  • official correction: mledoze/countries marks Vatican City (VA) as a UN member (unMember: true), which is factually wrong — the Holy See is a UN Permanent Observer State, not a member. scripts/build.cjs overrides this one entry so the official count is exactly 193; see UN_MEMBER_OVERRIDES in that file if a similar correction is ever needed for another entry.

Accuracy notes

getCountryByCoordinates and findNearestTimezone are proximity heuristics, not authoritative geocoding — this package ships point data (capitals, timezone representative points), not country border polygons. A coordinate near a shared border, or in a large country whose capital sits far from that point (e.g. Brazil, Australia, Russia), can resolve to the wrong neighbor. Use a dedicated point-in-polygon/boundary dataset if you need boundary-accurate results.

Development

npm run refresh-data   # re-fetch from source + rebuild data/*.json + validate
npm run build           # bundle src/ into dist/ (ESM + CJS) via tsup
npm test                # run the test suite

Project layout:

scripts/fetch/   one script per upstream source
scripts/build.cjs merges raw sources into data/*.json (the only merge point)
scripts/validate.cjs  data integrity checks, run in prepublishOnly
data/            shipped, built JSON — the actual product
src/             utility functions (ESM source)
types/           hand-written TypeScript definitions
test/            data integrity + unit tests (node:test)
dist/            built output (generated, not committed)

License

MIT — see LICENSE. Data is re-derived from the sources listed above; see their licenses for terms governing the underlying facts (GeoNames requires attribution, which this README provides).