Guesstimate time zone from time offset with JavaScript and Moment.js - javascript

I know this is not going to be foolproof because an offset isn't specific to a timezone, but with location data it seems like it would be possible to make an educated guess.
Basically, I would like to be able to take an object similar to this:
{
offset: -5,
location: 'America'
}
...and get back either a single or multiple matching time zones, which would be:
['America/Montreal', 'America/New_York', ...]
One solution I can think of is iterating through the zone data provided by moment-timezone, but that just doesn't seem like an elegant way to get this info.
Any ideas?

It's not really elegant but iterating through the time zone database allows to get all timezones associated with a given offset.
Note that the timezone database stores the variation of offset due to the daylight saving rules and also the historical evolution of time zones.
Given an offset in minutes, this function returns, according to the iana timezone database, the list of all timezones that used this offset once in the history or that will use this offset once in the futur.
function getZonesByOffset(offset){
//offset in minutes
results = [];
var tzNames = moment.tz.names();
for(var i in tzNames){
var zone = moment.tz.zone(tzNames[i]);
for(var j in zone.offsets){
if(zone.offsets[j] === offset){
//Add the new timezone only if not already present
var inside = false;
for(var k in results){
if(results[k] === tzNames[i]){
inside = true;
}
}
if(!inside){
results.push(tzNames[i]);
}
}
}
}

moment-timezone#0.5.0 added moment.tz.guess() which attempts to guess the user's most likely timezone by looking at Date#getTimezoneOffset and Date#toString. It then picks the zone with the largest population. It's not perfect, but it's close enough! Data is pulled from this table and Intl is used when available.
Fiddle
moment.tz.guess()
//= America/New_York (I'm in America/Montreal, but this works too)

Related

How to convert any timezone to local timezone in javascript?

The below code converts the date to local timezone:
function convertDateToServerDate(incomingDate) {
var serverOffset = new Date();
serverOffset = serverOffset.getTimezoneOffset();
var outgoingServerDate = new Date((incomingDate.getTime() + Math.abs(serverOffset * 60 * 1000)));
return outgoingServerDate;
}
I have a date in the IST timezone. I'm also in the IST timezone the above function changes the time whereas it should just return it. I also tried converting to UTC then back to the local but the same result. How to get a local timezone and do nothing if the date is already in local time zone?
You can't do this with vanilla Javascript without a library, because the Date class only comprehends two timezones: GMT, and local. (Source]
Libraries like momentjs and date-fns do not merely provide convenient functions, they very importantly also include hard-coded datasets that specify current real-world facts about time zone adjustments, including things like the dates where DST switches. This is vital, because if you look at the map of timezones, you'll see that the boundaries of the different zones are not straight lines. That's because they are determined by humans who made interesting compromises which are then enshrined in custom and law.
Many of those compromises are there so that people who share a jurisdiction can also share a clock. It would be enormously inconvenient for them otherwise, and many people would be adversely impacted every single day.
There is a proposal for a successor to Date, called Temporal, that would remedy this.
Best to use moment library. https://momentjs.com/timezone/docs/
moment().tz(String)
var a = moment.utc("2013-11-18 11:55").tz("Asia/Taipei");
var b = moment.utc("2013-11-18 11:55").tz("America/Toronto");
a.format(); // 2013-11-18T19:55:00+08:00
b.format(); // 2013-11-18T06:55:00-05:00
a.utc().format(); // 2013-11-18T11:55Z
b.utc().format(); // 2013-11-18T11:55Z
The server offset has to be set using INTL, hardcoded or come from the server
So something like this
const serverOffset = -240; // this is static
function convertDateToServerDate(incomingDate) {
const incomingOffset = incomingDate.getTimezoneOffset();
if (serverOffset === incomingOffset) return incomingDate;
console.log(serverOffset-incomingOffset,"difference")
const outGoingDate = new Date(incomingDate.getTime())
outGoingDate.setTime(incomingDate.getTime() + ((serverOffset-incomingOffset) * 60 * 1000));
return outGoingDate;
}
console.log(convertDateToServerDate(new Date()))

What's the efficient way to write a timestamp-value pair timeline in JavaScript?

I'm working on some code where a value can change over time and I need to keep track of that value over time so I can get the most recent value for any timestamp. I'm using Moment.js for timestamp manipulation.
I'm optimizing for a case where typically the value pair will never change over hundreds of thousands of timestamps, or rarely change, but I still need to know if / when it does, and what it was most recently at any point in time. Timestamp queries before the earliest recorded pair should come back false.
class Timeline {
constructor() {
this.valuePairs = []
}
setValueAt(value, timestamp) {
this.valuePairs.push({ timestamp, value })
}
getValueAt(timestamp) {
// obviously I could iterate through this.valuePairs brute force, get
// the difference between each value-pair's timestamp and the target
// timestamp, returning the value with the smallest difference, but I
// feel bad about even writing this code, it's incredibly
// inefficient, and for big datasets, this would create a massive
// array to search through and take up memory
let earliest = Infinity
let closest = {
value: false,
difference: Infinity,
}
for (let i = 0; i < this.valuePairs.length; i++) {
let valuePair = this.valuePairs[i]
let difference = timestamp - valuePair.timestamp
if (valuePair.timestamp < earliest) earliest = valuePair.timestamp
if (difference < closest.difference) {
closest.difference = difference
closest.value = value
}
}
if (closest.value && timestamp > earliest) {
closest.value
} else {
return value
}
}
}
This is so inefficient that it basically would crash any program processing large batches of data. But I honestly couldn't think of a good way to go about it. What's a more efficient way to write this class?
I think you can use Trie Tree to improve efficiency of your program. To use Trie Tree, you should covert timestamp to year, month, date, hour, minute, second and millisecond. And every element represents a node in Trie Tree as shown in the below. When you search a timestamp to find its value, you can look up a node in the Trie Tree which have a same year as the timestamp, then the same month and so on. If there's not a node which has the same year as the timestamp, you should look up the nearest neighbor. And this algorithm has constant time complexity. In the worst case, you should compare every millisecond and find the nearest value, and this will cost O(1000) time.

How to shift fullcalendar's display time by several minutes

This could be an embarrassingly easy question but I am new to Moment.js and fullcalendar.
The goal: Get fullcalendar to operate on a Moment/DateTime that is a few minutes offset from local computer time.
The rationale:
We want to synchronize the display time and nowIndicator with the old clocks in a building as opposed to the desktop's time.
Tried so far:
// Get current offset:
var localOffset = moment().utcOffset();
// Shift by 7 minutes for illustration
localOffset -= 7;
// Set new offset for moment
moment().utcOffset(localOffset);
alert(moment().utcOffset());
As of now it prints back the original UTC offset and the nowIndicator matches my system clock. This is using Moment.js 2.19.0.
Thanks for looking.
moment().utcOffset() is creating a new moment with the default offset. It has nothing to do with the object you previously set an offset on. momentJS works using individual objects which are instantiated by using the moment() constructor. It's not a static or global thing.
What you need to do is work with the moment object which you set the offset on:
var offsetMoment = moment();
offsetMoment.utcOffset(localOffset);
alert(offsetMoment.utcOffset());
ADyson's answer cleared the misconception about a moment object and its scope.
To answer the original problem of shifting FullCalendar's time by an arbitrary amount, use the 'now' option when initializing:
// Get current time as moment object and add 7 minute offset
var shiftedTime = moment().add(7, 'minutes');
// Set 'now' option in calendar initialization to new moment object
$('#calendar').fullCalendar({
// put your options and callbacks here
now: shiftedTime,
defaultView: 'agendaDay',
nowIndicator: true
});
The display of the calendar and the now indicator will be shifted 7 minutes relative to local machine time.

Javascript countdown and timezone and daylight saving time issues

Our team are having big issues with the JQuery countdown and we really need some help.
Initially, we had some ScriptSharp code that does this
JQueryCountdownOptions opts = new JQueryCountdownOptions();
opts.Layout = "<ul class=\"group\"> <li>{dn} <span>{dl}</span></li> <li>{hn} <span>{hl}</span></li> <li>{mn} <span>{ml}</span></li> <li>{sn} <span>{sl}</span></li> </ul>";
opts.Until = Number.ParseInt(timeLeft);
jQuery.Select("#countdownclock").Plugin<JQueryCountdown>().Countdown(opts);
jQuery.Select("#countdownclock").Show();
jQuery.Select("#bidBox").RemoveAttr("disabled");
What we noticed is that this uses the client's clock to countdown from. So, if the client decided to change his time to 5 hours ahead then the countdown would be 5 hours off.
To fix this we introduced some more code
In the view:
$(function () {
var expires = new Date(#year, #month, #day, #hours, #minutes, #seconds);
$('#clockDiv').countdown({ until: expires, timeZone: null, serverSync: serverTime, onTick: serverTime, tickInterval: 60 });
function serverTime() {
var time = null;
$.ajax({ url: '/Auction/SyncServerTime',
async: false, dataType: 'json',
success: function (result) {
time = new Date(result.serverTime);
}, error: function (http, message, exc) {
time = new Date();
}
});
return time;
}
});
In the controller
public JsonResult SyncServerTime()
{
var result = new JsonResult
{
Data = new
{
serverTime = DateTime.Now.ToString("MMM dd, yyyy HH:mm:ss zz")
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
return result;
}
This code ensures that no matter what the user sets his clock to the countdown timer will periodically sync to the server's time. Problem solved.
The only issue is that we have come up with other issues.
The problem is that when users are in different timezones then the countdowns of those users are different depending on the timezone offset that their timezone has. We have tried changing all sorts of parameters and still are having issues. To make matters worse if my timespan straddles a date when daylight saving time is applied then things go awry again, both for those in the same timezone and those in different ones. We have experimented with different code and parameters so the above is just what I did and is different from what my esteemed colleagues tried. What I am asking is surely, someone somewhere out there must have had a requirement to
Write a countdown that is independent of client time and based on server time.
Shows the same number of days, hours, minutes, seconds remaining no matter what timezone a user is in
Shows the same number of days, hours, minutes, seconds remaining for a user whose time will change in this period because of DST to user whose time will not change in this period because of DST
Shows the actual number of days, hours, minutes and seconds remaining for a user whose time will change in this period because of DST.
We cannot be the only people who have ever had this issue, surely. It cannot be this hard. Does anyone know a solution?
Thanks,
Sachin
I haven't dealt with the same scenarios personally, but seeing Date, timezone issues etc. pop up automatically triggers thoughts about some potential issues stemming from the use of local date objects as opposed to UTC date objects.
IMO, things are simply better off if all computation, serialization of dates only worked in the UTC space, and finally when it comes to present a date from a user, it is converted to local or appropriate time zone depending on the scenario. On the flip-side, the user enters local or some time zone relative entry, and immediately that is converted to UTC as the internal representation. This avoids all sorts of confusion across different layers/tiers of the app.
Its not really a solution to your specific problem, but perhaps something to consider that could lead to one.

Determine timezone from latitude/longitude without using web services like Geonames.org

is there any possibility to determine the timezone of point (lat/lon) without using webservices? Geonames.org is not stable enough for me to use :( I need this to work in PHP.
Thanks
I had this problem a while back and did exactly what adam suggested:
Download the database of cities from geonames.org
convert it to a compact lat/lon -> timezone list
use an R-Tree implementation to efficiently lookup the nearest city (or rather, its timezone) to a given coordinate
IIRC it took less than 1 second to populate the R-Tree, and it could then perform thousands of lookups per second (both on a 5 year old PC).
How exact do your results have to be? If a rough estimate is enough, calculate the offset yourself:
offset = direction * longitude * 24 / 360
where direction is 1 for east, -1 for west, and longitude is in (-180,180)
I ran into this problem while working on another project and looked into it very deeply. I found all of the existing solutions to be lacking in major ways.
Downloading the GeoNames data and using some spatial index to look up the nearest point is definitely an option, and it will yield the correct result a lot of the time, but it can easily fail if a query point is on the wrong side of a time zone border from the nearest point in the database.
A more accurate method is to use a digital map of the time zones and to write code to find the polygon in that map that contains a given query point. Thankfully, there is an excellent map of the time zones of the world available at http://efele.net/maps/tz/world/ (not maintained anymore). To write an efficient query engine, you need to:
Parse the ESRI shapefile format into a useful internal representation.
Write point-in-polygon code to test whether a given query point is in a given polygon.
Write an efficient spatial index on top of the polygon data so that you don't need to check every polygon to find the containing one.
Handle queries that are not contained by any polygon (e.g., in the ocean). In such cases, you should "snap to" the nearest polygon up to a certain distance, and revert to the "natural" time zone (the one determined by longitude alone) in the open ocean. To do this, you will need code to compute the distance between a query point and a line segment of a polygon (this is non-trivial since latitude and longitude are a non-Euclidean coordinate system), and your spatial index will need to be able to return nearby polygons, not just potentially containing polygons.
Each of those are worthy of their own Stack Overflow question/answer page.
After concluding that none of the existing solutions out there met my needs, I wrote my own solution and made it available here:
http://askgeo.com
AskGeo uses a digital map and has a highly optimized spatial index that allows for running more than 10,000 queries per second on my computer in a single thread. And it is thread safe, so even higher throughput is certainly possible. This is a serious piece of code, and it took us a long time to develop, so we are offering it under a commercial license.
It is written in Java, so using it in PHP would involve using:
http://php-java-bridge.sourceforge.net/doc/how_it_works.php
We are also open to porting it for a bounty. For details on the pricing, and for detailed documentation, see http://askgeo.com.
I hope this is useful. It certainly was useful for the project I was working on.
I know this is old, but I spent some time looking for this answer. Found something very useful. Google does time zone lookups by long/lat. No free tier anymore :-/
https://developers.google.com/maps/documentation/timezone/
You should be able to, if you know the polygon of the timezone to see if a given lat/lon is inside it.
World Time Zone Database
Latitude/Longitude Polygon Data
For areas on land, there are some shapefile maps that have been made for the timezones of the tz (Olson) database. They're not updated quite as regularly as the tz database itself, but it's a great starting point and seems to be very accurate for most purposes.
How about this ?
// ben#jp
function get_nearest_timezone($cur_lat, $cur_long, $country_code = '') {
$timezone_ids = ($country_code) ? DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $country_code)
: DateTimeZone::listIdentifiers();
if($timezone_ids && is_array($timezone_ids) && isset($timezone_ids[0])) {
$time_zone = '';
$tz_distance = 0;
//only one identifier?
if (count($timezone_ids) == 1) {
$time_zone = $timezone_ids[0];
} else {
foreach($timezone_ids as $timezone_id) {
$timezone = new DateTimeZone($timezone_id);
$location = $timezone->getLocation();
$tz_lat = $location['latitude'];
$tz_long = $location['longitude'];
$theta = $cur_long - $tz_long;
$distance = (sin(deg2rad($cur_lat)) * sin(deg2rad($tz_lat)))
+ (cos(deg2rad($cur_lat)) * cos(deg2rad($tz_lat)) * cos(deg2rad($theta)));
$distance = acos($distance);
$distance = abs(rad2deg($distance));
// echo '<br />'.$timezone_id.' '.$distance;
if (!$time_zone || $tz_distance > $distance) {
$time_zone = $timezone_id;
$tz_distance = $distance;
}
}
}
return $time_zone;
}
return 'none?';
}
//timezone for one NY co-ordinate
echo get_nearest_timezone(40.772222,-74.164581) ;
// more faster and accurate if you can pass the country code
echo get_nearest_timezone(40.772222, -74.164581, 'US') ;
I've written a small Java class to do this. It could be easily translated to PHP. The database is embedded in the code itself. It's accurate to 22km.
https://sites.google.com/a/edval.biz/www/mapping-lat-lng-s-to-timezones
The whole code is basically stuff like this:
if (lng < -139.5) {
if (lat < 68.5) {
if (lng < -140.5) {
return 371;
} else {
return 325;
}
...so I presume a translation to PHP would be easy.
Unfortunately, time zones are not regular enough for some simple function. See the map in Wikipedia - Time Zone
However, some very rough approximation can be calculated: 1 hour difference corresponds to 15 degrees longitude (360 / 24).
Another solution is to import a table of cities with timezones and then to use the Haversine formula to find the nearest city in that table, relevant to your coordinates.
I have posted a full description here: http://sylnsr.blogspot.com/2012/12/find-nearest-location-by-latitude-and.html
For an example of loading the data in MySQL, I have posted an example here (with sources for downloading a small data dump): http://sylnsr.blogspot.com/2012/12/load-timezone-data-by-city-and-country.html
Note that the accuracy of the look-up will be based on how comprehensive your look-up data is.
Credits and References:
MySQL Great Circle Distance (Haversine formula)
You can use time zone boundaries, provided here:
http://www.opensource.apple.com/source/TimeZoneData/
Not sure if this is useful or not, but I built a database of timezone shapes (for North America only), which is painstakingly accurate and current not just for borders, but also for daylight saving time observance. Also shapes for unofficial exceptions. So you could query the set of shapes for a given location could return multiple shapes that apply to that location, and choose the correct one for the time of year.
You can see an image of the shapes at http://OnTimeZone.com/OnTimeZone_shapes.gif. Blue shapes are around areas that do not observe daylight saving time, magenta shapes those that do observe daylight saving time, and neon green shapes (small and tough to see at that zoom level) are for areas with unofficial deviation from the official time zone. Lots more detail on that available at the OnTimeZone.com site.
The data available for download at OnTimeZone.com is free for non-commercial use. The shape data, which is not available for download, is available for commercial license.
I downloaded data that matches 5 digit zip codes to time zones, and data that determines the UTC offset for each time zone during DST and non-DST periods.
Then I simplified the data to only consider the first three digits of the ZIP Code, since all ZIP codes that share the first three digits are very close to each other; the three digits identify a unique mail distribution center.
The resulting Json file does require you to decide whether or not you are subject to DST currently, and it probably has some inaccuracy here and there. But it's a pretty good local solution that is very compact and simple to query.
Here it is:
https://gist.github.com/benjiwheeler/8aced8dac396c2191cf0
I use geocoder.ca
Input any location in North America, Output geocodes, area codes and timezone in json or jsonp.
For example: http://geocoder.ca/1600%20pennsylvania%20avenue,Washington,DC
Area Code: (202)
Time Zone: America/New_York
Json:
{"standard":{"staddress":"Pennsylvania Ave","stnumber":"1600","prov":"DC","city":"WASHINGTON","postal":"20011","confidence":"0.8"},"longt":"-76.972948","TimeZone":"America/New_York","AreaCode":"202","latt":"38.874533"}
You can use Google Timezone api. https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510&timestamp=1331161200&key=YOUR_API_KEY
{
"dstOffset" : 0,
"rawOffset" : -28800,
"status" : "OK",
"timeZoneId" : "America/Los_Angeles",
"timeZoneName" : "Pacific Standard Time"
}
You can get the timezone based on the location in javascript.
function initAutocomplete() {
// Create the autocomplete object, restricting the search to geographical
// location types.
autocomplete = new google.maps.places.Autocomplete(
/** #type {!HTMLInputElement} */(document.getElementById('Location')),
{types: ['geocode']});
// When the user selects an address from the dropdown, populate the address
// fields in the form.
autocomplete.addListener('place_changed', fillInAddress);
}
function fillInAddress() {
// Get the place details from the autocomplete object.
var place = autocomplete.getPlace();
fnGettimezone($('#Location').serialize());
for (var component in componentForm) {
document.getElementById(component).value = '';
document.getElementById(component).disabled = false;
}
// Get each component of the address from the place details
// and fill the corresponding field on the form.
for (var i = 0; i < place.address_components.length; i++) {
var addressType = place.address_components[i].types[0];
if (componentForm[addressType]) {
var val = place.address_components[i][componentForm[addressType]];
document.getElementById(addressType).value = val;
}
}
}
function fnGettimezone(location) {
$.ajax({
url: "http://maps.googleapis.com/maps/api/geocode/json?address=" + location + "&sensor=false",
dataType: 'json',
success: function (result) {
console.log(result);
// result is already a parsed javascript object that you could manipulate directly here
var myObject = JSON.parse(JSON.stringify(result));
lat = myObject.results[0].geometry.location.lat;
lng = myObject.results[0].geometry.location.lng;
console.log(myObject);
$.ajax({
url: "https://maps.googleapis.com/maps/api/timezone/json?location=" + lat + "," + lng + "&timestamp=1331161200&sensor=false",
dataType: 'json',
success: function (result) {
// result is already a parsed javascript object that you could manipulate directly here
var myObject = JSON.parse(JSON.stringify(result));
$('#timezone').val(myObject.timeZoneName);
}
});
}
});
}
This project builds a shapefile from data from OpenStreetMap and link to multiple libraries that handle it.
For PHP you can look at https://github.com/minube/geo-timezone

Categories

Resources