简体   繁体   中英

Divide the date range given by moment js by weeks

I want to divide a given date range into months. And I want to separate these months according to weeks. I want to get the date from the start date to the end of the month at 7-day intervals. For example: (Like 15/12/2020, 22/12/2020, 29/12/2020 -newMonth- 05/01/2020)

It is difficult to understand with this explanation. After all I want to create a json like this.

Starting Date: 15/11/2020 - End Date: 20/01/2021

[
  {
    "dates": [
      15, // Starting date(15/11/2020)
      22, // 1 week later
      29 // 1 week later
    ],
    "firstDay": "15/11/2020"
  },
    {
    "dates": [
      6,
      13,
      20,
      27
    ],
    "firstDay": "06/12/2020"
  },
  {
    "dates": [
      3,
      10,
      17
    ],
    "firstDay": "03/01/2021"
  }
]

There are a few tricks that you can use to create the array structure you want:

  1. Use a while loop, which keeps a current date that is constantly incremented by 7 days, to be compared against the end date. The current date should be initialized using the start date in the beginning of the loop.
  2. To perform the "group by month" in your desired structure, you will need to using an object (which serves as a dictionary). The object will use the month + year combination as key, so that if your date ranges span over multiple years you won't collapse months from different years into the same entry.

For each iteration of the while loop, you construct the key from the current date, and then check if the key exists in your dictionary. If not, then we can push this object into your dictionary:

{
  dates: [currentDate],
  firstDate: currentDate
}

If it already exists, then you simply push the date into the dates array of the sub-object.

Finally, to convert your dictionary into the array strcuture you wanted, using ES6 Object.values() will work. See proof-of-concept below:

 function generateRanges(startDate, endDate) { let current = moment(startDate, 'DD/MM/YYYY'); const end = moment(endDate, 'DD/MM/YYYY'); // A dictionary to track unique month+year combinations const daysByMonth = {}; while (current < end) { // Construct key based on month+year const key = `${current.month()}${current.year()}`; const date = current.date(); // If key already exists, then we push into the `dates` array if (key in daysByMonth) { daysByMonth[key].dates.push(date); } // Otherwise we construct a brand new sub-object else { daysByMonth[key] = { dates: [date], // Since this is the first time we encounter the key, // We can assume this is the earliest/first date of the month firstDate: current.format('DD/MM/YYYY') } } // At the end of the while loop, increment by a week, rinse and repeat current.add(7, 'days'); } // Once done, we only want the values in the dictionary // We don't need to keep the unique month+year key return Object.values(daysByMonth); } const range = generateRanges('15/11/2020', '20/01/2021'); console.log(range);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>

When dealing with date or datetime is always a good practice to use milliseconds. Also it is space efficient, a long integer = 4kb instead of a Date() object which is 32bytes.

In your case it is very straightforward to convert our dates to milliseconds, find how many weeks are between as integer and then run a loop increment by this iteration.

let from = new Date('2020/11/15').getTime();
let to = new Date('2021/01/20').getTime();
let week = 604800000;
let day = 86400000;
let allWeeks = [];
let current =0;

let weeks = (to-from)/day/7

for (i=0; i<weeks; i++){
  allWeeks.push(new Date(from += week).toLocaleDateString())
}

console.log(JSON.stringify(allWeeks))
//["22/11/2020","29/11/2020","06/12/2020","13/12/2020","20/12/2020","27/12/2020","03/01/2021","10/01/2021","17/01/2021","24/01/2021"]

Finally you will have a list applicable for JSON to build any logic you prefer.

I hope to pin point a differed approach to your case!

The Moment.JS has an API for adding days to a date: the add function.

In order to build your final object, you would start from your startDate, and add 7 days for each loop iteration until reaching the end date. Like this:

let currentDate = moment(startDate);

while(currentDate < endDate) { // Make sure endDate is also a moment object
  // Add the current date - you will want to adapt this
  dates.push(currentDate.clone()); // Since .add() will mutate the currentDate object, clone the object when adding it to the array so that it's not affected by the add()
  // Prepare for the next iteration
  currentDate.add({days: 7});
}

As for month changes, remember the last month for every iteration (use month ) and create a new object in your final array, then add dates to object instead of the previous one.

Side note : Months are zero-indexed in Javascript, which is not the case for the month day ( date )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM