BACnet.Protocol.BACnetDateTime (bacstack v0.1.0-dev.1)

Copy Markdown View Source

A BACnet DateTime is a simple SEQUENCE that combines a BACnet.Protocol.BACnetDate and a BACnet.Protocol.BACnetTime. It is the most common concrete timestamp form used throughout the protocol (application tag 12 when appearing as a primitive).

Because the two components are independent, a BACnet.Protocol.BACnetDateTime can itself act as a pattern: any special value present in the embedded BACnet.Protocol.BACnetDate or BACnet.Protocol.BACnetTime makes the whole value a pattern rather than a specific instant.

BACnet Specification References

  • ASN.1 production (Clause 21):
    BACnetDateTime ::= SEQUENCE {
        date Date,   -- see Clause 20.2.12 for restrictions
        time Time    -- see Clause 20.2.13 for restrictions
    }
  • Encoding: The encoding is simply the concatenation of the encodings of the two components (Clause 20.2.18 CHOICE / SEQUENCE rules apply when the value is context-tagged, e.g. inside BACnet.Protocol.BACnetTimestamp).
  • Usage contexts: time_of_device_restart, many "last changed" properties, Event Timestamps (the most common BACnet.Protocol.BACnetTimestamp alternative), schedule exception periods, and COV / event notification timestamps.

The conversion helpers delegate to the respective BACnet.Protocol.BACnetDate / BACnet.Protocol.BACnetTime functions and therefore inherit all pattern-resolution behaviour.

Examples

Converting to/from Elixir DateTime

iex> dt = %BACnetDateTime{date: %BACnetDate{year: 2025, month: 3, day: 15, weekday: 6}, time: %BACnetTime{hour: 14, minute: 30, second: 0, hundredth: 0}}
iex> {:ok, _elixir_dt} = BACnetDateTime.to_datetime(dt)
iex> BACnetDateTime.from_datetime(~U[2025-03-15 14:30:00Z])
%BACnetDateTime{date: %BACnetDate{year: 2025, month: 3, day: 15, weekday: 6}, time: %BACnetTime{hour: 14, minute: 30, second: 0, hundredth: 0}}

Pattern DateTime

iex> pattern = %BACnetDateTime{date: %BACnetDate{year: :unspecified, month: 12, day: 25, weekday: :unspecified}, time: %BACnetTime{hour: 0, minute: 0, second: 0, hundredth: 0}}
iex> BACnetDateTime.specific?(pattern)
false

Edge cases

A DateTime is only specific if both the date and time parts are specific:

iex> mixed = %BACnetDateTime{
...>   date: %BACnetDate{year: 2025, month: 4, day: 1, weekday: 2},
...>   time: %BACnetTime{hour: :unspecified, minute: 0, second: 0, hundredth: 0}
...> }
iex> BACnetDateTime.specific?(mixed)
false

Conversion delegates to the individual date/time helpers (including their reference date fallback behavior).

See Also

Summary

Types

t()

Represents a BACnet DateTime (a SEQUENCE of Date + Time).

Functions

Compares two BACnet DateTime.

Encodes the given BACnet DateTime into an application tag.

Converts a DateTime to a BACnet DateTime.

Converts a NaiveDateTime to a BACnet DateTime.

Parses a BACnet DateTime from BACnet application tags encoding.

Checks whether the given BACnet DateTime is a specific date-time value (every component is a numeric value).

Converts the BACnet DateTime to a NaiveDateTime.

Creates a new BACnet DateTime for the current UTC datetime.

Validates whether the given BACnet datetime is in form valid.

Types

t()

@type t() :: %BACnet.Protocol.BACnetDateTime{
  date: BACnet.Protocol.BACnetDate.t(),
  time: BACnet.Protocol.BACnetTime.t()
}

Represents a BACnet DateTime (a SEQUENCE of Date + Time).

The contained date and time may themselves contain special values (:unspecified, :even, etc.). In that case specific?/1 returns false and the value is treated as a pattern rather than a concrete instant.

Functions

compare(dt1, dt2)

@spec compare(t(), t()) :: :gt | :eq | :lt

Compares two BACnet DateTime.

Returns :gt if first datetime is later than the second, and :lt for vice versa. If the two datetimes are equal, :eq is returned.

Note that this is achieved by converting to DateTime and then comparing them.

encode(dt, opts \\ [])

@spec encode(t(), Keyword.t()) ::
  {:ok, BACnet.Protocol.ApplicationTags.encoding_list()} | {:error, term()}

Encodes the given BACnet DateTime into an application tag.

For tagged encoding, you'll have to strip this down further using manual efforts.

from_datetime(dt)

@spec from_datetime(DateTime.t()) :: t()

Converts a DateTime to a BACnet DateTime.

from_naive_datetime(dt)

@spec from_naive_datetime(NaiveDateTime.t()) :: t()

Converts a NaiveDateTime to a BACnet DateTime.

parse(tags)

Parses a BACnet DateTime from BACnet application tags encoding.

specific?(dt)

@spec specific?(t()) :: boolean()

Checks whether the given BACnet DateTime is a specific date-time value (every component is a numeric value).

to_datetime(dt, timezone \\ Application.get_env(:bacstack, :default_timezone, "Etc/UTC"), time_zone_database \\ Calendar.get_time_zone_database())

@spec to_datetime(t(), Calendar.time_zone(), Calendar.time_zone_database()) ::
  {:ok, DateTime.t()} | {:error, term()}

Converts the BACnet DateTime to a DateTime.

to_datetime!(dt, timezone \\ Application.get_env(:bacstack, :default_timezone, "Etc/UTC"), time_zone_database \\ Calendar.get_time_zone_database())

Bang-version of to_datetime/1.

to_naive_datetime(dt)

@spec to_naive_datetime(t()) :: {:ok, NaiveDateTime.t()} | {:error, term()}

Converts the BACnet DateTime to a NaiveDateTime.

to_naive_datetime!(dt)

@spec to_naive_datetime!(t()) :: NaiveDateTime.t() | no_return()

Bang-version of to_naive_datetime/1.

utc_now()

@spec utc_now() :: t()

Creates a new BACnet DateTime for the current UTC datetime.

valid?(t)

@spec valid?(t()) :: boolean()

Validates whether the given BACnet datetime is in form valid.

It only validates the struct is valid as per type specification.