Skip to content
109 changes: 92 additions & 17 deletions book/src/datatypes.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,23 @@ Examples:

| Type name | Size | Internally stored as |
| --------------- | ------ | --------------------------------- |
| TIME | 64 bit | Timespan in nanoseconds |
| TIME\_OF\_DAY | 64 bit | Nanoseconds since Jan 1, 1970 UTC |
| DATE | 64 bit | Nanoseconds since Jan 1, 1970 UTC |
| DATE\_AND\_TIME | 64 bit | Nanoseconds since Jan 1, 1970 UTC |

Note that RuSTy already treats `TIME`, `TIME_OF_DAY`, `DATE` and `DATE_AND_TIME` as 64 bit numbers.
Therefore the long pendants `LTIME`, `LTOD`, `LDATE` and `LDT` are mere aliases to the original types.
| TIME | 32 bit | Unsigned milliseconds |
| TIME\_OF\_DAY | 32 bit | Unsigned milliseconds since Jan 1, 1970 UTC |
| DATE | 32 bit | Unsigned seconds since Jan 1, 1970 UTC |
| DATE\_AND\_TIME | 32 bit | Unsigned seconds since Jan 1, 1970 UTC |
| LTIME | 64 bit | Nanoseconds |
| LTIME\_OF\_DAY | 64 bit | Nanoseconds since Jan 1, 1970 UTC |
| LDATE | 64 bit | Nanoseconds since Jan 1, 1970 UTC |
| LDATE\_AND\_TIME| 64 bit | Nanoseconds since Jan 1, 1970 UTC |

RuSTy treats the short date/time family (`TIME`, `TIME_OF_DAY`, `DATE`, `DATE_AND_TIME`) as 32-bit unsigned values.
Short `TIME`/`TOD` use milliseconds, while short `DATE`/`DT` use seconds.
The long family (`LTIME`, `LTOD`, `LDATE`, `LDT`) remains 64-bit nanosecond precision.

### DATE

The `DATE` datatype is used to represent a Date in the Gregorian Calendar.
Such a value is stored as an i64 with a precision in nanoseconds and denotes the number of nanoseconds
Such a value is stored as a `u32` with a precision in seconds and denotes the number of seconds
that have elapsed since January 1, 1970 UTC not counting leap seconds.
DATE literals start with `DATE#` or `D#` followed by a date in the format of `yyyy-mm-dd`.

Expand All @@ -111,8 +116,8 @@ Examples:
### DATE_AND_TIME

The `DATE_AND_TIME` datatype is used to represent a certain point in time in the Gregorian Calendar.
Such a value is stored as an `i64` with a precision in nanoseconds and denotes the
number of nanoseconds that have elapsed since January 1, 1970 UTC not counting leap seconds.
Such a value is stored as a `u32` with a precision in seconds and denotes the
number of seconds that have elapsed since January 1, 1970 UTC not counting leap seconds.
DATE_AND_TIME literals start with `DATE_AND_TIME#` or `DT#` followed by a date and time in the
format of `yyyy-mm-dd-hh:mm:ss`.

Expand All @@ -127,27 +132,27 @@ Examples:
### TIME_OF_DAY

The `TIME_OF_DAY` datatype is used to represent a specific moment in time in a day.
Such a value is stored as an `i64` value with a precision in nanoseconds and denotes the
number of nanoseconds that have elapsed since January 1, 1970 UTC not counting leap seconds.
Such a value is stored as a `u32` value with a precision in milliseconds and denotes the
number of milliseconds that have elapsed since January 1, 1970 UTC not counting leap seconds.
Hence this value is stored as a `DATE_AND_TIME` with the day fixed to 1970-01-01.
`TIME_OF_DAY` literals start with `TIME_OF_DAY#` or `TOD#` followed by a time in the
format of `hh:mm:ss`.

Note that only the seconeds-segment can have a fraction denoting the milliseconds.
Note that only the seconds-segment can have a fraction denoting the milliseconds.

Examples:

- `t1 : TIME_OF_DAY := TIME_OF_DAY#14:20:10.25;`
- `t2 : TIME_OF_DAY := TIME_OF_DY#0:00:1;`
- `t2 : TIME_OF_DAY := TIME_OF_DAY#0:00:1;`
- `t3 : TIME_OF_DAY := TOD#23:59:59.999;`

### TIME

The `TIME` datatype is used to represent a time-span.
A `TIME` value is stored as an `i64` value with a precision in nanoseconds.
TIME literals start with `TIME#` or `T#` followed by the `TIME` segements.
A `TIME` value is stored as a `u32` value with a precision in milliseconds.
TIME literals start with `TIME#` or `T#` followed by the `TIME` segments.

Supported segements are:
Supported segments are:

- `d` ... `f64` days
- `h` ... `f64` hours
Expand All @@ -165,6 +170,76 @@ Examples:
- `t2 : TIME := T#2d4.2h;`
- `t3 : TIME := T#-10s4ms16ns;`

### LDATE

The `LDATE` datatype is used to represent a Date in the Gregorian Calendar.
Such a value is stored as an `i64` with a precision in nanoseconds and denotes the number of nanoseconds
that have elapsed since January 1, 1970 UTC not counting leap seconds.
LDATE literals start with `LDATE#` or `LD#` followed by a date in the format of `yyyy-mm-dd`.

Examples:

- `d1 : LDATE := LDATE#2021-05-02;`
- `d2 : LDATE := LDATE#1-12-24;`
- `d3 : LDATE := LD#2000-1-1;`

### LDATE_AND_TIME

The `LDATE_AND_TIME` datatype is used to represent a certain point in time in the Gregorian Calendar.
Such a value is stored as an `i64` with a precision in nanoseconds and denotes the
number of nanoseconds that have elapsed since January 1, 1970 UTC not counting leap seconds.
LDATE_AND_TIME literals start with `LDATE_AND_TIME#` or `LDT#` followed by a date and time in the
format of `yyyy-mm-dd-hh:mm:ss`.

Note that only the seconds-segment can have a fraction denoting the milliseconds.

Examples:

- `d1 : LDATE_AND_TIME := LDATE_AND_TIME#2021-05-02-14:20:10.25;`
- `d2 : LDATE_AND_TIME := LDATE_AND_TIME#1-12-24-00:00:1;`
- `d3 : LDATE_AND_TIME := LDT#1999-12-31-23:59:59.999;`

### LTIME_OF_DAY

The `LTIME_OF_DAY` datatype is used to represent a specific moment in time in a day.
Such a value is stored as an `i64` value with a precision in nanoseconds and denotes the
number of nanoseconds that have elapsed since January 1, 1970 UTC not counting leap seconds.
Hence this value is stored as a `LDATE_AND_TIME` with the day fixed to 1970-01-01.
`LTIME_OF_DAY` literals start with `LTIME_OF_DAY#` or `LTOD#` followed by a time in the
format of `hh:mm:ss`.

Note that only the seconds-segment can have a fraction denoting the milliseconds.

Examples:

- `t1 : LTIME_OF_DAY := LTIME_OF_DAY#14:20:10.25;`
- `t2 : LTIME_OF_DAY := LTIME_OF_DAY#0:00:1;`
- `t3 : LTIME_OF_DAY := LTOD#23:59:59.999;`

### LTIME

The `LTIME` datatype is used to represent a time-span.
A `LTIME` value is stored as an `i64` value with a precision in nanoseconds.
LTIME literals start with `LTIME#` or `LT#` followed by the `LTIME` segments.

Supported segments are:

- `d` ... `f64` days
- `h` ... `f64` hours
- `m` ... `f64`minutes
- `s` ... `f64` seconds
- `ms` ... `f64` milliseconds
- `us` ... `f64` microseconds
- `ns` ... `u32` nanaoseconds

Note that only the last segment of a `LTIME` literal can have a fraction.

Examples:

- `t1 : LTIME := LTIME#2d4h6m8s10ms;`
- `t2 : LTIME := LT#2d4.2h;`
- `t3 : LTIME := LT#-10s4ms16ns;`

## Other types

The `BOOL` type can either be assigned `TRUE` or `FALSE`.
Expand Down
16 changes: 8 additions & 8 deletions book/src/libraries/api_lib_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,17 +269,17 @@ Below is a table of types and how they can be used from `C`
| ULINT | uint64_t | 64 | |
| REAL | float_t | 32 | |
| LREAL | double_t | 64 | |
| TIME | time_t | 64 | Note that all time and date types are 64 bit |
| TIME | time_t | 32 | Short TIME/TOD types use 32-bit milliseconds |
| LTIME | time_t | 64 | |
| DATE | time_t | 64 | |
| DATE | time_t | 32 | Short DATE/DT types use 32-bit seconds |
| LDATE | time_t | 64 | |
| DATE_AND_TIME | time_t | 64 | |
| DATE_AND_TIME | time_t | 32 | |
| LDATE_AND_TIME | time_t | 64 | |
| DT | time_t | 64 | |
| DT | time_t | 32 | |
| LDT | time_t | 64 | |
| TIME_OF_DAY | time_t | 64 | |
| TIME_OF_DAY | time_t | 32 | |
| LTIME_OF_DAY | time_t | 64 | |
| TOD | time_t | 64 | |
| TOD | time_t | 32 | |
| LTOD | time_t | 64 | |
| POINTER TO type | \*type | 64 | The Pointer size is equivalent to `LWORD` and not `DWORD` |
| REF_TO type | \*type | 64 | Prefer this type to `POINTER TO` for standard compliance |
Expand Down Expand Up @@ -345,7 +345,7 @@ pub struct myStruct {

### 2.5 `FUNCTION_BLOCK` initialization

When creating a library with `FUNCTION_BLOCK`s, you can implement initialization logic that runs when an instance is created.
When creating a library with `FUNCTION_BLOCK`s, you can implement initialization logic that runs when an instance is created.

For more details on `FB_INIT` in IEC61131-3, refer to the [Program Organization Units (POUs)](../pous.md#function_block-initialization) documentation.

Expand Down Expand Up @@ -373,7 +373,7 @@ void myFunctionBlock__FB_INIT(myFunctionBlock* fb_instance) {
// Initialize members here
fb_instance->a = 1;
fb_instance->b = 2;

// ...perform any other needed initialization
}
```
Expand Down
55 changes: 52 additions & 3 deletions compiler/plc_ast/src/literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub struct Date {
year: i32,
month: u32,
day: u32,
is_long: bool,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand All @@ -58,6 +59,7 @@ pub struct DateAndTime {
min: u32,
sec: u32,
nano: u32,
is_long: bool,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand All @@ -66,6 +68,7 @@ pub struct TimeOfDay {
min: u32,
sec: u32,
nano: u32,
is_long: bool,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand All @@ -78,6 +81,7 @@ pub struct Time {
pub micro: f64,
pub nano: u32,
pub negative: bool,
pub is_long: bool,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -177,6 +181,28 @@ impl Time {
pub fn is_negative(&self) -> bool {
self.negative
}

pub fn is_long(&self) -> bool {
self.is_long
}
}

impl Date {
pub fn is_long(&self) -> bool {
self.is_long
}
}

impl DateAndTime {
pub fn is_long(&self) -> bool {
self.is_long
}
}

impl TimeOfDay {
pub fn is_long(&self) -> bool {
self.is_long
}
}

impl Array {
Expand Down Expand Up @@ -209,7 +235,12 @@ impl AstLiteral {

/// Creates a new literal date
pub fn new_date(year: i32, month: u32, day: u32) -> Self {
AstLiteral::Date(Date { year, month, day })
AstLiteral::Date(Date { year, month, day, is_long: false })
}

/// Creates a new literal date with explicit width flavor
pub fn new_date_with_long_flag(year: i32, month: u32, day: u32, is_long: bool) -> Self {
AstLiteral::Date(Date { year, month, day, is_long })
}

/// Creates a new literal date and time
Expand All @@ -222,12 +253,30 @@ impl AstLiteral {
sec: u32,
nano: u32,
) -> Self {
AstLiteral::DateAndTime(DateAndTime { year, month, day, hour, min, sec, nano })
AstLiteral::DateAndTime(DateAndTime { year, month, day, hour, min, sec, nano, is_long: false })
}

/// Creates a new long literal date and time
pub fn new_long_date_and_time(
year: i32,
month: u32,
day: u32,
hour: u32,
min: u32,
sec: u32,
nano: u32,
) -> Self {
AstLiteral::DateAndTime(DateAndTime { year, month, day, hour, min, sec, nano, is_long: true })
}

/// Creates a new literal time of day
pub fn new_time_of_day(hour: u32, min: u32, sec: u32, nano: u32) -> Self {
AstLiteral::TimeOfDay(TimeOfDay { hour, min, sec, nano })
AstLiteral::TimeOfDay(TimeOfDay { hour, min, sec, nano, is_long: false })
}

/// Creates a new literal time of day with explicit width flavor
pub fn new_time_of_day_with_long_flag(hour: u32, min: u32, sec: u32, nano: u32, is_long: bool) -> Self {
AstLiteral::TimeOfDay(TimeOfDay { hour, min, sec, nano, is_long })
}

/// Creates a new literal null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ lazy_static! {
E139, Error, include_str!("./error_codes/E139.md"), // Linker invocation failed (spawn / cmdline)
E140, Error, include_str!("./error_codes/E140.md"), // ':=' used for an output parameter
E141, Error, include_str!("./error_codes/E141.md"), // Member access on a non-auto-deref pointer base
E142, Warning, include_str!("./error_codes/E142.md"), // Temporal literal overflow or underflow
);
}

Expand Down
10 changes: 10 additions & 0 deletions compiler/plc_diagnostics/src/diagnostics/error_codes/E142.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# E142

Temporal literal overflow or underflow.

This warning is emitted when a temporal literal cannot be represented safely.

Covered temporal literals include DATE, DT, TOD, TIME and their long variants.

For short temporal types (DATE/DT/TOD/TIME), values outside the 32-bit runtime storage domain trigger this warning.
For long temporal types, out-of-range literal encodings also trigger this warning.
Loading
Loading