Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for parsing and displaying times in HH:MM:SS format? #181

Closed
sharkdp opened this issue Sep 26, 2023 · 13 comments
Closed

Add support for parsing and displaying times in HH:MM:SS format? #181

sharkdp opened this issue Sep 26, 2023 · 13 comments
Labels

Comments

@sharkdp
Copy link
Owner

sharkdp commented Sep 26, 2023

See previous discussions here:

See also:

let time = 17.47 hours
let num_seconds = mod(time -> seconds, 60 seconds)
let num_minutes = mod(time - num_seconds, 60 minutes) -> minutes // floor
let num_hours = floor((time - num_minutes - num_seconds) / 1 hour) × hour -> hours
assert_eq(num_hours + num_minutes + num_seconds -> s, time -> s, 1ms)
print("{num_hours/h}:{num_minutes/min}:{num_seconds/s}")

@eminence
Copy link
Contributor

A related enhancement would be to support queries like now() + 5 days + 4 hours and get an answer like Nov 2nd 03:36:20

@triallax
Copy link
Contributor

triallax commented Dec 12, 2023

@eminence that would involve datetime handling, and to be frank, I'm not sure if we want to open that big can of worms (mostly related to timezone handling).

@eminence
Copy link
Contributor

I agree that adding datetime handling would be hard (lots of input formats to parse, tricky bits about how to convert between timezones, how to control output formats, etc), but I actually think there is a minimal version of date/time handling that is not so difficult that we can't do it :)

@septatrix
Copy link

septatrix commented Dec 12, 2023

I closed my related issue #274 in favor of discussing this here in one place as it mostly touches the same area though I also mentioned support for dates there.

My use case would be simple mathematical operations involving mostly dates (without time) though datetime support would also be very much appreciated. I also do not think that this is that big of a can of worms. Timezone handling with chrono in rust is quite doable. If that turns out too annoying though one could also implement this step by step. This could be split into the following:

  • Support for alternative formatting of time durations (the original topic of this issue)
  • Support for date arithmetic (Gregorian calendar)
  • Support for datetime arithmetic (UTC or naive local time, i.e. timezone less time)
  • Support for timezone arithmetic

@sharkdp
Copy link
Owner Author

sharkdp commented Dec 19, 2023

@septatrix I agree with everything you said. I also think we should do this step by step.

Let's focus on the first point: "alternative formatting of time durations". Do we want HH:MM:SS or something like HH hours + MM minutes + SS seconds? The latter has the advantage that it's valid Numbat syntax. Qalculate does something similar. For example: "3.54 hours" can be displayed as 3 h + 32 min + 24 s.

As for the opposite part, how to input times (and later dates) in a convenient way, I think it would be cool to support HH:MM and HH:MM:SS somehow. A straightforward way would be to have a special function that takes a string. Something like from_hhmmss("17:14:00"). But that is not very convenient. And this input format is all about convenience. We could try to handle 17:14:00 directly by treating <number>:<number>… in a special way in the parser. But I think that would be messy and introduce all sorts of weird edge cases. What is typically better (in terms of language design) is to have some sort of delimiter. At least on the left-hand side. For example, something like @17:14:00. Other ideas and thoughts in this direction would be very welcome.

@Qyriad
Copy link

Qyriad commented Dec 19, 2023

Hm, would <number>:<number>… be that bad? Colon is only used for type annotation right now, right? So a digit to the left of the colon would be unambiguous?

That being said, some kind of delimiter could allow for other kinds of datetime literals too.

@sharkdp
Copy link
Owner Author

sharkdp commented Dec 19, 2023

Colon is only used for type annotation right now, right?

Yes, type annotations, but also use statements (use units::stoney - technically a double-colon token) and alias declarations (@aliases(m: short)). So probably not that bad right now, true. It's still a bit inconvenient to tokenize/parse, though. Which is not just a concern for our implementation, but also for IDE support (syntax highlighting etc).

So a digit to the left of the colon would be unambiguous?

I believe you are right. But it might prevent other future uses of the colon operator in some cases (for example: Haskell uses colon as a "cons" infix operator, where numbers can be on the right-hand side of :).

That being said, some kind of delimiter could allow for other kinds of datetime literals too.

👍

@Qyriad
Copy link

Qyriad commented Dec 19, 2023

If you eventually want to be able to have things like spaces (or slashes) in them, square brackets [ ] and curly braces { } are unused (though #261 could change one of those), and with an additional delimiter inside that could even be some kind of extensible literal syntax (e.g. [T, 4:24 PM] or something).

In lieu of something like that, @ does read well for time, though.

@septatrix
Copy link

Let's focus on the first point: "alternative formatting of time durations". Do we want HH:MM:SS or something like HH hours + MM minutes + SS seconds? The latter has the advantage that it's valid Numbat syntax. Qalculate does something similar. For example: "3.54 hours" can be displayed as 3 h + 32 min + 24 s.

Hm I think both are perfectly fine (though a colon kinda implies for me a time of day, not a duration)¹. However, I was wondering when this alternative formatting should be used and think having it as a function makes the most sense. It would be very counter-intuitive if calculations resulting in times suddenly changed e.g. "150 minutes" to "2 hours + 30 minutes" or other scenarios in which to output is ambiguous. Having this as an explicit function would make that less confusing - and could allow different ways to output the value.

I am also not opposed to require usage of a function to parse such a time format. Whichever way we choose for time input - we should use something which could be expanded to date(time)s.

¹ ISO 8601 / RFC 3339 thus also use a different syntax: P3H32M24S

@sharkdp
Copy link
Owner Author

sharkdp commented Jan 3, 2024

For everyone involved in this thread, you might be interested in the discussion in #287 with an initial draft for datetime support in Numbat.

@sharkdp
Copy link
Owner Author

sharkdp commented Feb 8, 2024

Based on datetime handling in #287, we can implement a function called human, entirely in Numbat, that can do things like:

image

The code does not look great — mainly because we lack something like a let … in … or a where clause (see #112 (comment)), which would remove some duplication and make it more readable overall. The functionality also needs some refining and more testing. But it might be a good starting point:

fn __num_days(time: Time) -> Scalar =
  ((time / day) -> 1) // floor

fn __human_hms(time: Time) -> String =
  format_datetime("%-H hours, %-M minutes, %-S seconds", parse_datetime("0001-01-01T00:00:00Z") + time)

fn __human_time(time: Time) -> String =
  if time < 1 minute
    then format_datetime("%-S seconds", parse_datetime("0001-01-01T00:00:00Z") + time)
    else if time < 1 hour
      then format_datetime("%-M minutes, %-S seconds", parse_datetime("0001-01-01T00:00:00Z") + time)
      else __human_hms(time)

fn human(time: Time) =
    if __num_days(time) < 1
      then __human_time(time)
      else "{__num_days(time)} days, {__human_hms(mod(time, 1 day))}"

assert((1 second    // human) == "1 seconds")
assert((5 second    // human) == "5 seconds")
 
assert((60 seconds  // human) == "1 minutes, 0 seconds")
assert((73 seconds  // human) == "1 minutes, 13 seconds")
assert((1 minute    // human) == "1 minutes, 0 seconds")
assert((1.25 minute // human) == "1 minutes, 15 seconds")

assert((1 hour     // human) == "1 hours, 0 minutes, 0 seconds")

assert((1 day      // human) == "1 days, 0 hours, 0 minutes, 0 seconds")
assert((1.37 day   // human) == "1 days, 8 hours, 52 minutes, 48 seconds")

assert((1 week     // human) == "7 days, 0 hours, 0 minutes, 0 seconds")

@sharkdp
Copy link
Owner Author

sharkdp commented Feb 9, 2024

I closed my related issue #274 in favor of discussing this here in one place as it mostly touches the same area though I also mentioned support for dates there.

My use case would be simple mathematical operations involving mostly dates (without time) though datetime support would also be very much appreciated. I also do not think that this is that big of a can of worms. Timezone handling with chrono in rust is quite doable. If that turns out too annoying though one could also implement this step by step. This could be split into the following:

* [x]  Support for alternative formatting of time durations (the original topic of this issue)

* [x]  Support for date arithmetic (Gregorian calendar)

* [x]  Support for datetime arithmetic (UTC or naive local time, i.e. timezone less time)

* [x]  Support for timezone arithmetic

All points have been addressed in #287 and #327.

A convenient syntax for entering dates is still missing, but let's discuss this in #323.

@sharkdp sharkdp closed this as completed Feb 9, 2024
@sharkdp
Copy link
Owner Author

sharkdp commented Feb 13, 2024

We released a first version of date time handling in Numbat v1.10.1. For details, see

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants