Christian Nwamba JS preacher. Developer 🥑. Building the web with the community @concatenateConf @forLoopAfrica. JS and Senior Advocacy for the Next Billion Users through Microsoft.

Detect location and local timezone of users in JavaScript

7 min read 2148

Detecting the location of your users can be really useful if you want to personalize your user’s experience when they browse through your website. Want to show a uniquely tailored promotion? Want to change the language of your site or design based on where your users are visiting from?

These are some common use cases for detecting user location. It can also be to limit access to your website to either remain compliant or if you simply do not yet cater to certain locations. We will explore the various ways we can get your users’ location as well as their timezone (especially if you intend to send a lot of emails or generate a lot of reports for them).

Options for detecting location

There are two very popular ways to detect a user’s location in the browser directly:

  1. Using the Geolocation API
  2. Looking up user IP address

Geolocation API

The Geolocation API allows you to ask the user to share their present location. You can argue that it is the most trusted method for detecting location, as the user will tell you themselves.

However, in a scenario where you want to format the content displayed to the user before it gets rendered, this isn’t exactly ideal. Also, the Geolocation API may not work depending on the user browser permission settings.

To use the Geolocation API, you can do the following:

// Excerpt from https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API
function geoFindMe() {
  if (!navigator.geolocation){
   console.log("Geolocation is not supported by your browser");
    return;
  }
  function success(position) {
    var latitude  = position.coords.latitude;
    var longitude = position.coords.longitude;
    reverseGeocodingWithGoogle(latitude, longitude)
  }
  function error() {
    console.log("Unable to retrieve your location");
  }
  navigator.geolocation.getCurrentPosition(success, error);
}

It first checks that the browser has/supports Geolocation API. If it does, it executes the rest of the code which includes a success and error callback function. The navigator.geolocation.getCurrentPosition(success, error) gives you the exact coordinates of the user which you can put into Google maps to get the exact user location.

You can send a request to Google’s reverse Geocoding API. It would require getting an API key.

function reverseGeocodingWithGoogle(latitude, longitude) {
  fetch(`https://maps.googleapis.com/maps/api/geocode/json?
      latlng=${latitude},${longitude}&key={GOOGLE_MAP_KEY}`)
  .then( res => res.json())
  .then(response => {
      console.log("User's Location Info: ", response)
   })
   .catch(status => {
      console.log('Request failed.  Returned status of', status)
   })
}

The downside of using this method is that if the user does not allow you to get their location, you cannot detect their position accurately, or you may not even detect it at all. Also, it only works on secure servers (https). It is not supported on Internet Explorer 10 and below and OperaMini.

Looking up user IP address

This is by far the most common way of detecting user location. Unlike the Geolocation API, it can only give you limited information like Country and maybe City, depending on the IP lookup provider you are using.

Here is a simple lookup:

We made a custom demo for .
No really. Click here to check it out.

fetch('https://extreme-ip-lookup.com/json/')
.then( res => res.json())
.then(response => {
    console.log("Country: ", response.country);
 })
 .catch((data, status) => {
    console.log('Request failed');
 })

This works by making a request to the https://extreme-ip-lookup.com/json/ URL from the user’s browser, so that their location is detected. This resource provides country, city, time zone, longitude, and latitude among other things.

With a single query, you can tell which country a user is in and get their timezone. How convenient.

Many times, the information provided by IP lookup might be all you need. In that case, there would be no need getting the exact coordinates to pinpoint the user’s exact location in their city.

Here is a list of other places to check out when performing IP lookup:

Understand that IP lookup mostly gives you accurate information about country and timezone of the originating request. The city may be the location of the ISP. If you intend to get the exact city or region of a user, you should use the Geolocation API and find ways to convince the user to share their location with you.

Getting local timezone of users in JavaScript

It is tempting to conclude “this is easy”. Well, you could easily create a date object and send to your server and store that time. However, it comes with a lot of challenges as you have to worry about doing internal calculations and not having them go off. This is why it is more important to fetch the user timezone above everything else.

As you saw above, we can detect timezone from IP address lookup. All you have to do is pick out the timezone from the response object along with the user location. Below, we will explore other ways of detecting timezone.

Moment.js timezone function

Moment.js includes a function that guesses the timezone of a user. It is quite accurate, provided you are using the most recent version.

Below is a quick example of how it works:

<div id="guess"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.23/moment-timezone-with-data-2012-2022.min.js"></script>
<script>
  document.getElementById('guess').innerText = moment.tz.guess();
</script>

When you load the page, it shows you your current timezone. Create a index.html file and open the file in a browser. Copy the above code into the file and save it. Then refresh the browser tab you just opened. Cool right?

MomentTz uses Intl API which is a built-in JavaScript internationalization API. It also has its data bank it checks the result of the Intl API against to provide more accurate information. It also requires you to have included Moment.js before it can work.

Jstz package

Jstz is a simple lighter package for detecting timezone. I say lighter in comparison with Moment.js. It also makes use of the Intl API, so you can be confident of its results. To use the package, you can grab the CDN as follows:

<div id="guess"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstimezonedetect/1.0.6/jstz.min.js"></script>
<script>
  document.getElementById('guess').innerText = jstz.determine().name();
</script>

What you can notice from these libraries used for detecting timezone is that they do not require any network calls from your end. This means if you intend to only pick user timezones, you may not need to do IP lookups. And that’s good because they can get expensive as you’re paying for every call to the API.

The Internationalization API (Intl) itself

Don’t hate me, but let’s face it — if I showed you this, you may have ignored the rest of this article lol.

OK, so here is how to use it:

<div id="guess"></div>
<script>
  document.getElementById('guess').innerText = Intl.DateTimeFormat().resolvedOptions().timeZone;
</script>

Before you go like “Wow!!!” understand that the packages highlighted above take a lot into consideration when detecting timezones. This makes them slightly more accurate than Intl API alone. You may never notice the difference, but it feels good knowing someone’s got your back.

Building a JavaScript app for detecting user location and timezone

“Talk is cheap. Show me the code!” or so the saying goes. Let’s do one better and build a really simple app that detects a user’s location and timezone information, and tell them what the time would be like in three other timezones around the world.

Here is what the simple application looks like:

Here is how we pieced the code together to achieve that. Place the following code in a file named index.html:

<!-- index.html -->
<!doctype html>
<html class="no-js" lang="">
<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <title>Magic App</title>
    <meta name="description" content="Magic App">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="index.css">
</head> 
<body>
    <h1>Magic App Here</h1>
    <div id="main">
        <div id="time" class="sub">
            <div>
                <h2>Your local time</h2>
                <span class="local"></span>
            </div>
            <div>
                <h2>Magic App Server Time</h2>
                <span class="server"></span>
            </div>
        </div>
        <div id="location" class="sub">
            <h2>And you are in...</h2>
            <div class="address"></div>
        </div>
    </div>
    <!-- Script here -->
</body>
</html>

index.css file and add the following:

/*index.css*/
h1 {
  text-align: center
}
#main {
  max-width: 900px;
  display: table;
  margin-left: 30vw;
  margin-top: 10vh;
}

#main .sub {
  display: block;
  float: left;
  min-width: 300px;
  min-height: 300px;
  border: 0.5px solid #bbb;
  border-radius: 4px;
  padding: 15px;
}
#main .sub h2{
  font-size: 20px;
}
#main #time {
  margin-right: 5vw;
}

#main #time div {
  display: block;
  min-height: 120px;
}

This may be the most simple application you have ever seen. We’re not doing anything fancy, just going straight to the point. Standard HTML and CSS — that’s all.

Now, let’s add the JavaScript that brings all of these to life: add the following to index.html:

[...]
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstimezonedetect/1.0.6/jstz.min.js"></script>
<script>
  document.addEventListener("DOMContentLoaded", function(event) {
      // The main sauce here
  });
</script>
[...]

We have laid the foundation for what is to come. The first thing we want to do is detect whether the user’s browser supports the Geolocation API:

[...]
document.addEventListener("DOMContentLoaded", function(event) {
  if (!navigator.geolocation){
    console.log("Geolocation is not supported by your browser");
    ipLookup();
  } else {
    navigator.geolocation.getCurrentPosition(success, error);
  }
  // More sauce here
});
[...]

Now, we get to execute our code if the Geolocation API is available/accessible. If it is not, we just fall back to IP lookup, like we saw above.

Let’s create the success, error, and ipLookup methods:

[...]
document.addEventListener("DOMContentLoaded", function(event) {
  [...]
  function success(position) {
    var latitude  = position.coords.latitude;
    var longitude = position.coords.longitude;
    reverseGeocodingWithGoogle(latitude, longitude)
  }
  function error() {
    console.log("Unable to retrieve your location");
  }
  function ipLookup() {
    fetch('https://extreme-ip-lookup.com/json/')
    .then( res => res.json())
    .then(response => {
        fallbackProcess(response)
    })
    .catch((data, status) => {
        console.log('We could not find your location');
    })
  }
    // More sauce here
});
[...]

We have already seen how these work above, so I’m going to skip explaining each. Let’s add the reverseGeocodingWithGoogle method now:

[...]
document.addEventListener("DOMContentLoaded", function(event) {
  [...]
  function reverseGeocodingWithGoogle(latitude, longitude) {
    fetch(`https://maps.googleapis.com/maps/api/geocode/json?
      latlng=${latitude},${longitude}&key={GOOGLE_MAP_KEY}`)
    .then( res => res.json())
    .then(response => {
      processUserData(response)
    })
    .catch(status => {
      ipLookup()
    })
  }
  // Even more sauce here
});
[...]

You may have noticed that I introduced two new functions, processUserData and fallbackProcess. These are just to keep things clean and reusable. Let’s now add the both of them:

[...]
document.addEventListener("DOMContentLoaded", function(event) {
  [...]
  function processUserData(response) {
    var address = document.querySelector('.address')
    address.innerText = response.results[0].formatted_address
  }
  
  function fallbackProcess(response) {
    var address = document.querySelector('.address')
    address.innerText = `${response.city}, ${response.country}`
  }
  // timezone sauce here
});
[...]

You see the methods just perform assignments — nothing too complex. For the address variable, I should ordinarily define it in the global scope, but I brought it into the function so you would not miss it. Do note that this is not the best way to reuse code.

Now, let’s detect timezone:

[...]
document.addEventListener("DOMContentLoaded", function(event) {
  [...]
  var localTime = jstz.determine().name();
  var serverTime = "Asia/Novosibirsk";
  document.querySelector('.server').innerText = new Date().toLocaleString("en-US", {timeZone: serverTime});
  document.querySelector('.local').innerText = new Date().toLocaleString("en-US", {timeZone: localTime});
});
[...]

And that completes our magic app. You can check out the working app on Codepen below:

See the Pen
Detect Timezone and Location in JavaScript
by Chris Nwamba (@codebeast)
on CodePen.

Conclusion

I hope this article has been useful to you. I hope it helps you improve your user experience and to build a more internationalized application with JavaScript.

One last thing that can come in handy would be to get a DateTime string in a particular timezone using JavaScript. Not to worry, I got you. Here is the simple line of code you need:

new Date().toLocaleString("en-US", {timeZone: "TIMEZONE_STRING_HERE"})

Share your experiences using these tools, let’s all learn together.

: Debug JavaScript errors more easily by understanding the context

Debugging code is always a tedious task. But the more you understand your errors the easier it is to fix them.

LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to find out exactly what the user did that led to an error.

LogRocket records console logs, page load times, stacktraces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!

.
Christian Nwamba JS preacher. Developer 🥑. Building the web with the community @concatenateConf @forLoopAfrica. JS and Senior Advocacy for the Next Billion Users through Microsoft.

4 Replies to “Detect location and local timezone of users in JavaScript”

  1. thank you for the post! Just realised that you mixed up longitude and latitude in the reverseGeocodingWithGoogle function 😉

  2. Interesting post! I have one query related to DST. Is there any way in Javascript which will give us user’s tzid and tz long name based on tz database used in Joda library? These tzids are widely used universally and are defined by IANA database. Java’s Joda Library also use the same tz database.

    Joda library gives different DST data (not shown below) for same offset i.e. +04:00…like as below:

    (+04:00) Europe/Ulyanovsk, Greenwich Mean Time
    (+04:00) Europe/Volgograd, Moscow Standard Time
    (+04:00) Indian/Mahe, Seychelles Time
    (+04:00) Indian/Mauritius, Mauritius Time
    (+04:00) Indian/Reunion, Reunion Time

    we have to apply those DST rules while sending icalendar file (ICS files) to users. Users are located at different regions.

    if user’s timezone detected by JS is “+04:00 with some country”, then how can we decide which DST rule out of several should be applied? Because I dont have any country to region mapping.

Leave a Reply