A BACnet Timestamp is a CHOICE type that can represent three different notions of "when something happened". The most common form is a full BACnet Date and Time. For trend logs that produce very high-frequency samples, a simple sequence number is often used instead because it consumes far less space and avoids the cost of maintaining a real-time clock with sub-second precision. The third form (Time only) is used when only the time of day is significant.
Timestamps appear in Event Timestamps, in Trend Log records, in the Time Of Device Restart property, in logging notifications, and in many of the "last changed" properties throughout the object model. Because the three alternative encodings have very different sizes and precision characteristics, the choice of which form to use is often an important implementation decision that affects both memory usage and the ability of receiving systems to correlate events across devices.
The type is defined so that a recipient can always determine which of the three alternatives is present without any additional context, which makes it safe to forward or store timestamps in generic log buffers and history archives.
BACnet Specification References
- ASN.1 (Clause 21):
BACnetTimeStamp ::= CHOICE { time [0] Time, sequence-number [1] Unsigned (0..65535), dateTime [2] BACnetDateTime } - Encoding (20.2.18): When context-tagged the choice is indicated by the context tag number (0 = time-only, 1 = sequence number, 2 = date+time). The contained value uses its normal application or constructed encoding.
- Usage drivers: Trend Log (high-frequency samples favour sequence numbers),
Event Timestamps property (all three forms allowed),
time_of_device_restart.
Examples
Sequence number timestamp (common in Trend Logs)
iex> ts = %BACnetTimestamp{type: :sequence_number, sequence_number: 42837, time: nil, datetime: nil}
iex> ts.sequence_number
42837Full DateTime timestamp
iex> ts = %BACnetTimestamp{
...> type: :datetime,
...> sequence_number: nil,
...> time: nil,
...> datetime: %BACnetDateTime{
...> date: %BACnetDate{year: 2025, month: 4, day: 1, weekday: 2},
...> time: %BACnetTime{hour: 9, minute: 15, second: 0, hundredth: 0}
...> }
...> }
iex> ts.type
:datetimeTime-only timestamp
iex> ts = %BACnetTimestamp{type: :time, time: %BACnetTime{hour: 17, minute: 0, second: 0, hundredth: 0}, sequence_number: nil, datetime: nil}
iex> ts.type
:timeSee Also
Summary
Functions
Encodes the given BACnet timestamp into an application tag.
Decodes the given application tags encoding into a BACnet timestamp.
Validates whether the given BACnet timestamp is in form valid.
Types
@type t() :: %BACnet.Protocol.BACnetTimestamp{ datetime: BACnet.Protocol.BACnetDateTime.t() | nil, sequence_number: non_neg_integer() | nil, time: BACnet.Protocol.BACnetTime.t() | nil, type: :time | :sequence_number | :datetime }
A CHOICE timestamp (see ASN.1 BACnetTimeStamp production in Clause 21).
type: :time-> context tag 0,timefield presenttype: :sequence_number-> context tag 1,sequence_number(0..65535)type: :datetime-> context tag 2,datetimefield present
The discriminant type tells the encoder which context tag to emit.
Functions
@spec encode(t(), Keyword.t()) :: {:ok, BACnet.Protocol.ApplicationTags.encoding_list()} | {:error, term()}
Encodes the given BACnet timestamp into an application tag.
@spec parse(BACnet.Protocol.ApplicationTags.encoding_list()) :: {:ok, {t(), rest :: BACnet.Protocol.ApplicationTags.encoding_list()}} | {:error, term()}
Decodes the given application tags encoding into a BACnet timestamp.
Example:
iex> BACnetTimestamp.parse([{:tagged, {0, <<2, 12, 49, 0>>, 4}}])
{:ok,
{%BACnetTimestamp{
datetime: nil,
sequence_number: nil,
time: %BACnetTime{
hour: 2,
hundredth: 0,
minute: 12,
second: 49
},
type: :time
}, []}}
Validates whether the given BACnet timestamp is in form valid.
It only validates the struct is valid as per type specification.