countries-and-timezones
Minimalistic library for looking up countries, IANA timezones, and their UTC/DST offsets, kept in sync with the tz database.
Repository Health
Technical Analysis
countries-and-timezones is a zero-dependency TypeScript library that ships a compact, bundled snapshot of ISO 3166-1 country data cross-referenced with the IANA (tz database) timezone list. Given a country code it returns the timezones that country uses; given a timezone name it returns UTC and DST offsets (both as minutes and as formatted ±hh:mm strings) plus the countries that observe it. Deprecated/aliased timezone entries are tracked and excluded from results by default, with an option to include them.
The library is designed to be embedded almost anywhere: it publishes CommonJS and ESM builds from the same source, plus a minified UMD bundle (~9kb) that can be dropped directly into a browser via a <script> tag from a CDN, with no bundler required. The underlying data is refreshed periodically against upstream IANA releases and republished as new npm versions, so consumers get up-to-date offset and alias information without re-deriving it themselves.
What You Get
getCountry(id)— look up a country’s name and timezones by ISO 3166-1 codegetTimezone(name)— resolve a tz-database name to UTC offset, DST offset, and alias informationgetAllCountries()/getAllTimezones()— bulk lookup of the full bundled datasetgetTimezonesForCountry(id)/getCountriesForTimezone(name)/getCountryForTimezone(name)— cross-reference helpers between the two datasets- A
deprecatedoption on every lookup to opt in to superseded/aliased timezone entries - CommonJS, ESM, and a pre-built minified browser bundle, all generated from one TypeScript source
Common Use Cases
- Populating a country or timezone picker in a signup/settings form without shipping a full i18n library
- Converting a user’s selected country into its most likely IANA timezone for scheduling or date display
- Displaying a UTC/DST offset next to a timezone name in a dashboard or admin panel
- Validating that a timezone name submitted by a client is a real, current tz-database entry
- Grouping or filtering records (events, users, servers) by country-derived timezone in a Node backend
Under The Hood
Architecture
The package separates a tiny bundled dataset from the logic that expands it. src/data.json stores countries and timezones in a deliberately compact shape (short keys like c, u, d, r, a for countries/UTC offset/DST offset/renamed-flag/alias) to minimize bundle size, while src/build-country.ts and src/build-timezone.ts are pure functions that expand a raw data entry into the public Country/Timezone shape on demand. src/index.ts is the only stateful layer: it memoizes built countries and timezones in module-level maps so repeated lookups don’t re-run the expansion logic, and it lazily builds the reverse country-to-timezones index the first time any country is resolved. This keeps the public API surface small (seven exported functions) while isolating the compact-data-format concern from the lookup concern.
Tech Stack
Written in TypeScript and built with Vite (vite.config.ts) plus vite-plugin-dts for declaration files, producing three artifacts from one source: a CommonJS build (dist/), an ESM build (esm/), and a minified UMD bundle for direct browser <script> use. Linting runs through xo (an ESLint preset, airbnb-based) with Prettier formatting; releases are cut with release-it and conventional-changelog. There are no runtime dependencies at all — the dataset ships inside the package itself.
Code Quality
Tests use Vitest and live under test/unit/, with one file per exported function (get-country.ts, get-timezone.ts, get-countries-for-timezone.ts, etc.) plus dedicated cache-behavior tests (countries-cache.ts, timezones-cache.ts) and shared fixtures in test/utils/. Type-level correctness is additionally checked with tsd against types/index.test-d.ts. GitHub Actions runs npm test on every push and pull request. Functions consistently return null for not-found lookups rather than throwing, and TypeScript overloads distinguish the case where a literal CountryCode/TimezoneName is passed (non-nullable return) from a plain string (nullable return).
What Makes It Unique The library’s main technical choice is aggressively minimizing bundle size for a data-heavy package: the source dataset uses single-letter keys and alias-based deduplication (a deprecated timezone stores only a pointer to its replacement rather than duplicating offset data), and the build step produces a browser bundle small enough to load directly from a CDN with no bundler. This is a standard lookup-table pattern rather than a novel algorithm, but the compression and memoization choices are deliberate engineering for a package whose only job is to be embedded cheaply into many other projects.
Used by 6 apps in this directory
Chatwoot
Customer Support
Open-source omnichannel customer support platform with AI-powered agents, live chat, and self-hosting — a full Intercom and Zendesk alternative.
Ghost
CMS · Blogging
Open source headless Node.js CMS for professional publishing, paid memberships, and newsletters with a fully owned audience.
Ghostfolio
Invoicing Finance
Track your stocks, ETFs, and crypto with a privacy-first, self-hostable wealth management platform built for data-driven investors.
LobeHub
AI Assistants · Productivity · Automation
Your Chief Agent Operator — build, schedule, and collaborate with an entire AI team in one self-hostable workspace.
Swetrix
Analytics
Privacy-first, cookieless web analytics with error tracking, session replays, and performance monitoring — self-host or use Cloud.
Unleash
Developer Tools · Devops · Ab Testing Experimentation
The open-source feature management platform that lets you ship code to production and control who sees it — without redeploying.