Increment date and batch create Firestore documents - javascript

I have a map of data:
{
name: name,
time: <epoch in seconds>,
}
My goal is to create 6 documents with this data in Firestore, but with each document, the time field should be incremented by 1 week. So if my initial time is Sun Nov 17 2019 in epoch, there should be 6 documents with time (as Firestore timestamp) Sun Nov 17 2019, Sun Nov 24 2019, Sun Dec 1 2019, etc.
I'm using a https cloud function and tried doing this:
var name = 'test';
var time = 1573992000; // 7 AM EST on Sun, Nov 17 2019
var data = {
name: name,
time: time,
};
var start = new Date(time * 1000); // convert to milliseconds
var i;
for (i = 0; i < 6; i++) {
var ref = db.collection('shifts').doc();
start.setDate(new Date(start) + 7);
data['time'] = new admin.firestore.Timestamp(start.getTime() / 1000, 0);
batch.set(ref, data);
}
return batch.commit();
But all the documents appear in firestore with the same time (6 weeks after the initial). My guess is that it's something with the batch only setting the latest value. How can I avoid this without having to create 6 separate maps?

You usage of Date looks overall incorrect to me.
setDate() sets the date of the month to the exact value, and it will not increment the month if needed to accommadate values that go beyond what the month allows (that is to say, if you pass 32, no month would ever accept that).
Also this doesn't make sense to me: new Date(start) + 7. You can't simply add 7 to a date object. It looks like maybe you meant to add 7 to the date of the month represented in the date. But again, that won't work the way you expect if the value is too high for the given month.
All things considered, the Date object isn't going to help you out too much here. I suggest just doing date math on integer numbers. For example, if your start time is:
var time = 1573992000
You can add 7 days worth of seconds to it like this:
time + (7 * 24 * 60 * 60)
This should be easier to work with.

Related

How to addition seconds with seconds in JavaScript?

I have my current time in seconds and duration time in seconds.
I want to add both seconds to calculate the end time of the song (for example).
But i have a weird format in the result.
Here is my code :
// This is my song's duration
var duration = new Date("Sept 21, 2019 00:03:32");
var durationSeconds = duration.getSeconds();
// Current second
var date = new Date();
var seconds = date.getSeconds();
var differenceSecondsConverted = date.setSeconds(seconds + durationSeconds);
console.log(differenceSecondsConverted);
And the result is something like : 1569102592740
Thanks
Actually the code works as it should, probably, you just miss some concept.
The new Date() is a constructor that returns an instance of Date object.
This object have several properties. When you instantiate it, it returns something like Sun Sep 22 2019 01:21:14 GMT+0200 (CEST) which is string representation of current time.
However, this string representation is not how JS actually "thinks" about the time.
Internally, "time" for JS is number of milliseconds passed from January 1, 1970, 00:00:00.
It looks something like this: 1569108461979.
You may see it if you run Date.now();
Also, if you do any calculations (not directly, but using methods like .setDate) with new Date(), it will be internally calculated as milliseconds passed from 1, 1970, 00:00:00.
So, the main problem in your code is that your duration variable is not actually "duration".
It just contains an object that represents Sept 21, 2019 00:03:32.
It is just a moment in time (3 minutes, 32 seconds after midnight of 20 of September 2019).
To calculate when the song will end if it starts right now, you'd do something like:
let now = Date.now();
// Song duration in milliseconds
let songDuration = 201000;
let songEndTime = now + songDuration;
console.log( new Date(songEndTime) );
You can get a date object for the time in say 3:32 by adding 3 minutes and 32 seconds to the current date, e.g.
// time is minutes and seconds as mm:ss
function nowPlus(time) {
let [m, s] = time.split(':').map(Number);
let now = new Date();
now.setMinutes(now.getMinutes() + m, now.getSeconds() + s);
return now;
}
console.log('In 3:32 it will be: ' + nowPlus('3:32').toLocaleString(undefined, {hour12:false, hour: 'numeric', minute:'2-digit', second:'2-digit'}));

Create custom year, different to calendar year? JS/QML

I am trying to change the 'first day' and 'last day' of a year (normally 01/01/yyyy - 31/12/yyyy), to my own defined dates, being:
First Day: 01/04/yyyy (1st April)
Last Day: 31/03/yyyy (31st March)
I have found a similar question/answer here:
Dynamically change the year when calculating date ranges using JavaScript
with the answer being:
var todayDate = new Date(); //18/05/19
var FYFirst = todayDate.getFullYear(); //2019
if (todayDate.getMonth() < 3) { // earlier than april
FYFirst -= 1;
}
var FYLast = FYFirst + 1;
But where my users are storing dates taken out of an allowance, I need this to extend forward/back at least 2 years, so the above code would be currYear (01/04/2019 - 31/03/2020).
There would also be (currently being, but needs to by dynamic):
prevPrevYear: 01/04/17 - 31/03/18
prevYear: 01/04/18 - 31/03/19
nextYear: 01/04/20 - 31/03/21
nextNextYear: 01/04/21 - 31/03/22
In which the date is taken from the allowance of currYear,nextYear of whatever year the saved date falls into.
In this example let's say a users yearly 'allowance', is 40, and each day counts as 1, so when saving a date this deducts 1 from the allowance. I have all the math working for this, but need some way for the app to know if its deducting from this years 'allowance' or next years ect, this is written and stored to a Database - all of the math for this is currently working but rolls indefinitely without any yearly end!
My question is
How would I extend the above, to take into consideration the multiple year span needed when my users are storing dates? I am building my app using Felgo, working with QML!
It seems your year range is 1 Apr to 31 Mar the following year. You might call 1 Apr 2019 to 31 Mar 2020 "2019". You can create a simple function so that any date from 1 Jan to 31 Mar belongs to the previous year, e.g.:
function getMySpecialYear(date) {
var yr = date.getFullYear();
var fyStart = new Date(yr, 3, 1);
return date < fyStart? yr - 1 : yr;
}
[new Date(2019,0,1), // 1 Jan 2019 -> 2018
new Date(2019,5,1) // 1 Jun 2019 -> 2019
].forEach(date => console.log(date + ' - ' + getMySpecialYear(date)));
You can then sum the days for a particular range/year and see if the total is less than or greater than the allowance for that range/year.
Some places have a similar issue in that the financial year runs from 1 July to 30 June, they are often called things like "FY 2019/20".

How to programmatically determine the prior time period based upon unix time stamps in Javascript?

Essentially I have two unix timestamps, representing the first and last days of a given month. Is it possible programmatically determine the timestamps for the first and last of the previous month?
For example, I have the following two timestamps:
1467331201 --> July 1, 2016
1469923201 --> July 31, 2016
Essentially, can I manipulate these two numbers in a consistent way in order to the unix time (or Date object) for June 1, 2016 and June 30, 2016, respectively? Problem that I'm running into is that you cannot simply subtract a given amount because the amount of days in a month is variable.
You could use this function:
function getPreviousMonthRange(unixTime) {
var dt = new Date(unixTime * 1000);
dt.setUTCDate(0); // flips to the last day of previous month
var unixLast = dt.getTime();
dt.setUTCDate(1); // back to the first day of that same month
var unixFirst = dt.getTime();
return [unixFirst / 1000, unixLast / 1000];
}
// given first and last date (only one is really needed)
var unixTimeFirst = 1467331201;
var unixTimeLast = 1469923201;
// get previous month's first & last date
var [first, last] = getPreviousMonthRange(unixTimeFirst);
// output
console.log('previous month first day: ', first, new Date(first*1000));
console.log('previous month last day: ', last, new Date(last*1000));
Take a look at the following example:
// Specify a timestamp
var timestamp = 1467331201;
// Create a date object for the time stamp, the object works with milliseconds so multiply by 1000
var date = new Date(timestamp * 1000);
// Set the date to the previous month, on the first day
date.setUTCMonth(date.getUTCMonth() - 1, 1);
// Explicitly set the time to 00:00:00
date.setUTCHours(0, 0, 0);
// Get the timestamp for the first day
var beginTimestamp = date.getTime() / 1000;
// Increase the month by one, and set the date to the last day of the previous month
date.setUTCMonth(date.getUTCMonth() + 1, 0);
// Explicitly set the time to 23:59:59
date.setUTCHours(23, 59, 59);
// Get the timestamp for the last day
var endTimestamp = date.getTime() / 1000;
// Print the results
console.log('Timestamps for previous month: ');
console.log('Begin timestamp: ' + beginTimestamp);
console.log('End timestamp: ' + endTimestamp);
A timestamp must be specified in the variable on the top, this might be one of the two timestamps you suggested in your question, anywhere in a month.
This code then calculates the begin and end timestamp for the previous month as you've requested, and prints the results to the console.
Please note, that in this example the begin timestamp uses 00:00:00 as time, and the end timestamp uses 23:59:59 as time (the last second of that day). This can be configured the way you'd prefer.
In this case, we're working with the ...UTC... Date functions, because a Unix timestamp is in UTC time, not in the timezone the user is in.
The statement date.setMonth(date.getMonth() + 1, 0); is used to select the last day in the month. The next month is selected first, but because the day is set to 0 (and not 1) one day is subtracted giving you the preferred result. This is described here.
You can consider using Moment.js. I'm sure this is not exactly how you'd end up handling it but see below for an example of some helpful methods.
var lastDayOfJuly = moment(1469923201);
var firstDayOfJuly = lastDayOfJuly.startOf('month');
var lastDayOfJune = firstDayOfJuly.subtract(1, 'day');
var firstDayOfJune = lastDayOfJune.startOf('month");
Moment.js

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;

Simple javascript date math... not really

I am trying to create a simple script that gives me the next recycling date based on a biweekly schedule starting on Wed Jul 6, 2011. So I've created this simple function...
function getNextDate(startDate) {
if (today <= startDate) {
return startDate;
}
// calculate the day since the start date.
var totalDays = Math.ceil((today.getTime()-startDate.getTime())/(one_day));
// check to see if this day falls on a recycle day
var bumpDays = totalDays%14; // mod 14 -- pickup up every 14 days...
// pickup is today
if (bumpDays == 0) {
return today;
}
// return the closest day which is in 14 days, less the # of days since the last
// pick up..
var ms = today.getTime() + ((14- bumpDays) * one_day);
return new Date(ms);
}
and can call it like...
var today=new Date();
var one_day=1000*60*60*24; // one day in milliseconds
var nextDate = getNextDate(new Date(2011,06,06));
so far so good... but when I project "today" to 10/27/2011, I get Tuesday 11/8/2011 as the next date instead of Wednesday 11/9/2011... In fact every day from now thru 10/26/2011 projects the correct pick-up... and every date from 10/27/2011 thru 2/28/2012 projects the Tuesday and not the Wednesday. And then every date from 2/29/2012 (leap year) thru 10/24/2012 (hmmm October again) projects the Wednesday correctly. What am I missing? Any help would be greatly appreciated..
V
The easiest way to do this is update the Date object using setDate. As the comments for this answer indicate this isn't officially part of the spec, but it is supported on all major browsers.
You should NEVER update a different Date object than the one you did the original getDate call on.
Sample implementation:
var incrementDate = function (date, amount) {
var tmpDate = new Date(date);
tmpDate.setDate(tmpDate.getDate() + amount)
return tmpDate;
};
If you're trying to increment a date, please use this function. It will accept both positive and negative values. It also guarantees that the used date objects isn't changed. This should prevent any error which can occur if you don't expect the update to change the value of the object.
Incorrect usage:
var startDate = new Date('2013-11-01T11:00:00');
var a = new Date();
a.setDate(startDate.getDate() + 14)
This will update the "date" value for startDate with 14 days based on the value of a. Because the value of a is not the same is the previously defined startDate it's possible to get a wrong value.
Expanding on Exellian's answer, if you want to calculate any period in the future (in my case, for the next pay date), you can do a simple loop:
var today = new Date();
var basePayDate = new Date(2012, 9, 23, 0, 0, 0, 0);
while (basePayDate < today) {
basePayDate.setDate(basePayDate.getDate()+14);
}
var nextPayDate = new Date(basePayDate.getTime());
basePayDate.setDate(nextPayDate.getDate()-14);
document.writeln("<p>Previous pay Date: " + basePayDate.toString());
document.writeln("<p>Current Date: " + today.toString());
document.writeln("<p>Next pay Date: " + nextPayDate.toString());
This won't hit odd problems, assuming the core date services work as expected. I have to admit, I didn't test it out to many years into the future...
Note: I had a similar issue; I wanted to create an array of dates on a weekly basis, ie., start date 10/23/2011 and go for 12 weeks. My code was more or less this:
var myDate = new Date(Date.parse(document.eventForm.startDate.value));
var toDate = new Date(myDate);
var week = 60 * 60 * 24 * 7 * 1000;
var milliseconds = toDate.getTime();
dateArray[0] = myDate.format('m/d/Y');
for (var count = 1; count < numberOccurrences; count++) {
milliseconds += week;
toDate.setTime(milliseconds);
dateArray[count] = toDate.format('m/d/Y');
}
Because I didn't specify the time and I live in the US, my default time was midnight, so when I crossed the daylight savings time border, I moved into the previous day. Yuck. I resolved it by setting my time of day to noon before I did my week calculation.

Categories