How the browser's timezone silently reveals user location — and why that's incredibly useful
Every web developer eventually faces the same problem: you need to know where your user is. Maybe you want to preselect their country in a phone number field. Maybe you want to show prices in their local currency. Maybe you need to flag suspicious activity.
The knee-jerk solution? Hit an IP geolocation API. GeoIP databases, third-party services, server-side lookups — the whole circus. But what if I told you the browser is already whispering the answer, and all you need is two lines of JavaScript?
The Timezone Trick
Every modern browser exposes the user's timezone through the Intl API:
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
// "Europe/Vilnius"That's it. No API call. No backend. No user permission prompt. No GDPR consent banner. The browser just… tells you.
And here's the key insight: timezone identifiers are not just time offsets — they encode geography. Europe/Vilnius isn't just UTC+2. It's Lithuania. Asia/Tokyo is Japan. America/Chicago is the central United States.
The IANA timezone database that browsers use maps these identifiers to specific countries and regions. A library like countries-and-timezones makes the mapping trivial:
import { getTimezone } from 'countries-and-timezones';
const tz = getTimezone('Europe/Vilnius');
// { countries: ['LT'], utcOffset: 120, ... }You now have an ISO country code. From a timezone string. In the browser. With zero network requests.
Why This Works Better Than You'd Think
You might be skeptical. "Timezones span multiple countries," you say. And yes, America/New_York covers the eastern US and parts of Canada. But consider:
- Most timezone identifiers are city-specific.
Asia/Kolkatais India.Pacific/Aucklandis New Zealand.Africa/Lagosis Nigeria. These are unambiguous. - Even shared timezones narrow things down dramatically. Going from "somewhere on Earth" to "one of 2–3 countries" is an enormous improvement for UX defaults.
- Users set their timezone correctly. Unlike IP addresses — which can be masked by VPNs, proxies, and corporate networks — the system timezone reflects where the user's computer actually lives. People rarely change it because getting it wrong breaks their calendar, alarms, and every scheduled meeting.
This last point is critical. The timezone is one of the most honest signals a browser emits.
Real-World Use Case #1: Phone Number Country Code Preselection
Here's a scenario every payment form developer knows: the user needs to enter a phone number, and you need to preselect their country's dialing code. There are 200+ countries. Making the user scroll through all of them is hostile UX.
Here's how we solved it in a production payment form:
import { getCountries, getCountryCallingCode } from 'libphonenumber-js';
import { getTimezone } from 'countries-and-timezones';
function detectCountryFromTimezone(): string | null {
try {
const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
if (!userTimezone) return null;
const timezoneData = getTimezone(userTimezone);
if (!timezoneData?.countries || timezoneData.countries.length === 0) {
return null;
}
return timezoneData.countries[0];
} catch {
return null;
}
}
function getPhoneCountryCode(iso2: string): string | null {
try {
const code = getCountryCallingCode(iso2.toUpperCase());
return code ? `+${code}` : null;
} catch {
return null;
}
}
// Usage:
const country = detectCountryFromTimezone(); // "LT"
const dialCode = getPhoneCountryCode(country); // "+370"The user opens the payment form and their country code is already selected. No API call. No loading spinner. Instant.
Compare this to the IP geolocation approach:
Timezone DetectionIP Geolocation APISpeedInstant (synchronous)100–500ms network round-tripCostFreeFree tier limits, then paidReliabilityWorks offlineRequires networkPrivacyNo data leaves the browserSends user IP to third partyVPN usersShows actual locationShows VPN exit nodeAccuracyCountry-level (sometimes region)City-level (when it works)
For a phone number preselection, country-level accuracy is all you need — and the timezone approach delivers it better.
Real-World Use Case #2: Fraud Signal — Timezone vs. IP Mismatch
This is where things get really interesting. If you do have IP geolocation available on your backend, the timezone becomes a powerful fraud detection signal.
Consider this scenario:
- IP geolocation says the user is in New York, USA
- Browser timezone is Asia/Kolkata (India)
Something doesn't add up. A person physically in New York would have their system clock set to America/New_York. If their timezone says India, one of two things is happening:
- They're using a VPN or proxy to mask their real location (the IP is fake, the timezone is real)
- They forgot to update their timezone after traveling (rare — most OS auto-detect)
Either way, this is a signal worth flagging.
interface LocationSignals {
ipCountry: string; // From your backend IP lookup
timezoneCountry: string; // From browser timezone
mismatch: boolean;
riskLevel: 'low' | 'medium' | 'high';
}
function assessLocationRisk(ipCountry: string): LocationSignals {
const timezoneCountry = detectCountryFromTimezone();
if (!timezoneCountry) {
return {
ipCountry,
timezoneCountry: 'unknown',
mismatch: false,
riskLevel: 'medium', // Can't verify - mild caution
};
}
const mismatch = ipCountry.toUpperCase() !== timezoneCountry.toUpperCase();
return {
ipCountry,
timezoneCountry,
mismatch,
riskLevel: mismatch ? 'high' : 'low',
};
}
// User claims to be in the US, but timezone says India
const risk = assessLocationRisk('US');
// { ipCountry: 'US', timezoneCountry: 'IN', mismatch: true, riskLevel: 'high' }This is not hypothetical. Major platforms use timezone-IP mismatches as one of many signals in their fraud scoring systems. It's particularly effective because:
- VPNs don't change the system timezone. A user can route their traffic through any country, but their OS still reports the timezone they're physically in.
- It's hard to spoof. Changing the system timezone breaks too many things for most fraudsters to bother.
- It catches the low-effort fraud that automated IP masking enables.
You wouldn't block a user solely on this signal — legitimate travelers exist. But combined with other signals (new account, high-value transaction, unusual behavior), a timezone mismatch is a strong red flag.
Real-World Use Case #3: Smart Defaults Without the Overhead
Beyond phone codes and fraud, timezone-based country detection opens up a range of small UX wins:
Currency preselection:
const country = detectCountryFromTimezone();
const currencyMap = { US: 'USD', GB: 'GBP', JP: 'JPY', LT: 'EUR', IN: 'INR' };
const defaultCurrency = currencyMap[country] ?? 'USD';Language suggestion:
// User's timezone says Japan, but the page is in English
// → Show a subtle "View in Japanese?" promptDate/time format:
// Timezone says US → default to MM/DD/YYYY
// Timezone says Germany → default to DD.MM.YYYYRegional content:
// Timezone says Brazil → show Portuguese content first
// Timezone says France → highlight EU shipping optionsNone of these require the timezone approach. But all of them benefit from having a zero-cost, instant, privacy-friendly country signal available before any network request completes.
The Gotchas (Because Nothing Is Perfect)
Let's be honest about the limitations:
1. Some timezones span multiple countries
America/New_York covers both the US and parts of Canada. The countries-and-timezones library returns the first match, which is usually the most populous country in that timezone — a reasonable default, but not guaranteed to be right.
2. UTC offsets are not timezone identifiers
UTC+5:30 only exists in India and Sri Lanka (actually just India uses +5:30 exclusively). But UTC+1 covers dozens of countries. Always use the full IANA identifier (Europe/Berlin), not the offset.
3. Users can change their timezone
A developer in Lithuania working for a US company might set their system to America/New_York to align with their team. It's rare, but it happens.
4. Server environments don't have browser APIs
This is a client-side technique. If you need the timezone on the server, you'll have to pass it from the client — which introduces trust issues for security use cases.
5. Privacy considerations
While timezone detection doesn't send data anywhere, it does derive location information. Some privacy-focused applications might consider this inappropriate. Be transparent about what you infer and why.
The Implementation-Complete and Copy-Pasteable
Here's a production-ready utility you can drop into any project:
import { getTimezone } from 'countries-and-timezones';
/**
* Detects the user's country based on their browser timezone.
* Returns an ISO 3166-1 alpha-2 country code (e.g., "US", "GB", "LT")
* or null if detection fails.
*
* Zero network requests. Zero user permissions. Works offline.
*/
export function detectCountryFromTimezone(): string | null {
try {
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
if (!timezone) return null;
const data = getTimezone(timezone);
if (!data?.countries?.length) return null;
return data.countries[0];
} catch {
return null;
}
}The countries-and-timezones package is 12KB gzipped. libphonenumber-js is larger but you likely already have it if you're doing phone validation. The Intl API is supported in every modern browser and Node.js.
Why I Like This Pattern
There's something satisfying about solutions that are simultaneously:
- Simpler than the alternative (no API, no backend, no async)
- More reliable (no network dependency, no rate limits, no API keys)
- More privacy-respecting (no data leaves the browser)
- More honest (reflects actual location, not VPN exit node)
The timezone trick won't replace IP geolocation for everything. It won't give you city-level precision. But for the most common use case — "what country is this user probably in?" — it's the pragmatic choice.
Next time you reach for a geolocation API, ask yourself: do you really need latitude and longitude, or do you just need a country code? If it's the latter, the browser already has your answer.
