How can I make dayjs perform arithmatic on the formatted value or local time?

For reference, I live in New Zealand where we are currently in GMT+13. Currently posting on 25th February, 3pm.

I am using the dayjs library and I need to create an array of formatted dates, given a minimum and a maximum date. I calculate the minimum from an offset from today, chosen in the UI. I calculate the maximum from a predecided date.

My initial attempt was as follows,

export function generateLabelRange(): string[] {
    const minimumDate = getMinimumDate();
    const maximumDate = getMaximumDateFromGoalInformation();

    const labelRange = maximumDate.diff(minimumDate, 'days');

    let labels = []
    for (let offSet = 0; offSet <= labelRange; offSet++) {
        labels.push(minimumDate.add(offSet, 'days').format("YYYY-MM-DD"));
    }
    return labels;
}

In this example, minimumDate was created via dayjs().subtract(dateRange, 'days') and maximumDate was generated via dayjs("YYYY-MM-DD"). An example that is causing the problem is as follows,

const minimumDate = dayjs().subtract(14, 'days'); // Correctly 11th February
const maximumDate = dayjs("2024-03-05");          // Displayed as 4th March

The problem I am having is that the object represented by dayjs("2024-03-05") is actually the 4th of march, not the 5th. It is only the 5th when formatting it. This means, the maximumDate.diff(minimumDate, 'days') will result in 22. However, this is the difference between the 4th of march and 11th february, NOT the original date which I gave to create my date, which was the 5th of march. The correct result should be 23, but it loops only 22 times. Because of this, my label generation method will be off by 1, depending on the maximum date provided.

Therefore, to fix this for now I now compares the formatted dates,

export function generateLabelRange(): string[] {
    const minimumDate = getMinimumDate();
    const maximumDate = getMaximumDateFromGoalInformation().format("YYYY-MM-DD");

    let date = minimumDate;
    let formattedDate = date.format('YYYY-MM-DD');

    let labels: string[] = [];
    while (formattedDate !== maximumDate) {
        labels.push(formattedDate);

        date = date.add(1, 'days');
        formattedDate = date.format("YYYY-MM-DD");
    }

    labels.push(formattedDate);
    return labels;
}

Although this does not feel nice, tbh. How can I change dayjs to perform add/subtract based on the day that it formats to?

enter image description here

Writing this function, my original implementation used the diff method to compute the range and did not work.

The appeared as such,

   const labelRange = maximumDate.diff(minimumDate, 'days');
 
   let labels = []
   for (let offSet = 0; offSet <= labelRange; offSet++) {
       labels.push(minimumDate.add(offSet, 'days').format("YYYY-MM-DD"));
   }

The problem is that the diff method will compute the difference in days in mlliseconds and when converting this to ‘days’, it will always round down. Hence, the difference between,

  let today = dayjs("2024-02-26");
  let minimumDate = dayjs().subtract(3, 'days');
  today.diff(minimumDate, 'days');                  // 2
  today.diff(minimumDate, 'days', true)             // 2.159. This will always round down, i.e. 2.7 -> 2.

Therefore, to utilize diff and subtraction, we need to turn the start of the minimum date to midnight via startOf('days') to properly get 3. However, instead of using diff and iterating over a range we can use isAfter and a while loop.

In addition using dayjs(), objects will do arithmatic in local time but will be displayed in UTC + 0. For example,creating an object as dayjs("2024-02-26"), which is the 26th February. This will be displayed to the console as 25th Feb 2024, 11:00:00 GMT. This is because in New Zealand during daylight savings we are currently at GMT + 13, meaning the console will display it 13 hours prior in GMT + 0, which will be at 11am on the 25th of February.

Leave a Comment