aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--docs/README.md21
-rw-r--r--docs/moments.md642
-rw-r--r--docs/paths.md38
-rw-r--r--examples/time/time.tm210
-rw-r--r--examples/time/time_defs.h30
-rw-r--r--src/ast.c1
-rw-r--r--src/ast.h4
-rw-r--r--src/compile.c39
-rw-r--r--src/environment.c41
-rw-r--r--src/parse.c55
-rw-r--r--src/stdlib/datatypes.h3
-rw-r--r--src/stdlib/integers.c16
-rw-r--r--src/stdlib/integers.h2
-rw-r--r--src/stdlib/moments.c323
-rw-r--r--src/stdlib/moments.h44
-rw-r--r--src/stdlib/optionals.c1
-rw-r--r--src/stdlib/optionals.h1
-rw-r--r--src/stdlib/paths.c18
-rw-r--r--src/stdlib/paths.h6
-rw-r--r--src/stdlib/tomo.h1
-rw-r--r--src/typecheck.c3
-rw-r--r--src/types.c8
-rw-r--r--src/types.h3
-rw-r--r--test/moments.tm49
-rw-r--r--test/serialization.tm6
26 files changed, 298 insertions, 1269 deletions
diff --git a/Makefile b/Makefile
index 33055789..d76990df 100644
--- a/Makefile
+++ b/Makefile
@@ -106,7 +106,7 @@ clean:
examples: examples/base64/base64 examples/ini/ini examples/game/game \
examples/tomodeps/tomodeps examples/tomo-install/tomo-install examples/wrap/wrap examples/colorful/colorful
- ./build/tomo -qIL examples/commands examples/shell examples/base64 examples/log examples/ini examples/vectors examples/game \
+ ./build/tomo -qIL examples/time examples/commands examples/shell examples/base64 examples/log examples/ini examples/vectors examples/game \
examples/http examples/threads examples/tomodeps examples/tomo-install examples/wrap examples/pthreads examples/colorful
./build/tomo examples/learnxiny.tm
diff --git a/docs/README.md b/docs/README.md
index 4bfc2e1e..0034ab8b 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -21,7 +21,6 @@ Information about Tomo's built-in types can be found here:
- [Arrays](arrays.md)
- [Booleans](booleans.md)
- [Bytes](bytes.md)
-- [Moment](moments.md)
- [Enums](enums.md)
- [Floating point numbers](nums.md)
- [Integers](integers.md)
@@ -167,23 +166,3 @@ Nothing, aborts the program.
```tomo
fail("Oh no!")
```
-
----
-
-### `now`
-Gets the current time. This is an alias for `Moment.now()`.
-
-```tomo
-func now(->Moment)
-```
-
-None.
-
-**Returns:**
-The current moment as a Moment.
-
-**Example:**
-```tomo
->> now()
-= Sun Sep 29 20:12:33 2024
-```
diff --git a/docs/moments.md b/docs/moments.md
deleted file mode 100644
index 5a10f107..00000000
--- a/docs/moments.md
+++ /dev/null
@@ -1,642 +0,0 @@
-# Moments
-
-Tomo has a builtin datatype for representing a specific single point in time:
-`Moment`. This is similar to a "datetime" in other languages, but it does not
-represent a locale-specific time with a timezone. A Moment object is internally
-represented using a UNIX timestamp in seconds and a number of nanoseconds to
-represent sub-second times (in C, the equivalent of `struct timeval`). Moment
-values do not represent calendar dates or clock times, they represent an exact
-nanosecond-long sliver of time, such as the moment when a file was last
-modified on the filesystem or the current moment (`Moment.now()`).
-
-⚠️⚠️⚠️ **WARNING** ⚠️⚠️⚠️ Dates and times are deeply counterintuitive and you should
-be extremely cautious when writing code that deals with dates and times. Effort
-has been made to ensure that Tomo's `Moment` code uses standard libraries and
-is as correct as possible, but counterintuitive behaviors around time zones,
-daylight savings time, leap seconds, and other anomalous time situations can
-still cause bugs if you're not extremely careful.
-
-## Syntax
-
-Moment literals can be specified using [ISO
-8601](https://en.wikipedia.org/wiki/ISO_8601) syntax with an optional
-square-bracket delimited time zone name afterwards. A space may be used instead
-of a `T` in the ISO 8601 format for readability, and spaces may come before the
-timezone.
-
-```tomo
-2024-09-30
-2024-09-30T13:57
-2024-09-30 13:57
-2024-09-30 13:57:01
-2024-09-30 13:57:01 +04:00
-2024-09-30 13:57:01 [America/New_York]
-```
-
-## Time Zones
-
-Because humans are not able to easily understand UNIX timestamps, the default
-textual representation of `Moment` objects uses the current locale's
-preferred representation of the Moment in the current time zone:
-
-```tomo
->> Moment.now()
-= Sun Sep 29 18:20:12 2024 EDT
-```
-
-For various methods, it is assumed by default that users wish to perform
-calculations and specify moments using the local time zone and daylight
-savings time rules. For example, if a program is running in New York and it is
-currently 11pm on February 28th, 2023 (the last day of the month) in local
-time, it is assumed that "one month from now" refers to 11pm on March 28th,
-2024 in local time, rather than referring to one month from the current UTC
-time. In that example, the initial time would be 3am March 1, 2023 in UTC, so
-one month later would be 3am April 1, 2023 in UTC, which is which is 11am March
-31st in local time. Most users would be unpleasantly surprised to find out that
-when it's February 28th in local time, one month later is March 28th until 8pm,
-at which point it becomes March 31st!
-
-For various functions where time zones matter, there is an optional `timezone`
-argument that, if set, will override the timezone when performing calculations.
-If unspecified, it is assumed that the current local timezone should be used.
-Time zones are specified by name, such as `America/New_York` or `UTC`.
-
-## Moment Methods
-
-- [`func after(moment: Moment, seconds : Num = 0.0, minutes : Num = 0.0, hours : Num = 0.0, days : Int = 0, weeks : Int = 0, months : Int = 0, years : Int = 0, timezone : Text? = !Text -> Moment)`](#after)
-- [`func date(moment: Moment, timezone : Text? = !Text -> Text)`](#date)
-- [`func day_of_month(moment: Moment, timezone : Text? = !Text -> Int)`](#day_of_month)
-- [`func day_of_week(moment: Moment, timezone : Text? = !Text -> Int)`](#day_of_week)
-- [`func day_of_year(moment: Moment, timezone : Text? = !Text -> Int)`](#day_of_year)
-- [`func format(moment: Moment, format: Text = "%Y-%m-%dT%H:%M:%S%z", timezone : Text? = !Text -> Text)`](#format)
-- [`func from_unix_timestamp(timestamp: Int64 -> Moment)`](#from_unix_timestamp)
-- [`func get_local_timezone(->Text)`](#get_local_timezone)
-- [`func hour(moment: Moment, timezone : Text? = !Text -> Int)`](#hour)
-- [`func hours_till(moment: Moment, then:Moment -> Num)`](#hours_till)
-- [`func minute(moment: Moment, timezone : Text? = !Text -> Int)`](#minute)
-- [`func minutes_till(moment: Moment, then:Moment -> Num)`](#minutes_till)
-- [`func month(moment: Moment, timezone : Text? = !Text -> Int)`](#month)
-- [`func nanosecond(moment: Moment, timezone : Text? = !Text -> Int)`](#nanosecond)
-- [`func new(year : Int, month : Int, day : Int, hour : Int = 0, minute : Int = 0, second : Num = 0.0 -> Moment)`](#new)
-- [`func now(->Moment)`](#now)
-- [`func parse(text: Text, format: Text = "%Y-%m-%dT%H:%M:%S%z" -> Moment?)`](#parse)
-- [`func relative(moment: Moment, relative_to : Moment = Moment.now(), timezone : Text? = !Text -> Text)`](#relative)
-- [`func second(moment: Moment, timezone : Text? = !Text -> Int)`](#second)
-- [`func seconds_till(moment: Moment, then:Moment -> Num)`](#seconds_till)
-- [`func set_local_timezone(timezone : Text? = !Text -> Void)`](#set_local_timezone)
-- [`func time(moment: Moment, seconds : Bool = no, am_pm : Bool = yes, timezone : Text? = !Text -> Text)`](#time)
-- [`func unix_timestamp(moment:Moment->Int64)`](#unix_timestamp)
-
-### `after`
-Returns a Moment that occurs after the specified time differences. Time
-differences may be either positive or negative.
-
-**Note:** time offsets for days, months, weeks, and years do not refer to fixed
-time intervals, but are relative to which date they are applied to. For
-example, one year from January 1, 2024 is January 1, 2025, which is 366 days
-later because 2024 is a leap year. Similarly, adding one month may add anywhere
-from 28 to 31 days, depending on the starting month. Days and weeks are
-affected by leap seconds. For this reason, `after()` takes an argument,
-`timezone` which is used to determine in which timezone the offsets should be
-calculated.
-
-```tomo
-func after(moment: Moment, seconds : Num = 0.0, minutes : Num = 0.0, hours : Num = 0.0, days : Int = 0, weeks : Int = 0, months : Int = 0, years : Int = 0, timezone : Text? = !Text -> Moment)
-```
-
-- `moment`: The moment used as a starting point.
-- `seconds` (optional): An amount of seconds to offset the moment (default: 0).
-- `minutes` (optional): An amount of minutes to offset the moment (default: 0).
-- `hours` (optional): An amount of hours to offset the moment (default: 0).
-- `days` (optional): An amount of days to offset the moment (default: 0).
-- `weeks` (optional): An amount of weeks to offset the moment (default: 0).
-- `months` (optional): An amount of months to offset the moment (default: 0).
-- `years` (optional): An amount of years to offset the moment (default: 0).
-- `timezone` (optional): If specified, perform perform the calculations in the
- given timezone. If unspecified, the current local timezone will be used.
-
-**Returns:**
-A new `Moment` offset by the given amount.
-
-**Example:**
-```tomo
->> Moment(2024, 9, 29, hour=19):after(days=1, minutes=30)
-= Mon Sep 30 19:30:00 2024 EDT
-```
-
----
-
-### `date`
-Return a text representation of the moment using the `"%F"` format
-specifier, which gives the date in `YYYY-MM-DD` form.
-
-```tomo
-func date(moment: Moment, timezone : Text? = !Text -> Text)
-```
-
-- `moment`: The moment to get the date from.
-- `timezone` (optional): If specified, give the date in the given timezone (otherwise, use the current local timezone).
-
-**Returns:**
-The date in `YYYY-MM-DD` format.
-
-**Example:**
-```tomo
->> Moment(2024, 9, 29):date()
-= "2024-09-29"
-```
-
----
-
-### `day_of_month`
-Return the integer day of the month (1-31).
-
-```tomo
-func day_of_month(moment: Moment, timezone : Text? = !Text -> Int)
-```
-
-- `moment`: The moment to get the day of the month from.
-- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
-
-**Returns:**
-The day of the month as an integer (1-31).
-
-**Example:**
-```tomo
->> Moment(2024, 9, 29):day_of_month()
-= 29
-```
-
----
-
-### `day_of_week`
-Return the integer day of the week (1-7), where 1 = Sunday, 2 = Monday,
-3 = Tuesday, 4 = Wednesday, 5 = Thursday, 6 = Friday, 7 = Saturday.
-
-```tomo
-func day_of_week(moment: Moment, timezone : Text? = !Text -> Int)
-```
-
-- `moment`: The moment to get the day of the week from.
-- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
-
-**Returns:**
-The day of the week as an integer (1-7).
-
-**Example:**
-```tomo
->> Moment(2024, 9, 29):day_of_week()
-= 1
-```
-
----
-
-### `day_of_year`
-Return the integer day of the year (1-366, including leap years).
-
-```tomo
-func day_of_year(moment: Moment, timezone : Text? = !Text -> Int)
-```
-
-- `moment`: The moment to get the day of the year from.
-- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
-
-**Returns:**
-The day of the year as an integer (1-366).
-
-**Example:**
-```tomo
->> Moment(2024, 9, 29):day_of_year()
-= 272
-```
-
----
-
-### `format`
-Using the C-style [`strftime`](https://linux.die.net/man/3/strftime) format
-options, return a text representation of the given date in the given format. If
-`timezone` is specified, use that timezone instead of the current local
-timezone.
-
-```tomo
-func format(moment: Moment, format: Text = "%Y-%m-%dT%H:%M:%S%z", timezone : Text? = !Text -> Text)
-```
-
-- `moment`: The moment to format.
-- `format`: The `strftime` format to use (default: `"%Y-%m-%dT%H:%M:%S%z"`).
-- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
-
-**Returns:**
-Nothing.
-
-**Example:**
-```tomo
->> Moment(2024, 9, 29):format("%A")
-= "Sunday"
-```
-
----
-
-### `from_unix_timestamp`
-Return a moment object that represents the same moment in time as
-the given UNIX epoch timestamp (seconds since January 1, 1970 UTC).
-
-```tomo
-func from_unix_timestamp(timestamp: Int64 -> Moment)
-```
-
-- `timestamp`: The UNIX timestamp.
-
-**Returns:**
-A `Moment` object representing the same moment as the given UNIX timestamp.
-
-**Example:**
-```tomo
-# In the New York timezone:
->> Moment.from_unix_timestamp(0)
-= Wed Dec 31 19:00:00 1969
-```
-
-### `get_local_timezone`
-Get the local timezone's name (e.g. `America/New_York` or `UTC`. By default,
-this value is read from `/etc/localtime`, however, this can be overridden by
-calling `Moment.set_local_timezone(...)`.
-
-```tomo
-func get_local_timezone(->Text)
-```
-
-None.
-
-**Returns:**
-The name of the current local timezone.
-
-**Example:**
-```tomo
->> Moment.get_local_timezone()
-= "America/New_York"
-```
-
----
-
-### `hour`
-Return the hour of the day as an integer (1-24).
-
-```tomo
-func hour(moment: Moment, timezone : Text? = !Text -> Int)
-```
-
-- `moment`: The moment to get the hour from.
-- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
-
-**Returns:**
-The hour of the day as an integer (1-24).
-
-**Example:**
-```tomo
->> Moment(2024, 9, 29, 11, 59):hour()
-= 11
-```
-
----
-
-### `hours_till`
-Return the number of hours until a given moment.
-
-```tomo
-func hours_till(moment: Moment, then:Moment -> Num)
-```
-
-- `moment`: The starting point moment.
-- `then`: Another moment that we want to calculate the time offset from (in hours).
-
-**Returns:**
-The number of hours (possibly fractional, possibly negative) until the given time.
-
-**Example:**
-```tomo
-the_future := now():after(hours=1, minutes=30)
->> now():hours_till(the_future)
-= 1.5
-```
-
----
-
-### `minute`
-Return the minute of the day as an integer (0-59).
-
-```tomo
-func minute(moment: Moment, timezone : Text? = !Text -> Int)
-```
-
-- `moment`: The moment to get the minute from.
-- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
-
-**Returns:**
-The minute of the hour as an integer (0-59).
-
-**Example:**
-```tomo
->> Moment(2024, 9, 29, 11, 59):minute()
-= 59
-```
-
----
-
-### `minutes_till`
-Return the number of minutes until a given moment.
-
-```tomo
-func minutes_till(moment: Moment, then:Moment -> Num)
-```
-
-- `moment`: The starting point moment.
-- `then`: Another moment that we want to calculate the time offset from (in minutes).
-
-**Returns:**
-The number of minutes (possibly fractional, possibly negative) until the given time.
-
-**Example:**
-```tomo
-the_future := now():after(minutes=1, seconds=30)
->> now():minutes_till(the_future)
-= 1.5
-```
-
----
-
-### `month`
-Return the month of the year as an integer (1-12).
-
-```tomo
-func month(moment: Moment, timezone : Text? = !Text -> Int)
-```
-
-- `moment`: The moment to get the month from.
-- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
-
-**Returns:**
-The month of the year as an integer (1-12).
-
-**Example:**
-```tomo
->> Moment(2024, 9, 29, 11, 59):month()
-= 9
-```
-
----
-
-### `nanosecond`
-Return the nanosecond of the second as an integer (0-999,999,999).
-
-```tomo
-func nanosecond(moment: Moment, timezone : Text? = !Text -> Int)
-```
-
-- `moment`: The moment to get the nanosecond from.
-- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
-
-**Returns:**
-The nanosecond of the second as an integer (0-999,999,999).
-
-**Example:**
-```tomo
->> Moment(2024, 9, 29, 11, 59):month()
-= 9
-```
-
----
-
-### `new`
-Return a new `Moment` object representing the given time parameters expressed
-in local time. This function is the same as calling `Moment` directly as a
-constructor.
-
-```tomo
-func new(year : Int, month : Int, day : Int, hour : Int = 0, minute : Int = 0, second : Num = 0.0 -> Moment)
-```
-
-- `year`: The year.
-- `month`: The month of the year (1-12).
-- `day`: The day of the month (1-31).
-- `hour`: The hour of the day (0-23) (default: 0).
-- `minute`: The minute of the hour (0-59) (default: 0).
-- `second`: The second of the minute (0-59) (default: 0.0).
-
-**Returns:**
-A `Moment` representing the given information in local time. If the given
-parameters exceed reasonable bounds, the time values will wrap around. For
-example, `Moment.new(..., hour=3, minute=65)` is the same as
-`Moment.new(..., hour=4, minute=5)`. If any arguments cannot fit in a 32-bit
-integer, an error will be raised.
-
-**Example:**
-```tomo
->> Moment.new(2024, 9, 29)
-= Mon Sep 30 00:00:00 2024 EDT
-
-# March 1642, 2020:
->> Moment(2020, 4, 1643)
-= Sat Sep 28 00:00:00 2024 EDT
-```
-
----
-
-### `now`
-Get a `Moment` object representing the current date and time. This function
-is the same as the global function `now()`.
-
-```tomo
-func now(->Moment)
-```
-
-None.
-
-**Returns:**
-Returns a `Moment` object representing the current date and time.
-
-**Example:**
-```tomo
->> Moment.now()
-= Sun Sep 29 20:22:48 2024 EDT
-```
-
----
-
-### `parse`
-Return a new `Moment` object parsed from the given string in the given format,
-or `none` if the value could not be successfully parsed.
-
-```tomo
-func parse(text: Text, format: Text = "%Y-%m-%dT%H:%M:%S%z" -> Moment?)
-```
-
-- `text`: The text to parse.
-- `format`: The date format of the text being parsed (see:
- [strptime](https://linux.die.net/man/3/strptime) for more info on this
- format) (default: `"%Y-%m-%dT%H:%M:%S%z"`).
-
-**Returns:**
-If the text was successfully parsed according to the given format, return a
-`Moment` representing that information. Otherwise, return `none`.
-
-**Example:**
-```tomo
->> Moment.parse("2024-09-29", "%Y-%m-%d")!
-= Sun Sep 29 00:00:00 2024 EDT
-
->> Moment.parse("???", "%Y-%m-%d")
-= !Moment
-```
-
----
-
-### `relative`
-Return a plain English textual representation of the approximate time difference
-between two `Moment`s. For example: `5 minutes ago` or `1 day later`
-
-```tomo
-func relative(moment: Moment, relative_to : Moment = Moment.now(), timezone : Text? = !Text -> Text)
-```
-
-- `moment`: The moment whose relative time you're getting.
-- `relative_to` (optional): The time against which the relative time is calculated (default: `Moment.now()`).
-- `timezone` (optional): If specified, perform calculations in the given
- timezone (otherwise, use the current local timezone).
-
-**Returns:**
-Return a plain English textual representation of the approximate time
-difference between two `Moment`s. For example: `5 minutes ago` or `1 day
-later`. Return values are approximate and use only one significant unit of
-measure with one significant digit, so a difference of 1.6 days will be
-represented as `2 days later`. moments in the past will have the suffix `"
-ago"`, while moments in the future will have the suffix `" later"`.
-
-**Example:**
-```tomo
->> now():after(days=2):relative()
-= "2 days later"
-
->> now():after(minutes=-65):relative()
-= "1 hour ago"
-```
-
----
-
-### `second`
-Return the second of the minute as an integer (0-59).
-
-```tomo
-func second(moment: Moment, timezone : Text? = !Text -> Int)
-```
-
-- `moment`: The moment to get the second from.
-- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
-
-**Returns:**
-The second of the hour as an integer (0-59).
-
-**Example:**
-```tomo
->> Moment(2024, 9, 29, 11, 30, 59):second()
-= 59
-```
-
----
-
-### `seconds_till`
-Return the number of seconds until a given moment.
-
-```tomo
-func seconds_till(moment: Moment, then:Moment -> Num)
-```
-
-- `moment`: The starting point moment.
-- `then`: Another moment that we want to calculate the time offset from (in seconds).
-
-**Returns:**
-The number of seconds (possibly fractional, possibly negative) until the given time.
-
-**Example:**
-```tomo
-the_future := now():after(seconds=1)
->> now():seconds_till(the_future)
-= 1
-```
-
----
-
-### `set_local_timezone`
-Set the current local timezone to a given value by name (e.g.
-`America/New_York` or `UTC`). The local timezone is used as the default
-timezone for performing calculations and constructing `Moment` objects from
-component parts. It's also used as the default way that `Moment` objects are
-converted to text.
-
-```tomo
-func set_local_timezone(timezone : Text? = !Text -> Void)
-```
-
-- `timezone` (optional): if specified, set the current local timezone to the
- timezone with the given name. If null, reset the current local timezone to
- the system default (the value referenced in `/etc/localtime`).
-
-**Returns:**
-Nothing.
-
-**Example:**
-```tomo
-Moment.set_local_timezone("America/Los_Angeles")
-```
-
----
-
-### `time`
-Return a text representation of the time component of the given moment.
-
-```tomo
-func time(moment: Moment, seconds : Bool = no, am_pm : Bool = yes, timezone : Text? = !Text -> Text)
-```
-
-- `moment`: The moment whose time value you want to get.
-- `seconds`: Whether to include seconds in the time (default: `no`).
-- `am_pm`: Whether to use am/pm in the representation or use a 24-hour clock (default: `yes`).
-- `timezone` (optional): If specified, give the time in the given timezone (otherwise, use the current local timezone).
-
-**Returns:**
-A text representation of the time component of the moment.
-
-**Example:**
-```tomo
-moment := Moment(2024, 9, 29, hours=13, minutes=59, seconds=30)
-
->> dt:time()
-= "1:59pm"
-
->> dt:time(am_pm=no)
-= "13:59"
-
->> dt:time(seconds=yes)
-= "1:59:30pm"
-```
-
----
-
-### `unix_timestamp`
-Get the UNIX timestamp of the given moment (seconds since the UNIX epoch:
-January 1, 1970 UTC).
-
-```tomo
-func unix_timestamp(moment:Moment->Int64)
-```
-
-`moment`: The moment whose UNIX timestamp you want to get.
-
-**Returns:**
-A 64-bit integer representation of the UNIX timestamp.
-
-**Example:**
-```tomo
->> now():unix_timestamp()
-= 1727654730[64]
-```
diff --git a/docs/paths.md b/docs/paths.md
index cb4bb055..234ad44f 100644
--- a/docs/paths.md
+++ b/docs/paths.md
@@ -37,7 +37,7 @@ intended. Paths can be created from text with slashes using
## Path Methods
-- [`func accessed(path:Path, follow_symlinks=yes -> Moment?)`](#accessed)
+- [`func accessed(path:Path, follow_symlinks=yes -> Int64?)`](#accessed)
- [`func append(path: Path, text: Text, permissions: Int32 = 0o644[32] -> Void)`](#append)
- [`func append_bytes(path: Path, bytes: [Byte], permissions: Int32 = 0o644[32] -> Void)`](#append_bytes)
- [`func base_name(path: Path -> Text)`](#base_name)
@@ -45,7 +45,7 @@ intended. Paths can be created from text with slashes using
- [`func can_execute(path:Path -> Bool)`](#can_execute)
- [`func can_read(path:Path -> Bool)`](#can_read)
- [`func can_write(path:Path -> Bool)`](#can_write)
-- [`func changed(path:Path, follow_symlinks=yes -> Moment?)`](#changed)
+- [`func changed(path:Path, follow_symlinks=yes -> Int64?)`](#changed)
- [`func child(path: Path, child:Text -> Path)`](#child)
- [`func children(path: Path, include_hidden=no -> [Path])`](#children)
- [`func create_directory(path: Path, permissions=0o755[32] -> Void)`](#create_directory)
@@ -60,7 +60,7 @@ intended. Paths can be created from text with slashes using
- [`func is_file(path: Path, follow_symlinks=yes -> Bool)`](#is_file)
- [`func is_socket(path: Path, follow_symlinks=yes -> Bool)`](#is_socket)
- [`func is_symlink(path: Path -> Bool)`](#is_symlink)
-- [`func modified(path:Path, follow_symlinks=yes -> Moment?)`](#modified)
+- [`func modified(path:Path, follow_symlinks=yes -> Int64?)`](#modified)
- [`func owner(path: Path, follow_symlinks=yes -> Text?)`](#owner)
- [`func parent(path: Path -> Path)`](#parent)
- [`func read(path: Path -> Text?)`](#read)
@@ -80,22 +80,22 @@ intended. Paths can be created from text with slashes using
Gets the file access time of a file.
```tomo
-func accessed(path:Path, follow_symlinks: Bool = yes -> Moment?)
+func accessed(path:Path, follow_symlinks: Bool = yes -> Int64?)
```
- `path`: The path of the file whose access time you want.
- `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:**
-A [Moment](moments.md) representing when the file or directory was last
+A 64-bit unix epoch timestamp representing when the file or directory was last
accessed, or `none` if no such file or directory exists.
**Example:**
```tomo
>> (./file.txt):accessed()
-= Sun 16 Mar 2025 03:43:53 PM EDT EDT : Moment?
+= 1704221100?
>> (./not-a-file):accessed()
-= none : Moment?
+= none:Int64?
```
---
@@ -274,22 +274,22 @@ Gets the file change time of a file.
which is _not_ the file creation time.
```tomo
-func changed(path:Path, follow_symlinks: Bool = yes -> Moment?)
+func changed(path:Path, follow_symlinks: Bool = yes -> Int64?)
```
- `path`: The path of the file whose change time you want.
- `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:**
-A [Moment](moments.md) representing when the file or directory was last
+A 64-bit unix epoch timestamp representing when the file or directory was last
changed, or `none` if no such file or directory exists.
**Example:**
```tomo
>> (./file.txt):changed()
-= Sun 16 Mar 2025 03:43:53 PM EDT EDT : Moment?
+= 1704221100?
>> (./not-a-file):changed()
-= none : Moment?
+= none:Int64
```
---
@@ -633,22 +633,22 @@ func is_symlink(path: Path -> Bool)
Gets the file modification time of a file.
```tomo
-func modified(path:Path, follow_symlinks: Bool = yes -> Moment?)
+func modified(path:Path, follow_symlinks: Bool = yes -> Int64?)
```
- `path`: The path of the file whose modification time you want.
- `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:**
-A [Moment](moments.md) representing when the file or directory was last
+A 64-bit unix epoch timestamp representing when the file or directory was last
modified, or `none` if no such file or directory exists.
**Example:**
```tomo
>> (./file.txt):modified()
-= Sun 16 Mar 2025 03:43:53 PM EDT EDT : Moment?
+= 1704221100?
>> (./not-a-file):modified()
-= none : Moment?
+= none:Int64
```
---
@@ -714,10 +714,10 @@ raised.
**Example:**
```tomo
>> (./hello.txt):read()
-= "Hello" : Text?
+= "Hello"?
>> (./nosuchfile.xxx):read()
-= none : Text?
+= none:Text
```
---
@@ -738,10 +738,10 @@ returned.
**Example:**
```tomo
>> (./hello.txt):read()
-= [72[B], 101[B], 108[B], 108[B], 111[B]] : [Byte]?
+= [72[B], 101[B], 108[B], 108[B], 111[B]]?
>> (./nosuchfile.xxx):read()
-= none : [Byte]?
+= none:[Byte]
```
---
diff --git a/examples/time/time.tm b/examples/time/time.tm
new file mode 100644
index 00000000..909c24ec
--- /dev/null
+++ b/examples/time/time.tm
@@ -0,0 +1,210 @@
+# Time - a module for dealing with dates and times
+use ./time_defs.h
+
+enum Weekday(Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday)
+
+struct TimeInfo(year,month,day,hour,minute,second,nanosecond:Int, weekday:Weekday, day_of_year:Int, timezone:Text)
+
+struct Time(tv_sec:Int64, tv_usec:Int64; extern):
+ func now(->Time):
+ return inline C : Time {
+ struct timespec ts;
+ if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
+ fail("Couldn't get the time!");
+ (Time){.tv_sec=ts.tv_sec, .tv_usec=ts.tv_nsec/1000};
+ }
+
+ func local_timezone(->Text):
+ inline C {
+ if (_local_timezone.length < 0) {
+ static char buf[PATH_MAX];
+ ssize_t len = readlink("/etc/localtime", buf, sizeof(buf));
+ if (len < 0)
+ fail("Could not get local tz!");
+
+ char *zoneinfo = strstr(buf, "/zoneinfo/");
+ if (zoneinfo)
+ _local_timezone = Text$from_str(zoneinfo + strlen("/zoneinfo/"));
+ else
+ fail("Could not resolve local tz!");
+ }
+ }
+ return inline C : Text { _local_timezone; }
+
+ func set_local_timezone(timezone:Text):
+ inline C {
+ setenv("TZ", Text$as_c_string(_$timezone), 1);
+ _local_timezone = _$timezone;
+ tzset();
+ }
+
+ func format(t:Time, format="%c", timezone=Time.local_timezone() -> Text):
+ return inline C : Text {
+ struct tm result;
+ time_t time = _$t.tv_sec;
+ struct tm *final_info;
+ WITH_TIMEZONE(_$timezone, final_info = localtime_r(&time, &result));
+ static char buf[256];
+ size_t len = strftime(buf, sizeof(buf), String(_$format), final_info);
+ Text$from_strn(buf, len);
+ }
+
+ func new(year,month,day:Int, hour=0, minute=0, second=0.0, timezone=Time.local_timezone() -> Time):
+ return inline C : Time{
+ struct tm info = {
+ .tm_min=Int32$from_int(_$minute, false),
+ .tm_hour=Int32$from_int(_$hour, false),
+ .tm_mday=Int32$from_int(_$day, false),
+ .tm_mon=Int32$from_int(_$month, false) - 1,
+ .tm_year=Int32$from_int(_$year, false) - 1900,
+ .tm_isdst=-1,
+ };
+
+ time_t t;
+ WITH_TIMEZONE(_$timezone, t = mktime(&info));
+ (Time){.tv_sec=t + (time_t)_$second, .tv_usec=(suseconds_t)(fmod(_$second, 1.0) * 1e9)};
+ }
+
+ func unix_timestamp(t:Time -> Int64):
+ return inline C : Int64 { (int64_t)_$t.tv_sec }
+
+ func from_unix_timestamp(timestamp:Int64 -> Time):
+ return inline C : Time { (Time){.tv_sec=_$timestamp}; }
+
+ func seconds_till(t:Time, target:Time -> Num):
+ seconds := Num(target.tv_sec - t.tv_sec)
+ seconds += 1e-9*Num(target.tv_usec - t.tv_usec)
+ return seconds
+
+ func minutes_till(t:Time, target:Time -> Num):
+ return t:seconds_till(target)/60.
+
+ func hours_till(t:Time, target:Time -> Num):
+ return t:seconds_till(target)/3600.
+
+ func relative(t:Time, relative_to=Time.now(), timezone=Time.local_timezone() -> Text):
+ inline C {
+ struct tm info = {};
+ struct tm relative_info = {};
+ WITH_TIMEZONE(_$timezone, {
+ localtime_r(&_$t.tv_sec, &info);
+ localtime_r(&_$relative_to.tv_sec, &relative_info);
+ });
+ double second_diff = _$time$Time$seconds_till(_$relative_to, _$t);
+ if (info.tm_year != relative_info.tm_year && fabs(second_diff) > 365.*24.*60.*60.)
+ return num_format((long)info.tm_year - (long)relative_info.tm_year, "year");
+ else if (info.tm_mon != relative_info.tm_mon && fabs(second_diff) > 31.*24.*60.*60.)
+ return num_format(12*((long)info.tm_year - (long)relative_info.tm_year) + (long)info.tm_mon - (long)relative_info.tm_mon, "month");
+ else if (info.tm_yday != relative_info.tm_yday && fabs(second_diff) > 24.*60.*60.)
+ return num_format(round(second_diff/(24.*60.*60.)), "day");
+ else if (info.tm_hour != relative_info.tm_hour && fabs(second_diff) > 60.*60.)
+ return num_format(round(second_diff/(60.*60.)), "hour");
+ else if (info.tm_min != relative_info.tm_min && fabs(second_diff) > 60.)
+ return num_format(round(second_diff/(60.)), "minute");
+ else {
+ if (fabs(second_diff) < 1e-6)
+ return num_format((long)(second_diff*1e9), "nanosecond");
+ else if (fabs(second_diff) < 1e-3)
+ return num_format((long)(second_diff*1e6), "microsecond");
+ else if (fabs(second_diff) < 1.0)
+ return num_format((long)(second_diff*1e3), "millisecond");
+ else
+ return num_format((long)(second_diff), "second");
+ }
+ }
+ fail("Unreachable")
+
+ func time(t:Time, seconds=no, am_pm=yes, timezone=Time.local_timezone() -> Text):
+ time := if seconds and am_pm:
+ t:format("%l:%M:%S%P")
+ else if seconds and not am_pm:
+ t:format("%T")
+ else if not seconds and am_pm:
+ t:format("%l:%M%P")
+ else:
+ t:format("%H:%M")
+ return time:trim($/ /, trim_left=yes, trim_right=yes)
+
+ func date(t:Time, timezone=Time.local_timezone() -> Text):
+ return t:format("%F")
+
+ func info(t:Time, timezone=Time.local_timezone() -> TimeInfo):
+ return inline C : TimeInfo {
+ struct tm info = {};
+ WITH_TIMEZONE(_$timezone, localtime_r(&_$t.tv_sec, &info));
+ (_$time$TimeInfo$$type){
+ .year = I(info.tm_year + 1900),
+ .month = I(info.tm_mon + 1),
+ .day = I(info.tm_mday),
+ .hour = I(info.tm_hour),
+ .minute = I(info.tm_min),
+ .second = I(info.tm_sec),
+ .nanosecond = I(_$t.tv_usec),
+ .weekday = (_$time$Weekday$$type){.$tag=info.tm_wday + 1},
+ .day_of_year = I(info.tm_yday),
+ .timezone = _$timezone,
+ };
+ }
+
+ func after(t:Time, seconds=0.0, minutes=0.0, hours=0.0, days=0, weeks=0, months=0, years=0, timezone=Time.local_timezone() -> Time):
+ return inline C : Time {
+ double offset = _$seconds + 60.*_$minutes + 3600.*_$hours ;
+ _$t.tv_sec += (time_t)offset;
+
+ struct tm info = {};
+ WITH_TIMEZONE(_$timezone, localtime_r(&_$t.tv_sec, &info));
+
+ info.tm_mday += Int32$from_int(_$days, false) + 7*Int32$from_int(_$weeks, false);
+ info.tm_mon += Int32$from_int(_$months, false);
+ info.tm_year += Int32$from_int(_$years, false);
+
+ time_t t = mktime(&info);
+ (Time){
+ .tv_sec=t,
+ .tv_usec=_$t.tv_usec + (suseconds_t)(fmod(offset, 1.0) * 1e9),
+ };
+ }
+
+ func parse(text:Text, format="%Y-%m-%dT%H:%M:%S%z", timezone=Time.local_timezone() -> Time?):
+ return inline C : Time? {
+ struct tm info = {.tm_isdst=-1};
+ const char *str = Text$as_c_string(_$text);
+ const char *fmt = Text$as_c_string(_$format);
+ if (strstr(fmt, "%Z"))
+ fail("The %Z specifier is not supported for time parsing!");
+
+ char *invalid;
+ WITH_TIMEZONE(_$timezone, invalid = strptime(str, fmt, &info));
+ if (!invalid || invalid[0] != '\0')
+ return (_$time$$OptionalTime$$type){.is_none=true};
+
+ long offset = info.tm_gmtoff; // Need to cache this because mktime() mutates it to local tz >:(
+ time_t t;
+ WITH_TIMEZONE(_$timezone, t = mktime(&info));
+ (_$time$$OptionalTime$$type){.value={.tv_sec=t + offset - info.tm_gmtoff}};
+ }
+
+func _run_tests():
+ >> Time.now():format()
+ >> Time.set_local_timezone("Europe/Paris")
+ >> Time.now():format()
+ >> Time.set_local_timezone("America/New_York")
+ >> Time.now():format()
+ # >> Time.now():format(timezone="Europe/Paris")
+ # >> Time.now():format()
+ # >> Time.now():format("%Y-%m-%d")
+ # >> Time.new(2023, 11, 5):format()
+ # >> Time.local_timezone()
+
+ # >> Time.new(2023, 11, 5):seconds_till(Time.now())
+ # >> Time.new(2023, 11, 5):relative()
+
+ # >> Time.now():info()
+ # >> Time.now():time()
+ # >> Time.now():date()
+
+ # >> Time.parse("2023-11-05 01:01", "%Y-%m-%d %H:%M")
+ # >> Time.parse("2023-11-05 01:01", "%Y-%m-%d %H:%M", timezone="Europe/Paris")
+
+func main():
+ _run_tests()
diff --git a/examples/time/time_defs.h b/examples/time/time_defs.h
new file mode 100644
index 00000000..fd8fd4f3
--- /dev/null
+++ b/examples/time/time_defs.h
@@ -0,0 +1,30 @@
+#pragma once
+#include <time.h>
+#include <sys/time.h>
+
+typedef struct timeval Time;
+
+static OptionalText_t _local_timezone = NONE_TEXT;
+
+static INLINE Text_t num_format(long n, const char *unit)
+{
+ if (n == 0)
+ return Text("now");
+ return Text$format((n == 1 || n == -1) ? "%ld %s %s" : "%ld %ss %s", n < 0 ? -n : n, unit, n < 0 ? "ago" : "later");
+}
+
+static void set_local_timezone(Text_t tz)
+{
+ setenv("TZ", Text$as_c_string(tz), 1);
+ _local_timezone = tz;
+ tzset();
+}
+
+#define WITH_TIMEZONE(tz, body) ({ if (tz.length >= 0) { \
+ OptionalText_t old_timezone = _local_timezone; \
+ set_local_timezone(tz); \
+ body; \
+ set_local_timezone(old_timezone); \
+ } else { \
+ body; \
+ }})
diff --git a/src/ast.c b/src/ast.c
index d04fa437..00f03a6d 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -163,7 +163,6 @@ CORD ast_to_xml(ast_t *ast)
T(FieldAccess, "<FieldAccess field=\"%s\">%r</FieldAccess>", data.field, ast_to_xml(data.fielded))
T(Optional, "<Optional>%r</Optional>", ast_to_xml(data.value))
T(NonOptional, "<NonOptional>%r</NonOptional>", ast_to_xml(data.value))
- T(Moment, "<Moment/>")
T(DocTest, "<DocTest>%r%r</DocTest>", optional_tagged("expression", data.expr), optional_tagged("expected", data.expected))
T(Use, "<Use>%r%r</Use>", optional_tagged("var", data.var), xml_escape(data.path))
T(InlineCCode, "<InlineCode>%r</InlineCode>", xml_escape(data.code))
diff --git a/src/ast.h b/src/ast.h
index aa218334..2260436f 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -140,7 +140,6 @@ typedef enum {
Extern,
StructDef, EnumDef, LangDef,
Index, FieldAccess, Optional, NonOptional,
- Moment,
DocTest,
Use,
InlineCCode,
@@ -319,9 +318,6 @@ struct ast_s {
ast_t *value;
} Optional, NonOptional;
struct {
- Moment_t moment;
- } Moment;
- struct {
ast_t *expr, *expected;
bool skip_source:1;
} DocTest;
diff --git a/src/compile.c b/src/compile.c
index 18a1e9a1..9073de5e 100644
--- a/src/compile.c
+++ b/src/compile.c
@@ -39,18 +39,18 @@ static CORD compile_string_literal(CORD literal);
CORD promote_to_optional(type_t *t, CORD code)
{
- if (t == THREAD_TYPE || t == PATH_TYPE || t == PATH_TYPE_TYPE || t == MATCH_TYPE || t->tag == MomentType) {
+ if (t == THREAD_TYPE || t == PATH_TYPE || t == PATH_TYPE_TYPE || t == MATCH_TYPE) {
return code;
} else if (t->tag == IntType) {
switch (Match(t, IntType)->bits) {
- case TYPE_IBITS8: return CORD_all("((OptionalInt8_t){", code, "})");
- case TYPE_IBITS16: return CORD_all("((OptionalInt16_t){", code, "})");
- case TYPE_IBITS32: return CORD_all("((OptionalInt32_t){", code, "})");
- case TYPE_IBITS64: return CORD_all("((OptionalInt64_t){", code, "})");
+ case TYPE_IBITS8: return CORD_all("((OptionalInt8_t){.value=", code, "})");
+ case TYPE_IBITS16: return CORD_all("((OptionalInt16_t){.value=", code, "})");
+ case TYPE_IBITS32: return CORD_all("((OptionalInt32_t){.value=", code, "})");
+ case TYPE_IBITS64: return CORD_all("((OptionalInt64_t){.value=", code, "})");
default: errx(1, "Unsupported in type: ", type_to_str(t));
}
} else if (t->tag == ByteType) {
- return CORD_all("((OptionalByte_t){", code, "})");
+ return CORD_all("((OptionalByte_t){.value=", code, "})");
} else if (t->tag == StructType) {
return CORD_all("({ ", compile_type(Type(OptionalType, .type=t)), " nonnull = {.value=", code, "}; nonnull.is_none = false; nonnull; })");
} else {
@@ -516,7 +516,6 @@ CORD compile_type(type_t *t)
case BoolType: return "Bool_t";
case ByteType: return "Byte_t";
case CStringType: return "const char*";
- case MomentType: return "Moment_t";
case BigIntType: return "Int_t";
case IntType: return CORD_asprintf("Int%ld_t", Match(t, IntType)->bits);
case NumType: return Match(t, NumType)->bits == TYPE_NBITS64 ? "Num_t" : CORD_asprintf("Num%ld_t", Match(t, NumType)->bits);
@@ -563,7 +562,7 @@ CORD compile_type(type_t *t)
case TextType:
return Match(nonnull, TextType)->lang ? compile_type(nonnull) : "OptionalText_t";
case IntType: case BigIntType: case NumType: case BoolType: case ByteType:
- case ArrayType: case TableType: case SetType: case MomentType:
+ case ArrayType: case TableType: case SetType:
return CORD_all("Optional", compile_type(nonnull));
case StructType: {
if (nonnull == THREAD_TYPE)
@@ -688,7 +687,7 @@ CORD optional_into_nonnone(type_t *t, CORD value)
if (t->tag == OptionalType) t = Match(t, OptionalType)->type;
switch (t->tag) {
case IntType:
- return CORD_all(value, ".i");
+ return CORD_all(value, ".value");
case StructType:
if (t == THREAD_TYPE || t == MATCH_TYPE || t == PATH_TYPE || t == PATH_TYPE_TYPE)
return value;
@@ -730,8 +729,6 @@ CORD check_none(type_t *t, CORD value)
return CORD_all("(", value, ").is_none");
else if (t->tag == EnumType)
return CORD_all("({(", value, ").$tag == 0;})");
- else if (t->tag == MomentType)
- return CORD_all("({(", value, ").tv_usec < 0;})");
else if (t->tag == MutexedType)
return CORD_all("({", value, " == NULL;})");
print_err("Optional check not implemented for: ", type_to_str(t));
@@ -1803,7 +1800,6 @@ CORD expr_as_text(CORD expr, type_t *t, CORD color)
// NOTE: this cannot use stack(), since bools may actually be bit fields:
return CORD_asprintf("Bool$as_text((Bool_t[1]){%r}, %r, &Bool$info)", expr, color);
case CStringType: return CORD_asprintf("CString$as_text(stack(%r), %r, &CString$info)", expr, color);
- case MomentType: return CORD_asprintf("Moment$as_text(stack(%r), %r, &Moment$info)", expr, color);
case BigIntType: case IntType: case ByteType: case NumType: {
CORD name = type_to_cord(t);
return CORD_asprintf("%r$as_text(stack(%r), %r, &%r$info)", name, expr, color, name);
@@ -2227,7 +2223,6 @@ CORD compile_none(type_t *t)
case SetType: return "NONE_TABLE";
case TextType: return "NONE_TEXT";
case CStringType: return "NULL";
- case MomentType: return "NONE_MOMENT";
case PointerType: return CORD_all("((", compile_type(t), ")NULL)");
case ClosureType: return "NONE_CLOSURE";
case NumType: return "nan(\"null\")";
@@ -2268,10 +2263,6 @@ CORD compile(env_t *env, ast_t *ast)
return compile_none(t);
}
case Bool: return Match(ast, Bool)->b ? "yes" : "no";
- case Moment: {
- auto moment = Match(ast, Moment)->moment;
- return CORD_asprintf("((Moment_t){.tv_sec=%ld, .tv_usec=%ld})", moment.tv_sec, moment.tv_usec);
- }
case Var: {
binding_t *b = get_binding(env, Match(ast, Var)->name);
if (b)
@@ -3748,14 +3739,6 @@ CORD compile(env_t *env, ast_t *ast)
env_t *module_env = Table$str_get(*env->imports, name);
return compile(module_env, WrapAST(ast, Var, f->field));
}
- case MomentType: {
- if (streq(f->field, "seconds")) {
- return CORD_all("I64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").tv_sec)");
- } else if (streq(f->field, "microseconds")) {
- return CORD_all("I64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").tv_usec)");
- }
- code_err(ast, "There is no '", f->field, "' field on Moments");
- }
default:
code_err(ast, "Field accesses are not supported on ", type_to_str(fielded_t), " values");
}
@@ -3853,7 +3836,7 @@ CORD compile_type_info(type_t *t)
else if (t == PATH_TYPE_TYPE) return "&PathType$info";
switch (t->tag) {
- case BoolType: case ByteType: case IntType: case BigIntType: case NumType: case CStringType: case MomentType:
+ case BoolType: case ByteType: case IntType: case BigIntType: case NumType: case CStringType:
return CORD_all("&", type_to_cord(t), "$info");
case TextType: {
auto text = Match(t, TextType);
@@ -4134,8 +4117,8 @@ CORD compile_function(env_t *env, CORD name_code, ast_t *ast, CORD *staticdefs)
assert(args);
OptionalInt64_t cache_size = Int64$parse(Text$from_str(Match(cache, Int)->str));
CORD pop_code = CORD_EMPTY;
- if (cache->tag == Int && !cache_size.is_none && cache_size.i > 0) {
- pop_code = CORD_all("if (cache.entries.length > ", CORD_asprintf("%ld", cache_size.i),
+ if (cache->tag == Int && !cache_size.is_none && cache_size.value > 0) {
+ pop_code = CORD_all("if (cache.entries.length > ", CORD_asprintf("%ld", cache_size.value),
") Table$remove(&cache, cache.entries.data + cache.entries.stride*RNG$int64(default_rng, 0, cache.entries.length-1), table_type);\n");
}
diff --git a/src/environment.c b/src/environment.c
index 97672f2d..77f91f10 100644
--- a/src/environment.c
+++ b/src/environment.c
@@ -288,41 +288,13 @@ env_t *global_env(void)
{"escape_int", "Int$value_as_text", "func(i:Int -> Pattern)"},
{"escape_text", "Pattern$escape_text", "func(text:Text -> Pattern)"},
)},
- {"Moment", Type(MomentType), "Moment_t", "Moment", TypedArray(ns_entry_t,
- // Used as a default for functions below:
- {"now", "Moment$now", "func(->Moment)"},
-
- {"after", "Moment$after", "func(moment:Moment,seconds,minutes,hours=0.0,days,weeks,months,years=0,timezone=none:Text -> Moment)"},
- {"date", "Moment$date", "func(moment:Moment,timezone=none:Text -> Text)"},
- {"day_of_month", "Moment$day_of_month", "func(moment:Moment,timezone=none:Text -> Int)"},
- {"day_of_week", "Moment$day_of_week", "func(moment:Moment,timezone=none:Text -> Int)"},
- {"day_of_year", "Moment$day_of_year", "func(moment:Moment,timezone=none:Text -> Int)"},
- {"format", "Moment$format", "func(moment:Moment,format=\"%Y-%m-%dT%H:%M:%S%z\",timezone=none:Text -> Text)"},
- {"from_unix_timestamp", "Moment$from_unix_timestamp", "func(timestamp:Int64 -> Moment)"},
- {"get_local_timezone", "Moment$get_local_timezone", "func(->Text)"},
- {"hour", "Moment$hour", "func(moment:Moment,timezone=none:Text -> Int)"},
- {"hours_till", "Moment$hours_till", "func(now,then:Moment -> Num)"},
- {"minute", "Moment$minute", "func(moment:Moment,timezone=none:Text -> Int)"},
- {"minutes_till", "Moment$minutes_till", "func(now,then:Moment -> Num)"},
- {"month", "Moment$month", "func(moment:Moment,timezone=none:Text -> Int)"},
- {"microsecond", "Moment$microsecond", "func(moment:Moment,timezone=none:Text -> Int)"},
- {"new", "Moment$new", "func(year,month,day:Int,hour,minute=0,second=0.0,timezone=none:Text -> Moment)"},
- {"parse", "Moment$parse", "func(text:Text, format=\"%Y-%m-%dT%H:%M:%S%z\" -> Moment?)"},
- {"relative", "Moment$relative", "func(moment:Moment,relative_to=Moment.now(),timezone=none:Text -> Text)"},
- {"second", "Moment$second", "func(moment:Moment,timezone=none:Text -> Int)"},
- {"seconds_till", "Moment$seconds_till", "func(now:Moment,then:Moment -> Num)"},
- {"set_local_timezone", "Moment$set_local_timezone", "func(timezone=none:Text)"},
- {"time", "Moment$time", "func(moment:Moment,seconds=no,am_pm=yes,timezone=none:Text -> Text)"},
- {"unix_timestamp", "Moment$unix_timestamp", "func(moment:Moment -> Int64)"},
- {"year", "Moment$year", "func(moment:Moment,timezone=none:Text -> Int)"},
- )},
{"PathType", PATH_TYPE_TYPE, "PathType_t", "PathType$info", TypedArray(ns_entry_t,
{"Relative", "((PathType_t){.$tag=PATH_RELATIVE})", "PathType"},
{"Absolute", "((PathType_t){.$tag=PATH_ABSOLUTE})", "PathType"},
{"Home", "((PathType_t){.$tag=PATH_HOME})", "PathType"},
)},
{"Path", PATH_TYPE, "Path_t", "Path$info", TypedArray(ns_entry_t,
- {"accessed", "Path$accessed", "func(path:Path, follow_symlinks=yes -> Moment?)"},
+ {"accessed", "Path$accessed", "func(path:Path, follow_symlinks=yes -> Int64?)"},
{"append", "Path$append", "func(path:Path, text:Text, permissions=Int32(0o644))"},
{"append_bytes", "Path$append_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"},
{"base_name", "Path$base_name", "func(path:Path -> Text)"},
@@ -330,7 +302,7 @@ env_t *global_env(void)
{"can_execute", "Path$can_execute", "func(path:Path -> Bool)"},
{"can_read", "Path$can_read", "func(path:Path -> Bool)"},
{"can_write", "Path$can_write", "func(path:Path -> Bool)"},
- {"changed", "Path$changed", "func(path:Path, follow_symlinks=yes -> Moment?)"},
+ {"changed", "Path$changed", "func(path:Path, follow_symlinks=yes -> Int64?)"},
{"child", "Path$with_component", "func(path:Path, child:Text -> Path)"},
{"children", "Path$children", "func(path:Path, include_hidden=no -> [Path])"},
{"concatenated_with", "Path$concat", "func(a,b:Path -> Path)"},
@@ -347,7 +319,7 @@ env_t *global_env(void)
{"is_pipe", "Path$is_pipe", "func(path:Path, follow_symlinks=yes -> Bool)"},
{"is_socket", "Path$is_socket", "func(path:Path, follow_symlinks=yes -> Bool)"},
{"is_symlink", "Path$is_symlink", "func(path:Path -> Bool)"},
- {"modified", "Path$modified", "func(path:Path, follow_symlinks=yes -> Moment?)"},
+ {"modified", "Path$modified", "func(path:Path, follow_symlinks=yes -> Int64?)"},
{"owner", "Path$owner", "func(path:Path, follow_symlinks=yes -> Text?)"},
{"parent", "Path$parent", "func(path:Path -> Path)"},
{"read", "Path$read", "func(path:Path -> Text?)"},
@@ -562,10 +534,6 @@ env_t *global_env(void)
{"Path$escape_path", "func(path:Path -> Path)"},
{"Int$value_as_text", "func(i:Int -> Path)"});
ADD_CONSTRUCTORS("CString", {"Text$as_c_string", "func(text:Text -> CString)"});
- ADD_CONSTRUCTORS("Moment",
- {"Moment$now", "func(-> Moment)"},
- {"Moment$new", "func(year,month,day:Int,hour,minute=0,second=0.0,timezone=none:Text -> Moment)"},
- {"Moment$from_unix_timestamp", "func(timestamp:Int64 -> Moment)"});
ADD_CONSTRUCTORS("RNG", {"RNG$new", "func(-> RNG)"});
ADD_CONSTRUCTORS("Thread", {"Thread$new", "func(fn:func() -> Thread)"});
#undef ADD_CONSTRUCTORS
@@ -591,7 +559,6 @@ env_t *global_env(void)
{"exit", "tomo_exit", "func(message=none:Text, code=Int32(1) -> Abort)"},
{"fail", "fail_text", "func(message:Text -> Abort)"},
{"sleep", "sleep_num", "func(seconds:Num)"},
- {"now", "Moment$now", "func(->Moment)"},
};
for (size_t i = 0; i < sizeof(global_vars)/sizeof(global_vars[0]); i++) {
@@ -756,7 +723,7 @@ env_t *get_namespace_by_type(env_t *env, type_t *t)
switch (t->tag) {
case ArrayType: return NULL;
case TableType: return NULL;
- case CStringType: case MomentType:
+ case CStringType:
case BoolType: case IntType: case BigIntType: case NumType: case ByteType: {
binding_t *b = get_binding(env, CORD_to_const_char_star(type_to_cord(t)));
assert(b);
diff --git a/src/parse.c b/src/parse.c
index 5e2894f0..5190d92c 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -120,7 +120,6 @@ static PARSER(parse_holding);
static PARSER(parse_if);
static PARSER(parse_inline_c);
static PARSER(parse_int);
-static PARSER(parse_moment);
static PARSER(parse_lambda);
static PARSER(parse_lang_def);
static PARSER(parse_mutexed);
@@ -498,59 +497,6 @@ PARSER(parse_int) {
return NewAST(ctx->file, start, pos, Int, .str=str);
}
-PARSER(parse_moment) {
- const char *start = pos;
- bool negative = match(&pos, "-");
- if (!isdigit(*pos)) return NULL;
-
- struct tm info = {.tm_isdst=-1};
- char *after = strptime(pos, "%Y-%m-%d", &info);
- if (!after) return NULL;
- if (negative) info.tm_year = -(info.tm_year + 1900) - 1900;
- pos = after;
- if (match(&pos, "T") || spaces(&pos) >= 1) {
- after = strptime(pos, "%H:%M", &info);
- if (after) {
- pos = after;
- after = strptime(pos, ":%S", &info);
- if (after) pos = after;
- // TODO parse nanoseconds
- }
- }
-
- const char *before_spaces = pos;
- spaces(&pos);
- Moment_t moment;
- if (match(&pos, "[")) {
- size_t tz_len = strcspn(pos, "\r\n]");
- const char *tz = heap_strf("%.*s", tz_len, pos);
- // TODO: check that tz is a valid timezone
- pos += tz_len;
- expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this moment timezone");
- const char *old_tz = getenv("TZ");
- setenv("TZ", tz, 1);
- tzset();
- moment = (Moment_t){.tv_sec=mktime(&info)};
- if (old_tz) setenv("TZ", old_tz, 1);
- else unsetenv("TZ");
- } else if (*pos == 'Z' || *pos == '-' || *pos == '+') {
- after = strptime(pos, "%z", &info);
- if (after) {
- pos = after;
- long offset = info.tm_gmtoff; // Need to cache this because mktime() mutates it to local timezone >:(
- time_t t = mktime(&info);
- moment = (Moment_t){.tv_sec=t + offset - info.tm_gmtoff};
- } else {
- moment = (Moment_t){.tv_sec=mktime(&info)};
- }
- } else {
- pos = before_spaces;
- moment = (Moment_t){.tv_sec=mktime(&info)};
- }
-
- return NewAST(ctx->file, start, pos, Moment, .moment=moment);
-}
-
type_ast_t *parse_table_type(parse_ctx_t *ctx, const char *pos) {
const char *start = pos;
if (!match(&pos, "{")) return NULL;
@@ -1590,7 +1536,6 @@ PARSER(parse_term_no_suffix) {
ast_t *term = NULL;
(void)(
false
- || (term=parse_moment(ctx, pos)) // Must come before num/int
|| (term=parse_none(ctx, pos))
|| (term=parse_num(ctx, pos)) // Must come before int
|| (term=parse_int(ctx, pos))
diff --git a/src/stdlib/datatypes.h b/src/stdlib/datatypes.h
index 40b4712a..22cee673 100644
--- a/src/stdlib/datatypes.h
+++ b/src/stdlib/datatypes.h
@@ -109,9 +109,6 @@ typedef struct {
} Path_t;
#define OptionalPath_t Path_t
-typedef struct timeval Moment_t;
-#define OptionalMoment_t Moment_t
-
typedef struct RNGState_t* RNG_t;
typedef struct MutexedData_s {
diff --git a/src/stdlib/integers.c b/src/stdlib/integers.c
index c5764d46..8086239d 100644
--- a/src/stdlib/integers.c
+++ b/src/stdlib/integers.c
@@ -597,24 +597,24 @@ public void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t
{ \
Optional##KindOfInt##_t i = info->current; \
if (!i.is_none) { \
- KindOfInt##_t next; bool overflow = __builtin_add_overflow(i.i, info->step, &next); \
- if (overflow || (!info->last.is_none && (info->step >= 0 ? next > info->last.i : next < info->last.i))) \
+ KindOfInt##_t next; bool overflow = __builtin_add_overflow(i.value, info->step, &next); \
+ if (overflow || (!info->last.is_none && (info->step >= 0 ? next > info->last.value : next < info->last.value))) \
info->current = (Optional##KindOfInt##_t){.is_none=true}; \
else \
- info->current = (Optional##KindOfInt##_t){.i=next}; \
+ info->current = (Optional##KindOfInt##_t){.value=next}; \
} \
return i; \
} \
public to_attr Closure_t KindOfInt ## $to(c_type first, c_type last, Optional ## KindOfInt ## _t step) { \
KindOfInt##Range_t *range = GC_MALLOC(sizeof(KindOfInt##Range_t)); \
- range->current = (Optional##KindOfInt##_t){.i=first}; \
- range->last = (Optional##KindOfInt##_t){.i=last}; \
- range->step = step.is_none ? (last >= first ? 1 : -1) : step.i; \
+ range->current = (Optional##KindOfInt##_t){.value=first}; \
+ range->last = (Optional##KindOfInt##_t){.value=last}; \
+ range->step = step.is_none ? (last >= first ? 1 : -1) : step.value; \
return (Closure_t){.fn=_next_##KindOfInt, .userdata=range}; \
} \
public to_attr Closure_t KindOfInt ## $onward(c_type first, c_type step) { \
KindOfInt##Range_t *range = GC_MALLOC(sizeof(KindOfInt##Range_t)); \
- range->current = (Optional##KindOfInt##_t){.i=first}; \
+ range->current = (Optional##KindOfInt##_t){.value=first}; \
range->last = (Optional##KindOfInt##_t){.is_none=true}; \
range->step = step; \
return (Closure_t){.fn=_next_##KindOfInt, .userdata=range}; \
@@ -628,7 +628,7 @@ public void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t
if (Int$compare_value(full_int, I(max_val)) > 0) { \
return (Optional ## KindOfInt ## _t){.is_none=true}; \
} \
- return (Optional ## KindOfInt ## _t){.i=KindOfInt##$from_int(full_int, true)}; \
+ return (Optional ## KindOfInt ## _t){.value=KindOfInt##$from_int(full_int, true)}; \
} \
public CONSTFUNC c_type KindOfInt ## $gcd(c_type x, c_type y) { \
if (x == 0 || y == 0) return 0; \
diff --git a/src/stdlib/integers.h b/src/stdlib/integers.h
index 779bee1f..a057327b 100644
--- a/src/stdlib/integers.h
+++ b/src/stdlib/integers.h
@@ -20,7 +20,7 @@
#define DEFINE_INT_TYPE(c_type, type_name) \
typedef struct { \
- c_type i; \
+ c_type value; \
bool is_none:1; \
} Optional ## type_name ## _t; \
Text_t type_name ## $as_text(const void *i, bool colorize, const TypeInfo_t *type); \
diff --git a/src/stdlib/moments.c b/src/stdlib/moments.c
deleted file mode 100644
index d1abdfbe..00000000
--- a/src/stdlib/moments.c
+++ /dev/null
@@ -1,323 +0,0 @@
-// Moment methods/type info
-#include <ctype.h>
-#include <gc.h>
-#include <err.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "datatypes.h"
-#include "math.h"
-#include "moments.h"
-#include "optionals.h"
-#include "patterns.h"
-#include "stdlib.h"
-#include "text.h"
-#include "util.h"
-
-static OptionalText_t _local_timezone = NONE_TEXT;
-
-#define WITH_TIMEZONE(tz, body) ({ if (tz.length >= 0) { \
- OptionalText_t old_timezone = _local_timezone; \
- Moment$set_local_timezone(tz); \
- body; \
- Moment$set_local_timezone(old_timezone); \
- } else { \
- body; \
- }})
-
-public Text_t Moment$as_text(const void *moment, bool colorize, const TypeInfo_t*)
-{
- if (!moment)
- return Text("Moment");
-
- struct tm info;
- struct tm *final_info = localtime_r(&((Moment_t*)moment)->tv_sec, &info);
- static char buf[256];
- size_t len = strftime(buf, sizeof(buf), "%c %Z", final_info);
- Text_t text = Text$format("%.*s", (int)len, buf);
- if (colorize)
- text = Text$concat(Text("\x1b[36m"), text, Text("\x1b[m"));
- return text;
-}
-
-PUREFUNC public int32_t Moment$compare(const void *va, const void *vb, const TypeInfo_t*)
-{
- Moment_t *a = (Moment_t*)va, *b = (Moment_t*)vb;
- if (a->tv_sec != b->tv_sec)
- return (a->tv_sec > b->tv_sec) - (a->tv_sec < b->tv_sec);
- return (a->tv_usec > b->tv_usec) - (a->tv_usec < b->tv_usec);
-}
-
-CONSTFUNC public bool Moment$is_none(const void *m, const TypeInfo_t*)
-{
- return ((Moment_t*)m)->tv_usec < 0;
-}
-
-public Moment_t Moment$now(void)
-{
- struct timespec ts;
- if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
- fail("Couldn't get the time!");
- return (Moment_t){.tv_sec=ts.tv_sec, .tv_usec=ts.tv_nsec/1000};
-}
-
-public Moment_t Moment$new(Int_t year, Int_t month, Int_t day, Int_t hour, Int_t minute, double second, OptionalText_t tz)
-{
- struct tm info = {
- .tm_min=Int32$from_int(minute, false),
- .tm_hour=Int32$from_int(hour, false),
- .tm_mday=Int32$from_int(day, false),
- .tm_mon=Int32$from_int(month, false) - 1,
- .tm_year=Int32$from_int(year, false) - 1900,
- .tm_isdst=-1,
- };
-
- time_t t;
- WITH_TIMEZONE(tz, t = mktime(&info));
- return (Moment_t){.tv_sec=t + (time_t)second, .tv_usec=(suseconds_t)(fmod(second, 1.0) * 1e9)};
-}
-
-public Moment_t Moment$after(Moment_t moment, double seconds, double minutes, double hours, Int_t days, Int_t weeks, Int_t months, Int_t years, OptionalText_t tz)
-{
- double offset = seconds + 60.*minutes + 3600.*hours;
- moment.tv_sec += (time_t)offset;
-
- struct tm info = {};
- WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
-
- info.tm_mday += Int32$from_int(days, false) + 7*Int32$from_int(weeks, false);
- info.tm_mon += Int32$from_int(months, false);
- info.tm_year += Int32$from_int(years, false);
-
- time_t t = mktime(&info);
- return (Moment_t){
- .tv_sec=t,
- .tv_usec=moment.tv_usec + (suseconds_t)(fmod(offset, 1.0) * 1e9),
- };
-}
-
-CONSTFUNC public double Moment$seconds_till(Moment_t now, Moment_t then)
-{
- return (double)(then.tv_sec - now.tv_sec) + 1e-9*(double)(then.tv_usec - now.tv_usec);
-}
-
-CONSTFUNC public double Moment$minutes_till(Moment_t now, Moment_t then)
-{
- return Moment$seconds_till(now, then)/60.;
-}
-
-CONSTFUNC public double Moment$hours_till(Moment_t now, Moment_t then)
-{
- return Moment$seconds_till(now, then)/3600.;
-}
-
-public void Moment$get(
- Moment_t moment, Int_t *year, Int_t *month, Int_t *day, Int_t *hour, Int_t *minute, Int_t *second,
- Int_t *nanosecond, Int_t *weekday, OptionalText_t tz)
-{
- struct tm info = {};
- WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
-
- if (year) *year = I(info.tm_year + 1900);
- if (month) *month = I(info.tm_mon + 1);
- if (day) *day = I(info.tm_mday);
- if (hour) *hour = I(info.tm_hour);
- if (minute) *minute = I(info.tm_min);
- if (second) *second = I(info.tm_sec);
- if (nanosecond) *nanosecond = I(moment.tv_usec);
- if (weekday) *weekday = I(info.tm_wday + 1);
-}
-
-public Int_t Moment$year(Moment_t moment, OptionalText_t tz)
-{
- struct tm info = {};
- WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
- return I(info.tm_year + 1900);
-}
-
-public Int_t Moment$month(Moment_t moment, OptionalText_t tz)
-{
- struct tm info = {};
- WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
- return I(info.tm_mon + 1);
-}
-
-public Int_t Moment$day_of_week(Moment_t moment, OptionalText_t tz)
-{
- struct tm info = {};
- WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
- return I(info.tm_wday + 1);
-}
-
-public Int_t Moment$day_of_month(Moment_t moment, OptionalText_t tz)
-{
- struct tm info = {};
- WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
- return I(info.tm_mday);
-}
-
-public Int_t Moment$day_of_year(Moment_t moment, OptionalText_t tz)
-{
- struct tm info = {};
- WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
- return I(info.tm_yday);
-}
-
-public Int_t Moment$hour(Moment_t moment, OptionalText_t tz)
-{
- struct tm info = {};
- WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
- return I(info.tm_hour);
-}
-
-public Int_t Moment$minute(Moment_t moment, OptionalText_t tz)
-{
- struct tm info = {};
- WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
- return I(info.tm_min);
-}
-
-public Int_t Moment$second(Moment_t moment, OptionalText_t tz)
-{
- struct tm info = {};
- WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
- return I(info.tm_sec);
-}
-
-public Int_t Moment$microsecond(Moment_t moment, OptionalText_t tz)
-{
- (void)tz;
- return I(moment.tv_usec);
-}
-
-public Text_t Moment$format(Moment_t moment, Text_t fmt, OptionalText_t tz)
-{
- struct tm info;
- WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
- static char buf[256];
- size_t len = strftime(buf, sizeof(buf), Text$as_c_string(fmt), &info);
- return Text$format("%.*s", (int)len, buf);
-}
-
-public Text_t Moment$date(Moment_t moment, OptionalText_t tz)
-{
- return Moment$format(moment, Text("%F"), tz);
-}
-
-public Text_t Moment$time(Moment_t moment, bool seconds, bool am_pm, OptionalText_t tz)
-{
- Text_t text;
- if (seconds)
- text = Moment$format(moment, am_pm ? Text("%l:%M:%S%P") : Text("%T"), tz);
- else
- text = Moment$format(moment, am_pm ? Text("%l:%M%P") : Text("%H:%M"), tz);
- return Text$trim(text, Pattern(" "), true, true);
-}
-
-public OptionalMoment_t Moment$parse(Text_t text, Text_t format)
-{
- struct tm info = {.tm_isdst=-1};
- const char *str = Text$as_c_string(text);
- const char *fmt = Text$as_c_string(format);
- if (strstr(fmt, "%Z"))
- fail("The %Z specifier is not supported for time parsing!");
-
- char *invalid = strptime(str, fmt, &info);
- if (!invalid || invalid[0] != '\0')
- return NONE_MOMENT;
-
- long offset = info.tm_gmtoff; // Need to cache this because mktime() mutates it to local tz >:(
- time_t t = mktime(&info);
- return (Moment_t){.tv_sec=t + offset - info.tm_gmtoff};
-}
-
-static INLINE Text_t num_format(long n, const char *unit)
-{
- if (n == 0)
- return Text("now");
- return Text$format((n == 1 || n == -1) ? "%ld %s %s" : "%ld %ss %s", n < 0 ? -n : n, unit, n < 0 ? "ago" : "later");
-}
-
-public Text_t Moment$relative(Moment_t moment, Moment_t relative_to, OptionalText_t tz)
-{
- struct tm info = {};
- struct tm relative_info = {};
- WITH_TIMEZONE(tz, {
- localtime_r(&moment.tv_sec, &info);
- localtime_r(&relative_to.tv_sec, &relative_info);
- });
-
- double second_diff = Moment$seconds_till(relative_to, moment);
- if (info.tm_year != relative_info.tm_year && fabs(second_diff) > 365.*24.*60.*60.)
- return num_format((long)info.tm_year - (long)relative_info.tm_year, "year");
- else if (info.tm_mon != relative_info.tm_mon && fabs(second_diff) > 31.*24.*60.*60.)
- return num_format(12*((long)info.tm_year - (long)relative_info.tm_year) + (long)info.tm_mon - (long)relative_info.tm_mon, "month");
- else if (info.tm_yday != relative_info.tm_yday && fabs(second_diff) > 24.*60.*60.)
- return num_format(round(second_diff/(24.*60.*60.)), "day");
- else if (info.tm_hour != relative_info.tm_hour && fabs(second_diff) > 60.*60.)
- return num_format(round(second_diff/(60.*60.)), "hour");
- else if (info.tm_min != relative_info.tm_min && fabs(second_diff) > 60.)
- return num_format(round(second_diff/(60.)), "minute");
- else {
- if (fabs(second_diff) < 1e-6)
- return num_format((long)(second_diff*1e9), "nanosecond");
- else if (fabs(second_diff) < 1e-3)
- return num_format((long)(second_diff*1e6), "microsecond");
- else if (fabs(second_diff) < 1.0)
- return num_format((long)(second_diff*1e3), "millisecond");
- else
- return num_format((long)(second_diff), "second");
- }
-}
-
-CONSTFUNC public Int64_t Moment$unix_timestamp(Moment_t moment)
-{
- return (Int64_t)moment.tv_sec;
-}
-
-CONSTFUNC public Moment_t Moment$from_unix_timestamp(Int64_t timestamp)
-{
- return (Moment_t){.tv_sec=(time_t)timestamp};
-}
-
-public void Moment$set_local_timezone(OptionalText_t tz)
-{
- if (tz.length >= 0) {
- setenv("TZ", Text$as_c_string(tz), 1);
- } else {
- unsetenv("TZ");
- }
- _local_timezone = tz;
- tzset();
-}
-
-public Text_t Moment$get_local_timezone(void)
-{
- if (_local_timezone.length < 0) {
- static char buf[PATH_MAX];
- ssize_t len = readlink("/etc/localtime", buf, sizeof(buf));
- if (len < 0)
- fail("Could not get local tz!");
-
- char *zoneinfo = strstr(buf, "/zoneinfo/");
- if (zoneinfo)
- _local_timezone = Text$from_str(zoneinfo + strlen("/zoneinfo/"));
- else
- fail("Could not resolve local tz!");
- }
- return _local_timezone;
-}
-
-public const TypeInfo_t Moment$info = {
- .size=sizeof(Moment_t),
- .align=__alignof__(Moment_t),
- .metamethods={
- .as_text=Moment$as_text,
- .compare=Moment$compare,
- .is_none=Moment$is_none,
- },
-};
-
-// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/src/stdlib/moments.h b/src/stdlib/moments.h
deleted file mode 100644
index ff6d4119..00000000
--- a/src/stdlib/moments.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-// Moment objects
-
-#include <stdint.h>
-
-#include "datatypes.h"
-#include "integers.h"
-#include "optionals.h"
-#include "types.h"
-#include "util.h"
-
-Text_t Moment$as_text(const void *moment, bool colorize, const TypeInfo_t *type);
-PUREFUNC int32_t Moment$compare(const void *a, const void *b, const TypeInfo_t *type);
-CONSTFUNC public bool Moment$is_none(const void *m, const TypeInfo_t*);
-Moment_t Moment$now(void);
-Moment_t Moment$new(Int_t year, Int_t month, Int_t day, Int_t hour, Int_t minute, double second, OptionalText_t timezone);
-Moment_t Moment$after(Moment_t moment, double seconds, double minutes, double hours, Int_t days, Int_t weeks, Int_t months, Int_t years, OptionalText_t timezone);
-CONSTFUNC double Moment$seconds_till(Moment_t now, Moment_t then);
-CONSTFUNC double Moment$minutes_till(Moment_t now, Moment_t then);
-CONSTFUNC double Moment$hours_till(Moment_t now, Moment_t then);
-Int_t Moment$year(Moment_t moment, OptionalText_t timezone);
-Int_t Moment$month(Moment_t moment, OptionalText_t timezone);
-Int_t Moment$day_of_week(Moment_t moment, OptionalText_t timezone);
-Int_t Moment$day_of_month(Moment_t moment, OptionalText_t timezone);
-Int_t Moment$day_of_year(Moment_t moment, OptionalText_t timezone);
-Int_t Moment$hour(Moment_t moment, OptionalText_t timezone);
-Int_t Moment$minute(Moment_t moment, OptionalText_t timezone);
-Int_t Moment$second(Moment_t moment, OptionalText_t timezone);
-Int_t Moment$microsecond(Moment_t moment, OptionalText_t timezone);
-Text_t Moment$format(Moment_t moment, Text_t fmt, OptionalText_t timezone);
-Text_t Moment$date(Moment_t moment, OptionalText_t timezone);
-Text_t Moment$time(Moment_t moment, bool seconds, bool am_pm, OptionalText_t timezone);
-OptionalMoment_t Moment$parse(Text_t text, Text_t format);
-Text_t Moment$relative(Moment_t moment, Moment_t relative_to, OptionalText_t timezone);
-CONSTFUNC Int64_t Moment$unix_timestamp(Moment_t moment);
-CONSTFUNC Moment_t Moment$from_unix_timestamp(Int64_t timestamp);
-void Moment$set_local_timezone(OptionalText_t timezone);
-Text_t Moment$get_local_timezone(void);
-
-extern const TypeInfo_t Moment$info;
-
-// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
-
diff --git a/src/stdlib/optionals.c b/src/stdlib/optionals.c
index 462b2df2..db2c477f 100644
--- a/src/stdlib/optionals.c
+++ b/src/stdlib/optionals.c
@@ -7,7 +7,6 @@
#include "datatypes.h"
#include "integers.h"
#include "metamethods.h"
-#include "moments.h"
#include "nums.h"
#include "patterns.h"
#include "text.h"
diff --git a/src/stdlib/optionals.h b/src/stdlib/optionals.h
index 94e4d900..8d25c5f1 100644
--- a/src/stdlib/optionals.h
+++ b/src/stdlib/optionals.h
@@ -16,7 +16,6 @@
#define NONE_TABLE ((OptionalTable_t){.entries.length=-1})
#define NONE_CLOSURE ((OptionalClosure_t){.fn=NULL})
#define NONE_TEXT ((OptionalText_t){.length=-1})
-#define NONE_MOMENT ((OptionalMoment_t){.tv_usec=-1})
#define NONE_PATH ((Path_t){.type=PATH_NONE})
PUREFUNC bool is_null(const void *obj, const TypeInfo_t *non_optional_type);
diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c
index c7b560b9..05575620 100644
--- a/src/stdlib/paths.c
+++ b/src/stdlib/paths.c
@@ -255,28 +255,28 @@ public bool Path$can_execute(Path_t path)
#endif
}
-public OptionalMoment_t Path$modified(Path_t path, bool follow_symlinks)
+public OptionalInt64_t Path$modified(Path_t path, bool follow_symlinks)
{
struct stat sb;
int status = path_stat(path, follow_symlinks, &sb);
- if (status != 0) return NONE_MOMENT;
- return (Moment_t){.tv_sec=sb.st_mtime};
+ if (status != 0) return NONE_INT64;
+ return (OptionalInt64_t){.value=(int64_t)sb.st_mtime};
}
-public OptionalMoment_t Path$accessed(Path_t path, bool follow_symlinks)
+public OptionalInt64_t Path$accessed(Path_t path, bool follow_symlinks)
{
struct stat sb;
int status = path_stat(path, follow_symlinks, &sb);
- if (status != 0) return NONE_MOMENT;
- return (Moment_t){.tv_sec=sb.st_atime};
+ if (status != 0) return NONE_INT64;
+ return (OptionalInt64_t){.value=(int64_t)sb.st_atime};
}
-public OptionalMoment_t Path$changed(Path_t path, bool follow_symlinks)
+public OptionalInt64_t Path$changed(Path_t path, bool follow_symlinks)
{
struct stat sb;
int status = path_stat(path, follow_symlinks, &sb);
- if (status != 0) return NONE_MOMENT;
- return (Moment_t){.tv_sec=sb.st_ctime};
+ if (status != 0) return NONE_INT64;
+ return (OptionalInt64_t){.value=(int64_t)sb.st_ctime};
}
static void _write(Path_t path, Array_t bytes, int mode, int permissions)
diff --git a/src/stdlib/paths.h b/src/stdlib/paths.h
index 02afc494..9be81bdf 100644
--- a/src/stdlib/paths.h
+++ b/src/stdlib/paths.h
@@ -28,9 +28,9 @@ bool Path$is_symlink(Path_t path);
bool Path$can_read(Path_t path);
bool Path$can_write(Path_t path);
bool Path$can_execute(Path_t path);
-OptionalMoment_t Path$modified(Path_t path, bool follow_symlinks);
-OptionalMoment_t Path$accessed(Path_t path, bool follow_symlinks);
-OptionalMoment_t Path$changed(Path_t path, bool follow_symlinks);
+OptionalInt64_t Path$modified(Path_t path, bool follow_symlinks);
+OptionalInt64_t Path$accessed(Path_t path, bool follow_symlinks);
+OptionalInt64_t Path$changed(Path_t path, bool follow_symlinks);
void Path$write(Path_t path, Text_t text, int permissions);
void Path$write_bytes(Path_t path, Array_t bytes, int permissions);
void Path$append(Path_t path, Text_t text, int permissions);
diff --git a/src/stdlib/tomo.h b/src/stdlib/tomo.h
index 567e5d14..16e5ec57 100644
--- a/src/stdlib/tomo.h
+++ b/src/stdlib/tomo.h
@@ -17,7 +17,6 @@
#include "integers.h"
#include "memory.h"
#include "metamethods.h"
-#include "moments.h"
#include "mutexeddata.h"
#include "nums.h"
#include "optionals.h"
diff --git a/src/typecheck.c b/src/typecheck.c
index ba03e968..5da84442 100644
--- a/src/typecheck.c
+++ b/src/typecheck.c
@@ -852,7 +852,7 @@ type_t *get_type(env_t *env, ast_t *ast)
if (constructor)
return t;
else if (t->tag == StructType || t->tag == IntType || t->tag == BigIntType || t->tag == NumType
- || t->tag == ByteType || t->tag == TextType || t->tag == CStringType || t->tag == MomentType)
+ || t->tag == ByteType || t->tag == TextType || t->tag == CStringType)
return t; // Constructor
code_err(call->fn, "This is not a type that has a constructor");
}
@@ -1408,7 +1408,6 @@ type_t *get_type(env_t *env, ast_t *ast)
type_ast_t *type_ast = inline_code->type_ast;
return type_ast ? parse_type_ast(env, type_ast) : Type(VoidType);
}
- case Moment: return Type(MomentType);
case Unknown: code_err(ast, "I can't figure out the type of: ", ast_to_str(ast));
case Deserialize: return parse_type_ast(env, Match(ast, Deserialize)->type);
}
diff --git a/src/types.c b/src/types.c
index 8b9289cb..07b6ced0 100644
--- a/src/types.c
+++ b/src/types.c
@@ -29,7 +29,6 @@ CORD type_to_cord(type_t *t) {
case BoolType: return "Bool";
case ByteType: return "Byte";
case CStringType: return "CString";
- case MomentType: return "Moment";
case TextType: return Match(t, TextType)->lang ? Match(t, TextType)->lang : "Text";
case BigIntType: return "Int";
case IntType: return CORD_asprintf("Int%d", Match(t, IntType)->bits);
@@ -497,7 +496,6 @@ PUREFUNC size_t type_size(type_t *t)
case BoolType: return sizeof(bool);
case ByteType: return sizeof(uint8_t);
case CStringType: return sizeof(char*);
- case MomentType: return sizeof(Moment_t);
case BigIntType: return sizeof(Int_t);
case IntType: {
switch (Match(t, IntType)->bits) {
@@ -590,7 +588,6 @@ PUREFUNC size_t type_align(type_t *t)
case BoolType: return __alignof__(bool);
case ByteType: return __alignof__(uint8_t);
case CStringType: return __alignof__(char*);
- case MomentType: return __alignof__(Moment_t);
case BigIntType: return __alignof__(Int_t);
case IntType: {
switch (Match(t, IntType)->bits) {
@@ -704,11 +701,6 @@ type_t *get_field_type(type_t *t, const char *field_name)
if (streq(field_name, "length")) return INT_TYPE;
return NULL;
}
- case MomentType: {
- if (streq(field_name, "seconds")) return Type(IntType, .bits=TYPE_IBITS64);
- else if (streq(field_name, "microseconds")) return Type(IntType, .bits=TYPE_IBITS64);
- return NULL;
- }
default: return NULL;
}
}
diff --git a/src/types.h b/src/types.h
index eec3fac2..ae34c217 100644
--- a/src/types.h
+++ b/src/types.h
@@ -46,7 +46,6 @@ struct type_s {
IntType,
NumType,
CStringType,
- MomentType,
TextType,
ArrayType,
SetType,
@@ -76,7 +75,7 @@ struct type_s {
struct {
enum { TYPE_NBITS32=32, TYPE_NBITS64=64 } bits;
} NumType;
- struct {} CStringType, MomentType;
+ struct {} CStringType;
struct {
const char *lang;
struct env_s *env;
diff --git a/test/moments.tm b/test/moments.tm
deleted file mode 100644
index 657c1522..00000000
--- a/test/moments.tm
+++ /dev/null
@@ -1,49 +0,0 @@
-
-func main():
- >> 2024-1-1 12:00[America/New_York] == 2024-1-1T09:00[America/Los_Angeles]
- = yes
- >> 2024-1-1 12:00[America/New_York] == Moment(2024, 1, 1, hour=9, timezone="America/Los_Angeles")
- = yes
-
- >> t := 2024-1-2 13:45[America/New_York]
- >> t:after(days=40) == 2024-2-11T13:45:00[America/New_York]
- = yes
- >> t:date(timezone="America/New_York")
- = "2024-01-02"
-
- >> t:time(timezone="America/New_York")
- = "1:45pm"
-
- >> t:time(am_pm=no, timezone="America/New_York")
- = "13:45"
-
- >> t:relative(relative_to=t:after(minutes=65))
- = "1 hour ago"
-
- >> t:seconds_till(t:after(minutes=2))
- = 120.
-
- >> t:minutes_till(t:after(minutes=2))
- = 2.
-
- >> t:hours_till(t:after(minutes=60))
- = 1.
-
- >> t:day_of_week() # 1 = Sun, 2 = Mon, 3 = Tue
- = 3
-
- >> t:format("%A")
- = "Tuesday"
-
- >> t:unix_timestamp()
- = Int64(1704221100)
- >> t == Moment.from_unix_timestamp(1704221100)
- = yes
-
- >> t < t:after(minutes=1)
- = yes
-
- >> t < t:after(seconds=0.1)
- = yes
-
- >> now()
diff --git a/test/serialization.tm b/test/serialization.tm
index 9afb4bcf..6d08552f 100644
--- a/test/serialization.tm
+++ b/test/serialization.tm
@@ -5,12 +5,6 @@ enum MyEnum(Zero, One(x:Int), Two(x:Num, y:Text))
func main():
do:
- >> obj := now()
- >> bytes := obj:serialized()
- >> deserialize(bytes -> Moment) == obj
- = yes
-
- do:
>> obj := Int64(123)
>> bytes := obj:serialized()
>> deserialize(bytes -> Int64) == obj