arraysdategoogle-sheetsgoogle-sheets-formulaarray-formulas

how to postpone certain holidays that fall on weekend


I live in a country, where by law, certain holidays are not allowed to fall on a weekend. If they do, they need to be delayed to fall on the first available working day.

For example, the holiday of Saint-X always falls on the 19th day of April. If that day is a Saturday or Sunday, this holiday needs to be celebrated on the first free working day (not necessarily Monday - especially when Monday already hosts a different holiday). There were cases in the past when such a weekend-ish holiday needed to be postponed by 6 or even 9 days.

I'm having a hard time coming up with a formula to implement this simple rule. In my spreadsheet, I have a column A with regular holidays and column B with holidays that are required to be postponed.

Column C contains dates of days, and column D (expected output) should fix column C's special holidays.

[enter image description here]1

normal holidays special holidays A & B dates fix (expected output)
26/9/2025 4/10/2025 26/9/2025 26/9/2025 << C3 is holiday but Friday so the date will be copied
10/11/2025 18/10/2025 4/10/2025 6/10/2025 << 4th is Saturday so +2 because 6th is first working day
12/11/2025 19/10/2025 18/10/2025 21/10/2025 << +3
13/11/2025 20/10/2025 19/10/2025 22/10/2025 << +3
17/11/2025 8/11/2025 20/10/2025 20/10/2025 << copied over
1/1/2029 9/11/2025 8/11/2025 14/11/2025 << +6
7/1/2029 11/11/2025 9/11/2025 18/11/2025 << +9
8/1/2029 7/12/2025 10/11/2025 10/11/2025 << copied over
9/1/2029 31/12/2028 11/11/2025 11/11/2025 << copied over
6/1/2029 12/11/2025 12/11/2025 << copied over
10/1/2029 13/11/2025 13/11/2025 << copied over
17/11/2025 17/11/2025 << copied over
7/12/2025 8/12/2025 << +1
31/12/2028 2/1/2029 << +2
1/1/2029 1/1/2029 << copied over
6/1/2029 11/1/2029 << +5
7/1/2029 7/1/2029 << copied over
8/1/2029 8/1/2029 << copied over
9/1/2029 9/1/2029 << copied over
10/1/2029 10/1/2029 << copied over

spreadsheet

EDIT:

So far I got this far, but it is a nasty draggable formula with INDIRECTing that is not so ideal:

=IF(COUNTIF($B$3:$B$100, C3)=0, C3,
  WORKDAY.INTL(C3-1, 1, "0000011",
  FILTER({$A$3:$A$100; $B$3:$B$100; INDIRECT("D$3:D"&ROW()-1)},
        ({$A$3:$A$100; $B$3:$B$100; INDIRECT("D$3:D"&ROW()-1)} <> C3 )
    *LEN({$A$3:$A$100; $B$3:$B$100; INDIRECT("D$3:D"&ROW()-1)}))))

Converting it to LAMBDA:

=BYROW(C3:C30, LAMBDA(x, LET(a, A3:A30, b, B3:B30,
 d, INDIRECT(ADDRESS(3, COLUMN())&":"&ADDRESS(ROW(x)-1, COLUMN())), 
 IF(COUNTIF(b, x)=0, x, WORKDAY.INTL(x-1, 1, "0000011",
 FILTER({a; b; d}, ({a; b; d}<>x)*LEN({a; b; d})))))))

With this I'm still failing to implement circular_reference-free recursion via LAMBDA. Hope someone can help.


Solution

  • You may try:

    =LET(a, TOCOL(A2:A, 1),
         b, TOCOL(B2:B, 1),
         c, TOCOL(C2:C, 1),
         d, FILTER(c, MATCH(c, a, 0)),
         e, BYROW(b, LAMBDA(x, IF(WEEKDAY(x, 2) > 5, WORKDAY(x, 1, c), x))),
         f, VSTACK(d, e),
         SORT(SCAN("", f, LAMBDA(x, y, IF(COUNTIF(f, y) > 1, IF(x = y, WORKDAY(y, 1, c), y), y)))))
    

    This outputs a sorted version of your expected results and works with Iterative calculation turned off.

    OUTPUT

    D (sorted) E
    9/26/2025 9/26/2025
    10/6/2025 10/6/2025
    10/20/2025 10/20/2025
    10/21/2025 10/21/2025
    10/22/2025 10/22/2025
    11/10/2025 11/10/2025
    11/11/2025 11/11/2025
    11/12/2025 11/12/2025
    11/13/2025 11/13/2025
    11/14/2025 11/14/2025
    11/17/2025 11/17/2025
    11/18/2025 11/18/2025
    12/8/2025 12/8/2025
    1/1/2029 1/1/2029
    1/2/2029 1/2/2029
    1/7/2029 1/7/2029
    1/8/2029 1/8/2029
    1/9/2029 1/9/2029
    1/10/2029 1/10/2029
    1/11/2029 1/11/2029

    UPDATE

    If the mapping of dates is important for future use, you may also try:

    =LET(a, TOCOL(A2:A, 1),
         b, TOCOL(B2:B, 1),
         c, TOCOL(C2:C, 1),
         d, MAP(c, LAMBDA(x, IFNA(XLOOKUP(x, a, a), IF(WEEKDAY(XLOOKUP(x, b, b), 2) > 5, WORKDAY(XLOOKUP(x, b, b), 1, c), XLOOKUP(x, b, b))))),
         SCAN("", d, LAMBDA(x, y, IF(COUNTIF(d, y) > 1, IF(x = y, WORKDAY(y, 1, c), y), y))))
    

    OUTPUT

    A B C D
    9/26/2025 10/4/2025 9/26/2025 9/26/2025
    11/10/2025 10/18/2025 10/4/2025 10/6/2025
    11/12/2025 10/19/2025 10/18/2025 10/21/2025
    11/13/2025 10/20/2025 10/19/2025 10/22/2025
    11/17/2025 11/8/2025 10/20/2025 10/20/2025
    1/1/2029 11/9/2025 11/8/2025 11/14/2025
    1/7/2029 11/11/2025 11/9/2025 11/18/2025
    1/8/2029 12/7/2025 11/10/2025 11/10/2025
    1/9/2029 12/31/2028 11/11/2025 11/11/2025
    1/6/2029 11/12/2025 11/12/2025
    1/10/2029 11/13/2025 11/13/2025
    11/17/2025 11/17/2025
    12/7/2025 12/8/2025
    12/31/2028 1/2/2029
    1/1/2029 1/1/2029
    1/6/2029 1/11/2029
    1/7/2029 1/7/2029
    1/8/2029 1/8/2029
    1/9/2029 1/9/2029
    1/10/2029 1/10/2029

    Note: The MAP function can be changed to the BYROW function, and it should produce the same result.