A17 Timezones

A17 Timezones

AREA 17 operates primarily out of studios in New York and Paris. But we also have satellite offices in several other parts of the world. At times there can be a nine hour time differential between staff members. Thankfully email, Hipchat, Google Hangouts, Skype, and a few years of practice means this isn’t much of a problem for us. However, I sometimes still have problems with doing the mental arithmetic necessary to calculate what time it is for everyone everywhere else—especially when organising meetings. To try and alleviate this, I built a web app over the holidays that displays all of the primary timezones in which AREA 17 operates so with quick glance you can see what time it is in those places.

A17 Timezones

This handy little app also displays the current weather and temperature in each locale so you’ll have something to talk about at the beginning of a Google Hangout or appreciate just how cold our Argentinian ex-pat, Fernando, is during his first New York City winter. Plus, us British are renowned for complaining about the weather. These weather reports are powered by Forecast.io from the Dark Sky App people and are updated automatically every 30 minutes.

To help plan meetings you can type a time—let’s say, “3pm” or “14:30″—and an overlay shows you converted times in each of the other timezones around the world.

A17 Timezones

There is a settings pane if you want to change the display to hide any of the elements, change the units to Fahrenheit from Celsius or the time format from 12 to 24 hour.

Some technical details

I built this site over the course of a few evenings while eating biscuits and watching James Bond films. It’s a bit hacked together so not as well organised or thought out as perhaps it could be.

I was going to do the timezones times myself as JavaScript has a “getTimezoneOffset” method which compares the current user’s timezone with UTC and I know the various time offsets required. However, it occurred to me that the change from winter time to summer time could mess up these offsets. I considered comparing times in a location in January and July and comparing the UTC offsets. Or, I could just use moment.js and moment-timezone.js which have nice helpers for time functions and claim to handle summer time automatically.

Normally plugins annoy me as they often include lots of code that I don’t need; the same is true here but it did save me lots of time. It’s also frustrating testing timezone based applications here in the UK as currently GMT is UTC and so the time offset is 0. I do think its comical how us Brits put the centre of the worlds’ timezones here in the UK. I can recommend a visit to the Greenwich Observatory to learn about time, clocks, sailing and timezones—I went a few years ago and handstanded the prime meridian.

The analog clocks are made using divs, moved around by CSS animations written inline by JavaScript on load. CSS animations are used so that the browser can optimise the animation and so that I’m not having to update the clocks on a requestAnimationFrame or a setInterval, constantly writing to the page, and probably making laptop fans whirr into life. I went for a ticking second hand rather than letting it sweep around like a luxury watch because I’d never used the steps timing function of CSS animations before as we generally use linear or some sort of easing as the timing function. Also high-end watches don’t truly sweep around—instead they tick multiple times per second; for example, Rolex watches tick 10 times per second to create the distinctive second hand sweep motion.

The markup for the clocks is very simple:

And the SCSS looks like:

.clock {
    position: relative;
    margin: 0 auto 10px;
    width: 120px;
    height: 120px;
    border: 3px solid $white;
    border-radius: 50%;

    &:after {
      content: "";
      position: absolute;
      z-index: 2;
      top: 50%;
      left: 50%;
      width: 6px;
      height: 6px;
      margin: -3px 0 0 -3px;
      background: $white;
      border-radius: 50%;
    }

    .hours,
    .minutes,
    .seconds {
      position: absolute;
      z-index: 1;
      left: 50%;
      top: 50%;
      width: 4px;
      margin: 0 0 0 -2px;
      background: $white;
      border-radius: 2px;
      -webkit-transform-origin: 50% 100%;
      transform-origin: 50% 100%;
    }

    .hours {
      height: 30px;
      margin-top: -30px;
      -webkit-animation: time_hours 43200s infinite linear;
      animation: time_hours 43200s infinite linear;
    }

    .minutes {
      height: 50px;
      margin-top: -50px;
      -webkit-animation: time_minutes 3600s infinite linear;
      animation: time_minutes 3600s infinite linear;
    }

    .seconds {
      background: $red;
      z-index: 3;
      width: 2px;
      height: 48px;
      margin: -48px 0 0 -1px;
      -webkit-animation: time_seconds 60s steps(60 ,end) infinite;
      animation: time_seconds 60s steps(60 ,end) infinite;

      &:after {
        content: "";
        position: absolute;
        background: $red;
        height: 10px;
        left: 0;
        right: 0;
        top: 100%;
      }
    }
  }

I used pseudo content to draw a circle in the middle of the clock face as a pivot for the hands and some more pseudo content to extend the second hand past the pivot, vaguely inspired by the iPhone clock app icon. Fiddling with the transform origin allowed the hands to rotate around the pivot point of the clock face. I considered making the clock face in Illustrator and exporting as an SVG and then animating that but ended up liking the simple clock face.

Once the JavaScript loads and works out the time for each timezone, it updates the initial position for each of the clocks and writes the animation keyframes for the hands. Fortunately clocks require pretty simple maths; every minute and second is 1/60th of 360 degrees, so six degrees per minute and second. Every hour is 1/12th of 360 degrees, so 30 degrees. I decided that, although I wanted the second hand to tick and not sweep, I wanted the hour and minute hands to sweep because that’s what they appear to do on clocks. I guess it’s a UX thing that allows people to see it’s approaching a new hour. This makes the math slightly more complex, but essentially you want:

  • minutes angle = (minutes * 6) + (seconds angle / 60)
  • hours angle = (hours * 30) + (minutes angle / 12)

The clocks then get updated, by JavaScript to be something like like:

And then to make sure the hands actually rotate around the clock a full 360 degrees back to their start point, I used JavaScript to write in the animation keyframes with an end position being the angles above, plus 360. CSS rotate values above 360 get auto translated otherwise the clocks would end up doing a weird jitter at the 12 o’clock position. The JavaScript writes something like this in:

My own paranoia about the clocks being accurate meant that I have a setTimout that re-writes the rotate values and animations every 30 minutes to maintain accuracy, although I have no evidence to suggest the clocks become inaccurate.

I added digital type clocks because, to be honest, I’ve always struggled to read analogue clocks. I’ve always appreciated the elegance of a nice analogue time-piece but never liked having to decipher the information they tell you; digital clocks just straight out tell you the time. At the moment the time of the clocks is checked and written every second, though should probably have a check inside the JavaScript that if the time hasn’t changed, don’t write to the page.

For the weather I looked at several public API’s and then spotted Forecast had one. I immediately chose their API because I’m a big fan of their weather apps. Again, this was a personal project for internal use—had it been a full AREA 17 project I would’ve evaluated the other API’s much more closely. The Forecast API calls run through a php proxy for cross domain purposes and to hide the API key. But this seems to cause some annoying pause on load so I might change to a JSONP call. Each page load triggers five API requests and then every 30 mins, five more API requests to update the weather conditions. The API allows 1000 requests per day for free and then $1/1000 calls after that. It’s hooked up to my credit card so if you sit and refresh this endlessly I will hunt you down.

A17 Timezones

Dark Sky open sourced their cute animated icons, or “Skycons” as they call them. Each one is a little requestAnimationFrame animated HTML5 Canvas. Frustratingly they looked blurry on Retina screens if you didn’t use Safari so I forked their code, fixed it, and submitted a pull request to them. Safari incorrectly auto handles window.devicePixelRatio and context.backingStorePixelRatio in a way that Firefox and Chrome don’t and shouldn’t. Though I wish they did so that canvas would automatically look great on HiDPI screens without a fix and also since this was a personal project and bugs like this nearly made me miss the start of “Diamonds are Forever”.

The app stores your settings using JavaScript localStorage. One thing I learned is that looping checkboxes to listen for clicks and to create an object based on checkbox name and value is easy while looping radios, in comparison, is a pain. Once I’d started group sets of radio inputs and looked at the checked states of the radios within the groups it all fell into place.

Also, since I’ve been going on and on recently about not using jQuery, I didn’t. Instead I used my fork of Remy Sharp’s min.js updated to add reading/writing of attributes and CSS properties in a more jQuery like way over the version I had earlier. It’s tiny in comparison to jQuery and only does a few things that I do over and over and over on all projects.

Future development

I’m pretty happy with the result and we’re all back at work after the holidays and James Bond films won’t be on the television now until Easter. I might consult our design directors, David and Martin, for some design tips. Also my dislike of plugins may lead me to writing out moment.js, moment-timezone.js and the forecast api proxy which account for all the bulk in JavaScript weight on this and I’m only using very small bits of each. I would also like to feed the app a JSON file updatable by a very simple CMS to add/remove timezones displayed when we need them.