Nicholas Brush
Nicholas Brush

Reputation: 11

Trying to do a date addition while loop in javascript having a hell of a time

Basically I want to begin with a starting date (August 1st 2017) and loop to 90 days past the current date in two month increments pushing each date into an array. The output should look something like this ['2017-08-01','2017-10-01','2017-12-01' ... end date]. However, for whatever reason adding two months also increments the day (only on the first loop) resulting in this result:

 [  '2017-08-01', '2017-10-02',
    '2017-12-02', '2018-02-02',
    '2018-04-02', '2018-06-02',
    '2018-08-02', '2018-10-02',
    '2018-12-02', '2019-02-02',
    '2019-04-02', '2019-06-02',
    '2019-08-02', '2019-10-02',
    '2019-12-02', '2020-02-02',
    '2020-04-02', '2020-06-02',
    '2020-08-02', '2020-10-02',
    '2020-12-02', '2021-02-02',
    '2021-04-02', '2021-06-02',
    '2021-08-02', '2021-10-02',
    '2021-12-02', '2022-01-12 end' ]

Here is the loop:

createDateRange : ( endDate : Date) :Array<Date> => {
   const dateArray = [];

   const startDate = new Date('2017-08-01')
   let datePointer = startDate
   while(endDate > datePointer) {
       dateArray.push(datePointer.toISOString().split('T')[0])
       datePointer.setMonth(datePointer.getMonth() + 2)
       datePointer.setDate(1)
   }
    
   dateArray.push(endDate.toISOString().split('T')[0] + ' end')

   return dateArray
}

I just don't understand. I am setting date to 1 every time right before pushing to the array, why would it switch back to two?

EDIT: Here is some code which I have run in a .js file.

const testDate = () => {
  let dateArray = [];
  const endDate = new Date()
  endDate.setDate(endDate.getDate() + 90)

  let datePointer = new Date('2017-08-01')
  let datePointerTest = new Date(datePointer.getFullYear(), datePointer.getMonth(), datePointer.getDate())

  console.log(datePointer, 'datePointer')
  console.log(datePointerTest, 'datePointerTest')

  while (endDate > datePointer) {
    let internalArray = []

    let startInterval = new Date(datePointer.getFullYear(), datePointer.getMonth(), 1)
    let endInterval = new Date(datePointer.getFullYear(), datePointer.getMonth(), 0)

    internalArray.push(startInterval.toISOString().split('T')[0])
    internalArray.push(endInterval.toISOString().split('T')[0])

    dateArray.push(internalArray)

    datePointer = new Date(datePointer.getFullYear(), datePointer.getMonth() + 2, 1)
  }

  dateArray[dateArray.length - 1][1] = endDate.toISOString().split('T')[0]

  return dateArray

}

let tester = testDate()

console.log(tester)

Here is the output:

2017-08-01T00:00:00.000Z datePointer
2017-07-31T07:00:00.000Z datePointerTest
[
  [ '2017-07-01', '2017-08-31' ],
  [ '2017-09-01', '2017-10-31' ],
  [ '2017-11-01', '2017-12-31' ],
  [ '2018-01-01', '2018-02-28' ],
  [ '2018-03-01', '2018-04-30' ],
  [ '2018-05-01', '2018-06-30' ],
  [ '2018-07-01', '2018-08-31' ],
  [ '2018-09-01', '2018-10-31' ],
  [ '2018-11-01', '2018-12-31' ],
  [ '2019-01-01', '2019-02-28' ],
  [ '2019-03-01', '2019-04-30' ],
  [ '2019-05-01', '2019-06-30' ],
  [ '2019-07-01', '2019-08-31' ],
  [ '2019-09-01', '2019-10-31' ],
  [ '2019-11-01', '2019-12-31' ],
  [ '2020-01-01', '2020-02-29' ],
  [ '2020-03-01', '2020-04-30' ],
  [ '2020-05-01', '2020-06-30' ],
  [ '2020-07-01', '2020-08-31' ],
  [ '2020-09-01', '2020-10-31' ],
  [ '2020-11-01', '2020-12-31' ],
  [ '2021-01-01', '2021-02-28' ],
  [ '2021-03-01', '2021-04-30' ],
  [ '2021-05-01', '2021-06-30' ],
  [ '2021-07-01', '2021-08-31' ],
  [ '2021-09-01', '2021-10-31' ],
  [ '2021-11-01', '2021-12-31' ],
  [ '2022-01-01', '2022-01-12' ]
]

The goal here is to create an array of two month intervals from the start date, ending with the target date. Couple of questions about this.

Edit 2: welp, figured it out. At least, I got it working how I want and I think I know the issue. Apparently, it seems like javascript interprets date objects using your timezone in some way or another. So when I set datePointer as '2017-08-01' it was interpretted as '2017-07-31 and some amount of hours' presumably due to the time difference between me and UTC or whatever. Got it working by setting the initial date pointer at 2017-08-02.

Upvotes: 0

Views: 106

Answers (1)

RobG
RobG

Reputation: 147403

Why do datePointer and datePointerTest log differently?

Because in:

let datePointer = new Date('2017-08-01')

the date is parsed as UTC, it's effectively 2017-08-01T00:00:00Z, or equivalent to (noting zero indexed months):

let datePointer = new Date(Date.UTC(2017, 7, 1));

Whereas in:

let datePointerTest = new Date(datePointer.getFullYear(), datePointer.getMonth(), datePointer.getDate())

you're getting the local values, so it's equivalent to:

let datePointerTest = new Date(2017, 6, 31)

because you're getting local values not UTC and the values are treated as local. So the dates will have a time value that is different by one day (because your local date was the day earlier) plus your local offset (which appears to be -7) because of the local to UTC conversion. So there are two issues to deal with.

Why does the first startInterval land at 2018-07-01 when the datepointer at the beginning is at date 2018-08-01?

Because you started with 2017-08-01T00:00:00Z which for you is 2017-07-31T14:00:00.000-07:00. You then create a date using the local values:

let startInterval = new Date(datePointer.getFullYear(), datePointer.getMonth(), 1)

which is effectively (noting month is zero indexed to 6 is July)

let startInterval = new Date(2019, 6, 1);

and since the time isn't set, it defaults to 00:00:00.000, so now you have 2017-07-01T00:00:00.000-07:00 which is 2017-07-01T07:00:00.000Z.

You snip the UTC time part and just push the date, so see 2017-07-01.

The key is to not use the built–in parser (EVER!) and keep everything in the same timezone, so either local or UTC. Here's a simple function to do what you want based on local dates, it could use UTC but it would just be more to type.

// Get 2 month intervals from start of month of startDate
// until end of month of endDate
// startDate and endDate in YYYY-MM-DD format
function getIntervals(startDate, endDate) {

  // Formatting helper - YYYY-MM-DD
  let f = d => d.toLocaleString('en-ca').substring(0,10);

  // endDate defaults to today
  if (!endDate) endDate = f(new Date());

  let [startY, startM] = startDate.split(/\W/);
  let startD = new Date(startY, startM-1, 1);
  let [endY, endM] = endDate.split(/\W/);

  // Use calendar month == ECMAscript month + 1
  // to get end of end month
  let endD = new Date(endY, endM, 0);
  let dates = [];

  while (startD < endD) {
    // Use 0 date to get end of previous month, deals with
    // different length months.
    dates.push([f(startD), f(new Date(startD.getFullYear(), startD.getMonth()+2,0))]);
    // Increment start of month
    startD.setMonth(startD.getMonth() + 2);
  }
 
  return dates;
}

console.log(getIntervals('2017-08-01'));

Upvotes: 1

Related Questions