Javascript time difference - javascript

I am trying to simply calculate the time difference of 5:30:00 - 2:30:00. Obviously this should result in 3:00:00
However when I execute following code in console
var a = new Date(0,0,0,5,30,0)
var b = new Date(0,0,0,2,30,0)
var c = new Date(a-b)
console.log(c.getHours() + ":" + c.getMinutes() + ":" + c.getSeconds())
The result is 4:00:00.
What is causing this problem? And how should I handle it?

Date constructor is not suitable to either represent or deal the time spans.
There are no built-in tools to handle time spans in JS, so you need to implement one yourself.
Thankfully the time string -> seconds conversion is trivial:
const timeToSec = time => time.split(':').reduce((acc, v) => acc * 60 + parseInt(v), 0);
Then you can deal with seconds:
const diffInSeconds = timeToSec('5:30:00') - timeToSec('2:30:00'); // 10800
The reverse transformation of seconds -> time string is also trivial (and tbh it's simple reversed of the timeToSec implementation) and I'm leaving it as a home work.

The reason why people get different results is timezone.
When you calculate c as the difference between two dates, you actually get a date relative to 01.01.1970. In this case, when you do:
console.log(c);
You get something like:
1970-01-01T03:00:00.000Z
This is in UTC Date format.
But now if you would display c in the local time zone:
console.log(c.toLocaleDateString()+ ' ' + c.toLocaleTimeString());
... then you get maybe this:
1-1-1970 04:00:00
If you then take the hours of that date with getHours(), you get them from the date/time as it is in your time zone, in your case you are on GMT+1, which means the outcome is 4.
To avoid this time zone conversion, use the UTC versions of the getXXXX functions, like getUTCHours. Note that some time zones have non-integer hour differences with UTC (with an half-hour part), so they would need to use getUTCMinutes as well.
Be aware that converting date differences to Date format will start to give wrong results when you cover larger spans, crossing 29 February, ...etc. Differences are best calculated by taking the date differences (in milliseconds) without conversion to Date. From there it is straightforward to calculate the number of seconds, minutes, ...etc.

I would like to assume that your question is not merely about the difference between two numbers, but it is a real problem.
Then, the answer is: without specifying the day(s), the difference between two hours is meaningless, you must always specify which day(s) you are talking about.
For example Europe, Berlin:
Sunday, 27 March 2016, 02:00:00 clocks were turned
forward 1 hour.
Like in your example, this would lead to pay someone 1 hour more than it had worked...
Sunday, 30 October 2016, 03:00:00 clocks are turned backward 1 hour
..calculating the same interval Sunday, 30 October 2016 would do the opposite.
Moreover, be aware that daylight saving time has become standard in the U.S., Canada, and most European countries. However, most of the world doesn't even use it.
Moreover, during the past, starting day and ending day of saving time has been changed from year to year, and the starting hour has been also changed, i.e. you cannot assume always 2.00 at night - so reconstruct an interval without knowing this information would not lead to a correct result.
A possible solution to calculate correctly a duration, is always to store next the Local Date/Time also the UTC Date/Time and do the calculation keeping both in account, so you have both the Timezone and the Daylight Saving Time Shift to get back the exact start date/time and ending date/time.
If you don't have this information already stored, then you should retrieve them, for example, from a online Time Database.

usage
node time_diff.js 5:30:00 2:30:00
will output: 03:00:00
code of time_diff.js:
#!/usr/bin/env node
if (!process.argv[2] || !process.argv[3]) {
console.log('usage: time_diff hh:mm:ss hh:mm:ss');
process.exit(1);
}
const timeToSec = (time) => time.split(':').reduce((acc, v) => acc * 60 + parseInt(v), 0);
const diffInSeconds = (time1, time2) => timeToSec(time2) - timeToSec(time1);
const hhmmss = (secs) => {
var minutes = Math.floor(secs / 60);
secs = secs % 60;
var hours = Math.floor(minutes / 60);
minutes = minutes % 60;
return pad(hours) + ":" + pad(minutes) + ":" + pad(secs);
function pad(num) {
return ("0" + num).slice(-2);
}
};
try {
console.log(hhmmss(diffInSeconds(process.argv[3], process.argv[2])));
}
catch (err) {
console.log('usage: time_diff hh:mm:ss hh:mm:ss');
process.exit(1);
}

Try this simple plugin to get time differences.
https://github.com/gayanSandamal/good-time
import the goodTimeDiff method from good-time.js to your project
import {goodTimeDiff} from './scripts/good-time.js'
declare an object to give settings like below. let settings = {}
now assign time values to the declared object variable. *time must be in standard format and must be a string! *'from' is optional the default value will be the browser current time.
let settings = {
'from': '2019-01-13T00:00:29.251Z',
'to': '2018-09-22T17:15:29.251Z'
}
now calllback the method called goodTimeDiff() and pass the settings object variable as a parameter.
goodTimeDiff(settings)
Finally assign the method to any variable you want.
let lastCommentedTime = goodTimeDiff(timeSettings)

a - b results in three hours, but in milliseconds. You just need to convert milliseconds to hours (which is not new Date(milliseconds)).
try: (a-b)/1000/60/60
Formatted:
var a = new Date(0,0,0,5,30,0)
var b = new Date(0,0,0,2,30,0)
var diff = (a.getTime()-b.getTime())
var h = Math.floor(diff/1000/60/60)
var m = ('0' + Math.floor((diff/1000/60)%60) ).substr(-2)
var s = ('0' + Math.floor((diff/1000)%60) ).substr(-2)
console.log(h + ':' + m + ':' + s)
EDIT
For those who want to treat a time span as a date... just get the UTC date, which means Coordinated Universal Time. In other words, don't use timezone aware methods:
var a = new Date(0,0,0,5,30,0)
var b = new Date(0,0,0,2,30,0)
var c = new Date(a-b)
console.log(c.getUTCHours() + ":" + c.getUTCMinutes() + ":" + c.getUTCSeconds())
Be aware though, this will fall apart on edge cases...

Related

Convert Text to Integer in JS

I am trying to use Tampermonkey to find a UTC time offset and return it as as a time. The website shows an offset which I pull here
waitForKeyElements (".UTCText", getTZ_Offset);
which returns a string
console.log ("Found timezone offset: ", tzOffset);
usually like this 08:00 It can be + or -
Then i want to convert that into actual time. Eg if UTC time is 00:00, I would like to print a string "The users time is 08:00" if the offset was +08:00.
I thought i could use momentjs to get UTC time moment().utcOffset(tzOffset) and pass the offset.
When i do that it just returns NaN
What am I doing wrong?
Multiply the part before the : by 60, and add it to the second part:
const tzOffset = '08:00';
const [hourOffset, minuteOffset] = tzOffset.split(':').map(Number);
const totalMinuteOffset = hourOffset * 60 + minuteOffset;
console.log(totalMinuteOffset);
If the input may be negative, then check that as well:
const tzOffset = '-08:00';
const [_, neg, hourOffset, minuteOffset] = tzOffset.match(/(-)?(\d{2}):(\d{2})/);
const totalMinuteOffset = (neg ? -1 : 1) * (hourOffset * 60 + Number(minuteOffset));
console.log(totalMinuteOffset);
A few time zones differ from UTC not only by hours, but by minutes as well (eg, UTC +5:30, UTC +9:30), so just parseInt, even if it worked, wouldn't be reliable everywhere.

JavaScript Date Validation when using firefox [duplicate]

I am creating a datetime string that looks like this: 2010-07-15 11:54:21
And with the following code I get invalid date in Firefox but works just fine in Chrome
var todayDateTime = year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + seconds;
var date1 = new Date(todayDateTime);
In firefox date1 is giving me an invalid date, but in chrome its working just fine what would the main cause be?
You can't instantiate a date object any way you want. It has to be in a specific way. Here are some valid examples:
new Date() // current date and time
new Date(milliseconds) //milliseconds since 1970/01/01
new Date(dateString)
new Date(year, month, day, hours, minutes, seconds, milliseconds)
or
d1 = new Date("October 13, 1975 11:13:00")
d2 = new Date(79,5,24)
d3 = new Date(79,5,24,11,33,0)
Chrome must just be more flexible.
Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
From apsillers comment:
the EMCAScript specification requires exactly one date format (i.e., YYYY-MM-DDTHH:mm:ss.sssZ) but custom date formats may be freely supported by an implementation: "If the String does not conform to that [ECMAScript-defined] format the function may fall back to any implementation-specific heuristics or implementation-specific date formats." Chrome and FF simply have different "implementation-specific date formats."
This works in all browsers -
new Date('2001/01/31 12:00:00 AM')
new Date('2001-01-31 12:00:00')
Format: YYYY-MM-DDTHH:mm:ss.sss
Details: http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
Option 1 :
Suppose your timestring has a format that looks like this :
'2016-03-10 16:00:00.0'
In that case, you could do a simple regex to convert it to ISO 8601 :
'2016-03-10 16:00:00.0'.replace(/ /g,'T')
This would procude the following output :
'2016-03-10T16:00:00.0'
This is the standard datetime format, and thus supported by all browsers :
document.body.innerHTML = new Date('2016-03-10T16:00:00.0') // THIS IS SAFE TO USE
Option 2 :
Suppose your timestring has a format that looks like this :
'02-24-2015 09:22:21 PM'
Here, you can do the following regex :
'02-24-2015 09:22:21 PM'.replace(/-/g,'/');
This, too, produces a format supported by all browsers :
document.body.innerHTML = new Date('02/24/2015 09:22:21 PM') // THIS IS SAFE TO USE
Option 3 :
Suppose you have a time string that isn't easy to adjust to one of the well-supported standards.
In that case, it's best to just split your time string into different pieces and use them as individual parameters for Date :
document.body.innerHTML = new Date(2016, 2, 26, 3, 24, 0); // THIS IS SAFE TO USE
This works in most browsers as well
new Date('2001/01/31 12:00:00')
That is the format of
"yyyy/MM/dd HH:mm:ss"
If you still want to create date using dashes, you can use this format:
var date = new Date('2013-08-31T17:00:00Z')
But bear in mind, that it creates time according to UTC. Meaning, if you live in GMT+3 (3 hours ahead of GMT) timezone, it will add this timezone offset to the time. So the above example will have this value, if GMT+3 (note that it is hour 20:00 and not 17:00):
Sat Aug 31 2013 20:00:00 GMT+0300 (FLE Standard Time)
Be sure to add 'Z' letter at the end, because otherwise Chrome and Firefox will parse the string differently (one will add time offset and the other won't).
I was having a similar issue in both Firefox and Safari when working with AngularJS. For example, if a date returned from Angular looked like this:
2014-06-02 10:28:00
using this code:
new Date('2014-06-02 10:28:00').toISOString();
returns an invalid date error in Firefox and Safari. However in Chrome it works fine. As another answer stated, Chrome is most likely just more flexible with parsing date strings.
My eventual goal was to format the date a certain way. I found an excellent library that handled both the cross browser compatibility issue and the date formatting issue. The library is called moment.js.
Using this library the following code works correctly across all browsers I tested:
moment('2014-06-02 10:28:00').format('MMM d YY')
If you are willing to include this extra library into your app you can more easily build your date string while avoiding possible browser compatibility issues. As a bonus you will have a good way to easily format, add, subtract, etc dates if needed.
This should work for you:
var date1 = new Date(year, month, day, hour, minute, seconds);
I had to create date form a string so I have done it like this:
var d = '2013-07-20 16:57:27';
var date1 = new Date(d.substr(0, 4), d.substr(5, 2), d.substr(8, 2), d.substr(11, 2), d.substr(14, 2), d.substr(17, 2));
Remember that the months in javascript are from 0 to 11, so you should reduce the month value by 1, like this:
var d = '2013-07-20 16:57:27';
var date1 = new Date(d.substr(0, 4), d.substr(5, 2) - 1, d.substr(8, 2), d.substr(11, 2), d.substr(14, 2), d.substr(17, 2));
Simple Solution, This works with All Browsers,
var StringDate = "24-11-2017"
var DateVar = StringDate.split("-");
var DateVal = new Date(DateVar[1] + "/" + DateVar[0] + "/" + DateVar[2]);
alert(DateVal);
One situation I've run into was when dealing with milliseconds. FF and IE will not parse this date string correctly when trying to create a new date.
"2014/11/24 17:38:20.177Z"
They do not know how to handle .177Z. Chrome will work though.
This is what worked for me on Firefox and Chrome:
// The format is 'year-month-date hr:mins:seconds.milliseconds'
const d = new Date('2020-1-4 12:00:00.999')
// we use the `.` separator between seconds and milliseconds.
Good Luck...
There is a W3C specification defining possible date strings that should be parseable by any browser (including Firefox and Safari):
Year:
YYYY (e.g., 1997)
Year and month:
YYYY-MM (e.g., 1997-07)
Complete date:
YYYY-MM-DD (e.g., 1997-07-16)
Complete date plus hours and minutes:
YYYY-MM-DDThh:mmTZD (e.g., 1997-07-16T19:20+01:00)
Complete date plus hours, minutes and seconds:
YYYY-MM-DDThh:mm:ssTZD (e.g., 1997-07-16T19:20:30+01:00)
Complete date plus hours, minutes, seconds and a decimal fraction of a
second
YYYY-MM-DDThh:mm:ss.sTZD (e.g., 1997-07-16T19:20:30.45+01:00)
where
YYYY = four-digit year
MM = two-digit month (01=January, etc.)
DD = two-digit day of month (01 through 31)
hh = two digits of hour (00 through 23) (am/pm NOT allowed)
mm = two digits of minute (00 through 59)
ss = two digits of second (00 through 59)
s = one or more digits representing a decimal fraction of a second
TZD = time zone designator (Z or +hh:mm or -hh:mm)
According to YYYY-MM-DDThh:mmTZD, the example 2010-07-15 11:54:21 has to be converted to either 2010-07-15T11:54:21Z or 2010-07-15T11:54:21+02:00 (or with any other timezone).
Here is a short example showing the results of each variant:
const oldDateString = '2010-07-15 11:54:21'
const newDateStringWithoutTZD = '2010-07-15T11:54:21Z'
const newDateStringWithTZD = '2010-07-15T11:54:21+02:00'
document.getElementById('oldDateString').innerHTML = (new Date(oldDateString)).toString()
document.getElementById('newDateStringWithoutTZD').innerHTML = (new Date(newDateStringWithoutTZD)).toString()
document.getElementById('newDateStringWithTZD').innerHTML = (new Date(newDateStringWithTZD)).toString()
div {
padding: 10px;
}
<div>
<strong>Old Date String</strong>
<br>
<span id="oldDateString"></span>
</div>
<div>
<strong>New Date String (without Timezone)</strong>
<br>
<span id="newDateStringWithoutTZD"></span>
</div>
<div>
<strong>New Date String (with Timezone)</strong>
<br>
<span id="newDateStringWithTZD"></span>
</div>
In fact, Chrome is more flexible to deal with different string format. Even if you don't figure out its String format, Chrome still can successfully convert String to Date without error. Like this:
var outputDate = new Date(Date.parse(inputString));
But for Firefox and Safari, things become more complex. In fact, in Firefox's document, it already says: (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse)
A string representing an RFC2822 or ISO 8601 date (other formats may be used, but results may be unexpected).
So, when you want to use Date.parse in Firefox and Safari, you should be careful. For me, I use a trick method to deal with it. (Note: it might be not always correct for all cases)
if (input.indexOf("UTC") != -1) {
var tempInput = inputString.substr(0, 10) + "T" + inputString.substr(11, 8) + "Z";
date = new Date(Date.parse(tempInput));
}
Here it converts 2013-08-08 11:52:18 UTC to 2013-08-08T11:52:18Z first, and then its format is fit words in Firefox's document. At this time, Date.parse will be always right in any browser.
In Firefox, any invalid Date is returned as a Date object as Date 1899-11-29T19:00:00.000Z, therefore check if browser is Firefox then get Date object of string "1899-11-29T19:00:00.000Z".getDate(). Finally compare it with the date.
I have used following date format and it's working in all browser.
var target_date = new Date("Jul 17, 2015 16:55:22").getTime();
var days, hours, minutes, seconds;
var countdown = document.getElementById("countdown");
remaining = setInterval(function () {
var current_date = new Date().getTime();
var seconds_left = (target_date - current_date) / 1000;
days = parseInt(seconds_left / 86400);
seconds_left = seconds_left % 86400;
hours = parseInt(seconds_left / 3600);
seconds_left = seconds_left % 3600;
minutes = parseInt(seconds_left / 60);
seconds = parseInt(seconds_left % 60);
countdown.innerHTML = "<b>"+days + " day, " + hours + " hour, "
+ minutes + " minute, " + seconds + " second.</b>";
}, 1000);

calculate difference in dates, get difference in hours mins and seconds - ideally by js, otherwise moment.js

I've managed in calculating date differences by:
converting unix date received into js date,
Saving current date as js date,
passing both to moment.js together with their format to get diff
converting to milliseconds
difference in ms is converted to a moment and returns hours mins secs
I've run into an issue where specific versions of moment works this out, and others throws exception as nan internally when calc differences. Would love to do it using just plain js, hopefully circumventing this scenario.
Uploaded a fiddle, it doesnt run unless you comment out the moment part since didnt find a moment.js version on cdn.
I'm more after the logic and a bit of pseudocode/syntax rather than a working example. The JS version's issue is that when the calculated difference between both unix dates is then converted into a date *1000 for milliseconds, it becomes a 1970 date. also the getMinutes() in js get the literal minute at that timestamp, not to overall amount of minutes ,same for hours etc..
This is the moment JS example:
var now = new Date(Date.now()),
ms = moment(then, "DD/MM/YYYY HH:mm:ss").diff(moment(now, "DD/MM/YYYY HH:mm:ss")),
d = moment.duration(ms),
formattedMomentDateDifference = Math.floor(d.asHours()) + ":";
formattedMomentDateDifference += Math.floor(d.minutes()) + ":";
formattedMomentDateDifference += Math.floor(d.seconds());
$('#momentdifference').val(formattedMomentDateDifference);
and below is the js dates example:
var then = cleanedReceivedDate, //cleaned received date in unix
difference = Math.floor(then - now)*1000, /* difference in milliseconds */
msDifferenceInDate = new Date(difference),
hoursDiff = msDifferenceInDate.getHours(),
minutesDiff = "0"+msDifferenceInDate.getHours(),
secondsDiff = "0"+msDifferenceInDate.getSeconds(),
formattedTime = hoursDiff + ':' + minutesDiff.substr(-2) + ':' + secondsDiff.substr(-2);
$('#jsdifference').val(formattedMomentDateDifference);
JS fiddle
Matt has linked to a duplicate for moment.js, so this is just a POJS solution.
UNIX time values are seconds since the epoch, ECMAScript time values are milliseconds since the same epoch. All you need to do is convert both to the same unit (either seconds or milliseconds) and turn the difference into hours, minutues and seconds.
The UNIX time value for say 2016-10-02T00:00:00Z is 1475366400, so to get the hours, minutes and seconds from then to now in your host system's time zone, do some simple mathematics on the difference from then to now:
var then = 1475366400, // Unix time value for 2016-10-02T00:00:00Z
now = Date.now(), // Current time value in milliseconds
diff = now - then*1000, // Difference in milliseconds
sign = diff < 0? '-' : '';
diff *= sign == '-'? -1 : 1;
var hrs = diff/3.6e6 | 0,
mins = diff%3.6e6 / 6e4 | 0,
secs = diff%6e4 / 1e3 ;
// Helper to pad single digit numbers
function z(n){return (n<10?'0':'') + n}
console.log(sign + hrs + ':' + z(mins) + ':' + z(secs));
PS
Using Date.now in new Date(Date.now()) is entirely redundant, the result is identical to new Date().

How to check if DST (Daylight Saving Time) is in effect, and if so, the offset?

This is a bit of my JS code for which this is needed:
var secDiff = Math.abs(Math.round((utc_date-this.premiere_date)/1000));
this.years = this.calculateUnit(secDiff,(86400*365));
this.days = this.calculateUnit(secDiff-(this.years*(86400*365)),86400);
this.hours = this.calculateUnit((secDiff-(this.years*(86400*365))-(this.days*86400)),3600);
this.minutes = this.calculateUnit((secDiff-(this.years*(86400*365))-(this.days*86400)-(this.hours*3600)),60);
this.seconds = this.calculateUnit((secDiff-(this.years*(86400*365))-(this.days*86400)-(this.hours*3600)-(this.minutes*60)),1);
I want to get the datetime in "ago", but if the DST is in use then the dates are off by 1 hour. I don't know how to check if the DST is in effect or not.
How can I know when the daylight saving starts and ends?
This code uses the fact that getTimezoneOffset returns a greater value during Standard Time versus Daylight Saving Time (DST). Thus it determines the expected output during Standard Time, and it compares whether the output of the given date the same (Standard) or less (DST).
Note that getTimezoneOffset returns positive numbers of minutes for zones west of UTC, which are usually stated as negative hours (since they're "behind" UTC). For example, Los Angeles is UTC–8h Standard, UTC-7h DST. getTimezoneOffset returns 480 (positive 480 minutes) in December (winter, Standard Time), rather than -480. It returns negative numbers for the Eastern Hemisphere (such -600 for Sydney in winter, despite this being "ahead" (UTC+10h).
Date.prototype.stdTimezoneOffset = function () {
var jan = new Date(this.getFullYear(), 0, 1);
var jul = new Date(this.getFullYear(), 6, 1);
return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
}
Date.prototype.isDstObserved = function () {
return this.getTimezoneOffset() < this.stdTimezoneOffset();
}
var today = new Date();
if (today.isDstObserved()) {
alert ("Daylight saving time!");
}
This answer is quite similar to the accepted answer, but doesn't override the Date prototype, and only uses one function call to check if Daylight Savings Time is in effect, rather than two.
The idea is that, since no country observes DST that lasts for seven months[1], in an area that observes DST the offset from UTC time in January will be different to the one in July.
While Daylight Savings Time moves clocks forwards, JavaScript always returns a greater value during Standard Time. Therefore, getting the minimum offset between January and July will get the timezone offset during DST.
We then check if the dates timezone is equal to that minimum value. If it is, then we are in DST; otherwise we are not.
The following function uses this algorithm. It takes a date object, d, and returns true if daylight savings time is in effect for that date, and false if it is not:
function isDST(d) {
let jan = new Date(d.getFullYear(), 0, 1).getTimezoneOffset();
let jul = new Date(d.getFullYear(), 6, 1).getTimezoneOffset();
return Math.max(jan, jul) !== d.getTimezoneOffset();
}
Create two dates: one in June, one in January. Compare their getTimezoneOffset() values.
if January offset > June offset, client is in northern hemisphere
if January offset < June offset, client is in southern hemisphere
if no difference, client timezone does not observe DST
Now check getTimezoneOffset() of the current date.
if equal to June, northern hemisphere, then current time zone is DST (+1 hour)
if equal to January, southern hemisphere, then current time zone is DST (+1 hour)
I was faced with this same problem today but since our daylight saving starts and stops at differing times from the USA (at least from my understanding), I used a slightly different route..
var arr = [];
for (var i = 0; i < 365; i++) {
var d = new Date();
d.setDate(i);
newoffset = d.getTimezoneOffset();
arr.push(newoffset);
}
DST = Math.min.apply(null, arr);
nonDST = Math.max.apply(null, arr);
Then you simply compare the current timezone offset with DST and nonDST to see which one matches.
The getTimezoneOffset() method in JavaScript, in a browser, returns the number of minutes offset from the 00:00 time zone. For example, America/New_York time zone in Daylight Savings (DST) returns the number 300. 300 minutes is 5 hours difference from zero. 300 minutes divided by 60 minutes is 5 hours. Every time zone is compared to the zero time zone, +00:00 / Etc/GMT / Greenwich time.
MDN Web Docs
The next thing that you must know, is that the offset has the opposite sign of the actual time zone.
Information about time zones is maintained by the Internet Assigned Numbers Authority (iana)
iana time zones
A nicely formatted table of Time Zones is supplied by joda.org
joda-time Time Zones
+00:00 or Etc/GMT is Greenwich time
All time zones are offset from +00:00 / "Etc/GMT" / Greenwich time
Daylight Savings Time is always an earlier time than the "regular" time in the summer. You set your clocks back in the fall season. ("Fall Back" slogan to remember what to do)
So, America/New_York time in Daylight Savings (winter) is one hour before the regular time. So, for example, what was normally 5 p.m. in the afternoon in New York city in the summer, is now 4 p.m. America/New_York time in Daylight Savings. The name "America/New_York" time is a "Long Format" time zone name. The east coast of the U.S typically calls their time zone Eastern Standard Time (EST)
If you want to compare today's time zone offset to the time zone offset of some other date, you need to know that mathematical sign (+/- "Positive / Negative") of the time zone offset is the opposite of the time zone.
Look at the time zone table at joda.org and find the time zone for "America/New_York" It will have a negative sign in front of the Standard Offset.
The earth rotates counter-clockwise on it's axis. A person watch the sunrise in Greenwich sees the sunrise 5 hours before someone in New York City will see the sunrise. And someone on the West Coast of the U.S. will see the sunrise after someone on the East Coast of the U.S. sees the sunrise.
There's a reason why you need to know all of this. So that you'll be able to logically determine whether some JavaScript code is getting the DST status correctly or not, without needing to test every time zone at different times of the year.
Imagine that it's November in New York City, and the clocks have been set back an hour. In the summer in New York City, the offset is 240 minutes or 4 hours.
You can test this by creating a date that is in July and then getting the offset.
var July_Date = new Date(2017, 6, 1);
var july_Timezone_OffSet = July_Date.getTimezoneOffset();
console.log('july_Timezone_OffSet: ' + july_Timezone_OffSet)
What will print to the browser's developer tools console log?
Answer is: 240
So, now you can create a date in January and see what your browser returns for a time zone offset for the winter season.
var Jan_Date = new Date(2017, 0, 1);//Month is zero indexed - Jan is zero
var jan_Timezone_OffSet = Jan_Date.getTimezoneOffset();
console.log('jan_Timezone_OffSet: ' + jan_Timezone_OffSet)
Answer is: 300
Obviously 300 is bigger than 240. So, what does this mean? Should you write code that tests for the winter offset being bigger than the summer offset? Or the summer offset less than the winter offset? If there is a difference between the summer and winter time zone offsets, then you can assume that DST is being used for this time zone. But that doesn't tell you if today is using DST for the browsers time zone. So, you'll need to get the time zone offset for today.
var today = new Date();
var todaysTimeZone = today.getTimezoneOffset();
console.log('todaysTimeZone : ' + todaysTimeZone)
Answer is: ? - Depends on the time of year
If today's time zone offset and the summer time zone offset is the same, AND the summer and winter time zone offsets are different, then by logical deduction, today must be NOT be in DST.
Can you omit comparing the summer and winter time zone offsets, (To know if DST is used for this time zone) and just compare today's time zone offset to the summer TZ offset, and always get the correct answer?
today's TZ Offset !== Summer TZ Offset
Well, is today in the winter or summer? If you knew that then you could apply the following logic:
if ( it_is_winter && ( todays_TZ_Offset !== summer_TZ_Offset) {
var are_We_In_DST = true;
}
But the problem is, that you don't know if today's date is in winter or summer. Every time zone can have it's own rules for when DST starts and stops. You'd need to keep track of every time zone's rules for every time zone in the world. So, if there is a better and easier way then you might as well do it the better and easier way.
What we are left with, is that you need to know if this time zone uses DST, and then compare today's time zone offset with the summer time zone offset. That will always give you a reliable answer.
The final logic is:
if ( DST_Is_Used_In_This_Time_Zone && ( todays_TZ_Offset !== summer_TZ_Offset) {
var are_We_In_DST = true;
}
Function to determine if the time zone in the browser uses DST:
function is_DST_Used_In_This_TimeZone() {
var Jan_Date, jan_Timezone_OffSet, July_Date, july_Timezone_OffSet
offsetsNotEqual, thisYear, today;
today = new Date();//Create a date object that is now
thisYear = today.getFullYear();//Get the year as a number
Jan_Date = new Date(thisYear, 0, 1);//Month is zero indexed - Jan is zero
jan_Timezone_OffSet = Jan_Date.getTimezoneOffset();
console.log('jan_Timezone_OffSet: ' + jan_Timezone_OffSet)
July_Date = new Date(thisYear, 6, 1);
july_Timezone_OffSet = July_Date.getTimezoneOffset();
console.log('july_Timezone_OffSet: ' + july_Timezone_OffSet)
offsetsNotEqual = july_Timezone_OffSet !== jan_Timezone_OffSet;//True if not equal
console.log('offsetsNotEqual: ' + offsetsNotEqual);
return offsetsNotEqual;//If the offsets are not equal for summer and
//winter then the only possible reason is that DST is used for
//this time zone
}
Based on Matt Johanson's comment on the solution provided by Sheldon Griffin I created the following code:
Date.prototype.stdTimezoneOffset = function() {
var fy=this.getFullYear();
if (!Date.prototype.stdTimezoneOffset.cache.hasOwnProperty(fy)) {
var maxOffset = new Date(fy, 0, 1).getTimezoneOffset();
var monthsTestOrder=[6,7,5,8,4,9,3,10,2,11,1];
for(var mi=0;mi<12;mi++) {
var offset=new Date(fy, monthsTestOrder[mi], 1).getTimezoneOffset();
if (offset!=maxOffset) {
maxOffset=Math.max(maxOffset,offset);
break;
}
}
Date.prototype.stdTimezoneOffset.cache[fy]=maxOffset;
}
return Date.prototype.stdTimezoneOffset.cache[fy];
};
Date.prototype.stdTimezoneOffset.cache={};
Date.prototype.isDST = function() {
return this.getTimezoneOffset() < this.stdTimezoneOffset();
};
It tries to get the best of all worlds taking into account all the comments and previously suggested answers and specifically it:
1) Caches the result for per year stdTimezoneOffset so that you don't need to recalculate it when testing multiple dates in the same year.
2) It does not assume that DST (if it exists at all) is necessarily in July,
and will work even if it will at some point and some place be any month.
However Performance-wise it will work faster if indeed July (or near by months) are indeed DST.
3) Worse case it will compare the getTimezoneOffset of the first of each month. [and do that Once per tested year].
The assumption it does still makes is that the if there is DST period is larger then a single month.
If someone wants to remove that assumption he can change loop into something more like whats in the solutin provided by Aaron Cole - but I would still jump half a year ahead and break out of the loop when two different offsets are found]
Future-Proof Solution That Works In All Time Zones
Let x be the expected number of milliseconds into the year of interest without factoring in daylight savings.
Let y be the number of milliseconds since the Epoch from the start of the year of the date of interest.
Let z be the number of milliseconds since the Epoch of the full date and time of interest
Let t be the subtraction of both x and y from z: z - y - x. This yields the offset due to DST.
If t is zero, then DST is not in effect. If t is not zero, then DST is in effect.
"use strict";
function dstOffsetAtDate(dateInput) {
var fullYear = dateInput.getFullYear()|0;
// "Leap Years are any year that can be exactly divided by 4 (2012, 2016, etc)
// except if it can be exactly divided by 100, then it isn't (2100,2200,etc)
// except if it can be exactly divided by 400, then it is (2000, 2400)"
// (https://www.mathsisfun.com/leap-years.html).
var isLeapYear = ((fullYear & 3) | (fullYear/100 & 3)) === 0 ? 1 : 0;
// (fullYear & 3) = (fullYear % 4), but faster
//Alternative:var isLeapYear=(new Date(currentYear,1,29,12)).getDate()===29?1:0
var fullMonth = dateInput.getMonth()|0;
return (
// 1. We know what the time since the Epoch really is
(+dateInput) // same as the dateInput.getTime() method
// 2. We know what the time since the Epoch at the start of the year is
- (+new Date(fullYear, 0)) // day defaults to 1 if not explicitly zeroed
// 3. Now, subtract what we would expect the time to be if daylight savings
// did not exist. This yields the time-offset due to daylight savings.
- ((
((
// Calculate the day of the year in the Gregorian calendar
// The code below works based upon the facts of signed right shifts
// • (x) >> n: shifts n and fills in the n highest bits with 0s
// • (-x) >> n: shifts n and fills in the n highest bits with 1s
// (This assumes that x is a positive integer)
-1 + // first day in the year is day 1
(31 & ((-fullMonth) >> 4)) + // January // (-11)>>4 = -1
((28 + isLeapYear) & ((1-fullMonth) >> 4)) + // February
(31 & ((2-fullMonth) >> 4)) + // March
(30 & ((3-fullMonth) >> 4)) + // April
(31 & ((4-fullMonth) >> 4)) + // May
(30 & ((5-fullMonth) >> 4)) + // June
(31 & ((6-fullMonth) >> 4)) + // July
(31 & ((7-fullMonth) >> 4)) + // August
(30 & ((8-fullMonth) >> 4)) + // September
(31 & ((9-fullMonth) >> 4)) + // October
(30 & ((10-fullMonth) >> 4)) + // November
// There are no months past December: the year rolls into the next.
// Thus, fullMonth is 0-based, so it will never be 12 in Javascript
(dateInput.getDate()|0) // get day of the month
)&0xffff) * 24 * 60 // 24 hours in a day, 60 minutes in an hour
+ (dateInput.getHours()&0xff) * 60 // 60 minutes in an hour
+ (dateInput.getMinutes()&0xff)
)|0) * 60 * 1000 // 60 seconds in a minute * 1000 milliseconds in a second
- (dateInput.getSeconds()&0xff) * 1000 // 1000 milliseconds in a second
- dateInput.getMilliseconds()
);
}
// Demonstration:
var date = new Date(2100, 0, 1)
for (var i=0; i<12; i=i+1|0, date.setMonth(date.getMonth()+1|0))
console.log(date.getMonth()+":\t"+dstOffsetAtDate(date)/60/60/1000+"h\t"+date);
date = new Date(1900, 0, 1);
for (var i=0; i<12; i=i+1|0, date.setMonth(date.getMonth()+1|0))
console.log(date.getMonth()+":\t"+dstOffsetAtDate(date)/60/60/1000+"h\t"+date);
// Performance Benchmark:
console.time("Speed of processing 16384 dates");
for (var i=0,month=date.getMonth()|0; i<16384; i=i+1|0)
date.setMonth(month=month+1+(dstOffsetAtDate(date)|0)|0);
console.timeEnd("Speed of processing 16384 dates");
I believe that the above code snippet is superior to all other answers posted here for many reasons.
This answer works in all time zones, even Antarctica/Casey.
Daylight savings is very much subject to change. It might be that 20 years from now, some country might have 3 DST periods instead of the normal 2. This code handles that case by returning the DST offset in milliseconds, not just whether DST is in effect or not in effect.
The size of the months of the year and the way that Leap Years work fits perfectly into keeping our time on track with the sun. Heck, it works so perfectly that all we ever do is just adjust mere seconds here and there. Our current system of leap years has been in effect since February 24th, 1582, and will likely stay in effect for the foreseeable future.
This code works in timezones that do not use DST.
This code works in historic times before when DST was implemented (such as the 1900s).
This code is maximally integer-optimized and should give you no problem if called in a tight loop. After running the code snippet above, scroll down to the bottom of the output to see the performance benchmark. My computer is able to process 16384 dates in 29ms on FireFox.
However, if you are not preparing for over 2 DST periods, then the below code can be used to determine whether DST is in effect as a boolean.
function isDaylightSavingsInEffect(dateInput) {
// To satisfy the original question
return dstOffsetAtDate(dateInput) !== 0;
}
The moment.js library provides an .isDst() method on its time objects.
moment#isDST checks if the current moment is in daylight saving time.
moment([2011, 2, 12]).isDST(); // false, March 12 2011 is not DST
moment([2011, 2, 14]).isDST(); // true, March 14 2011 is DST
I've found that using the Moment.js library with some of the concepts described here (comparing Jan to June) works very well.
This simple function will return whether the timezone that the user is in observes Daylight Saving Time:
function HasDST() {
return moment([2017, 1, 1]).isDST() != moment([2017, 6, 1]).isDST();
}
A simple way to check that this works (on Windows) is to change your timezone to a non DST zone, for example Arizona will return false, whereas EST or PST will return true.
Use Moment.js (https://momentjs.com/)
moment().isDST(); will give you if Day light savings is observed.
Also it has helper function to calculate relative time for you. You don't need to do manual calculations
e.g moment("20200105", "YYYYMMDD").fromNow();
Your're close but a little off. You never need to calculate your own time as it is a result of your own clock. It can detect if you are using daylight saving time in your location but not for a remote location produced by the offset:
newDateWithOffset = new Date(utc + (3600000*(offset)));
This will still be wrong and off an hour if they are in DST. You need for a remote time account if they are currently inside their DST or not and adjust accordingly. try calculating this and change your clock to - lets say 2/1/2015 and reset the clock back an hour as if outside DST. Then calculate for an offset for a place that should still be 2 hours behind. It will show an hour ahead of the two hour window. You would still need to account for the hour and adjust. I did it for NY and Denver and always go the incorrect (hour ahead) in Denver.
I recently needed to create a date string with UTC and DST, and based on Sheldon's answer I put this together:
Date.prototype.getTimezone = function(showDST) {
var jan = new Date(this.getFullYear(), 0, 1);
var jul = new Date(this.getFullYear(), 6, 1);
var utcOffset = new Date().getTimezoneOffset() / 60 * -1;
var dstOffset = (jan.getTimezoneOffset() - jul.getTimezoneOffset()) / 60;
var utc = "UTC" + utcOffset.getSign() + (utcOffset * 100).preFixed(1000);
var dst = "DST" + dstOffset.getSign() + (dstOffset * 100).preFixed(1000);
if (showDST) {
return utc + " (" + dst + ")";
}
return utc;
}
Number.prototype.preFixed = function (preCeiling) {
var num = parseInt(this, 10);
if (preCeiling && num < preCeiling) {
num = Math.abs(num);
var numLength = num.toString().length;
var preCeilingLength = preCeiling.toString().length;
var preOffset = preCeilingLength - numLength;
for (var i = 0; i < preOffset; i++) {
num = "0" + num;
}
}
return num;
}
Number.prototype.getSign = function () {
var num = parseInt(this, 10);
var sign = "+";
if (num < 0) {
sign = "-";
}
return sign;
}
document.body.innerHTML += new Date().getTimezone() + "<br>";
document.body.innerHTML += new Date().getTimezone(true);
<p>Output for Turkey (UTC+0200) and currently in DST: UTC+0300 (DST+0100)</p>
<hr>
Is there an issue using the Date.toString().indexOf('Daylight Time') > -1
"" + new Date()
Sat Jan 01 100050 00:00:00 GMT-0500 (Eastern Standard Time)
"" + new Date(...)
Sun May 01 100033 00:00:00 GMT-0400 (Eastern Daylight Time)
This seems compatible with all browsers.
Update:
After trying to use these functions in a custom datetime picker, I noticed that switching from March to April toggled the timezone as expected, since my zone toggles DST in March. Unexpectedly, it was toggling to the next time zone over instead of switching between Standard and Daylight in the same time zone.
Turns out that's because my original functions were always creating new Date() for the current time or the arbitrary fixed time in the past. Comparing that to the the relative times from March and April meant it would logically detect the DST toggle as switching time zones instead.
The solution was to pass the relative times into the utility functions, so all my comparisons were for the relative time instead of now or the arbitrary fixed time. Lost some of the compactness, but now the logic works as needed.
Updates to workflow:
t parameter defaults to new Date()
For fixed time, pass in an existing Date
For current time, pass in null or nothing
std() updated to use t.setMonth(v); to change the month for fixed times
.getTimezoneOffset() cannot chain to .setMonth(), so we need to swap from one-line notation to use closures ({}), terminators (;), and return
console.log() example loops through each month (0 to 11)
The fixed date object needs to be cloned using the same timestamp (let ts = +t;)
The + before the Date type casts it to a number with the Unix timestamp
Date() also accepts Unix timestamps to create fixed times
If we don't clone it, each call would pass around the same Date object with the months set to 6, which defeats the purpose
Ok, we're not actually cloning, just creating a new object using the same settings; same difference ;)
let ns = {
std: (t = new Date()) => Math.max(...[0, 6].map(v => {
t.setMonth(v);
return t.getTimezoneOffset();
})),
is_dst: (t = new Date()) => t.getTimezoneOffset() < ns.std(t),
utc: (t, std = 0) => {
t = t || new Date();
let z = std ? ns.std(t) : t.getTimezoneOffset(),
zm = z % 60;
return 'UTC' + (z > 0 ? '-' : '+') + (z / 60) + (zm ? ':' + zm : '');
}
};
//current time only
console.log(ns.std(), ns.is_dst(), ns.utc(), ns.utc(null, 1));
//iterate each month
let t = new Date(2021,0,1);
for (let i = 0; i < 12; i++) {
t.setMonth(i);
let ts = +t;
console.log(t.toDateString().split(" ")[1], ns.std(new Date(ts)), ns.is_dst(new Date(ts)), ns.utc(new Date(ts)), ns.utc(new Date(ts), 1));
}
Expanding on the compact and cryptic solution from #nkitku to turn it into a set of reusable functions.
Workflow:
All functions are scoped in a namespace ns so they don't conflict with other functions in the code that may have the same name
Namespacing also allows for compact function notation; std: ()=>Math.max(), is equivalent to function std(){ return Math.max(); }
std() returns the timezone offset in Standard Time
[0, 6] sets up a comparison of a month without DST and a month with DST
0 for January, since Date.setMonth() is zero-indexed
6 for July
Apparently, Standard Time is not in January for everyone, so we have to check both January and July
...[] converts the Array of months to a Set so we can apply the map() function
Raw arrays cannot run map()
map() runs a set of variables on the same function and returns an array of results
Create a new Date object with year, month, day
The year (95 in the example) is arbitrary since the year isn't important for this calculation
The month plugs in our values [0, 6] as a variable v
The day (1 in the example) is also arbitrary
Logically we could have created a new Date(), then .setMonth(v), but using the arbitrary numbers is more compact and faster
Now that we have the dates, getTimezoneOffset() returns the offsets for each month and pushes them to the results array
Math.max() finds the largest value from the results, which will be the Standard Time offset
is_dst() checks if it is currently Daylight Savings Time
new Date().getTimezoneOffset() gets the current offset, with or without DST
ns.std() gets the offset in Standard Time
If the current offset is lower, then it's DST
utc() returns a string in UTC notation
The std parameter defaults to off
z = std ? ns.std() : new Date().getTimezoneOffset() sets the time to DST or standard based on the flag
zm = z % 60 captures minutes since some zones use 30 minutes for example
(z > 0 ? '-' : '+') assigns the correct sign per UTC notation; positive offset values are shown as negative offsets in the notation
(z / 60) captures the hours in single-digit format per the notation, so no need to .toString().padStart(2,'0)` for double-digit format
(zm ? ':' + zm : '') appends minutes if they exist for the timezone
Since this version is meant to be compact, you could save even more space by stripping out extraneous whitespace. Though that's really a job for a minifier.
std:()=>Math.max(...[0,6].map(v=>new Date(95,v,1).getTimezoneOffset())),
const ns = {
std: () => Math.max(...[0, 6].map(v => new Date(95, v, 1).getTimezoneOffset())),
is_dst: () => new Date().getTimezoneOffset() < ns.std(),
utc: (std = 0) => {
let z = std ? ns.std() : new Date().getTimezoneOffset(),
zm = z % 60;
return 'UTC' + (z > 0 ? '-' : '+') + (z / 60) + (zm ? ':' + zm : '');
}
};
console.log(ns.std(), ns.is_dst(), ns.utc(), ns.utc(1));
With https://date-fns.org/v2.22.1/docs/Time-Zones can be solved with one line
new Date().getUTCHours() + getTimezoneOffset('Europe/Amsterdam') / 1000 / 60 / 60;

Countdown timer with cookies

I know there have been a lot of topics like this but I just have problem to which I couldn't find the answer.
My script is:
window.onload = function(){
// 200 seconds countdown
var countdown = 14400;
//current timestamp
var now = Date.parse(new Date());
//ready should be stored in your cookie
if ( !document.cookie )
{
document.cookie = Date.parse(new Date (now + countdown * 1000)); // * 1000 to get ms
}
//every 1000 ms
setInterval(function()
{
var diff = ( document.cookie - Date.parse(new Date()) );
if ( diff > 0 )
{
var message = diff/1000 + " seconds left";
}
else
{
var message = "finished";
}
document.body.innerHTML = message;
},1000);
}
I want to make countdown timer which tells user time how much left depending on his cookie value. So far I managed to calculate difference between two values but I don't know how to make format like, let's say, "dd/mm/yy hh:mm:ss" from difference timestamp (diff). Is it possible at all?
What you want is a function that converts difference in (mili)seconds to something like
5d 4h 3m 2s
If you don't mind having a large number of days for times periods > a few months, then you could use something like this:
function human_time_difference(diff) {
var s = diff % 60; diff = Math.floor(diff / 60);
var min = diff % 60; diff = Math.floor(diff / 60);
var hr = diff % 24; diff = Math.floor(diff / 24);
var days = diff;
return days + 'd ' + hr + 'h ' + min + 'm ' + s + 's';
}
If you have the difference in miliseconds, you'll need to pass the that number divided by 1000. You can also use Math.round to get rid of fractions, but you could just as well leave them on if you want that information displayed.
Getting months and years is a little trickier for a couple of reasons:
The number of days in a month varies.
When you're going from the middle of one month to the middle of the next, the time span doesn't cover any whole months, even if the number of days > 31 (e.g. How many months are there between the 2nd of June and the 30th of July??).
If you really want the number of months between two times, the number of seconds between them is not enough. You have to use calendar logic, which requires passing in the start and end date + time.
PS: When you post a question, avoid irrelevant details. For example, your question has nothing to do with cookies, setInterval, or onload handlers. The only part that you don't know is how to convert (mili)seconds to days, hours, etc. It might be helpful to supply some background on why you're trying to do something, but if it's not essential to understand the basic question, put it at the end so that people don't have to wade through it before getting to the essential part. The same advice applies to your title; make sure it's relevant by excluding irrelevant details (e.g. cookies and counting down).
JavaScript doesn't have any built in date formatting methods like you might expect if you've done any PHP. Instead, you have to build the string manually. However, there are a number of getter methods that will be useful to this end. See 10 ways to format time and date using JavaScript.
Also, just so you know. Date.parse doesn't return the millisecond portion of the time stamp (it rounds down). If you need the milliseconds, you can do either of the following
var d = new Date();
var timestamp_ms = Date.parse(d) + d.getMilliseconds();
or just
var timestamp_ms = +d;
I do not understand why you check the cookie by if ( !document.cookie ) But it doesnot work on my browser so I modified it into if ( document.cookie )
Try toString function and other. Look them up in javascript Date object reference. For example,
var t = new Date;
t.setTime(diff);
var message = t.toTimeString() + " seconds left";
This will print 11:59:58 seconds left on my browser.

Categories