diff --git a/Makefile b/Makefile index 3305578..d76990d 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 4bfc2e1..0034ab8 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 5a10f10..0000000 --- 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 cb4bb05..234ad44 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 0000000..909c24e --- /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 0000000..fd8fd4f --- /dev/null +++ b/examples/time/time_defs.h @@ -0,0 +1,30 @@ +#pragma once +#include +#include + +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 d04fa43..00f03a6 100644 --- a/src/ast.c +++ b/src/ast.c @@ -163,7 +163,6 @@ CORD ast_to_xml(ast_t *ast) T(FieldAccess, "%r", data.field, ast_to_xml(data.fielded)) T(Optional, "%r", ast_to_xml(data.value)) T(NonOptional, "%r", ast_to_xml(data.value)) - T(Moment, "") T(DocTest, "%r%r", optional_tagged("expression", data.expr), optional_tagged("expected", data.expected)) T(Use, "%r%r", optional_tagged("var", data.var), xml_escape(data.path)) T(InlineCCode, "%r", xml_escape(data.code)) diff --git a/src/ast.h b/src/ast.h index aa21833..2260436 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, @@ -318,9 +317,6 @@ struct ast_s { struct { ast_t *value; } Optional, NonOptional; - struct { - Moment_t moment; - } Moment; struct { ast_t *expr, *expected; bool skip_source:1; diff --git a/src/compile.c b/src/compile.c index 18a1e9a..9073de5 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 97672f2..77f91f1 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 5e2894f..5190d92 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 40b4712..22cee67 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 c5764d4..8086239 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 779bee1..a057327 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 d1abdfb..0000000 --- a/src/stdlib/moments.c +++ /dev/null @@ -1,323 +0,0 @@ -// Moment methods/type info -#include -#include -#include -#include -#include -#include -#include - -#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 ff6d411..0000000 --- a/src/stdlib/moments.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -// Moment objects - -#include - -#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 462b2df..db2c477 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 94e4d90..8d25c5f 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 c7b560b..0557562 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 02afc49..9be81bd 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 567e5d1..16e5ec5 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 ba03e96..5da8444 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 8b9289c..07b6ced 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 eec3fac..ae34c21 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 657c152..0000000 --- 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 9afb4bc..6d08552 100644 --- a/test/serialization.tm +++ b/test/serialization.tm @@ -4,12 +4,6 @@ struct Foo(name:Text, next=none:@Foo) 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()