Moment: isSameOrAfter gives random output when comparing moment() to moment()

Created on 21 Feb 2017  ·  4Comments  ·  Source: moment/moment

Description
So, me and a colleague found a bug in our code which turned out to be caused by us using an undefined variable (basically moment(undefined) and then trying to compare that to a new moment instance (moment()) with the method isSameOrAfter.

So what we basically used, was this:

let isSame = moment(undefined).isSameOrAfter(moment());

Running that code once or twice is gonna return true, but not always. Once in a while it returns false, I guess it's because creating two instances of moment, after each other, with no time defined might create them with a few microseconds difference.

Here's a test I ran in both Node v7.1.0 on Windows 10 and in Chrome/56.0.2924.87 with Moment.js 2.17.1.

const moment = require('moment');

function isSameOrAfter() {
    console.log('isSameOrAfter')
    for (let i = 0; i < 1000; i++) {
        let output = moment().isSameOrAfter(moment());
        if (!output) {
            console.log(output, i);
        }
    }
}

function isSame() {
    console.log('isSame')
    for (let i = 0; i < 1000; i++) {
        let output = moment().isSame(moment());
        if (!output) {
            console.log(output, i);
        }
    }
}

function sameExactVariable() {
    console.log('Another test comparing the same exact variable')
    for (let i = 0; i < 1000; i++) {
        const now = moment();
        let output = now.isSame(now);
        if (!output) {
            console.log(output, i);
        }
    }
}

isSameOrAfter();
isSame();
sameExactVariable();

The first two function will log false a few times out of 1000, and the third function won't. It only logs when it comes upon an exception.

Environment:
Tue Feb 21 2017 14:36:06 GMT+0100 (W. Europe Standard Time)
2017-02-21 14:36:06
-60
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
2.17.1

Most helpful comment

@imrvshah First note that

...isSameOrAfter(moment().format('MM-DD-YYYY'))

creates a new moment with the current time, formats it as a string, and passes that string to isSameOrAfter(), which parses it again. You should have just said:

...isSameOrAfter(moment())

And saved the moment -> string -> moment work. That's much better code, but I'd also bet it fixes the bug. Have you noticed deprecation warnings saying "hey, you're not supposed to pass strings to moment like that"? Moment knows how to parse ISO 8601 strings like moment("1982-05-25"). It also knows how to parse strings where the format is specified, like moment("05-25-1982", "MM-DD-YYYY"). But it has no idea that a string like "05-25-1982" is meant to be "MM-DD-YYYY" without you telling it. So it lets the browser guess by just handing that string to the Date constructor. In other words, your code unwraps like this:

m.isSameOrAfter("05-25-1982");
m.isSameOrAfter(moment("05-25-1982"))
m.isSameOrAfter(moment(new Date("05-25-1982")))

There's a reason we deprecated that: it gives inconsistent results based on what browser is running it. So what you're running into is that iOS doesn't support that format natively. In my Safari dev console:

> new Date("05-25-1982")
Invalid Date

That gave you bad comparisons that left you with an empty array.

All 4 comments

Probably I have the same kind of issue. It is working as expected on chrome and Android device but not in iOS.

let arrDates: Array<String> = []; _.each(Dates, (date) => { if (moment(date).isSameOrAfter(moment().format('MM-DD-YYYY'))) { arrDates.push(date); } });

in iOS I am getting array null while it is working on chrome and Android.

Environment:

Cordova CLI: 6.4.0
Ionic Framework Version: 2.0.0
Ionic CLI Version: 2.1.18
Ionic App Lib Version: 2.1.9
Ionic App Scripts Version: 1.0.0
ios-deploy version: 1.9.0
ios-sim version: 5.0.13
OS: macOS Sierra
Node Version: v6.9.2
Xcode version: Xcode 8.2.1 Build version 8C1002

@ErikMartensson

I guess it's because creating two instances of moment, after each other, with no time defined might create them with a few microseconds difference.

Yes, moment() or moment(undefined)--which are identical--means now. JS dates have millisecond resolution, so if now is a different millisecond, which will sometimes happen, one time is after the other. AFAIK, JS doesn't define what order moment().isSameOrAfter(moment()) evaluates those two moment() calls,. But since it obviously has to do the second call before executing isSameOrAfter() anyway, it wouldn't surprise me if the second one in practice always comes first. That would mean that if there's a time difference it would also fail the "orAfter" part of the test. You could confirm this with a debugger.

I don't know what to tell you. This is just how finite-resolution datetimes have to work; "now" means a different time based on when you say it.

@imrvshah First note that

...isSameOrAfter(moment().format('MM-DD-YYYY'))

creates a new moment with the current time, formats it as a string, and passes that string to isSameOrAfter(), which parses it again. You should have just said:

...isSameOrAfter(moment())

And saved the moment -> string -> moment work. That's much better code, but I'd also bet it fixes the bug. Have you noticed deprecation warnings saying "hey, you're not supposed to pass strings to moment like that"? Moment knows how to parse ISO 8601 strings like moment("1982-05-25"). It also knows how to parse strings where the format is specified, like moment("05-25-1982", "MM-DD-YYYY"). But it has no idea that a string like "05-25-1982" is meant to be "MM-DD-YYYY" without you telling it. So it lets the browser guess by just handing that string to the Date constructor. In other words, your code unwraps like this:

m.isSameOrAfter("05-25-1982");
m.isSameOrAfter(moment("05-25-1982"))
m.isSameOrAfter(moment(new Date("05-25-1982")))

There's a reason we deprecated that: it gives inconsistent results based on what browser is running it. So what you're running into is that iOS doesn't support that format natively. In my Safari dev console:

> new Date("05-25-1982")
Invalid Date

That gave you bad comparisons that left you with an empty array.

@icambron

I agree with you and figure out the case in a console it was deprecation warning because of date format.

Thanks for your comment about not wrapping it multiple times and just use. ...isSameOrAfter(moment())

Was this page helpful?
0 / 5 - 0 ratings