city-timezone-lookup

Lookup timezone ID, local time, UTC offset, and DST status by city + country (or coordinates) in n8n workflows, with optional nearest-match fallback.

Documentation

Banner image

n8n-nodes-city-timezone-lookup

npm version

This project provides a custom n8n community node for resolving timezone and
local-time details from either:

  • a city and country/region, such as Bucharest and Romania
  • a latitude and longitude pair, such as 44.43225 and 26.10626

The node helps workflows normalize location input into reliable timezone data,
including the IANA timezone ID, current local time, GMT offset, DST status, and
lookup metadata. It is useful for scheduling, routing, reporting, reminders,
customer-local notifications, and any automation that needs to understand the
current time for a place.

Table of Contents

Technologies Used

  • n8n - workflow automation platform.
  • @n8n/node-cli - local node build,
    lint, development, and release tooling.
  • TypeScript - node implementation language.
  • Luxon - local time, offset, and DST
    calculation.
  • city-timezones - city and
    region lookup data.
  • countries-and-timezones
    • country code, country name, and timezone metadata.
  • @photostructure/tz-lookup
    • coordinate to IANA timezone resolution.
  • Docker Compose - optional local n8n runtime for loading the node in a container.

How It Works

The City Timezone Lookup node receives one n8n input item and returns one output
item. For each item it:

  1. Reads the configured city/country fields and optional coordinate fields.
  2. Uses coordinates first when both latitude and longitude are provided.
  3. Otherwise resolves the timezone from city and country/region.
  4. Applies nearest-match fallback only when enabled and only when the match is
    unambiguous.
  5. Computes the current local time, GMT offset, offset minutes, and DST status
    for the resolved IANA timezone.
  6. Returns the timezone result plus metadata describing how the match was made.

The node also caches successful timezone resolutions during one workflow
execution, so repeated identical locations in the same run do not repeat the
underlying lookup work.

Prerequisites

  • Node.js >=18.10
  • npm >=9
  • n8n with community nodes enabled
  • git, when installing from source
  • Docker and Docker Compose, when using the included local container setup

Installation

Option 1: Install as an n8n community package

Install the package directly with npm:

npm install n8n-nodes-city-timezone-lookup

Or install it from the n8n UI:

  1. Open n8n.
  2. Go to Settings -> Community Nodes.
  3. Select Install.
  4. Enter n8n-nodes-city-timezone-lookup.
  5. Restart n8n if your runtime requires it.
  6. Search for City Timezone Lookup in the workflow editor.

For self-hosted n8n, make sure community packages are enabled:

N8N_COMMUNITY_PACKAGES_ENABLED=true

Option 2: Load the node from this repository with Docker Compose

This repository includes a docker-compose.yaml that mounts the project into
n8n's custom extensions directory.

  1. Create the environment file:
cp env/n8n.env.example env/n8n.env
  1. Review env/n8n.env and adjust host, auth, protocol, certificate, and
    timezone settings for your environment.

  2. Install dependencies and build the node:

npm install
npm run build
  1. Start n8n:
docker compose up -d
  1. Open n8n at:
http://localhost:5678

If HTTPS is enabled in env/n8n.env, use the configured HTTPS URL instead.

Option 3: Install locally into an existing n8n custom directory

Use this approach when you already run n8n outside this repository.

git clone https://github.com/tudor555/n8n-nodes-city-timezone-lookup.git
cd n8n-nodes-city-timezone-lookup
npm install
npm run build

Then place or symlink the built package where your n8n instance loads custom
nodes. For many self-hosted setups this is under:

~/.n8n/custom

Restart n8n after adding the package.

Usage Overview

  1. Add the City Timezone Lookup node to a workflow.
  2. Connect it after any trigger or data-producing node.
  3. Configure one of these input modes:
    • City mode: set City and Country/Region.
    • Coordinate mode: open Additional Options and set both
      Latitude (optional) and Longitude (optional).
  4. Leave Allow Nearest Match enabled when you want safe fallback behavior for
    slightly imperfect city or region text.
  5. Execute the workflow and use the output fields in later nodes.

Coordinate lookup takes priority when both latitude and longitude are provided,
even if city and country/region are also filled in.

Supported Inputs

Field Type Required Description
city string Required for city mode City name to resolve, for example Berlin or Bucharest.
countryOrRegion string Required for city mode Country code, country name, or supported alias/region text, for example DE, Germany, US, United States, or Bucuresti.
latitude number Required for coordinate mode Latitude from -90 to 90. Must be provided together with longitude.
longitude number Required for coordinate mode Longitude from -180 to 180. Must be provided together with latitude.
nearest boolean Optional Enables safe fallback matching for city/country lookups. Defaults to true.

You must provide either both city and countryOrRegion, or both latitude
and longitude.

Output Fields

Successful output always includes the core timezone and local-time fields:

Field Type Description
inputSource string Input path used by the node: cityCountry or coordinates.
lookupMethod string Resolution method: exactCityCountry, partialCityRegion, cityOnlyFallback, or coordinates.
timezoneId string IANA timezone identifier, such as Europe/Bucharest.
localTime string Current local time in the resolved timezone as an ISO timestamp.
gmtOffset string Current UTC offset, such as +03:00.
gmtOffsetMinutes number Current UTC offset in minutes.
isDST boolean Whether daylight saving time is currently active for the timezone.
timezoneCandidates string[] Timezone IDs considered by the resolver.
fallbackUsed boolean Whether the result came from fallback logic.
nearest boolean Whether nearest-match fallback was enabled for the lookup.

City/country output can also include:

Field Type Description
city string Original city input used for the lookup.
countryOrRegion string Original country/region input used for the lookup.
fallbackReason string Reason fallback was used, such as partialCityRegionMatch or countryOrRegionNotMatched.
matchedLocation object Best matched location with city, country, country code, province, latitude, longitude, and population.
matchCount number Number of location candidates behind the selected timezone.
ambiguousMatchesResolvedBy string Present when multiple candidates were accepted because they share one timezone.
candidateMatches object[] Up to 10 candidate locations considered by the resolver.

Coordinate output also includes:

Field Type Description
coordinates object The latitude and longitude used for lookup.

When n8n's Continue On Fail setting is enabled and a lookup fails, the item
returns:

{
	"errorMessage": "No timezone match was found for \"Example, Unknown\".",
	"failedInput": {
		"city": "Example",
		"countryOrRegion": "Unknown",
		"additionalOptions": {
			"nearest": true
		}
	}
}

Example Workflow

Basic workflow:

  1. Add a Manual Trigger node.
  2. Add City Timezone Lookup.
  3. Set City to Bucharest.
  4. Set Country/Region to Romania.
  5. Execute the workflow.

Example successful output:

{
	"inputSource": "cityCountry",
	"lookupMethod": "exactCityCountry",
	"timezoneId": "Europe/Bucharest",
	"localTime": "2026-07-19T14:30:00+03:00",
	"gmtOffset": "+03:00",
	"gmtOffsetMinutes": 180,
	"isDST": true,
	"fallbackUsed": false,
	"timezoneCandidates": ["Europe/Bucharest"],
	"city": "Bucharest",
	"countryOrRegion": "Romania",
	"matchedLocation": {
		"city": "Bucharest",
		"country": "Romania",
		"countryCode": "RO",
		"province": "Bucuresti",
		"latitude": 44.43225,
		"longitude": 26.10626,
		"population": 1877155
	},
	"matchCount": 1,
	"candidateMatches": [
		{
			"city": "Bucharest",
			"country": "Romania",
			"countryCode": "RO",
			"province": "Bucuresti",
			"timezoneId": "Europe/Bucharest",
			"latitude": 44.43225,
			"longitude": 26.10626,
			"population": 1877155
		}
	],
	"nearest": true
}

Coordinate example:

{
	"inputSource": "coordinates",
	"lookupMethod": "coordinates",
	"timezoneId": "Europe/Bucharest",
	"localTime": "2026-07-19T14:30:00+03:00",
	"gmtOffset": "+03:00",
	"gmtOffsetMinutes": 180,
	"isDST": true,
	"timezoneCandidates": ["Europe/Bucharest"],
	"fallbackUsed": false,
	"coordinates": {
		"latitude": 44.43225,
		"longitude": 26.10626
	},
	"nearest": true
}

localTime, gmtOffset, and isDST are computed at workflow execution time,
so these values change depending on when the workflow runs.

Project Structure

.
├── nodes
│   └── CityTimezoneLookup
│       ├── CityTimezoneLookup.node.ts
│       ├── CityTimezoneLookup.node.json
│       ├── cityTimezoneLookup.svg
│       ├── cityTimezoneLookup.dark.svg
│       └── helpers
│           ├── countriesAndTimezones.ts
│           ├── localTime.ts
│           ├── timezoneResolver.ts
│           └── timezoneTypes.ts
├── docs
│   ├── fallbackLogic.md
│   ├── nodeStructure.md
│   ├── performance.md
│   └── testing.md
├── env
│   └── n8n.env.example
├── docker-compose.yaml
├── package.json
├── tsconfig.json
└── README.md

Development

Install dependencies:

npm install

Run n8n with the node loaded for local development:

npm run dev

Build the distributable node:

npm run build

Run lint checks:

npm run lint

Before contributing, read:

Contribution checklist:

  • Keep behavior scoped to the City Timezone Lookup node.
  • Update README and docs when input, output, fallback, or installation behavior
    changes.
  • Run npm run build and npm run lint before opening a pull request.
  • Include focused tests when test infrastructure is added.
  • Do not commit local n8n runtime data, secrets, or generated environment files.

References

Detailed documentation lives in docs/:

Related project files:

Troubleshooting

Node does not appear in n8n

Check:

  • The package was installed or mounted in the directory n8n uses for custom
    extensions.
  • N8N_COMMUNITY_PACKAGES_ENABLED=true is set for self-hosted n8n.
  • The node was built with npm run build.
  • n8n was restarted after installation.

Docker Compose starts but n8n cannot load the node

Check:

  • env/n8n.env exists.
  • The repository is mounted at
    /home/node/.n8n/custom/n8n-nodes-city-timezone-lookup.
  • Dependencies were installed and dist/ was generated before starting n8n.

Useful commands:

docker compose logs -f
docker compose down
docker compose up -d

Lookup fails for city and country

Check:

  • Both City and Country/Region are populated.
  • The country value is a country code or recognizable country/region name.
  • Allow Nearest Match is enabled if you expect fallback behavior.
  • Ambiguous city names may fail when multiple candidates map to different
    timezones.

Coordinate lookup fails

Check:

  • Both latitude and longitude are provided.
  • Latitude is between -90 and 90.
  • Longitude is between -180 and 180.

Discussion