Find the next closest date in MM/DD/YYYY format JavaScript

17,551

Solution 1

There is no need for a sorting algorithm. You only need to iterate once and find the closest date that is greater or equals today.

Pseudocode

closest <- infinity
foreach date in dates:
    if (date >= now and date < closest) then
        closest <- d
return closest

JavaScript

const dates = [
  '2/3/2015',
  '7/5/2015',
  '1/21/2016',
  '2/19/2016',
  '7/1/2016',
  '10/22/2019',
  '08/12/2019',
];

const now = new Date();

let closest = Infinity;

dates.forEach(function(d) {
   const date = new Date(d);

   if (date >= now && (date < new Date(closest) || date < closest)) {
      closest = d;
   }
});

console.log(closest);

Solution 2

Personally I would use a library such as the very good Moment.JS library, to handle all the horrible complexity of dates.

It has a difference method:

http://momentjs.com/docs/#/displaying/difference/

e.g.

var a = moment([2007, 0, 29]);
var b = moment([2007, 0, 28]);
a.diff(b) // 86400000

It would then be trivial to Math.min() the differences of each date in your list.

There's also a moment.min, which might shortcut this entirely, if all your dates are in the future already:

http://momentjs.com/docs/#/get-set/min/

Solution 3

A naïve implementation would be to parse each date as a string and sort them in ascending order. Then, remove any dates that are in the past, and get the first child of the array of remaining dates. See this jsbin example:

var dates = [
  '2/3/2015',
  '7/5/2015',
  '1/21/2016',
  '2/19/2016',
  '7/1/2016'
];

// parse each string as a Date object and sort them in ascending order
function sortDates(dates) {
  return dates.map(function(date) {
    return new Date(date).getTime();
  }).sort(function(a, b) {
    return a - b;
  });
}

var orderedDates = sortDates(dates);

// remove any dates in the past, and get the first child of the array of remaining dates
var nextDate = orderedDates.filter(function(date) {
  return (Date.now() - date) > 0;
})[0];

Keep in mind that this depends on the format of the date string that you pass to the Date object (in other words, is 1/12/2015 January 12th, or December 1st? JavaScript will parse it as January 12th.

Solution 4

This really depends upon your dates and data structures (the ones shown in original example are not so great for me).

From the other answers...

To take the example from Josh, you could also keep a pointer to which date you are using, or simply shift off of a sorted queue of dates to make it work, but it's really adding noise to your code, disrupting the purpose.

Frederik.L answer is really beautiful code, but it would still have to be executed multiple times, so I cannot recommend it.

Feedback warning

I've been given feedback in comments that Date.parse can behave inconsistently. I'll move to passing a date parsing callback function, and demonstrate Date.UTC usage in the callback for OP-specific date format. Please be careful when defining your own callbacks, and please do not copy-paste.

Suggestion

I'd suggest utilizing Date functions i.e. Date.parse; but also try where possible to get data sources sorted without needing application-level sorting. Then you can store-once and step through the array using array.shift() or similar;

Ideally also YYYY-MM-DD

  • Four-Digit Year
  • Two-Digit Month
  • Two-Digit Day
  • ... (continue from least occurring to most occurring)

sample code

var dates = [
    '2/3/2015',
    '7/5/2015',
    '7/1/2016',
    '1/21/2016',
    '2/19/2016'
]; // unsorted garbage dates

var DateList = function( dateList, getDate ) {
    var sortedDates = dateList.sort( function(a, b) {
        return getDate(a) - getDate(b);
    });
    this.next = function() {
        var dt = sortedDates.shift();
        sortedDates.push(dt); // comment to remove cyclical nature
        return dt;
    }
};

// specific implementation parser for this format
var getDisgustingDateFormat = function(dStr) {
    var dParts = dStr.split('/');
    return new Date(Date.UTC(dParts[2],dParts[0],dParts[1]));
};
var dl = new DateList( dates, getDisgustingDateFormat );

Usage

dl.next(); // "2/3/2015"
dl.next(); // "7/5/2015"
dl.next(); // "1/21/2016"
dl.next(); // "2/19/2016"
dl.next(); // "7/1/2016"
dl.next(); // "2/3/2015"

Hope this helps (Updated for clarity)

Solution 5

You can use while loop, new Date()

var dates = ["2/3/2015","7/5/2015","1/21/2016","2/19/2016","7/1/2016"]
, d = "1/22/2016", n = -1, res = null;

while (++n < dates.length && new Date(dates[n]) < new Date(d));
res = dates[n] || d;
console.log(res)

Share:
17,551
MahoreLee
Author by

MahoreLee

Updated on July 24, 2022

Comments

  • MahoreLee
    MahoreLee almost 2 years

    I have an array of dates formatted as MM/DD/YYYY. I need to find the next closest date in the future starting from today. Say today was 1/22/2016 then 2/19/2016 would return.

    2/3/2015
    7/5/2015
    1/21/2016
    2/19/2016
    7/1/2016
    

    I've tried doing substrings to get the month, day, year separate and attempting a sort based off those values but surely there has to be a better way.

  • MrMesees
    MrMesees almost 8 years
    I Like this, but I think it's processing heavy. Each new date requires a loop over every single date, so for this reason I prefer my answer, but your answer reads very well and I can see the rationale.
  • MrMesees
    MrMesees almost 8 years
    I'd suggest skipping out on the map as it is destructive to the horrible date format used.
  • Josh Beam
    Josh Beam almost 8 years
    @MrMesees thanks for your comment, can you elaborate what you mean a little bit?
  • MrMesees
    MrMesees almost 8 years
    So if you do not use the map function, you can still filter, and simply parse the dates twice (see my answer)
  • Frederik.L
    Frederik.L almost 8 years
    @MrMesees Sorting an array comes at an heavier price than a single iteration over it. Since the OP didn't mention that he wants to get a sorted array, I didn't see a need to sort it. In fact, he said that he needs "the next closest date", not all of them.
  • MrMesees
    MrMesees almost 8 years
    Please consider that having a list will generally mean you'll want to do more than one thing with that list. I'm not trying to nit-pick, as I've said I really like your code, it's just not IMHO the best fit for this problem, so I thought I'd reach out and suggest areas for improvement; I'd welcome the same for my answer
  • Frederik.L
    Frederik.L almost 8 years
    What happens generally still isn't what the OP asked for... or at least he didn't say he wanted that. I'll try to clear that out. I think that an unwanted sorting is a bad idea since you add unnecessary complexity.
  • ceremcem
    ceremcem almost 8 years
    Because it's fun :) But stackoverflow is such a strange place that you may get a down vote or may be questioned even though your code works as intended and might help the OP or someone else in some way...
  • RobG
    RobG almost 8 years
    @MrMesees—what existing Date function functionality is that?
  • RobG
    RobG almost 8 years
    new Date(string) and new Date(Date.parse(string)) will return identical results as they are functionally equivalent. Strings should never be parsed using either.
  • guest271314
    guest271314 almost 8 years
    "Say today was 1/22/2016 then 2/19/2016 would return"
  • MrMesees
    MrMesees almost 8 years
    I agree the format given is a poor choice, but if OP were to switch to YYYY-MM-DD as I've suggested I'm sure there would be no issues
  • RobG
    RobG almost 8 years
    My comment isn't about the format. YYYY-MM-DD is not parsed correctly (i.e. per ECMA-262) by all browsers in use. Some parse it as UTC, some as local, some allow out of range values and some won't parse it at all. ;-)
  • MrMesees
    MrMesees almost 8 years
    I've edited my example to accept a user-defined callback and shown use of Date.UTC to parse that date manually with a string split. I've also noted the problem with Date.parse... Maybe one day language devs will make a language without so many bugs and bad decisions.
  • MrMesees
    MrMesees almost 8 years
    @RobG I was not aware of problems in Date.parse at the time of the comment, it's been removed. I still think that this is not very friendly code example.
  • RobG
    RobG almost 8 years
    Don't hold your breath. Given the opportunity to fix at least one aspect of parsing ISO 8601 formatted dates, TC39 failed miserably. See TC39:Date Time String Format: default time zone difference from ES5 not web-compatible #87.
  • MrMesees
    MrMesees almost 8 years
    I'm pretty sure almost always, dates and times should be stored and retrieved in UTC; More specifically when parsing without tz data, this makes things simple. Display-wise I can see an argument for local date times. So to my mind missing tz data should always imply UTC. Anyway thanks @RobG luckily we do use a lib for parsing dates in my business; I'll take "use native JS date functions" off the menu lol
  • MrMesees
    MrMesees almost 8 years
  • Frederik.L
    Frederik.L almost 8 years
    @ceremcem I did not down vote your answer since other people did enough yet, but I can explain why it got down votes and why I usually down vote answers like this on SO. Fun stuff is cool to write, but painful to read. It's even more painful to edit and maintain. As developers, we must show the community that they should use cleaner code, instead of faster, wobbling code. The exception is made when there is a significant performance issue and the wobbling thing is greatly resolving the issue.
  • ceremcem
    ceremcem almost 8 years
    @Frederik.L I totally agree about writing cleaner code. Then I totally accept the downvotes. Thank you.
  • zach_21
    zach_21 over 3 years
    this returns infinity
  • Karl Patrick Espiritu
    Karl Patrick Espiritu over 3 years
    yep I can confirm that this returns Infinity