1

I'm writing a function that calculates how many days ago a given date was from today. (e.g. yesterday = 1, last week = 7, today = 0, tomorrow = -1 and so on)

Seemed simple enough, and using the JavaScript Date() function I initially wrote this:

let historicalDate = new Date(2017,05,17).getTime(); // example date: last week
let diff = Math.round((new Date().getTime() - historicalDate) / (24*60*60*1000) );

After I got some weird results, I neatened up the code, but still got the same issue, as follows:

/**
* Returns an integer, representing the number of days since a given date
**/
function getNumDaysFromDate(historicalDate){
  const day = 24*60*60*1000;              // The number of milliseconds in one day
  const now = new Date().getTime();       // The time right now 
  const then = historicalDate.getTime();  // The time comparing to
  return Math.round((now - then) / day ); // Find difference in milliseconds, then days
}

// Test1: last week, should return 7
let creationDate1 = new Date(2017,05,17);
console.log("Last week:", getNumDaysFromDate(creationDate1)); // Fail, prints -23

// Test2: yesterday, should return 1
let creationDate2 = new Date(2017,05,23);
console.log("Yesterday:", getNumDaysFromDate(creationDate2)); // Fail, prints -29

// Test3: Today, should return 0
let creationDate3 = new Date();
console.log("Today:", getNumDaysFromDate(creationDate3)); // Pass, prints 0

// Test4: day affer tomrrow, should return -2
let creationDate4 = new Date(2017,05,26);
console.log("Future:", getNumDaysFromDate(creationDate4)); // Fail, prints -32

All the above results appear to be all about 1 month out, (except for 'test 3', today).

I'm sure there is an obvious or simple reason for this, that one of you will spot instantly, but I have spent the last couple of hours mind-blown by it!

Thanks in advance!

Edit: If possible, I'd like to avoid using a library like Moment.js, as this should be possible nativity (?), and is the only date-related calc in my application.

Alicia Sykes
  • 5,997
  • 7
  • 36
  • 64
  • use momentjs and avoid headaches. – yBrodsky May 24 '17 at 20:54
  • I'm not accepting defeat over something so simple! But Yes, that would be one solution. I wanted to consider avoiding using a library if I could do it in 1 or 2 lines. – Alicia Sykes May 24 '17 at 20:57
  • Please check this question: https://stackoverflow.com/a/1296374/693275 – Rob M. May 24 '17 at 20:59
  • 1
    When initializing a new Date with the constructor you specified, the month numbering starts from 1 as January, so May is 5. However, using the empty constructor `new Date()` it is 0 for January and 4 for May. Hence the shift. – Michał Szydłowski May 24 '17 at 21:02
  • 1
    Compare `new Date(2017, 05, 17).getMonth()` and `new Date().getMonth()` – Michał Szydłowski May 24 '17 at 21:03
  • 24*60*60*1000 will work for any recent dates and for almost all conditions, but due to leap seconds you'll start to get boundary conditions and 1-off errors if you start going back further. Using momentjs like @yBrodsky suggested will avoid that. – Dan Smolinske May 24 '17 at 21:09
  • dupe? https://stackoverflow.com/questions/542938/how-do-i-get-the-number-of-days-between-two-dates-in-javascript – dbandstra May 24 '17 at 21:20

2 Answers2

5

Be careful: the Javascript date API is completely insane (exactly like the Java date API).

Month starts with 0 (January) and goes up to 11 (December). So new Date(2017,5,17) actually means June 17th 2017.

Rolf Schäuble
  • 690
  • 4
  • 15
  • 1
    This is the right answer, previous answers just work by chance as the dates in the example are all in May. – rstar May 24 '17 at 21:09
  • I would argue that it is not, because it points out a problem but it doesn't really answer how to circumvent it. I've since edited my answer to include this concern. – R. McManaman May 24 '17 at 21:12
  • @R.McManaman: well, my answer shows that the assumption itself is wrong. "new Date(2017,5,17)" isn't a date in May, it's a date in June. So the code itself is correct, it was just fed with wrong data. – Rolf Schäuble May 24 '17 at 21:17
  • Thank you @Rolf Schäuble. Clearly points out a key problem in my code, where I misunderstood some JavaScript logic (or illogic!). +1 – Alicia Sykes May 24 '17 at 21:18
  • @RolfSchäuble Fair enough! I posted an answer as well, so I suppose I may have been a little defensive in my comment! I apologize for that, your answer is correct :) – R. McManaman May 24 '17 at 21:20
  • If your basis for "*the Javascript date API is completely insane*" is that months are zero indexed, your bar for "insanity" is set very low. Zero indexed months are quite handy, the API is meant for programmers who should be able to handle such things. – RobG May 24 '17 at 22:27
  • 2
    @RobG: Programmers are humans. Humans are conditioned from quite early on that the first month in the year is January (1 == January). Defining an API that goes against basic expectations is insane. And this isn't just a theoretical problem: I have seen countless bugs just because of this insanity. Do not expect programmers to be able to handle any counter-intuitive API; instead, design APIs that match (justified) expectations. – Rolf Schäuble May 24 '17 at 22:44
  • The API has been around for ages, I can't see it changing. ;-) PS, I do think the javascript Date is seriously deficient, but only in regard to parsing and formatting. Otherwise it works pretty well. – RobG May 24 '17 at 23:38
  • 1
    "exactly like the Java date API" - Make that the *old* legacy Java date-time API, now supplanted by the [java.time](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html) classes, the most excellent framework for date-time work. Regarding RobG comment, the Java world was saddled for many years with a lousy set of classes for date-time but managed to replace it with Joda-Time that later inspired java.time. The java.time source code is GPL and could be ported… – Basil Bourque May 25 '17 at 03:39
-3

remind the months in JavaScript are zero based (Jan = 0, Feb = 1, ...). So if you need may it's 4 (not 5).

/**
* Returns an integer, representing the number of days since a given date
**/
function getNumDaysFromDate(historicalDate){
  const day = 24*60*60*1000;              // The number of milliseconds in one day
  const now = new Date().getTime();       // The time right now 
  const then = historicalDate.getTime();  // The time comparing to
  var value = Math.round((now - then) / day );
  if(value == 0){
     return 0
    }
  else{
    return value+30;
    }// Find difference in milliseconds, then days
}

// Test1: last week, should return 7
let creationDate1 = new Date(2017,04,17); // 17th of May 2017
console.log("Last week:", getNumDaysFromDate(creationDate1)); // Fail, prints -23

// Test2: yesterday, should return 1
let creationDate2 = new Date(2017,04,23); // 23 of May 2017
console.log("Yesterday:", getNumDaysFromDate(creationDate2)); // Fail, prints -29

// Test3: Today, should return 0
let creationDate3 = new Date();
console.log("Today:", getNumDaysFromDate(creationDate3)); // Pass, prints 0

// Test4: day affer tomrrow, should return -2
let creationDate4 = new Date(2017,04,26); // 26th of May 2017
console.log("Future:", getNumDaysFromDate(creationDate4)); // Fail, prints -32
  • Thanks for your answer, but the results from the code snippet were incorrect (e.g. yesterday was not 32 days ago). I thin because you used @Rishikesh Chandra's solution, and then also modified the dates ;) – Alicia Sykes May 24 '17 at 21:31