Javascript countdown and timezone and daylight saving time issues - javascript

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.

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()))

Get the real time of a user

I'm facing a problem to get the real 'trusted' user time, preventing them to cheat if they change their computer's time.
Weather I use a plain date object, or moment timezone or even google timezone api I just can't get the 'real' time of a user if I try manipulating the current time.
If we are at 20:00 (no matther the location) and the user tempers with the time to set it as 11:00 then I always end up with that time and not the real one, either by
const time = new Date();
const timestamp = (time.getTime() / 1000 + time.getTimezoneOffset() * 60);
const url = 'https://maps.googleapis.com/maps/api/timezone/json?location=-31.369926900000003,-64.2218601&timestamp=1568396292&key=MY_API_KEY';
this.httpDuplicate.get(url ).subscribe((res: any) => {
if (res) {
const dst = res.dstOffset;
const raw = res.rawOffset;
const fixed = (timestamp + raw + dst) * 1000;
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const currentTime = momenttz().tz(timezone).format();
console.warn('initial time ', new Date(time),
' - google time ', new Date(fixed),
' - moment timezone ', currentTime);
// all these values are wrong and point to 11:00 rather than 20:00
}
});
Is there a way to achieve this ? What I want in the end is to get the right time for a certain position... clearly not trusting the user's system time, but do trust its location (though there are ways to alter this as well)
The reason it fails is because you reference the Date object you made based on the user's client time ('time' variable).
If you don't trust the system time, you'd have to get the time from somewhere else, like an external server that you do trust, or the backend of your application if you have one (server time).
For example: http://worldtimeapi.org/
Of course, you'd also need the user's location in that case. I can imagine you don't trust that either since you also don't trust the time, so in that scenario there's no way to do it.

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.

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

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)

Displaying another computer's time on a web page using Javascript? [duplicate]

This question already has answers here:
Clock on webpage using server and system time?
(8 answers)
Closed 9 years ago.
I am working on a very time-sensitive web application. One of the business rules given to me is that the application's behavior must always depend on the time on the web server, regardless of what time is on the client's clock. To make this clear to the user, I was asked to display the server's time in the web application.
To this end, I wrote the following Javascript code:
clock = (function () {
var hours, minutes, seconds;
function setupClock(updateDisplayCallback) {
getTimeAsync(getTimeCallback);
function getTimeCallback(p_hours, p_minutes, p_seconds) {
hours = p_hours;
minutes = p_minutes;
seconds = p_seconds;
setInterval(incrementSecondsAndDisplay, 1000);
}
function incrementSecondsAndDisplay() {
seconds++;
if (seconds === 60) {
seconds = 0;
minutes++;
if (minutes === 60) {
minutes = 0;
hours++;
if (hours === 24) {
hours = 0;
}
}
}
updateDisplayCallback(hours, minutes, seconds);
}
}
// a function that makes an AJAX call and invokes callback, passing hours, minutes, and seconds.
function getTimeAsync(callback) {
$.ajax({
type: "POST",
url: "Default.aspx/GetLocalTime",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
var date, serverHours, serverMinutes, serverSeconds;
date = GetDateFromResponse(response);
serverHours = date.getHours();
serverMinutes = date.getMinutes();
serverSeconds = date.getSeconds();
callback(serverHours, serverMinutes, serverSeconds);
}
})
}
return {
setup: setupClock
};
})();
The function passed in for updateDisplayCallback is a simple function to display the date on the web page.
The basic idea is that the Javascript makes an asynchronous call to look up the server's time, store it on the client, and then update it once per second.
At first, this appears to work, but as time goes by, the displayed time gets behind a few seconds every minute. I left it running overnight, and when I came in the next morning, it was off by more than an hour! This is entirely unacceptable because the web application may be kept open for days at a time.
How can I modify this code so that the web browser will continuously and accurately display the server's time?
Javascript's setInterval is not accurate enough to allow you to keep the time like this.
My solution would be:
Periodically get the server's time in milliseconds (it does not need to be very often as the two clocks will hardly deviate that much)
Get the client time in milliseconds
Calculate the clock deviation between server and client (client-server)
Periodically update the display of the clock by getting the client time and adding the clock deviation
Edit:
To be more accurate, you could measure the round trip time of the server's request, divide it by 2 and factor that delay into the clock deviation. Assuming round trips are symmetrical in their duration, this would give a more accurate calculation.
setInterval is not a reliable way to schedule time critical events. It may take less or more than 1000ms to run your callback depending on how busy JavaScript it is at the moment.
A better approach would be to take a shorter interval and use new Date().getTime() to check if a second has passed.
The minimum interval browsers allow is as high 10.
Thanks for the answers. I have up-voted both answers so far as they contain useful information. However, I am not using the exact answer prescribed in either answer.
What I finally decided on is a bit different.
I wrote about what I learned on my personal web page.
First of all, I now understand that using setInterval(..., 1000) is not good enough to have something done once per second for a long time. However, 'polling' the time with a much shorter interval looking for the second to change seems very inefficient to me.
I decided that it does make sense to keep track of the 'offset' between the server time and the client time.
My final solution is to do the following:
(1) Do an AJAX call to the server to get the time. The function also checks the client time and computes the difference between the server time and the client time, in milliseconds. Due to network latency and other factors, this initial fetch may be off by a few seconds. For my purposes, this is okay.
(2) Execute a tick function. Each time tick executes, it checks how long it has been since the last time tick executed. It will use this time to compute an argument to be passed to setTimeout so that the time display is updated approximately once per second.
(3) Each time the tick function computes the time to be displayed, it takes the client time and adds the difference that was computed in step (1). This way, I don't depend upon the client to have the time set correctly, but I do depend upon the client to accurately measure elapsed time. For my purposes, this is okay. The most important thing is that regardless of how setTimeout may be inaccurate or interrupted by other processes (such as a modal dialog, for instance), the time displayed should always be accurate.

Categories

Resources