Giter Club home page Giter Club logo

birl's Introduction

Hi πŸ‘‹, I'm Shayan!

backend developer with background in theoretical physics


massivefermion massivefermion


massivefermion

Open source projects

  • Radish: Redis client
  • Mungo: MongoDB client
  • Birl: Date/Time handling
  • Blah: Fake data generator
  • Juno: Flexible JSON decoding
  • Puddle: Resource pool manager
  • Bison: BSON encoder and decoder
  • Ranger: Create ranges over any type
  • Dove: Currently experimental http client
  • Phony: International phone number validator
  • Lox: Educational programming language and runtime
  • Calculator-rs: Just to learn parsing and Rust

birl's People

Contributors

dominiklutley avatar erikareads avatar massivefermion avatar maxdeviant avatar michaeljones avatar pendletong avatar raffomania avatar yertools avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

birl's Issues

Time + datetime types lack timezone information

Hello!

Currently none of the types of store time zone information, so any time these types of use the programmer will also need to store in another variable the time zone and then adjust for it. This is quite errorprone, I think it would be best to instead have the library handle that's for the programmer.

Thanks,
Louis

Where has from_iso8601 gone?

This function has been removed and there's no changelog saying how to upgrade. What's the new approach?

Thanks,
Louis

parse and to_string for Weekday and TimeOfDay

Hey, I've been using this package and am finding it very useful.

I'm working a lot with TimeOfDay and Weekday and found myself writing a few helper functions, particularly parse_weekday(), week_day_to_string() and time_of_day_to_string().

Are these something you would be interested in adding to the package? I could work on this if you'd like.

Timing off by factor of 100 on windows

At least, I assume it's a windows issue. I tested it on a Ubuntu system and did not have the same issue.

Minimal Example:

import birl
import birl/duration
import gleam/erlang/process
import gleam/io
import gleam/string

// Based on https://github.com/bcpeinhardt/learn_otp_with_gleam/blob/681c23174fb24d0f7e92ca581fd71306eb4d3063/src/tasks.gleam#L158
fn time(name: String, f: fn() -> a) -> a {
  let start = birl.now()
  let x = f()
  let end = birl.now()
  io.println(name <> " start:\t" <> birl.to_naive_time_string(start))
  io.println(name <> " end:  \t" <> birl.to_naive_time_string(end))
  let difference = birl.difference(end, start) |> duration.decompose
  io.println(name <> " took: \t" <> string.inspect(difference))
  x
}

pub fn main() {
  // Sleep for 2 seconds
  time("timing test", fn() { process.sleep(2000) })
}

Output on Ubuntu system (expected):

timing test start:      15:07:18.823
timing test end:        15:07:20.853
timing test took:       [#(2, Second), #(28, MilliSecond), #(539, MicroSecond)]

Output on Windows system:

timing test start:      15:07:51.380
timing test end:        15:07:53.399
timing test took:       [#(20, MilliSecond), #(174, MicroSecond)]

As you can see, printing the time works fine. It's just the difference that is the issue.

I'm not experienced enough to diagnose exactly what is going on, but I assume that the Duration type that normally takes microseconds is being given 100 microsecond intervals.

Time Zones do not Account for DST

The following code

birl.from_unix(1_711_456_116) |> birl.set_timezone("America/New_York") |> birl.to_iso8601 |> io.debug

produces 2024-03-26T07:28:36.000-05:00 when it should produce 2024-03-26T08:28:36.000-04:00 because America/New_York is in EDT time right now, which has the offset of -04:00. Between sometime in March and sometime in November each year in America the timezone offset values changes to an hour ahead because of Daylight Saving Time.

set_time_of_day does not set monotonic time value

I came across an issue where I tried to do:

let now = birl.now()
let start_time =
  birl.set_time_of_day(
    now,
    birl.TimeOfDay(hour: 8, minute: 0, second: 0, milli_second: 0),
  )
let diff = birl.difference(now, start_time)

But this always returns Duration(0) because set_time_of_day doesn't seem to change the monotonic_time and difference uses the monotonic_time if it can?

I'm afraid I'm not sure what the solution is. Possibly to set the monotonic time to None if it is not possible to calculate it from the new time of day.

I have worked around it by converting now to an iso8601 string and re-parsing it at which point it doesn't have a monotonic_time so the approach works.

Comparisons between naive times do not account for TimeOfDay value

Thanks for the package, I've gotten lots of use out of it! One thing I noticed is that running this code:

  let assert Ok(t1) = birl.parse("2019-01-01T14:00:00.000-04:00")
  let assert Ok(t2) = birl.parse("2019-01-01T14:00:00.001-04:00")
  birl.compare(t1, t2)
  |> io.debug

will result in Lt, while running this code:

  let assert Ok(t1) = birl.parse("2019-01-01T14:00:00.000")
  let assert Ok(t2) = birl.parse("2019-01-01T14:00:00.001")
  birl.compare(t1, t2)
  |> io.debug

will result in Eq, and this code:

  let assert Ok(t1) = birl.parse("2019-01-01T14:00:00.000")
  let assert Ok(t2) = birl.parse("2019-01-02T14:00:00.001")
  birl.compare(t1, t2)
  |> io.debug

will result in Lt.

It seems to me like the time of day should still be considered when comparing naive times, not just the calendar date.

If you want to preserve the original functionality of only comparing the calendar date, maybe another function should be added like compare_date. I can implement it if the maintainer agrees, any thoughts?

Naive date time type

Hello!

Chatting with @Nicd today about datetime libraries and he raised a good point about the Elixir date time module- it provides a NaiveDateTime struct in addition to the others.

This functionality is very useful, I think there's something to gain here. I'm not entirely sure what the ideal API should be, but it could be good to explore.

Cheers,
Louis

birl crashing

Birl is used by startest and my tests started crashing somewhat randomly. I think it started happening at midnight..

This is a minimal reproduction of the crash

  birl.now()
  |> io.debug()
  |> birl.get_offset()
  |> io.debug()
Time(1717157489658988, -129600000000, Some("Pacific/Auckland"), Some(165446))
exception error: #{function => <<"get_offset">>,line => 1206,
                 message => <<"Assertion pattern match failed">>,
                 module => <<"birl">>,
                 value => {error,nil},
                 gleam_error => let_assert}

Will report back after I sleep to see if anything changes

birl.now() uses incorrect offset for specific periods

If you perform birl.now() during the period of 00:00 and 01:00 on the first of a new month when in the timezone Europe/Dublin during IST the offset becomes -47 hours rather than +1 hour. This causes the get_offset function to error.
Creating a time from unix and setting the timezone to Europe/Dublin doesn't have the same issue
This only occurs on the erlang version of the now which means it is in the local_offset function in birl_ffi.erl

(edit: changed DST to IST to be more accurate)

Type Time and NaiveTime differently

Hey everyone, just wanted to start a discussion. Since Gleam has a really nice type system and as a language heavily encourages you to use types to reduce the possibility of bugs or crashes, I thought it would really help reduce time related bugs if Time and NaiveTime were different types. I know a big downside is that it would add a lot more code to this package and probably bump birl up a major version, but it will really make sure that when people do comparisons and conversions between times, they know how time offsets will be handled. For example, if I wanted to compare two time values, say 2019-01-02T14:00:00.000 and 2019-01-02T14:00:00.000-04:00, it is not obvious to me how the compare function will behave in this case. Maybe only the naive times are compared, maybe they are both converted to UTC and compared, I cannot know until I run some tests. If instead I was forced to convert them both to the same time type myself, then I could be sure how they are being compared: I could give the first an offset and compare it to the second with the compare function. Or I could make the second naive by calling to_naive (and rename the current func to to_naive_string) and compare them with a compare_naive function. This gives me, the application implementer, explicit control over how I deal with time offsets and zones, instead of assuming the birl package will handle them the way I want every time.

Support rfc 3339

Hello!

RFC 3339 is a much simpler and easier to understand format than ISO 8601, it would be great to support it! Most the time when people say "iso format" they are using the (imo) nicer subset of ISO 8601 that is also RFC 3339.

Cheers,
Louis

Include monotonic time in the datetime and time types

Hello!

Currently if one want to use dates for order the programmer needs to be aware of the problems with wall time and that it can be backwards, etc, and know that in this situation they should instead use the instant type instead of or in addition to.

I think we could remove this sharp edge by copying Go's novel approach of combining both into a single type, resulting in one difficult to mis-use type that likely does what you want regardless of how deep your understanding is of time.

The go documentation has some good information on their system here: https://pkg.go.dev/time#Time

Thanks,
Louis

time.legible_difference crashes with 0.12.0

Hello!

I've found a crash with the latest version. The crash comes from within inner_blur when this is run:

time.legible_difference(time.now(), DateTime(1683354989000000, 0, None))

With 0.11.0 it succeeds.

Thanks,
Louis

Comparisons between different timezones

Hi !

Following a discussion we had on gleam-lang/hexpm on timestamps, it appears that when comparing two datetime on different timezones, the behaviour is to just compare the millis. But even if on the same timestamp, they don't have the same time because there's an offset.

Is it the desired behaviour?

Birl appropriate for benchmarking?

Hey there!
I'm doing some very basic benchmarking with the following

fn time(name: String, f: fn() -> a) -> a {
  let start = birl.now()
  let x = f()
  let end = birl.now()
  let difference = birl.difference(end, start) |> duration.blur_to(duration.MilliSecond)
  io.println(name <> " took: " <> int.to_string(difference) <> "ms")
  x
}

which I use like

let parallel_freq = time("parallel", fn() { parallel_letter_frequency(workload, 200_000) })

My question is

  1. Is using Birl for benchmarking this way appropriate and
  2. Would a more general timer function be a useful addition?

Add more documentation, including examples

Hello!

The documentation at the moment is very sparse so it's hard to know what the different parsing and formatting functions do. It would be fab if there was more documentation, including examples and explanations of the different formats.

Thanks,
Louis

Comparing durations

Hello!

TL;DR is it ok to unwrap the Int value from Duration? If not, should there be a duration.compare function?


Given a package release date, I need to check if it was released within the last hour. I didn't find a function for comparing durations in the duration module, so I'm wondering if there is a preferred way of doing that?

First, I chose to unwrap the underlying integer values and compare them, but I thought I shouldn't be depending on the implementation details of the Duration type.

fn format_date(datetime: Time) -> String {
  let now = birl.now()
  let Duration(package_age_ms) = birl.difference(now, datetime)
  let Duration(age_threshold_ms) = duration.hours(1)
  case package_age_ms < age_threshold_ms {
    True -> "Just now!"
    False -> birl.legible_difference(now, datetime)
  }
}

Then I noticed there is a blur_to function, which lets me write less code, but its behavior is somewhat uncommon

if the duration is not an integer multiple of the unit, the remainder will be disgarded if it’s less than two thirds of the unit

fn format_date(datetime: Time) -> String {
  let now = birl.now()
  let package_age = birl.difference(now, datetime)
  case duration.blur_to(package_age, duration.Hour) < 1 {
    True -> "Just now!"
    False -> birl.legible_difference(now, datetime)
  }
}

Do you think adding a new function such as fn compare(a: Duration, b: Duration) -> Bool is warranted?


Just FYI: While writing this and collecting my thoughts, I noticed the birl.compare function and found a third solution to this

fn format_date(datetime: Time) -> String {
  let now = birl.now()
  let one_hour_ago = birl.subtract(now, duration.hours(1))
  case birl.compare(datetime, one_hour_ago) {
    order.Gt -> "Just now!"
    _ -> birl.legible_difference(now, datetime)
  }
}

Setting time of day crashes when in a timezone that is behind UTC and it is past midnight in UTC

The test function:

pub fn birl_set_time_of_day_test() {
  birl.now()
  |> birl.set_time_of_day(birl.TimeOfDay(9, 30, 0, 0))
}

fails when it is past 12:00 AM UTC but before (I assume, haven't waited to test it yet) 4:00 AM UTC on a system which has a time zone of EDT(UTC-04).

On my system, birl.now() produces Time(1714523701784880, 158400000000, Some("America/New_York"), Some(247434))

The stack trace is:

birl_set_time_of_day_test
     #{function => <<"to_parts">>,gleam_error => let_assert,line => 1237,
       message => <<"Assertion pattern match failed">>,module => <<"birl">>,
       value => {error,nil}}
     location: birl.to_parts:1237
     stacktrace:
       birl.to_parts
       birl.set_time_of_day

I believe a time zone of (UTC-02) would crash between 12:00 AM UTC and 2:00 AM UTC, but I haven't tested it yet because it takes waiting. I believe the issue is when the date of the local time is different than the date of the UTC time, but could be wrong.

I can work around this by setting my system's time zone to either UTC or a timezone ahead of it. I am in the EDT time zone and just randomly started getting crashes after 8pm while working.

EDIT: while I was testing it further I realized converting to UTC from EDT right now (Apr 30, 8:39pm) also changes the month in the calendar date, so maybe that is the issue. I will run the test again tomorrow past 8pm and see.

What Am I Doing Wrong?

I am trying to manually time some functions runtime in Gleam. I am using birl. However, the following timing code doesnt seem to work at all. Am I doing something wrong? (by wrong, I mean that the actually runtime of the code is much longer then the millisecond differences produced by the code below)

import birl
import birl/duration
import gleam/int
import gleam/io
import gleam/list


pub fn main() {

  let times = [birl.utc_now()]
  // do something
  let times = times |> list.append([birl.utc_now()])
  // do something
  let times = times |> list.append([birl.utc_now()])
  // do something
  let times = times |> list.append([birl.utc_now()])

  // print timing results
  list.each(list.zip(list.drop(times, 1), times), fn(pair) {
    let #(a, b) = pair
    let duration.Duration(micro) = birl.difference(a, b)
    let milli = micro / 1000
    io.print(",")
    io.print(milli |> int.to_string)
  })
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.