adobe / react-spectrum

A collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.
https://react-spectrum.adobe.com
Apache License 2.0
12.12k stars 1.06k forks source link

[internationalized/date] Add eachMonthOfInterval query #6646

Open yajihum opened 2 days ago

yajihum commented 2 days ago

Provide a general summary of the feature here

We would like to make the eachMonthOfInterval query available in @internationalized/date, similar to what is available in date-fns. Without this feature, users have to either define the function themselves or install date-fns, which is inconvenient.

I don't think it's necessary to cover everything available in date-fns, but it would be helpful to add convenient features (usable with the DateValue type) for using RAC.

๐Ÿค” Expected Behavior?

It would be nice to be able to obtain each month spanning the specified start and end dates.

let start = new CalendarDateTime(2023, 11, 15, 9, 20);
let end = new CalendarDateTime(2024, 2, 10, 9, 0);

eachMonthOfInterval(start, end); 
// => [ 2023-12-01T00:00, 2024-01-01T00:00, 2024-02-01T00:00 ]

๐Ÿ˜ฏ Current Behavior

Since this query does not exist, we need to either define it from scratch or install date-fns.

๐Ÿ’ Possible Solution

I think it can be implemented as follows. I plan to submit a PR.

/** Returns an array of the start dates of each month within the specified date interval. */
export function eachMonthOfInterval(start: DateValue, end: DateValue): DateValue[] {
  const startDate = startOfMonth(start).set({hour: 0, minute: 0});
  const endDate = endOfMonth(end).set({hour: 0, minute: 0});

  if (startDate.compare(endDate) > 0) {
    throw new Error('The start date must be before the end date.');
  }

  function collectDates(currentDate: DateValue, endDate: DateValue): DateValue[] {
    if (currentDate.compare(endDate) > 0) {
      return [];
    }
    return [currentDate, ...collectDates(startOfMonth(currentDate.add({months: 1})), endDate)];
  }

  return collectDates(startDate.add({months: 1}), endDate);
}

๐Ÿ”ฆ Context

When implementing calendar display using RAC, I wanted to obtain eachMonthOfInterval, but it was not available in @internationalized/date, so I had to install date-fns separately. Since handling dates in RAC requires the DateValue type instead of the standard JavaScript Date type, converting to DateValue is also cumbersome. Therefore, it would be great if we could obtain eachMonthOfIntervaldirectly in the DateValue type from the start.

๐Ÿ’ป Examples

No response

๐Ÿงข Your Company/Team

No response

๐Ÿ•ท Tracking Issue

No response