Packages
protox
1.6.9
2.0.9
2.0.8
2.0.7
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
2.0.0-dev
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
retired
1.7.3
1.7.2
1.7.1
1.7.0
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
1.0.0
0.25.0
0.24.0
0.23.1
0.23.0
0.22.0
0.21.0
0.20.0
0.19.1
0.19.0
0.18.0
0.17.0
0.16.2
0.16.1
0.16.0
0.15.2
0.15.1
0.15.0
0.14.0
0.13.0
0.12.1
0.12.0
0.11.1
0.11.0
0.10.0
0.9.1
0.9.0
0.8.0
0.7.1
0.7.0
A fast, easy to use and 100% conformant Elixir library for Google Protocol Buffers (aka protobuf)
Current section
Files
Jump to
Current section
Files
lib/google/protobuf/timestamp.ex
defmodule Protox.Google.Protobuf.Timestamp do
@moduledoc false
use Protox.Define,
enums: [],
messages: [
{
Google.Protobuf.Timestamp,
:proto3,
[
Protox.Field.new!(
kind: {:scalar, 0},
label: :optional,
name: :seconds,
tag: 1,
type: :int64
),
Protox.Field.new!(
kind: {:scalar, 0},
label: :optional,
name: :nanos,
tag: 2,
type: :int32
)
]
}
]
def max_timestamp_rfc(), do: "9999-12-31T23:59:59.999999999Z"
def max_timestamp_nanos(), do: 253_402_300_799_999_999_999
def min_timestamp_rfc(), do: "0001-01-01T00:00:00Z"
def min_timestamp_nanos(), do: -62_135_596_800_000_000_000
end
defimpl Protox.JsonMessageDecoder, for: Google.Protobuf.Timestamp do
alias Protox.Google.Protobuf.Timestamp
def decode_message(_initial_message, nil), do: nil
def decode_message(initial_message, json) do
if not String.contains?(json, "T") do
raise Protox.JsonDecodingError.new("Missing 'T' in timestamp")
end
# We only use DateTime to validate format as it does correctly parse timestamps
# with nanosecond precision, but the result has only a microsecond precision.
# It would have been preferable to use only :calendar.rfc3339_to_system_time,
# but it's too lax as it accepts 'z' (which should be uppercase)
# which is not accepted by the conformance tests.
if match?({:error, _}, DateTime.from_iso8601(json)) do
raise Protox.JsonDecodingError.new("Invalid timestamp format")
end
unix_timestamp =
json
|> String.to_charlist()
|> :calendar.rfc3339_to_system_time([{:unit, :nanosecond}])
if unix_timestamp < Timestamp.min_timestamp_nanos() do
raise Protox.JsonDecodingError.new("Timestamp < #{Timestamp.min_timestamp_rfc()}")
else
nanos = rem(unix_timestamp, 1_000_000_000)
seconds = div(unix_timestamp, 1_000_000_000)
struct!(initial_message, seconds: seconds, nanos: nanos)
end
end
end
defimpl Protox.JsonMessageEncoder, for: Google.Protobuf.Timestamp do
alias Protox.Google.Protobuf.Timestamp
def encode_message(msg, json_encode) do
unix_timestamp = msg.seconds * 1_000_000_000 + msg.nanos
cond do
unix_timestamp > Timestamp.max_timestamp_nanos() ->
raise Protox.JsonEncodingError.new("#{msg.__struct__} > #{Timestamp.max_timestamp_rfc()}")
unix_timestamp < Timestamp.min_timestamp_nanos() ->
raise Protox.JsonEncodingError.new("#{msg.__struct__} < #{Timestamp.min_timestamp_rfc()}")
true ->
# Conformance tests recommend to remove useless fractional part.
suffix_to_remove =
cond do
rem(msg.nanos, 1_000_000_000) == 0 -> ".000000000Z"
rem(msg.nanos, 1_000_000) == 0 -> "000000Z"
rem(msg.nanos, 1_000) == 0 -> "000Z"
true -> "Z"
end
unix_timestamp
|> :calendar.system_time_to_rfc3339([{:unit, :nanosecond}, {:offset, 'Z'}])
|> List.to_string()
|> String.replace_trailing(suffix_to_remove, "Z")
|> json_encode.()
end
end
end