How to validate if one date is longer than the other considering day, month and year

Asked

Viewed 261 times

1

I need to validate if the "Current Date" is larger than the "Due Date", but it seems that Moment.js only compares the days and does not take month/year into account.

var dataAtual = moment(new Date()).format('DD/MM/YYYY');//12/01/2021
var dataVencimento = moment(item.dataVencimento).format('DD/MM/YYYY');

console.log(moment(dataAtual).isAfter(dataVencimento)) //true

12/01/2021 (Date of Today) is no higher than 01/02/2021. The result should be false, but he compared only the day, because day 12 is bigger than day 01 and returned true. You are wrong!

I am working with monthly due dates and the return should be as I reported in red in the image below:

inserir a descrição da imagem aqui

Is there any way to make him consider day/month/year and validate the dates correctly?

2 answers

4


The problem is that format returns a string, and strings are compared lexicographically, ie even digits like 0 and 1 are treated as characters, and therefore a string that starts with 0 is considered "smaller" than one starting with 1 (you can read here to better understand this behavior).

In the case of Moment.js, isAfter accepts strings as parameter, and it tries to do the Parsing, but as you passed a string in "dd/mm/yyyy" format and it is not guaranteed that this will be recognized, it fails to do the comparison correctly (from documentation: "Browser support for Parsing strings is inconsistent. Because there is no Specification on which formats should be supported, what Works in some browsers will not work in other browsers").

Obs: in my test, it gave error instead of returning true, but anyway it should not be done this way.

If you want to compare dates, do not string them. In this case, use the objects themselves moment:

var dataAtual = moment(); // data atual
var dataVencimento = moment('2021-02-01');

console.log(dataAtual.isAfter(dataVencimento, 'day')); // false
<script src="https://momentjs.com/downloads/moment.min.js"></script>

In case I used 'day' as the second parameter so that it does not take into account the timetable (see documentation), because I understood that you only compare the day, month and year, regardless of the time. This is necessary because the current date/time will also have the time and without the second parameter, this is also taken into account in the comparison.


Just for the record, your mistake was confusing dates with formats. But understand one thing: as I said before here, here and here, dates have no format.

A date is just a concept, an idea: it represents a specific point in the calendar.

The date of "1 January 1970", for example, represents this: the specific point of the calendar that corresponds to the first day of January 1970. To express this idea in text form, I can write it in different ways:

  • 01/01/1970 (a common format in many countries, including Brazil)
  • 1/1/1970 (American format, reversing day and month)
  • 1970-01-01 (the format ISO 8601)
  • First of January 1970 (in good Portuguese)
  • January 1st, 1970 (in English)
  • 1970 年 1 月 1 日 (in Japanese)
  • and many others...

Note that each of the above formats is different, but all represent the same date (the same numerical values of the day, month and year).

In this case, an object moment represents a date, and format returns a string representing this date in some format.

If you want to compare dates, don’t use strings, use the dates themselves.


But just out of curiosity, a format that allows a direct comparison without these problems is defined by ISO 8601:

var dataAtual = moment().format('YYYY-MM-DD'); // data atual
var dataVencimento = moment('2021-02-01').format('YYYY-MM-DD');

console.log(dataAtual > dataVencimento); // false
<script src="https://momentjs.com/downloads/moment.min.js"></script>

But of course, if you already have the dates, it makes no sense to turn them into strings to compare them, and the dates themselves can already be compared directly.

  • 1

    Thanks for the excellent reply @hkotsubo! It’s very clear now! Hug!

0

One possibility, without using external libraries, is to do this:

// obter a data de hoje no formato yyyy-mm-dd
let hoje = new Date(2021, 1 - 1, 12); // para 12/01/2021
let hojeYMD = (new Date(hoje.getTime() -
    (hoje.getTimezoneOffset()*60*1000))).toISOString().split('T')[0];

// obter a data de vencimento no formato yyyy-mm-dd 
let dataVencimento = "01/02/2021"
let trechos = dataVencimento.split("/");
let dataVencimentoYMD = [trechos[2], trechos[1], trechos[0]].join("-")

// comparar as duas datas que estão como string e no mesmo formato
let vencido = hojeYMD > dataVencimentoYMD;
console.log(vencido);

How it works:

  • let hoje = new Date(2021, 1 - 1, 12) is to set today’s date of your example;
  • let hojeYMD = new Date(hoje.getTime() - (hoje.getTimezoneOffset()*60*1000)) converts the current time zone date to UTC (time zone 0 - GMT - Greenwich), .toISOString() converts the date to yyyy-mm-ddThh:nn:ss.zzzz format and .split('T')[0] sets the date of the time and takes only the date;
  • let dataVencimento = "01/02/2021" is the date of your table;
  • let trechos = dataVencimento.split("/") separates the day, month and year from the due date;
  • let dataVencimentoYMD = [trechos[2], trechos[1], trechos[0]].join("-") recreates the maturity date string in yyyy-mm-dd format;
  • let vencido = hojeYMD > dataVencimentoYMD compares the two string variables that are in yyyy-mm-dd format;
  • console.log(vencido) prints in the Console the boolean value (true or false) indicating if the due date is earlier than today’s date.

Demo

// obter a data de hoje no formato yyyy-mm-dd
let hoje = new Date(2021, 1 - 1, 12); // para 12/01/2021
let hojeYMD = (new Date(hoje.getTime() -
    (hoje.getTimezoneOffset()*60*1000))).toISOString().split('T')[0];

// obter a data de vencimento no formato yyyy-mm-dd 
let dataVencimento = "01/02/2021"
let trechos = dataVencimento.split("/");
let dataVencimentoYMD = [trechos[2], trechos[1], trechos[0]].join("-")

// comparar as duas datas que estão como string e no mesmo formato
let vencido = hojeYMD > dataVencimentoYMD;
console.log(vencido);

  • Add or subtract a value to getTime is not a good one (it may even "work", but actually you are creating a date referring to a different moment, and not "converting the Timezone"). That’s because the Date Javascript actually represents a timestamp (a point in the timeline), and does not have a Timezone attached. For more details, read here, here and here. Another detail is that toISOString already returns the date converted to UTC, so no need to use getTimezoneOffset, just do hoje.toISOString()

  • I understood. Thank you so much for your contribution. What do you think about using this code to generate hojeYMD? let hoje = new Date(); /* ou new Date(2021, 1 - 1, 12); */&#xA;&#xA;let hojeYMD = [hoje.getFullYear(), (hoje.getMonth() + 1).toString().padStart(2, '0'), hoje.getDate().toString().padStart(2, '0')].join('-');

  • Continuing the previous comment: I, for example, am in the Brasilia time zone (GMT-0300). If now were 22h, the hoje.toISOString() would return the date tomorrow.

  • Yes, there will be several corner cases of this type, so it is best to compare objects Date directly, without turning into string

Browser other questions tagged

You are not signed in. Login or sign up in order to post.