numbat 1.23.0

A statically typed programming language for scientific computations with first class support for physical dimensions and units.
Documentation
use core::strings
use core::quantities
use units::si
use units::time

@description("Returns the current date and time.")
fn now() -> DateTime

@description("Parses a string (date and time) into a `DateTime` object. See [here](./date-and-time.md#date-time-formats) for an overview of the supported formats.")
@example("datetime(\"2022-07-20T21:52+0200\")")
@example("datetime(\"2022-07-20 21:52 Europe/Berlin\")")
@example("datetime(\"2022/07/20 09:52 PM +0200\")")
fn datetime(input: String) -> DateTime

@description("Formats a `DateTime` object as a string.")
@example("format_datetime(\"This is a date in %B in the year %Y.\", datetime(\"2022-07-20 21:52 +0200\"))")
fn format_datetime(format: String, input: DateTime) -> String

@description("Returns the users local timezone.")
@example("get_local_timezone()")
fn get_local_timezone() -> String

@description("Returns a timezone conversion function, typically used with the conversion operator.")
@example("datetime(\"2022-07-20 21:52 +0200\") -> tz(\"Europe/Amsterdam\")")
@example("datetime(\"2022-07-20 21:52 +0200\") -> tz(\"Asia/Taipei\")")
fn tz(tz: String) -> Fn[(DateTime) -> DateTime]

@description("Timezone conversion function targeting the users local timezone (`datetime -> local`).")
let local: Fn[(DateTime) -> DateTime] = tz(get_local_timezone())

@description("Timezone conversion function to UTC.")
let UTC: Fn[(DateTime) -> DateTime] = tz("UTC")

fn _today_str() = format_datetime("%Y-%m-%d", now())

@description("Returns the current date at midnight (in the local time).")
fn today() -> DateTime = datetime("{_today_str()} 00:00:00")

@description("Parses a string (only date) into a `DateTime` object.")
@example("date(\"2022-07-20\")")
fn date(input: String) -> DateTime =
  if str_contains(" ", input)
    then datetime(str_replace(" ", " 00:00:00 ", input))
    else datetime("{input} 00:00:00")

@description("Parses a string (time only) into a `DateTime` object.")
fn time(input: String) -> DateTime =
  datetime("{_today_str()} {input}")

fn _add_days(dt: DateTime, n_days: Scalar) -> DateTime
fn _add_months(dt: DateTime, n_months: Scalar) -> DateTime
fn _add_years(dt: DateTime, n_years: Scalar) -> DateTime

@description("Adds the given time span to a `DateTime`. This uses leap-year and DST-aware calendar arithmetic with variable-length days, months, and years.")
@example("calendar_add(datetime(\"2022-07-20 21:52 +0200\"), 2 years)")
fn calendar_add(dt: DateTime, span: Time) -> DateTime =
   if span == 0
     then dt
   else if has_unit(span, days)
     then _add_days(dt, span / days)
   else if has_unit(span, months)
     then _add_months(dt, span / months)
   else if has_unit(span, years)
     then _add_years(dt, span / years)
   else if has_unit(span, seconds) || has_unit(span, minutes) || has_unit(span, hours)
     then dt + span
   else
     error("calendar_add: Unsupported unit for `span`")

@description("Subtract the given time span from a `DateTime`. This uses leap-year and DST-aware calendar arithmetic with variable-length days, months, and years.")
@example("calendar_sub(datetime(\"2022-07-20 21:52 +0200\"), 3 years)")
fn calendar_sub(dt: DateTime, span: Time) -> DateTime =
  calendar_add(dt, -span)

@description("Get the day of the week from a given `DateTime`.")
@example("weekday(datetime(\"2022-07-20 21:52 +0200\"))")
fn weekday(dt: DateTime) -> String = format_datetime("%A", dt)