Skip to content

Commit bc02a4d

Browse files
authored
feat: add timestamptz types (#266)
1 parent 6cd11de commit bc02a4d

File tree

7 files changed

+465
-3
lines changed

7 files changed

+465
-3
lines changed

lib/types/timestamptz.ex

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
defmodule AshPostgres.Timestamptz do
2+
@moduledoc """
3+
Implements the PostgresSQL [timestamptz](https://www.postgresql.org/docs/current/datatype-datetime.html) (aka `timestamp with time zone`) type.
4+
5+
Postgres [*strongly recommends*](https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_timestamp_.28without_time_zone.29) using this type instead of the standard timestamps/datetimes without a time zone. Generally speaking, it is best practice to use the [nanosecond-precision](`AshPostgres.TimestamptzUsec`) variant.
6+
7+
The basic reason `timestamptz` exists is to guarantee that the precise moment in time is stored as microseconds since January 1st, 2000 in UTC. This guarantee eliminates many time arithmetic problems, and ensures portability.
8+
9+
It does not actually store a timezone, in spite of the name. As far as Elixir/Ecto is concerned, it it always of type `DateTime` and set to UTC. Using this type ensures Postgres internally uses the same contract as Ecto's `:utc_datetime`, which is to always store `DateTime` in UTC. This is especially helpful if you need to do complex time arithmetic in SQL fragments, or build reports/materialized views that use localized time formatting.
10+
11+
Using this type ubiquitously in your schemas is particularly beneficial for consistency, and this is currently [under consideration](https://github.com/ash-project/ash_postgres/issues/264) as a configuration option for the default datetime storage type.
12+
13+
```elixir
14+
attribute :timestamp, AshPostgres.Timestamptz
15+
timestamps type: AshPostgres.Timestamptz
16+
```
17+
18+
Alternatively, you can set up a shortname:
19+
20+
```elixir
21+
# config.exs
22+
config :ash, :custom_types, timestamptz: AshPostgres.Timestamptz
23+
```
24+
25+
After saving, you will need to run `mix compile ash --force`.
26+
27+
```elixir
28+
attribute :timestamp, :timestamptz
29+
timestamps type: :timestamptz
30+
```
31+
32+
33+
34+
35+
"""
36+
use Ash.Type.NewType, subtype_of: :datetime, constraints: [precision: :second]
37+
38+
@impl true
39+
def storage_type(_constraints) do
40+
:timestamptz
41+
end
42+
end

lib/types/timestamptz_usec.ex

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
defmodule AshPostgres.TimestamptzUsec do
2+
@moduledoc """
3+
Implements the PostgresSQL [timestamptz](https://www.postgresql.org/docs/current/datatype-datetime.html) (aka `timestamp with time zone`) type with nanosecond precision.
4+
5+
```elixir
6+
attribute :timestamp, AshPostgres.TimestamptzUsec
7+
timestamps type: AshPostgres.TimestamptzUsec
8+
```
9+
10+
Alternatively, you can set up a shortname:
11+
12+
```elixir
13+
# config.exs
14+
config :ash, :custom_types, timestamptz_usec: AshPostgres.TimestamptzUsec
15+
```
16+
17+
After saving, you will need to run `mix compile ash --force`.
18+
19+
```elixir
20+
attribute :timestamp, :timestamptz_usec
21+
timestamps type: :timestamptz_usec
22+
```
23+
24+
25+
26+
Please see `AshPostgres.Timestamptz` for details about the usecase for this type.
27+
"""
28+
use Ash.Type.NewType, subtype_of: :datetime, constraints: [precision: :microsecond]
29+
30+
@impl true
31+
def storage_type(_constraints) do
32+
:"timestamptz(6)"
33+
end
34+
end

mix.exs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ defmodule AshPostgres.MixProject do
135135
Types: [
136136
AshPostgres.Type,
137137
AshPostgres.Tsquery,
138-
AshPostgres.Tsvector
138+
AshPostgres.Tsvector,
139+
AshPostgres.Timestamptz,
140+
AshPostgres.TimestamptzUsec
139141
],
140142
Extensions: [
141143
AshPostgres.Extensions.Vector

0 commit comments

Comments
 (0)