Skip to content

Commit 5da0800

Browse files
committed
Dates and times!
1 parent 4f2a420 commit 5da0800

3 files changed

Lines changed: 88 additions & 29 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Renamed to `pog`.
66
- Configuration and query API redesigned.
7+
- Introduced the `Date`, `Time`, and `Timestamp` types.
78

89
## v0.15.0 - Unreleased
910

src/pog.gleam

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
////
33
//// Gleam wrapper around pgo library
44

5+
// TODO: add time and timestamp with zone once PGO supports them
6+
57
import gleam/dynamic.{type DecodeErrors, type Decoder, type Dynamic}
8+
import gleam/float
9+
import gleam/int
610
import gleam/list
711
import gleam/option.{type Option, None, Some}
812
import gleam/result
@@ -272,13 +276,22 @@ pub fn bytea(a: BitArray) -> Value
272276
@external(erlang, "pog_ffi", "coerce")
273277
pub fn array(a: List(a)) -> Value
274278

275-
/// Coerce a timestamp represented as `#(#(year, month, day), #(hour, minute, second))` into a `Value`.
276-
@external(erlang, "pog_ffi", "coerce")
277-
pub fn timestamp(a: #(#(Int, Int, Int), #(Int, Int, Int))) -> Value
279+
pub fn timestamp(timestamp: Timestamp) -> Value {
280+
coerce_value(#(date(timestamp.date), time(timestamp.time)))
281+
}
282+
283+
pub fn date(date: Date) -> Value {
284+
coerce_value(#(date.year, date.month, date.day))
285+
}
286+
287+
pub fn time(time: Time) -> Value {
288+
let seconds = int.to_float(time.seconds)
289+
let seconds = seconds +. int.to_float(time.microseconds) /. 1_000_000.0
290+
coerce_value(#(time.hours, time.minutes, seconds))
291+
}
278292

279-
/// Coerce a date represented as `#(year, month, day)` into a `Value`.
280293
@external(erlang, "pog_ffi", "coerce")
281-
pub fn date(a: #(Int, Int, Int)) -> Value
294+
fn coerce_value(a: anything) -> Value
282295

283296
pub type TransactionError {
284297
TransactionQueryError(QueryError)
@@ -656,18 +669,57 @@ pub fn error_code_name(error_code: String) -> Result(String, Nil) {
656669
}
657670
}
658671

659-
/// Checks to see if the value is formatted as `#(#(Int, Int, Int), #(Int, Int, Int))`
660-
/// to represent `#(#(year, month, day), #(hour, minute, second))`, and returns the
661-
/// value if it is.
662-
pub fn decode_timestamp(value: dynamic.Dynamic) {
663-
dynamic.tuple2(
664-
dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int),
665-
dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int),
672+
pub fn decode_timestamp(
673+
value: dynamic.Dynamic,
674+
) -> Result(Timestamp, DecodeErrors) {
675+
dynamic.decode2(
676+
Timestamp,
677+
dynamic.element(0, decode_date),
678+
dynamic.element(1, decode_time),
666679
)(value)
667680
}
668681

669-
/// Checks to see if the value is formatted as `#(Int, Int, Int)` to represent a date
670-
/// as `#(year, month, day)`, and returns the value if it is.
671-
pub fn decode_date(value: dynamic.Dynamic) {
672-
dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int)(value)
682+
pub fn decode_date(value: dynamic.Dynamic) -> Result(Date, DecodeErrors) {
683+
dynamic.decode3(
684+
Date,
685+
dynamic.element(0, dynamic.int),
686+
dynamic.element(1, dynamic.int),
687+
dynamic.element(2, dynamic.int),
688+
)(value)
689+
}
690+
691+
pub fn decode_time(value: dynamic.Dynamic) -> Result(Time, DecodeErrors) {
692+
case dynamic.tuple3(dynamic.int, dynamic.int, decode_seconds)(value) {
693+
Error(e) -> Error(e)
694+
Ok(#(hours, minutes, #(seconds, microseconds))) ->
695+
Ok(Time(hours:, minutes:, seconds:, microseconds:))
696+
}
697+
}
698+
699+
fn decode_seconds(value: dynamic.Dynamic) -> Result(#(Int, Int), DecodeErrors) {
700+
case dynamic.int(value) {
701+
Ok(i) -> Ok(#(i, 0))
702+
Error(_) ->
703+
case dynamic.float(value) {
704+
Error(e) -> Error(e)
705+
Ok(i) -> {
706+
let floored = float.floor(i)
707+
let seconds = float.round(floored)
708+
let microseconds = float.round({ i -. floored } *. 1_000_000.0)
709+
Ok(#(seconds, microseconds))
710+
}
711+
}
712+
}
713+
}
714+
715+
pub type Date {
716+
Date(year: Int, month: Int, day: Int)
717+
}
718+
719+
pub type Time {
720+
Time(hours: Int, minutes: Int, seconds: Int, microseconds: Int)
721+
}
722+
723+
pub type Timestamp {
724+
Timestamp(date: Date, time: Time)
673725
}

test/pog_test.gleam

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ pub fn selecting_rows_test() {
125125
INSERT INTO
126126
cats
127127
VALUES
128-
(DEFAULT, 'neo', true, ARRAY ['black'], '2022-10-10 11:30:30', '2020-03-04')
128+
(DEFAULT, 'neo', true, ARRAY ['black'], '2022-10-10 11:30:30.1', '2020-03-04')
129129
RETURNING
130130
id"
131131

@@ -151,11 +151,14 @@ pub fn selecting_rows_test() {
151151
|> should.equal(1)
152152
returned.rows
153153
|> should.equal([
154-
#(id, "neo", True, ["black"], #(#(2022, 10, 10), #(11, 30, 30)), #(
155-
2020,
156-
3,
157-
4,
158-
)),
154+
#(
155+
id,
156+
"neo",
157+
True,
158+
["black"],
159+
pog.Timestamp(pog.Date(2022, 10, 10), pog.Time(11, 30, 30, 100_000)),
160+
pog.Date(2020, 3, 4),
161+
),
159162
])
160163

161164
pog.disconnect(db)
@@ -361,7 +364,7 @@ pub fn array_test() {
361364
pub fn datetime_test() {
362365
start_default()
363366
|> assert_roundtrip(
364-
#(#(2022, 10, 12), #(11, 30, 33)),
367+
pog.Timestamp(pog.Date(2022, 10, 12), pog.Time(11, 30, 33, 101)),
365368
"timestamp",
366369
pog.timestamp,
367370
pog.decode_timestamp,
@@ -371,7 +374,7 @@ pub fn datetime_test() {
371374

372375
pub fn date_test() {
373376
start_default()
374-
|> assert_roundtrip(#(2022, 10, 11), "date", pog.date, pog.decode_date)
377+
|> assert_roundtrip(pog.Date(2022, 10, 11), "date", pog.date, pog.decode_date)
375378
|> pog.disconnect
376379
}
377380

@@ -469,11 +472,14 @@ pub fn expected_maps_test() {
469472
|> should.equal(1)
470473
returned.rows
471474
|> should.equal([
472-
#(id, "neo", True, ["black"], #(#(2022, 10, 10), #(11, 30, 30)), #(
473-
2020,
474-
3,
475-
4,
476-
)),
475+
#(
476+
id,
477+
"neo",
478+
True,
479+
["black"],
480+
pog.Timestamp(pog.Date(2022, 10, 10), pog.Time(11, 30, 30, 0)),
481+
pog.Date(2020, 3, 4),
482+
),
477483
])
478484

479485
pog.disconnect(db)

0 commit comments

Comments
 (0)