Skip to content

A DATE parameter loses its class under + / - : p - 7 is inferred as INTEGER, while prop(x) - 7 and currentDate() - 7 stay DATE #1783

Description

@CrushBy

Arithmetic on a parameter of class DATE does not keep the DATE class: p - 7 is inferred as INTEGER, so the expression is rejected everywhere a DATE is required. The same arithmetic on a DATE-valued property or function is accepted.

The computed value is never wrong — only the inferred class is. The failure is therefore a load-time type error, not a data problem.

Reproduction

A self-contained module. Add DateParamRepro to the top module's REQUIRE and start the server.

MODULE DateParamRepro;

REQUIRE System, Utils, Time;

CLASS Sample 'Sample';
day 'Day' = DATA DATE (Sample);

// the parameter itself is a perfectly good DATE
plain (Sample s, DATE p)      { day(s) <- p; }        // OK

// ... but arithmetic on it is not
minusParam (Sample s, DATE p) { day(s) <- p - 7; }    // FAILS

Observed

Each construct was checked on its own, because one semantic error is reported per load:

construct result
day(s) <- p; OK — the parameter is a DATE
day(s) <- p - 7; ASSIGN's arguments' types don't match
day(s) <- p + 7; ASSIGN's arguments' types don't match
older (Sample s, DATE p) = TRUE IF day(s) < p - 7; value of class 'DATE' is not comparable with value of class 'INTEGER'
day(s) <- DATE(p - 7); OK
day(s) <- sum(p, -7); OK
day(s) <- day(s) - 7; OK — property on the left
day(s) <- currentDate() - 7; OK — function on the left

The comparison case is the informative one: it names the inferred class outright, so p - 7 is coming out as INTEGER.

The last two rows are the contrast that makes this look like a bug rather than a deliberate restriction — the very same <expr> - 7, differing only in whether the left operand is a parameter or a property/function, is accepted.

The value is correct

Only the inference is affected. Checked at runtime through /eval with p = 2026-08-21:

run(DATE p) {
    RETURN 'p-7 raw='     + STRING[30](p - 7)
        + ' | DATE(p-7)=' + STRING[30](DATE(p - 7))
        + ' | sum(p,-7)=' + STRING[30](sum(p, -7));
}
p-7 raw=2026-08-14 | DATE(p-7)=2026-08-14 | sum(p,-7)=2026-08-14

All three forms produce the right date, so DATE(p - 7) is a genuine workaround rather than a cast that papers over a wrong value.

Expected

DATE ± INTEGER should yield DATE regardless of whether the left operand is a parameter, a property or a function — matching sum[DATE, INTEGER] and subtract[DATE, LONG] from Time.

Why it is easy to get wrong

The two error messages point in different directions, and neither mentions the parameter:

  • ASSIGN's arguments' types don't match names neither operand class nor the parameter, so from the assignment alone the natural (and wrong) conclusion is that DATE ± INTEGER is simply not assignable to a DATE property.
  • Only the comparison message names INTEGER, and a codebase can easily have no comparison on a parameter — every currentDate() + N and dateProp(x) - N in it keeps working, which hides the pattern.

If the current behaviour turns out to be intended, an error message naming the parameter and its inferred class would already remove most of the confusion.

Workarounds

day(s) <- sum(p, -7);      // Time helper - preferred
day(s) <- DATE(p - 7);     // explicit cast, verified to give the same value

Environment

  • lsFusion 7.0-SNAPSHOT, build 392
  • Java 17.0.9, Windows 11, PostgreSQL 18
  • Reproduced at load time — both on a normal server start and with -Dsettings.dryRun=true

Only DATE was checked; DATETIME and TIME parameters were not tested.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions