Safe time comparison in Moment.js - javascript

In current scenario it is safe to assume that all compared dates are of the same day, so only time is stored.
However, it is evident that there is non-zero probability of Y2K-like problem.
This piece of code may fail when being executed at 00:00 (roughly) if momentTimeA line is evaluated at 23:59:59.999 of one day and momentTimeB line is evaluated at 00:00:00.000 of another day:
// timeA is '06:00:00', momentTimeA is '2016-11-11T06:00:00.000';
const momentTimeA = moment(timeA, 'H:mm:ss');
// ...
// timeB is '03:00:00', momentTimeB is '2016-11-12T03:00:00.000';
const momentTimeB = moment(timeB, 'H:mm:ss');
// should be positive
momentTimeA.diff(momentTimeB);
Currently this code that does time comparisons runs synchronously, but it may become asynchronous later, this will considerably increase the risk.
How this problem should be addressed?

Since you are dealing with a time range that might cross over midnight, and you're not accounting for dates, then you will need to detect and adjust for this on your own.
// your inputs
var timeA = '06:00:00';
var timeB = '03:00:00';
// today's date as a string
var today = moment().format('YYYY-MM-DD');
// both moments created using the same date
var momentTimeA = moment(today + ' ' + timeA, 'YYYY-MM-DD H:mm:ss');
var momentTimeB = moment(today + ' ' + timeB, 'YYYY-MM-DD H:mm:ss');
// check if midnight is crossed
if (momentTimeA.isAfter(momentTimeB)) {
// move A back a day (this is an assumption!)
var yesterday = moment().subtract(1, 'day').format('YYYY-MM-DD');
momentTimeA = moment(yesterday + ' ' + timeA, 'YYYY-MM-DD H:mm:ss');
}
// now you can take the difference
var delta = momentTimeB.diff(momentTimeA);
Note you had A and B reversed in your original diff. Assuming A comes first, use B.diff(A) to get a positive result.
Also, note that in the above code, it's important that you don't just subtract a day from the moment to adjust it, but that you re-create a new moment on yesterday at the given time. This is because you are working in local time, which could possibly have a DST transition on one of the times provided.

Related

moment.js | check isBefore getting confused with date

I'm currently working with a project where I need to find if a time in HH:mm is before another time.
I am using moment time zone and setting the time zone globally to UTC.
The date which I am comparing is: 2020-09-02T00:00:00.0000+00:00
I am running a check where I am doing the following:
const example = '2020-09-02T00:00:00.0000+00:00'
const time = moment(example)
const timeStart = moment('08:00', 'HH:mm')
console.log(time.isBefore(timeStart))
The console log returns a result of false.
The reason I believe is that the timeStart is evaluating to the current day, so it's failing due to the date being in the future. I need to stop moment from comparing the date, is there a way to do this?
What I am trying to achieve is something of the following
'00:00:00'.isBefore('08:00')
const time = moment('2020-09-02T00:00:00.0000+00:00')
const timeStart = moment('08:00', 'HH:mm')
console.log(time)
console.log(timeStart)
console.log(time.isBefore(timeStart))
<script src="https://cdn.jsdelivr.net/momentjs/2.13.0/moment.min.js"></script>
You can do this with moment.js by converting the timestamp to a moment object, then cloning the object and setting its time to the required comparison time. That way you're always comparing times on the same date.
To keep everything as UTC, use utc. E.g.
let ts = '2020-09-02T00:00:00.0000+00:00';
let d = moment.utc(ts); // Invoke UTC mode
let time = '08:30';
let [h, m] = time.split(':');
let e = d.clone().hours(h).minutes(m).seconds(0).milliseconds(0);
console.log(d.format() + ' is before\n' +
e.format() + '? ' + d.isBefore(e));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js"></script>
You can compare just the time with plain JS fairly easily:
/**
* Compare a time to just the time part of a
* Date, all using UTC.
*
* #param {Date} date - date to compare
* #param {string} time - time in HH:mm format
* #returns {boolean} true if time in date is before time passed to function
*/
function isTimeBefore(date = new Date(), time) {
// Get time value for start of UTC day
// Copy date so don't affect original
let dayStart = new Date(+date).setUTCHours(0,0,0,0);
// Convert the time to milliseconds since midnight
let [h, m] = time.split(':');
let ms = h*3.6e6 + m*6e4;
// Compare to the date's milliseconds elapsed since midnight
return ms > (date - dayStart);
}
// UTC timestamp
let ts = '2020-09-02T00:00:00.0000+00:00';
// Convert to Date
let d = new Date(ts);
// local time
let time = '08:30';
console.log('UTC: ' + d.toISOString() + '\nis before ' + time + ' UTC? : ' + isTimeBefore(d, time));
The built–in parser should generally be avoided, however the above uses it to convert the timestamp to a Date because it's about the only supported format that is reliably parsed by browsers in use.
const time = moment('2020-09-02T00:00:00.0000+00:00')
const timeStart = moment('09:00', 'HH:mm')
time.isBefore(timeStart) //returns true
returns true to me.

Past 2hours date and Time in IST Format javascript

I know that
var currentTime = new Date();
var currentOffset = currentTime.toISOString();
will give current date & time in IST format. Can anyone help me how to get past 2 hours date & time in IST format
To calculate a time difference, you can use a combination of the relevant get and set methods. After you get the value, you perform the desired calculation and use the result as the argument for the set.
Note that the default timezone is based on system settings. So performing such a change has no bearing on the timezone (i.e. for me the code outputs in PDT).
var time = new Date();
var currentOffset = time.getTimezoneOffset();
console.log('Current time: ' + time.toISOString());
console.log('Current offset: ' + currentOffset);
time.setHours(time.getHours() - 2);
var pastOffset = time.getTimezoneOffset();
console.log('Past time: ' + time.toISOString());
console.log('Past offset: ' + currentOffset);

Comparing 2 strings with number values JS

Im trying to check to see if current time is lower than time that is responded from an API. Problem is they are both strings. The API response contains characters such as : and -, so parseInt is not working (at least that's my theory why its not working)
var d = new Date();
var hour = d.getHours();
var minutes = d.getMinutes();
var year = d.getFullYear();
var month = d.getMonth() +1;
var day = d.getDate();
var seconds = d.getSeconds();
var time = year+'-'+month+'-'+day+' '+hour+':'+minutes+':'+seconds;
time returns
"2016-11-7 15:48:2"
API Response is
"2016-11-07 20:06:00"
I have confirmed they are both strings
time < APIresponse
Always returns false
Are there any known solutions? Thanks in advance.
Preface: Timezone
Your current code assumes that the date/time you're getting from the API is in "local time," because you're comparing it with the current date/time in the browser's local timezone. APIs frequently provide date/times in UTC rather than "local" time, so beware of that assumption and double-check it.
If you want to do it at the string level
...you need to ensure when building time that you zero-pad the numbers, so for instance not just 7 for the day of the month, but 07. Then you'll end up with strings that have the fields in a valid comparable order (because the most significant field [year] is first, and the least significant field [seconds] is last), so a lexicographic comparison of the strings is valid.
So for instance, you'd create time like this:
var time = pad(year, 4) + '-' + pad(month, 2) + '-' + pad(day, 2) + ' ' + pad(hour, 2) + ':' + pad(minutes, 2) + ':' + pad(seconds, 2);
...where pad is a function you define that adds as many 0s as needed to ensure the string is as long as the second argument defines.
Then you can do:
if (time < timeStringFromAPI)
Note: If the API's response is giving you the date/time in UTC rather than local time, you'll need to use the UTC version of the accessor functions (e.g., getUTCHours, getUTCFullYear, etc.) rather than the ones you're using, which are for local time.
If you want to do it at the date level
...then you need to convert the date you're getting from the API to a Date. It's almost in a form you can reliable parse on modern browsers,
but not quite; some browsers will parse that string as local time, others as UTC.
If you're sure it's in local time, then the best thing to do is split it into its parts and use the multipart Date constructor:
var parts = timeStringFromAPI.split(/[-:]/);
var apiDate = new Date(
+parts[0], // Year
+parts[1] - 1, // Month
+parts[2], // Day
+parts[3], // Hours
+parts[4], // Minutes
+parts[5] // Seconds
);
If you're sure it's in UTC, then you can either do the above but with new Date(Date.UTC(...)) rather than just new Date(...), or you can put the string into the JavaScript date/time format and parse that:
var apiDate = new Date(timeStringFromAPI.replace(" ", "T") + "Z");
That takes the "2016-11-07 20:06:00" and changes it to "2016-11-07T20:06:00Z", which can reliably be parsed on all non-obsolete browsers.
Then you can do
if (new Date() < apiDate) {
try this :
var curDate = new Date();
then compare in this way
if (new Date(yourdate) <= curDate)
{
something...
}
var d1 = "2016-11-7 15:48:2";
var d2 = "2016-11-07 20:06:00";
if (new Date(d1) < new Date(d2)) {
alert('true')
}

moment.js startOf issue

I am trying to get the start and end of a day (which is a few days from today) using moment.js. This is the code I have:
var today = moment();
var day = today.add(-5, "days");
var startOfDay = day.startOf("day");
var endOfDay = day.endOf("day");
console.log("today " + today.format());
console.log("day " + day.format());
console.log("start " + startOfDay.format());
console.log("end " + endOfDay.format());
And these are the logs:
I2015-11-10T15:19:02.930Z]today 2015-11-10T15:19:02+00:00
I2015-11-10T15:19:02.931Z]day 2015-11-05T15:19:02+00:00
I2015-11-10T15:19:02.932Z]start 2015-11-05T23:59:59+00:00
I2015-11-10T15:19:02.933Z]end 2015-11-05T23:59:59+00:00
As you can see, the start and end dates are exactly the same. The end date is as expected, however, the startOf function appears to be doing exactly what the endOf function does.
Is there perhaps something I am missing?
Dates are mutable, and are altered by the method calls. Your two dates are both actually the same date object. That is, day.startOf("day") returns the value of day both times you call it. You can make copies however:
var startOfDay = moment(day).startOf("day");
var endOfDay = moment(day).endOf("day");
That constructs two new instances.

Error in calculating Timezone of Brasilia

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.

Categories

Resources