Error in calculating Timezone of Brasilia - javascript

function(startDate, lastDate){
var midDate = newDate(startDate.getFullYear(),startDate.getMonth(),Math.ceil((lastDate.getDate() + startDate.getDate()) / 2));
var startDateOffset = startDate.getTimezoneOffset();
var lastDateOffset = lastDate.getTimezoneOffset();
var midDateOffset = midDate.getTimezoneOffset();
var finalDate = new Date();
if(startDateOffset == midDateOffset){
if((lastDate.getDate() - midDate.getDate()) > 1){
alert("loop");
finalDate = this.getDaylightChangeDate(midDate, lastDate);
}
}
I am getting start date as 20 oct , end date as 22nd and mid date again as 20th oct . This is happening only for the location Brasilia , also startDateOffset & midDateOffset is 180 as output.. which is throwing the code in infinite loop as mid date is equal to start date always. Please suggest some other method in javascript to calculate the mid date

There is nothing wrong with your code. The mid date between 2012-10-20T00:00 and 2012-10-21T23:00 is 2012-10-20T23:30. Of course your code can't expect the dates to differ if the input dates are no more than 2 days apart. What would even happen if you passed in the same date as start and end?
If those dates were recorded in different time zones, you always should use UTC to calculate differences.

Related

Javascript Date parsing returning strange results in Chrome

I observed some strange Date behaviour in Chrome (Version 74.0.3729.131 (Official Build) (64-bit)).
Following javascript was executed in the Chrome Dev Console:
new Date('1894-01-01T00:00:00+01:00')
// result: Mon Jan 01 1894 00:00:00 GMT+0100 (Central European Standard Time)
new Date('1893-01-01T00:00:00+01:00')
// result: Sat Dec 31 1892 23:53:28 GMT+0053 (Central European Standard Time)
I have already read about non standard date parsing via the Date ctor in different browsers, although providing valid ISO8601 values.
But this is more than strange o_o
In Firefox (Quantum 66.0.3 (64-Bit)) the same calls result in expected Date objects:
new Date('1894-01-01T00:00:00+01:00')
// result: > Date 1892-12-31T23:00:00.000Z
new Date('1893-01-01T00:00:00+01:00')
// result: > Date 1893-12-31T23:00:00.000Z
Is this a bug in Chrome?
My input is valid ISO8601 i guess?
The most important question is, how do I fix this? (hopefully without parsing the input string myself)
Okay, seems like this behaviour cannot be avoided, so you should parse dates manually. But the way to parse it is pretty simple.
If we are parsing date in ISO 8601 format, the mask of date string looks like this:
<yyyy>-<mm>-<dd>T<hh>:<mm>:<ss>(.<ms>)?(Z|(+|-)<hh>:<mm>)?
1. Getting date and time separately
The T in string separates date from time. So, we can just split ISO string by T
var isoString = `2019-05-09T13:26:10.979Z`
var [dateString, timeString] = isoString.split("T")
2. Extracting date parameters from date string
So, we have dateString == "2019-05-09". This is pretty simple now to get this parameters separately
var [year, month, date] = dateString.split("-").map(Number)
3. Handling time string
With time string we should make more complex actions due to its variability.
We have timeString == "13:26:10Z"
Also it's possible timeString == "13:26:10" and timeString == "13:26:10+01:00
var clearTimeString = timeString.split(/[Z+-]/)[0]
var [hours, minutes, seconds] = clearTimeString.split(":").map(Number)
var offset = 0 // we will store offset in minutes, but in negation of native JS Date getTimezoneOffset
if (timeString.includes("Z")) {
// then clearTimeString references the UTC time
offset = new Date().getTimezoneOffset() * -1
} else {
var clearOffset = timeString.split(/[+-]/)[1]
if (clearOffset) {
// then we have offset tail
var negation = timeString.includes("+") ? 1 : -1 // detecting is offset positive or negative
var [offsetHours, offsetMinutes] = clearOffset.split(":").map(Number)
offset = (offsetMinutes + offsetHours * 60) * negation
} // otherwise we do nothing because there is no offset marker
}
At this point we have our data representation in numeric format:
year, month, date, hours, minutes, seconds and offset in minutes.
4. Using ...native JS Date constructor
Yes, we cannot avoid it, because it is too cool. JS Date automatically match date for all negative and too big values. So we can just pass all parameters in raw format, and the JS Date constructor will create the right date for us automatically!
new Date(year, month - 1, date, hours, minutes + offset, seconds)
Voila! Here is fully working example.
function convertHistoricalDate(isoString) {
var [dateString, timeString] = isoString.split("T")
var [year, month, date] = dateString.split("-").map(Number)
var clearTimeString = timeString.split(/[Z+-]/)[0]
var [hours, minutes, seconds] = clearTimeString.split(":").map(Number)
var offset = 0 // we will store offset in minutes, but in negation of native JS Date getTimezoneOffset
if (timeString.includes("Z")) {
// then clearTimeString references the UTC time
offset = new Date().getTimezoneOffset() * -1
} else {
var clearOffset = timeString.split(/[+-]/)[1]
if (clearOffset) {
// then we have offset tail
var negation = timeString.includes("+") ? 1 : -1 // detecting is offset positive or negative
var [offsetHours, offsetMinutes] = clearOffset.split(":").map(Number)
offset = (offsetMinutes + offsetHours * 60) * negation
} // otherwise we do nothing because there is no offset marker
}
return new Date(year, month - 1, date, hours, minutes + offset, seconds)
}
var testDate1 = convertHistoricalDate("1894-01-01T00:00:00+01:00")
var testDate2 = convertHistoricalDate("1893-01-01T00:00:00+01:00")
var testDate3 = convertHistoricalDate("1894-01-01T00:00:00-01:00")
var testDate4 = convertHistoricalDate("1893-01-01T00:00:00-01:00")
console.log(testDate1.toLocaleDateString(), testDate1.toLocaleTimeString())
console.log(testDate2.toLocaleDateString(), testDate2.toLocaleTimeString())
console.log(testDate3.toLocaleDateString(), testDate3.toLocaleTimeString())
console.log(testDate4.toLocaleDateString(), testDate4.toLocaleTimeString())
Note
In this case we are getting Date instance with all its own values (like .getHours()) being normalized, including timezone offset. The testDate1.toISOString will still return weird result. But if you are working with this date, it will probably 100% fit your needings.
Hope that helped :)
This might be the case when all browsers follow their own standards for encoding date formats (but I am not sure on this part). Anyways a simple fix for this is to apply the toISOString method.
const today = new Date();
console.log(today.toISOString());

Each day a different item in an array - javascript

I have this script that makes a button redirect to a whatsapp page, on the URL (a href) I need to insert the number that's gonna be contacted.
What I need to do is each day a different number fills this URL.
Example:
day1 - phonen1,
day2 - phonen2,
...,
day 13 - phonen13,
//(starts over)
day14 - phonen1,
day15 - phonen2,
...
<a id="whatsapp" target="_blank" href="https://api.whatsapp.com/send?phone=5519997820734">Link</a>
<script>
phones= ["phonen1", "phonen2", ..., "phonen13"];
document.getElementById("whatsapp").href = "https://api.whatsapp.com/send?phone=5519"+ phones[i] +"";
</script>
you can use the date object with for loop like this:
<a id="whatsapp" target="_blank" href="https://api.whatsapp.com/send?phone=5519997820734">Link</a>
<script>
phones= ["phonen1", "phonen2", ..., "phonen13"];
var d = new Date();
var todayDate = d.getDate();
for (var i = todayDate; i > 13; i= i-13) {
todayDate = todayDate - 13;
}
document.getElementById("whatsapp").href = "https://api.whatsapp.com/send?phone=5519"+phones[i] + todayDate;
</script>
Simple Answer:
You can do this using a Date to count the number of days since the unix epoch, and mod that count by the length of your phones array to get an index that moves to the next item every 24 hours:
let phones = ["phonen1", "phonen2", "phonen3", "phonen4"];
const ms_per_day = 24 * 60 * 60 * 1000;
// (new Date()).getTime() gets the number of ms since 1 January 1970 00:00:00 UTC
// we divide by ms_per_day and floor to get the number of 24-hour cycles (this will increment each UTC day)
let days_since_epoch = Math.floor((new Date()).getTime() / ms_per_day);
// we mod by the length of phones to get a number in the range [0, phones.length)
let phones_index = days_since_epoch % phones.length;
document.getElementById("whatsapp").href = "https://api.whatsapp.com/send?phone=5519" + phones[phones_index];
console.log("Set link to", document.getElementById("whatsapp").href);
<a id="whatsapp" target="_blank" href="https://api.whatsapp.com/send?phone=5519997820734"> Link </a>
Caveats:
Working with time is complicated. The above method doesn't get the number of days exactly:
Due to the differing lengths of days (due to daylight saving changeover), months and years, expressing elapsed time in units greater than hours, minutes and seconds requires addressing a number of issues and should be thoroughly researched before being attempted.
...and the crossover time is in UTC anyway, so it's non-obvious when the above code will switch numbers (it won't be at midnight). But it will do so once every 24 hours, which should be sufficient for the use case described in the post.
One other caveat is that the number won't actually change until the user refreshes the page and reruns the script.
Use the date object to create an index into your array
<a id="whatsapp" target="_blank" href="https://api.whatsapp.com/send?phone=5519997820734">Link</a>
<script>
var phones= ["phone1","phone2","phone3","phone4","phone5","phone6","phone7","phone8","phone9","phone10","phone11","phone12","phone13","phone14"];
var startOfDay1 = new Date('July 1, 2018 00:00:00');//be aware this is the client timezone
var diffFromNow = Date.now() - startOfDay1.getTime();//get the difference in ms between now and midnight of "day 1"
console.log(diffFromNow);
var diffFromNowDays = diffFromNow/(24*60*60*1000);//turn that into a day value
var daynum = Math.floor(diffFromNowDays % 14);//constrain to 14 days
console.log(daynum);//zero based index
document.getElementById("whatsapp").href = "https://api.whatsapp.com/send?phone=5519"+ phones[daynum] +"";
</script>
Ollin's answer is great, but you can use local midnight as follows if you wish. Use the remainder operator % with the number of whole days since a particular point in time, any epoch will do.
If you want to do the changeover at midnight local time, then use local midnight for the epoch and current day. Use Math.round to remove daylight saving effects.
The following will change the value returned from the array at local 00:00:00.001 each day:
// Number of whole local days since date to today
function daysDiff(date) {
// Copy date, set to start of day
var d = new Date(+date);
d.setHours(0,0,0,0);
// Get start of current day
var e = new Date();
e.setHours(0,0,0,0);
// Return whole day count
return Math.round((e - d)/8.64e7);
}
// Select item from array based on number of whole days
// from epoch to today. Default is 1 July 2018
function getFromArray(array, epoch) {
if (!epoch) {
// If not provided, use 1 July 2018
epoch = new Date(2018,6,1);
}
var d = daysDiff(epoch);
return array[d % array.length];
}
var nums = [1,2,3,4,5,6,7,8,9,10,11,12,13,14];
// On local date 12 July 2018 returns 12
console.log(getFromArray(nums));

How do I subtract two dates?

I have a date stored in a database as a string. It looks like this:
Tue Aug 23 2016 00:00:00 GMT-0500 (CDT)
I basically want to tell if today's date is before or after the date in the database.
The code below should sort of explain what I want. The problem is that after the difference variable doesn't return a number variable, which is what I need.
var expire = value.vaccines;
var today = new Date();
var difference = today-expire;
if(difference <= 0){
$(element).css({"color": "#0040ff"});
}
Any ideas on how to subtract these two dates and get a number value?
Assuming both your objects are Date
Although you only require the returned value in milliseconds, I've added the extra step of formatting the value.
Math.floor((today - expire) / (1000*60*60*24))
Taken from Here
You can just you can calculate the difference between the two Date objects and get the absolute value with Math.abs():
var today = new Date(),
expire = value.vaccines,
difference = Math.abs(today - expire); // difference in milliseconds
if (difference <= 0) {
$(element).css({
"color": "#0040ff"
});
}
Check that expire is a valid Date object.

Javascript moment - timezone, difference between dates in different time zones

I have:
var now = moment.format(); //get current time
var days = 5; //days I need to subtract from time(then)
var then = '05/02/2016 12:00 am';
Now I need to get difference between now and then substract(-) 5 days but in +0000 so GMT +0.
so now must be in user localtime and then must be at +0000 GMT.
How I can get difference between this dates in days, hours, minutes, seconds?
I try:
var now = moment().format();
var then = moment('05/02/2016 12:00 am').utcOffset(+0000).format();
then = moment(then).subtract(5,'days');
d = moment.utc(moment(now).diff(moment(then))).format("DD HH:mm:ss");
but I get result- which is wrong...
"27 18:48:55"
The problem is that you're trying to use a time difference as a time. You need to use moment.duration() with the return value of the diff. You should also call then.diff(now) to get a positive difference. There were also some unnecessary calls to .format() and moment() that I removed.
var now = moment();
var then = moment('05/02/2016 12:00 am').utcOffset(+0000).subtract(5, 'days');
var duration = moment.duration(then.diff(now));
console.log(duration.days(), duration.hours(), duration.minutes(), duration.seconds());
logs
4 3 15 46

Unix offset with fullCalendar difference

I want to place a check when I'm getting a momentjs instance through fullCalendar.
I'm at the eventRender callback
var calendar = $('#calendar').fullCalendar('getCalendar');
var atime = calendar.moment();
var atime_h = atime.format("HH:mm");
atime = atime.unix();
var start = calendar.moment(event.start);
var start_u = start.unix();
var start_h = start.format("HH:mm");
console.log(atime);
console.log(atime_h);
console.log(start_u);
console.log(start_h);
Now what that logs is this:
1408024477
15:54
1407888000
00:00
1408024477 == Thu Aug 14 15:54:37 2014 == is correct
But 1407888000 == Wed Aug 13 02:00:00 2014, where I would expect 00:00 instead of 02:00
So there's a difference between the event .unix()/format.() and the moment I created.
Anyone got a clue what's going on?
Edit:
So what happens is that if I create two new moments: moment() and a moment().utc(), I get the same timestamp for both. But when I then display them, there is a difference of two hours.
The .utc one returns two hours in the past, the one without the correct one for me. The timestamp is not two hours back.
But with the event.start (which has _isUTC=true, the timestamp is two hours in the future (!), and it displays it correct when formatted.
So maybe I need to have my event.start to be not UTC and two hours back somehow?
Edit by request in comment, this is what I use now:
var start = calendar.moment(event.start);
console.log(start);
start_utc = new Date(start.year(), start.month(), start.date(), start.hour(), start.minute(), start.second());
var start = calendar.moment(start_utc);
console.log(start);
Try converting your event.start date to utc first, here's how to do it in vanilla js:
start_utc = new Date(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate(), start.getUTCHours(), start.getUTCMinutes(), start.getUTCSeconds());
Then you can call .unix() on it and it should give you the expected timestamp.

Categories

Resources