Please enable JavaScript to view this site.

 

Navigation: Appendices > Q++Studio > Common UI Elements

Variable Holiday Names: Matching and Precedence

Scroll Prev Up Next More

This topic is a detailed technical discussion of the rules governing the generation of variable holiday names.

 

➢The less technical holiday name variations topic should be your first source of information, unless you have a very specific question or issue.

 

A variable holiday name consists of a plain name followed by variations such as:

 

Holiday
>>>2027=Special Holiday
>>>DUP=<hol> Holiday

 

Each variation has a condition to the left of the equal sign and replacement text to its right. This appendix explains how conditions are matched, which combinations are recognized, and what happens when several variations match.

 

There is no single universal rule that "the most specific variation wins". Ordinary variations can accumulate in their listed order. Year-qualified variations have a separate priority system. Exact DAY conditions have another special rule, and DEFAULT and ALWAYS are handled separately.

 

What the conditions test

 

The recurrence rule first produces the holiday's occurrences. Name variations then operate on each occurrence separately.

 

Calendar conditions test the calculated date passed to the name evaluator: its weekday, day of month, month and Gregorian year. Thus a moved occurrence is tested using its resulting date, including the resulting year if it crosses New Year.

 

•MOV, DUP and BASE test the occurrence's recorded status. A date does not become a DUP merely because its name says "day in lieu".

•DAY and DUR use the duration information recorded for that occurrence. They describe its position within the generated multi-day holiday, not its day of the month. Use these conditions on occurrences with duration information.

•Weekdays are numbered Monday=1 through Sunday=7. Month abbreviations are JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC.

 

Use uppercase keywords and month abbreviations, and write combined conditions without spaces. An unrecognized condition does not provide a useful fallback; do not assume that a plausible-looking combination is understood.

 

Conditions that can be used without a year

 

Calendar conditions

 

Condition

Meaning

1 through 7

Weekday: 1 means Monday and 7 means Sunday.

01 through 31

Day of the month: 01 means the first.

JAN through DEC

Month.

MMM_w

Month and weekday: JAN_5 means a Friday in January.

MMM_dd

Month and day: JAN_05 means January 5.

 

The number of digits matters. JAN_5 and JAN_05 are different conditions.

 

Occurrence-status conditions

 

Condition

Meaning

MOV

A moved occurrence.

DUP

An additional, duplicated occurrence.

BASE

The base occurrence marked as such by the recurrence rule.

MOV_w / DUP_w

Moved/duplicated occurrence on weekday w.

MOV_dd / DUP_dd

Moved/duplicated occurrence on day dd of the month.

MOV_MMM / DUP_MMM

Moved/duplicated occurrence in month MMM.

 

Examples: DUP_1, DUP_01, DUP_DEC, MOV_5, MOV_25 and MOV_MAY.

 

Duration conditions

 

Condition

Meaning

DAYn

Day n of the holiday, such as DAY2.

DAY~n

Any duration position other than n.

DAY<n

Position before n: DAY<3 means days 1 and 2.

DAY>n

Position after n: DAY>3 means day 4 onward.

DURn/t

Day n of a holiday whose recorded total duration is t.

 

For example, DUR2/3 tests both "second day" and "three days in total". It is more restrictive than DAY2, but it does not automatically outrank DAY2. The special treatment of exact DAYn conditions is described in section 7.

 

Year conditions

 

The following year conditions may stand alone:

 

Condition

Meaning

YYYY

That Gregorian year.

YYYY..ZZZZ

Inclusive range YYYY through ZZZZ.

YYYY+

YYYY and subsequent years.

YYYY-

YYYY and preceding years.

YYYY+L

Leap years from YYYY onward.

YYYY+C

Common years from YYYY onward.

YYYY+Lw

Leap years from YYYY onward whose January 1 is weekday w.

YYYY+Cw

Common years from YYYY onward whose January 1 is weekday w.

 

For example:

 

Example

Meaning

2027

All occurrences dated in 2027.

2027..2030

All occurrences dated from 2027 through 2030.

2024+L

Leap years from 2024 onward.

2023+C6

Common years from 2023 onward beginning on Saturday.

 

In the last example, 6 tests the weekday of January 1, not the weekday of the holiday itself.

 

Use the documented four-digit year range 2000–9999. This is a year test, not an alternative calendar date: a holiday calculated from another calendar still uses the Gregorian year of the occurrence here.

 

Which combinations are recognized?

 

Any of the year conditions in section 3 can prefix any ordinary condition in section 2, separated by one underscore. The ordinary condition can itself be one of the recognized compound forms.

 

Recognized examples:

 

Recognized combination

Meaning

2027_5

Friday occurrence in 2027.

2027_05

Occurrence on the fifth of a month in 2027.

2027_JAN

Occurrence in January 2027.

2027_JAN_5

Friday occurrence in January 2027.

2027_JAN_05

Occurrence on January 5, 2027.

2027_DUP

Duplicated occurrence in 2027.

2027_DUP_1

Duplicated Monday occurrence in 2027.

2027_DUP_DEC

Duplicated occurrence in December 2027.

2027_BASE

Base occurrence in 2027.

2027_DAY2

Second duration day in 2027.

2027..2030_DUR2/3

Second of three duration days in 2027–2030.

2024+L_MOV_MAR

Moved March occurrence in leap years from 2024 onward.

2023+C6_5

Friday occurrence in a qualifying common year whose January 1 is Saturday.

 

The underscore is not a general-purpose AND operator. Conditions cannot be combined arbitrarily.

 

Unsupported combinations

 

These combinations are NOT recognized by this implementation:

 

Unsupported combination

Reason

BASE_1 / BASE_25 / BASE_DEC

BASE does not accept a weekday, day-of-month or month suffix.

DUP_DEC_1

DUP accepts one qualifier, not a month plus weekday.

MOV_JAN_05

MOV does not accept a combined month-and-day qualifier.

JAN_DUP

The supported order is DUP_JAN.

DAY2_DUP / DAY2_JAN

Duration cannot be joined to status or month in this way.

2027_DEFAULT / 2027_ALWAYS

DEFAULT and ALWAYS are standalone control keywords.

2027-L / 2027-C

Leap/common-year qualifiers exist for +, not -.

 

Adding a year prefix does not make an unsupported suffix valid. For example, 2027_BASE_1 and 2027_DUP_DEC_1 are also unsupported.

 

There is no general Boolean expression syntax: no parentheses, AND, OR, or arbitrary negation. DAY~n is a specifically implemented duration condition.

 

Ordinary variations: listed order and <hol>

 

Evaluation begins with the plain name. Matching ordinary variations are processed in their listed order.

 

A matching variation without <hol> replaces the current name. A matching variation containing <hol> incorporates the current name.

 

Example:

 

Holiday
>>>JAN=<hol> in January
>>>5=<hol> on Friday

 

For a Friday occurrence in January, the result is:

 

Holiday in January on Friday

 

Changing the last line to:

 

>>>5=Friday Holiday

 

makes the result:

 

Friday Holiday

 

There is no automatic specificity ordering between JAN, JAN_5, 5, 01, MOV, DUP, BASE or DUR. For example, a later JAN variation can replace the result of an earlier JAN_5 variation. Put broader variations first and intended refinements later, or use self-contained replacement text where appropriate.

 

Important: the year-priority system does not prevent a later ordinary variation from changing a year-specific name.

 

Example:

 

Holiday
>>>2027=Special Holiday
>>>5=Friday Holiday

 

On a Friday in 2027, the result is Friday Holiday. Reversing those two lines makes the result Special Holiday.

 

Use <hol> only once in a replacement. In the current implementation only the first occurrence is substituted; further occurrences remain literal at this stage.

 

Year-qualified variations: priority order

 

A year-qualified condition must match completely, including its ordinary suffix if present, before it can claim a priority.

 

Lower numbers below mean higher priority:

 

Priority

Condition category

Example

1

Exact year with suffix

2027_DUP

2

Exact year alone

2027

3

Inclusive year range with suffix

2027..2030_DUP

4

Inclusive year range alone

2027..2030

5

Leap year + January 1 weekday + suffix

2024+L4_DUP

6

Leap year + January 1 weekday alone

2024+L4

7

Common year + January 1 weekday + suffix

2023+C6_DUP

8

Common year + January 1 weekday alone

2023+C6

9

Leap year from minimum, with suffix

2024+L_DUP

10

Leap year from minimum, alone

2024+L

11

Common year from minimum, with suffix

2023+C_DUP

12

Common year from minimum, alone

2023+C

13

Minimum year with suffix

2027+_DUP

14

Minimum year alone

2027+

15

Maximum year with suffix

2030-_DUP

16

Maximum year alone

2030-

 

Leap-year and common-year rows cannot compete for the same occurrence, but the list gives the ranks assigned by the implementation.

 

Within each category, "with suffix" has priority over "alone". The complexity of the suffix does not create further priority levels. For example, 2027_JAN and 2027_JAN_5 both have rank 1 when they match.

 

The priority does not depend on how recent a minimum year is, how narrow a year range is, or how close an upper limit is.

 

Examples:

 

•2027 beats 2020+.

•2027..2030 beats 2020+.

•A matching 2027_DUP beats 2027.

•A non-matching 2027_DUP does not prevent 2027 from applying.

•A matching 2020+ beats a matching 2030-, regardless of line order.

•2020+ and 2025+ have equal rank.

•2020..2030 and 2027..2028 have equal rank.

 

In particular, matching minimum-year conditions (YYYY+) rank above matching maximum-year conditions (YYYY-), both with and without a suffix. This is a category priority, not a comparison of the numerical year limits.

 

Do not use overlapping equal-rank conditions as if the newest or narrowest one automatically wins.

 

How the two passes work

 

The first pass processes the variations in line order and records the first matching year variation at the best rank reached so far.

 

• A later lower-priority year variation is skipped.

 

• A later higher-priority year variation is applied and becomes the new recorded best match.

 

• A later equal-priority year variation is also applied, but does not replace the recorded best match.

 

If a higher-priority match was discovered after an earlier year match, the evaluator performs a second pass. It starts again with the plain name and processes the ordinary variations, but admits only the recorded best year-qualified line.

 

This removes the text contributed by earlier, lower-priority year matches. The chosen year-qualified line still runs at its original position relative to ordinary variations.

 

Equal-priority conditions therefore have a significant implementation detail: if no second pass is required, multiple equal-priority year matches can accumulate. If a second pass is required, only the first matching line at the best rank is retained.

 

Equal-priority example

 

For an occurrence in 2027:

 

Holiday
>>>2020..2030=<hol> A
>>>2027..2028=<hol> B

 

produces Holiday A B.

 

But:

 

Holiday
>>>2000+=General
>>>2020..2030=<hol> A
>>>2027..2028=<hol> B

 

produces Holiday A in the current implementation. The improvement from a minimum-year condition to a range triggers the second pass, which retains the first matching range.

 

For predictable names, avoid competing equal-rank year conditions or make their date conditions mutually exclusive.

 

Exact DAY conditions and broader DAY conditions

 

An exact DAYn match has special treatment relative to DAY~n, DAY<n and DAY>n.

 

Before evaluating names, Q++Studio looks for a matching unqualified exact DAYn line or a current-exact-year DAYn line.

 

For the second day of a holiday, either of these can trigger that recognition:

 

DAY2

 

2027_DAY2 (when the occurrence is in 2027)

 

If found, the broader DAY~n, DAY<n and DAY>n branches are suppressed, even when one of those broader variations appears earlier in the list.

 

Example, on day 2:

 

Festival
>>>DAY>1=Later Day
>>>DAY2=Second Day

 

The result is Second Day. Reversing these two lines still gives Second Day.

 

This is not a universal priority for all duration conditions:

 

• DUR2/3 is not suppressed by DAY2.

 

• A weekday, month or other ordinary condition can still change the name.

 

• A DAY2 line is still subject to year priority if it has a year prefix.

 

Year-range DAY conditions

 

The preliminary scan recognizes an exact current-year prefix, but does not strip year ranges or + / - prefixes. Thus 2020+_DAY2 is first recognized during normal evaluation, when reached, rather than in the preliminary scan.

 

When an exact DAYn match is encountered during evaluation, the flag suppressing broader DAY conditions is also set. That flag is not reset between the first and second passes.

 

Consequently, combinations of year-qualified DAY conditions, broad DAY conditions and competing year priorities can have effects that are not described by a simple "most specific line wins" rule. Prefer a straightforward exact DAYn arrangement and avoid relying on this interaction.

 

DEFAULT

 

DEFAULT is a fallback, not an initial replacement for the plain name.

 

If no ordinary or year-qualified variation is applied in the relevant pass, the DEFAULT replacement is used. Its <hol> refers to the plain name.

 

Holiday
>>>DEFAULT=Ordinary Holiday
>>>5=Friday Holiday

 

A Friday occurrence uses Friday Holiday. An occurrence on another weekday uses Ordinary Holiday.

 

Moving DEFAULT above or below the other lines does not change this behavior. It is remembered during the scan and used only afterward, if no condition matched. If several DEFAULT lines exist, the last one supplies the fallback; use only one.

 

DEFAULT is not a supported suffix and cannot be restricted using 2027_DEFAULT.

 

ALWAYS

 

ALWAYS is a final replacement, rather than another ordinary matching line. Its position in the list does not give it ordinary line-order behavior.

 

Unless deletion has been detected at the end of the relevant variation pass, the evaluator eventually replaces the name with the right-hand side of ALWAYS.

 

Use a complete, fixed name:

 

>>>ALWAYS=Easter Holidays

 

The current implementation assigns this text literally. It does not expand <hol> inside ALWAYS, and the assignment occurs after the local token-processing stage in this function. Do not use ALWAYS as a way to accumulate text.

 

If several ALWAYS lines exist, the last one supplies the final replacement; use only one. Forms such as 2027_ALWAYS are not recognized.

 

<DELETE>: check the name that remains

 

<DELETE> is a right-hand-side marker, not a left-side condition.

 

A matching variation such as >>>JAN=<DELETE> replaces the current holiday name with the literal marker <DELETE>. Consequently, a later <hol> expands to that marker, or to the accumulated text containing it. A later matching variation that bypasses <hol> supplies an independent replacement and is not affected by that earlier deletion marker.

 

This follows the same replacement model as ordinary text: the name produced so far is retained only when the next replacement incorporates <hol>. Deletion is not an irreversible action at the moment the marker is assigned.

 

At the end of each variation pass, the evaluator checks whether the current name contains <DELETE>. If it does, the name is reduced to <DELETE> and ALWAYS is prevented from overriding that deletion in that pass.

 

However, encountering <DELETE> does not immediately stop the loop and is not remembered in a separate permanent "deleted" flag. A later replacement that does not preserve the current name can remove it before the end-of-pass test.

 

Example, on a Friday in January:

 

Holiday
>>>JAN=<DELETE>
>>>5=Friday Holiday

 

The resulting name is Friday Holiday at the variation stage: the second line replaced the deletion marker.

 

If the last line instead reads:

 

>>>5=<hol> on Friday

 

the deletion marker survives within the current name, and the final end-of-pass test reduces that name to <DELETE>.

 

The statement "<DELETE> takes precedence over ALWAYS" therefore applies when <DELETE> is still present when the deletion check runs. It does not mean that every matching deletion line is immune to subsequent replacements.

 

To make a deletion reliable, ensure that subsequent applicable variations cannot replace it with ordinary text. For year-qualified deletions, also check whether the line survives the year-priority selection.

 

Automatic annotations and abbreviations

 

Some callers request automatic additions such as "(day in lieu)", "(celebration)" or "(day n of t)". These options are processed after the main variation passes; they are not additional left-side keywords or extra priority levels.

 

Explicit status/duration branches can suppress the corresponding automatic annotations. In the current implementation, some branches disable an annotation even when their full condition does not match, and changes to these options can survive into a second pass. For diagnosing the exact final text, the caller's options therefore matter too.

 

ALWAYS is assigned after these automatic additions and the local token-processing stage, so it replaces their result as well.

 

Holiday abbreviations use the same variation evaluator with <abr> instead of <hol>. The abbreviation wrapper passes no automatic-annotation options. The same condition syntax, priorities and line-order considerations apply.

 

A short diagnostic checklist

 

If a name is unexpected, check:

 

1. The occurrence's final date and its recorded MOV/DUP/BASE/duration status.

2. Uppercase spelling and the number of digits: 5 differs from 05.

3. Whether the complete left-side combination is supported.

4. Which ordinary lines match, and the order in which they appear.

5. Whether a replacement keeps <hol> or discards the current name.

6. The rank of every completely matching year-qualified condition.

7. Equal-rank overlaps and whether a second pass is triggered.

8. An exact DAYn condition suppressing broader duration conditions.

9. DEFAULT being only a fallback, and ALWAYS being a final replacement.

10. Whether a later replacement removes an earlier <DELETE>.

11. Automatic annotations requested by the caller.

 

For routine rules, the shorter Variable Holiday Names topic remains the recommended starting point.

 

See also: variable holiday names, variable holiday abbreviations, holiday duration, weekday recurrence settings.

 

 


Topic 190105, last updated on 24-Oct-2026