P2038

Code

P2038

Message

Date literal is outside the range the date type can represent

This error occurs when a date literal names a date the compiler cannot represent.

A date is stored as a count of seconds since 1970-01-01, and the count is unsigned, so the dates a program can write are the ones that count reaches. The 32-bit types run out in 2106; the 64-bit ones reach as far as a date can be written:

Date type ranges

Type

Earliest

Latest

DATE

1970-01-01

2106-02-07

DATE_AND_TIME

1970-01-01-00:00:00

2106-02-07-06:28:15

LDATE

1970-01-01

9999-12-31

LDATE_AND_TIME

1970-01-01-00:00:00

9999-12-31-23:59:59

A date outside its type’s range has no second count to be stored as, so the compiler reports it rather than storing a different date than the one the program wrote.

Example

The following code will generate error P2038:

PROGRAM main
    VAR
        expiry : DATE := DATE#2200-01-01;
    END_VAR
END_PROGRAM

To fix this error, use a date the type can hold:

PROGRAM main
    VAR
        expiry : DATE := DATE#2099-12-31;
    END_VAR
END_PROGRAM

Dates before 1970

The count of seconds is unsigned, so it has nowhere to put a date before the epoch. A historical date is reported for the same reason a distant future one is:

PROGRAM main
    VAR
        built : DATE := DATE#1969-07-20;  (* Error: before 1970-01-01 *)
    END_VAR
END_PROGRAM

Where a program needs to record a date outside the range, store the offset from a date inside it — days or seconds in an integer variable — rather than the date itself.

The literal names its own type

A date literal is held to the range of the type its prefix names, not the range of the variable it is stored into. DATE#2200-01-01 is out of range even in an LDATE variable, the same way INT#40000 is not an INT whatever it is assigned to:

PROGRAM main
    VAR
        expiry : LDATE := DATE#2200-01-01;  (* Error: DATE# names a DATE *)
    END_VAR
END_PROGRAM

Write the prefix of the type you mean, and the 64-bit types reach well past 2106:

PROGRAM main
    VAR
        expiry : LDATE := LDATE#2200-01-01;
    END_VAR
END_PROGRAM

LDATE and LDATE_AND_TIME require IEC 61131-3 Edition 3. See Edition Support.

Narrowing is reported too: an LDATE# literal does not fit a DATE variable, which is reported as P4022.

See Also

Think IronPLC is wrong about this?

If you believe this diagnostic is incorrect, open an issue on GitHub with a small sample that demonstrates the problem.