Skip to content

Commit

Permalink
Accept an offset in RFC function
Browse files Browse the repository at this point in the history
  • Loading branch information
lpil committed Dec 27, 2024
1 parent 977ddf9 commit 719f847
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 37 deletions.
61 changes: 38 additions & 23 deletions src/gleam/time/timestamp.gleam
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import gleam/float
import gleam/int
import gleam/order
import gleam/result
import gleam/string
import gleam/time/duration.{type Duration}

Expand Down Expand Up @@ -140,30 +139,46 @@ pub fn add(timestamp: Timestamp, duration: Duration) -> Timestamp {
|> normalise
}

// TODO: docs
// TODO: rename?
pub fn to_rfc3339_utc(timestamp: Timestamp) -> String {
let seconds = int.modulo(timestamp.seconds, 60) |> result.unwrap(0)
let total_minutes = floored_div(timestamp.seconds, 60.0)
let minutes =
{ int.modulo(timestamp.seconds, 60 * 60) |> result.unwrap(0) } / 60
let hours =
{ int.modulo(timestamp.seconds, 24 * 60 * 60) |> result.unwrap(0) }
/ { 60 * 60 }
/// Convert a timestamp to a RFC 3339 formatted time string, with an offset
/// supplied in minutes.
///
/// The output of this function is also ISO 8601 compatible so long as the
/// offset not negative.
///
/// # Examples
///
/// ```gleam
/// to_rfc3339(from_unix_seconds(1000), 0)
/// // -> "1970-01-01T00:00:00Z"
/// ```
///
pub fn to_rfc3339(timestamp: Timestamp, offset_minutes offset: Int) -> String {
let total = timestamp.seconds - { offset * 60 }
let seconds = modulo(total, 60)
let total_minutes = floored_div(total, 60.0)
let minutes = modulo(total, 60 * 60) / 60
let hours = modulo(total, 24 * 60 * 60) / { 60 * 60 }
let #(years, months, days) = to_civil(total_minutes)
let offset_minutes = modulo(offset, 60)
let offset_hours = int.absolute_value(floored_div(offset, 60.0))

let n = fn(n) { int.to_string(n) |> string.pad_start(2, "0") }
n(years)
<> "-"
<> n(months)
<> "-"
<> n(days)
<> "T"
<> n(hours)
<> ":"
<> n(minutes)
<> ":"
<> n(seconds)
<> "Z"
let out = ""
let out = out <> n(years) <> "-" <> n(months) <> "-" <> n(days)
let out = out <> "T"
let out = out <> n(hours) <> ":" <> n(minutes) <> ":" <> n(seconds)
case int.compare(offset, 0) {
order.Eq -> out <> "Z"
order.Gt -> out <> "+" <> n(offset_hours) <> ":" <> n(offset_minutes)
order.Lt -> out <> "-" <> n(offset_hours) <> ":" <> n(offset_minutes)
}
}

fn modulo(n: Int, m: Int) -> Int {
case int.modulo(n, m) {
Ok(n) -> n
Error(_) -> 0
}
}

fn floored_div(numerator: Int, denominator: Float) -> Int {
Expand Down
40 changes: 26 additions & 14 deletions test/gleam/time/timestamp_test.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -106,44 +106,56 @@ pub fn system_time_0_test() {
let assert True = now < christmas_day_2025
}

pub fn to_rfc3339_utc_0_test() {
pub fn to_rfc3339_0_test() {
timestamp.from_unix_seconds(1_735_309_467)
|> timestamp.to_rfc3339_utc
|> timestamp.to_rfc3339(0)
|> should.equal("2024-12-27T14:24:27Z")
}

pub fn to_rfc3339_utc_1_test() {
pub fn to_rfc3339_1_test() {
timestamp.from_unix_seconds(1)
|> timestamp.to_rfc3339_utc
|> timestamp.to_rfc3339(0)
|> should.equal("1970-01-01T00:00:01Z")
}

pub fn to_rfc3339_utc_2_test() {
pub fn to_rfc3339_2_test() {
timestamp.from_unix_seconds(0)
|> timestamp.to_rfc3339_utc
|> timestamp.to_rfc3339(0)
|> should.equal("1970-01-01T00:00:00Z")
}

pub fn to_rfc3339_utc_3_test() {
pub fn to_rfc3339_3_test() {
timestamp.from_unix_seconds(123_456_789)
|> timestamp.to_rfc3339_utc
|> timestamp.to_rfc3339(0)
|> should.equal("1973-11-29T21:33:09Z")
}

pub fn to_rfc3339_utc_4_test() {
pub fn to_rfc3339_4_test() {
timestamp.from_unix_seconds(31_560_000)
|> timestamp.to_rfc3339_utc
|> timestamp.to_rfc3339(0)
|> should.equal("1971-01-01T06:40:00Z")
}

pub fn to_rfc3339_utc_5_test() {
pub fn to_rfc3339_5_test() {
timestamp.from_unix_seconds(-12_345_678)
|> timestamp.to_rfc3339_utc
|> timestamp.to_rfc3339(0)
|> should.equal("1969-08-11T02:38:42Z")
}

pub fn to_rfc3339_utc_6_test() {
pub fn to_rfc3339_6_test() {
timestamp.from_unix_seconds(-1)
|> timestamp.to_rfc3339_utc
|> timestamp.to_rfc3339(0)
|> should.equal("1969-12-31T23:59:59Z")
}

pub fn to_rfc3339_7_test() {
timestamp.from_unix_seconds(60 * 60 + 60 * 5)
|> timestamp.to_rfc3339(65)
|> should.equal("1970-01-01T00:00:00+01:05")
}

pub fn to_rfc3339_8_test() {
timestamp.from_unix_seconds(0)
|> timestamp.to_rfc3339(-120)
|> should.equal("1970-01-01T02:00:00-02:00")
}

0 comments on commit 719f847

Please sign in to comment.