Rename datetime -> moment

This commit is contained in:
Bruce Hill 2024-11-17 14:49:03 -05:00
parent c1ae79ac3c
commit 4231789b71
20 changed files with 311 additions and 310 deletions

View File

@ -32,7 +32,7 @@ LDLIBS=-lgc -lcord -lm -lunistring -lgmp -ldl
BUILTIN_OBJS=stdlib/siphash.o stdlib/arrays.o stdlib/bools.o stdlib/bytes.o stdlib/channels.o stdlib/nums.o stdlib/integers.o \
stdlib/pointers.o stdlib/memory.o stdlib/text.o stdlib/threads.o stdlib/c_strings.o stdlib/tables.o \
stdlib/types.o stdlib/util.o stdlib/files.o stdlib/ranges.o stdlib/shell.o stdlib/paths.o stdlib/rng.o \
stdlib/optionals.o stdlib/patterns.o stdlib/metamethods.o stdlib/functiontype.o stdlib/stdlib.o stdlib/datetime.o
stdlib/optionals.o stdlib/patterns.o stdlib/metamethods.o stdlib/functiontype.o stdlib/stdlib.o stdlib/moments.o
TESTS=$(patsubst %.tm,%.tm.testresult,$(wildcard test/*.tm))
all: libtomo.so tomo

6
ast.h
View File

@ -141,7 +141,7 @@ typedef enum {
Extern,
StructDef, EnumDef, LangDef,
Index, FieldAccess, Optional, NonOptional,
DateTime,
Moment,
DocTest,
Use,
InlineCCode,
@ -309,8 +309,8 @@ struct ast_s {
ast_t *value;
} Optional, NonOptional;
struct {
DateTime_t dt;
} DateTime;
Moment_t moment;
} Moment;
struct {
ast_t *expr;
const char *output;

View File

@ -227,7 +227,7 @@ CORD compile_type(type_t *t)
case BoolType: return "Bool_t";
case ByteType: return "Byte_t";
case CStringType: return "char*";
case DateTimeType: return "DateTime_t";
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);
@ -276,7 +276,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 DateTimeType:
case ArrayType: case TableType: case SetType: case MomentType:
return CORD_all("Optional", compile_type(nonnull));
case StructType: {
if (nonnull == THREAD_TYPE)
@ -414,7 +414,7 @@ static CORD check_null(type_t *t, CORD value)
return CORD_all("(", value, ").is_null");
else if (t->tag == EnumType)
return CORD_all("((", value, ").tag == 0)");
else if (t->tag == DateTimeType)
else if (t->tag == MomentType)
return CORD_all("((", value, ").tv_usec < 0)");
errx(1, "Optional check not implemented for: %T", t);
}
@ -1511,7 +1511,7 @@ CORD expr_as_text(env_t *env, 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 DateTimeType: return CORD_asprintf("DateTime$as_text(stack(%r), %r, &DateTime$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);
@ -1906,7 +1906,7 @@ CORD compile_null(type_t *t)
case ChannelType: return "NULL";
case TextType: return "NULL_TEXT";
case CStringType: return "NULL";
case DateTimeType: return "NULL_DATETIME";
case MomentType: return "NULL_MOMENT";
case PointerType: return CORD_all("((", compile_type(t), ")NULL)");
case ClosureType: return "NULL_CLOSURE";
case NumType: return "nan(\"null\")";
@ -1960,9 +1960,9 @@ CORD compile(env_t *env, ast_t *ast)
return compile_null(t);
}
case Bool: return Match(ast, Bool)->b ? "yes" : "no";
case DateTime: {
auto dt = Match(ast, DateTime)->dt;
return CORD_asprintf("((DateTime_t){.tv_sec=%ld, .tv_usec=%ld})", dt.tv_sec, dt.tv_usec);
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);
@ -3114,8 +3114,8 @@ CORD compile(env_t *env, ast_t *ast)
return compile_string_literal(Match(Match(call->args->value, TextJoin)->children->ast, TextLiteral)->cord);
type_t *actual = get_type(env, call->args->value);
return CORD_all("Text$as_c_string(", expr_as_text(env, compile(env, call->args->value), actual, "no"), ")");
} else if (t->tag == DateTimeType) {
// DateTime constructor:
} else if (t->tag == MomentType) {
// Moment constructor:
binding_t *new_binding = get_binding(Match(fn_t, TypeInfoType)->env, "new");
CORD arg_code = compile_arguments(env, ast, Match(new_binding->type, FunctionType)->args, call->args);
return CORD_all(new_binding->code, "(", arg_code, ")");
@ -3590,7 +3590,7 @@ CORD compile_type_info(env_t *env, type_t *t)
else if (t == RNG_TYPE) return "&RNG$info";
switch (t->tag) {
case BoolType: case ByteType: case IntType: case BigIntType: case NumType: case CStringType: case DateTimeType:
case BoolType: case ByteType: case IntType: case BigIntType: case NumType: case CStringType: case MomentType:
return CORD_all("&", type_to_cord(t), "$info");
case TextType: {
auto text = Match(t, TextType);

View File

@ -22,7 +22,7 @@ Information about Tomo's built-in types can be found here:
- [Booleans](booleans.md)
- [Bytes](bytes.md)
- [Channels](channels.md)
- [DateTime](datetime.md)
- [Moment](moments.md)
- [Enums](enums.md)
- [Floating point numbers](nums.md)
- [Integer Ranges](ranges.md)
@ -177,11 +177,11 @@ fail("Oh no!")
### `now`
**Description:**
Gets the current time. This is an alias for `DateTime.now()`.
Gets the current time. This is an alias for `Moment.now()`.
**Signature:**
```tomo
func now(->DateTime)
func now(->Moment)
```
**Parameters:**
@ -189,7 +189,7 @@ func now(->DateTime)
None.
**Returns:**
The current moment as a DateTime.
The current moment as a Moment.
**Example:**
```tomo

View File

@ -1,23 +1,24 @@
# DateTime
# Moments
Tomo has a builtin datatype for representing a specific single point in time:
`DateTime`. A DateTime 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`). DateTime values do not represent calendar
dates or clock times, they represent an exact moment in time, such as the
moment when a file was last modified on the filesystem or the current moment
(`DateTime.now()`).
`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
moment in 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 `DateTime` code uses standard libraries and
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
DateTime literals can be specified using [ISO
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
@ -35,16 +36,16 @@ timezone.
## Time Zones
Because humans are not able to easily understand UNIX timestamps, the default
textual representation of `DateTime` objects uses the current locale's
preferred representation of the DateTime in the current time zone:
textual representation of `Moment` objects uses the current locale's
preferred representation of the Moment in the current time zone:
```tomo
>> DateTime.now()
>> 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 datetimes using the local time zone and daylight
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,
@ -60,12 +61,12 @@ 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`.
## DateTime Methods
## Moment Methods
### `after`
**Description:**
Returns a DateTime that occurs after the specified time differences. Time
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
@ -79,28 +80,28 @@ calculated.
**Signature:**
```tomo
func after(datetime: DateTime, 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 -> DateTime)
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)
```
**Parameters:**
- `datetime`: The datetime used as a starting point.
- `seconds` (optional): An amount of seconds to offset the datetime (default: 0).
- `minutes` (optional): An amount of minutes to offset the datetime (default: 0).
- `hours` (optional): An amount of hours to offset the datetime (default: 0).
- `days` (optional): An amount of days to offset the datetime (default: 0).
- `weeks` (optional): An amount of weeks to offset the datetime (default: 0).
- `months` (optional): An amount of months to offset the datetime (default: 0).
- `years` (optional): An amount of years to offset the datetime (default: 0).
- `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 `DateTime` offset by the given amount.
A new `Moment` offset by the given amount.
**Example:**
```tomo
>> DateTime(2024, 9, 29, hour=19):after(days=1, minutes=30)
>> Moment(2024, 9, 29, hour=19):after(days=1, minutes=30)
= Mon Sep 30 19:30:00 2024 EDT
```
@ -109,17 +110,17 @@ A new `DateTime` offset by the given amount.
### `date`
**Description:**
Return a text representation of the datetime using the `"%F"` format
Return a text representation of the moment using the `"%F"` format
specifier, which gives the date in `YYYY-MM-DD` form.
**Signature:**
```tomo
func date(datetime: DateTime, timezone : Text? = !Text -> Text)
func date(moment: Moment, timezone : Text? = !Text -> Text)
```
**Parameters:**
- `datetime`: The datetime to get the date from.
- `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:**
@ -127,7 +128,7 @@ The date in `YYYY-MM-DD` format.
**Example:**
```tomo
>> DateTime(2024, 9, 29):date()
>> Moment(2024, 9, 29):date()
= "2024-09-29"
```
@ -140,12 +141,12 @@ Return the integer day of the month (1-31).
**Signature:**
```tomo
func day_of_month(datetime: DateTime, timezone : Text? = !Text -> Int)
func day_of_month(moment: Moment, timezone : Text? = !Text -> Int)
```
**Parameters:**
- `datetime`: The datetime to get the day of the month from.
- `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:**
@ -153,7 +154,7 @@ The day of the month as an integer (1-31).
**Example:**
```tomo
>> DateTime(2024, 9, 29):day_of_month()
>> Moment(2024, 9, 29):day_of_month()
= 29
```
@ -167,12 +168,12 @@ Return the integer day of the week (1-7), where 1 = Sunday, 2 = Monday,
**Signature:**
```tomo
func day_of_week(datetime: DateTime, timezone : Text? = !Text -> Int)
func day_of_week(moment: Moment, timezone : Text? = !Text -> Int)
```
**Parameters:**
- `datetime`: The datetime to get the day of the week from.
- `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:**
@ -180,7 +181,7 @@ The day of the week as an integer (1-7).
**Example:**
```tomo
>> DateTime(2024, 9, 29):day_of_week()
>> Moment(2024, 9, 29):day_of_week()
= 1
```
@ -193,12 +194,12 @@ Return the integer day of the year (1-366, including leap years).
**Signature:**
```tomo
func day_of_year(datetime: DateTime, timezone : Text? = !Text -> Int)
func day_of_year(moment: Moment, timezone : Text? = !Text -> Int)
```
**Parameters:**
- `datetime`: The datetime to get the day of the year from.
- `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:**
@ -206,7 +207,7 @@ The day of the year as an integer (1-366).
**Example:**
```tomo
>> DateTime(2024, 9, 29):day_of_year()
>> Moment(2024, 9, 29):day_of_year()
= 272
```
@ -222,12 +223,12 @@ timezone.
**Signature:**
```tomo
func format(datetime: DateTime, format: Text = "%Y-%m-%dT%H:%M:%S%z", timezone : Text? = !Text -> Text)
func format(moment: Moment, format: Text = "%Y-%m-%dT%H:%M:%S%z", timezone : Text? = !Text -> Text)
```
**Parameters:**
- `datetime`: The datetime to format.
- `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).
@ -236,7 +237,7 @@ Nothing.
**Example:**
```tomo
>> DateTime(2024, 9, 29):format("%A")
>> Moment(2024, 9, 29):format("%A")
= "Sunday"
```
@ -245,12 +246,12 @@ Nothing.
### `from_unix_timestamp`
**Description:**
Return a datetime object that represents the same moment in time as
Return a moment object that represents the same moment in time as
the given UNIX epoch timestamp (seconds since January 1, 1970 UTC).
**Signature:**
```tomo
func from_unix_timestamp(timestamp: Int64 -> DateTime)
func from_unix_timestamp(timestamp: Int64 -> Moment)
```
**Parameters:**
@ -258,12 +259,12 @@ func from_unix_timestamp(timestamp: Int64 -> DateTime)
- `timestamp`: The UNIX timestamp.
**Returns:**
A `DateTime` object representing the same moment as the given UNIX timestamp.
A `Moment` object representing the same moment as the given UNIX timestamp.
**Example:**
```tomo
# In the New York timezone:
>> DateTime.from_unix_timestamp(0)
>> Moment.from_unix_timestamp(0)
= Wed Dec 31 19:00:00 1969
```
@ -272,7 +273,7 @@ A `DateTime` object representing the same moment as the given UNIX timestamp.
**Description:**
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 `DateTime.set_local_timezone(...)`.
calling `Moment.set_local_timezone(...)`.
**Signature:**
```tomo
@ -288,7 +289,7 @@ The name of the current local timezone.
**Example:**
```tomo
>> DateTime.get_local_timezone()
>> Moment.get_local_timezone()
= "America/New_York"
```
@ -301,12 +302,12 @@ Return the hour of the day as an integer (1-24).
**Signature:**
```tomo
func hour(datetime: DateTime, timezone : Text? = !Text -> Int)
func hour(moment: Moment, timezone : Text? = !Text -> Int)
```
**Parameters:**
- `datetime`: The datetime to get the hour from.
- `moment`: The moment to get the hour from.
- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
**Returns:**
@ -314,7 +315,7 @@ The hour of the day as an integer (1-24).
**Example:**
```tomo
>> DateTime(2024, 9, 29, 11, 59):hour()
>> Moment(2024, 9, 29, 11, 59):hour()
= 11
```
@ -323,17 +324,17 @@ The hour of the day as an integer (1-24).
### `hours_till`
**Description:**
Return the number of hours until a given datetime.
Return the number of hours until a given moment.
**Signature:**
```tomo
func hours_till(datetime: DateTime, then:DateTime -> Num)
func hours_till(moment: Moment, then:Moment -> Num)
```
**Parameters:**
- `datetime`: The starting point datetime.
- `then`: Another datetime that we want to calculate the time offset from (in hours).
- `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.
@ -354,12 +355,12 @@ Return the minute of the day as an integer (0-59).
**Signature:**
```tomo
func minute(datetime: DateTime, timezone : Text? = !Text -> Int)
func minute(moment: Moment, timezone : Text? = !Text -> Int)
```
**Parameters:**
- `datetime`: The datetime to get the minute from.
- `moment`: The moment to get the minute from.
- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
**Returns:**
@ -367,7 +368,7 @@ The minute of the hour as an integer (0-59).
**Example:**
```tomo
>> DateTime(2024, 9, 29, 11, 59):minute()
>> Moment(2024, 9, 29, 11, 59):minute()
= 59
```
@ -376,17 +377,17 @@ The minute of the hour as an integer (0-59).
### `minutes_till`
**Description:**
Return the number of minutes until a given datetime.
Return the number of minutes until a given moment.
**Signature:**
```tomo
func minutes_till(datetime: DateTime, then:DateTime -> Num)
func minutes_till(moment: Moment, then:Moment -> Num)
```
**Parameters:**
- `datetime`: The starting point datetime.
- `then`: Another datetime that we want to calculate the time offset from (in minutes).
- `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.
@ -407,12 +408,12 @@ Return the month of the year as an integer (1-12).
**Signature:**
```tomo
func month(datetime: DateTime, timezone : Text? = !Text -> Int)
func month(moment: Moment, timezone : Text? = !Text -> Int)
```
**Parameters:**
- `datetime`: The datetime to get the month from.
- `moment`: The moment to get the month from.
- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
**Returns:**
@ -420,7 +421,7 @@ The month of the year as an integer (1-12).
**Example:**
```tomo
>> DateTime(2024, 9, 29, 11, 59):month()
>> Moment(2024, 9, 29, 11, 59):month()
= 9
```
@ -433,12 +434,12 @@ Return the nanosecond of the second as an integer (0-999,999,999).
**Signature:**
```tomo
func nanosecond(datetime: DateTime, timezone : Text? = !Text -> Int)
func nanosecond(moment: Moment, timezone : Text? = !Text -> Int)
```
**Parameters:**
- `datetime`: The datetime to get the nanosecond from.
- `moment`: The moment to get the nanosecond from.
- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
**Returns:**
@ -446,7 +447,7 @@ The nanosecond of the second as an integer (0-999,999,999).
**Example:**
```tomo
>> DateTime(2024, 9, 29, 11, 59):month()
>> Moment(2024, 9, 29, 11, 59):month()
= 9
```
@ -455,13 +456,13 @@ The nanosecond of the second as an integer (0-999,999,999).
### `new`
**Description:**
Return a new `DateTime` object representing the given time parameters expressed
in local time. This function is the same as calling `DateTime` directly as a
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.
**Signature:**
```tomo
func new(year : Int, month : Int, day : Int, hour : Int = 0, minute : Int = 0, second : Num = 0.0 -> DateTime)
func new(year : Int, month : Int, day : Int, hour : Int = 0, minute : Int = 0, second : Num = 0.0 -> Moment)
```
**Parameters:**
@ -474,19 +475,19 @@ func new(year : Int, month : Int, day : Int, hour : Int = 0, minute : Int = 0, s
- `second`: The second of the minute (0-59) (default: 0.0).
**Returns:**
A `DateTime` representing the given information in local time. If the given
A `Moment` representing the given information in local time. If the given
parameters exceed reasonable bounds, the time values will wrap around. For
example, `DateTime.new(..., hour=3, minute=65)` is the same as
`DateTime.new(..., hour=4, minute=5)`. If any arguments cannot fit in a 32-bit
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
>> DateTime.new(2024, 9, 29)
>> Moment.new(2024, 9, 29)
= Mon Sep 30 00:00:00 2024 EDT
# March 1642, 2020:
>> DateTime(2020, 4, 1643)
>> Moment(2020, 4, 1643)
= Sat Sep 28 00:00:00 2024 EDT
```
@ -495,12 +496,12 @@ integer, an error will be raised.
### `now`
**Description:**
Get a `DateTime` object representing the current date and time. This function
Get a `Moment` object representing the current date and time. This function
is the same as the global function `now()`.
**Signature:**
```tomo
func now(->DateTime)
func now(->Moment)
```
**Parameters:**
@ -508,11 +509,11 @@ func now(->DateTime)
None.
**Returns:**
Returns a `DateTime` object representing the current date and time.
Returns a `Moment` object representing the current date and time.
**Example:**
```tomo
>> DateTime.now()
>> Moment.now()
= Sun Sep 29 20:22:48 2024 EDT
```
@ -521,12 +522,12 @@ Returns a `DateTime` object representing the current date and time.
### `parse`
**Description:**
Return a new `DateTime` object parsed from the given string in the given format,
Return a new `Moment` object parsed from the given string in the given format,
or a null value if the value could not be successfully parsed.
**Signature:**
```tomo
func parse(text: Text, format: Text = "%Y-%m-%dT%H:%M:%S%z" -> DateTime?)
func parse(text: Text, format: Text = "%Y-%m-%dT%H:%M:%S%z" -> Moment?)
```
**Parameters:**
@ -538,15 +539,15 @@ func parse(text: Text, format: Text = "%Y-%m-%dT%H:%M:%S%z" -> DateTime?)
**Returns:**
If the text was successfully parsed according to the given format, return a
`DateTime` representing that information. Otherwise, return a null value.
`Moment` representing that information. Otherwise, return a null value.
**Example:**
```tomo
>> DateTime.parse("2024-09-29", "%Y-%m-%d")!
>> Moment.parse("2024-09-29", "%Y-%m-%d")!
= Sun Sep 29 00:00:00 2024 EDT
>> DateTime.parse("???", "%Y-%m-%d")
= !DateTime
>> Moment.parse("???", "%Y-%m-%d")
= !Moment
```
---
@ -555,27 +556,27 @@ If the text was successfully parsed according to the given format, return a
**Description:**
Return a plain English textual representation of the approximate time difference
between two `DateTime`s. For example: `5 minutes ago` or `1 day later`
between two `Moment`s. For example: `5 minutes ago` or `1 day later`
**Signature:**
```tomo
func relative(datetime: DateTime, relative_to : DateTime = DateTime.now(), timezone : Text? = !Text -> Text)
func relative(moment: Moment, relative_to : Moment = Moment.now(), timezone : Text? = !Text -> Text)
```
**Parameters:**
- `datetime`: The datetime whose relative time you're getting.
- `relative_to` (optional): The time against which the relative time is calculated (default: `DateTime.now()`).
- `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 `DateTime`s. For example: `5 minutes ago` or `1 day
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`. Datetimes in the past will have the suffix `"
ago"`, while datetimes in the future will have the suffix `" later"`.
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
@ -595,12 +596,12 @@ Return the second of the minute as an integer (0-59).
**Signature:**
```tomo
func second(datetime: DateTime, timezone : Text? = !Text -> Int)
func second(moment: Moment, timezone : Text? = !Text -> Int)
```
**Parameters:**
- `datetime`: The datetime to get the second from.
- `moment`: The moment to get the second from.
- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
**Returns:**
@ -608,7 +609,7 @@ The second of the hour as an integer (0-59).
**Example:**
```tomo
>> DateTime(2024, 9, 29, 11, 30, 59):second()
>> Moment(2024, 9, 29, 11, 30, 59):second()
= 59
```
@ -617,17 +618,17 @@ The second of the hour as an integer (0-59).
### `seconds_till`
**Description:**
Return the number of seconds until a given datetime.
Return the number of seconds until a given moment.
**Signature:**
```tomo
func seconds_till(datetime: DateTime, then:DateTime -> Num)
func seconds_till(moment: Moment, then:Moment -> Num)
```
**Parameters:**
- `datetime`: The starting point datetime.
- `then`: Another datetime that we want to calculate the time offset from (in seconds).
- `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.
@ -646,8 +647,8 @@ the_future := now():after(seconds=1)
**Description:**
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 `DateTime` objects from
component parts. It's also used as the default way that `DateTime` objects are
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.
**Signature:**
@ -666,7 +667,7 @@ Nothing.
**Example:**
```tomo
DateTime.set_local_timezone("America/Los_Angeles")
Moment.set_local_timezone("America/Los_Angeles")
```
---
@ -674,26 +675,26 @@ DateTime.set_local_timezone("America/Los_Angeles")
### `time`
**Description:**
Return a text representation of the time component of the given datetime.
Return a text representation of the time component of the given moment.
**Signature:**
```tomo
func time(datetime: DateTime, seconds : Bool = no, am_pm : Bool = yes, timezone : Text? = !Text -> Text)
func time(moment: Moment, seconds : Bool = no, am_pm : Bool = yes, timezone : Text? = !Text -> Text)
```
**Parameters:**
- `datetime`: The datetime whose time value you want to get.
- `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 datetime.
A text representation of the time component of the moment.
**Example:**
```tomo
dt := DateTime(2024, 9, 29, hours=13, minutes=59, seconds=30)
moment := Moment(2024, 9, 29, hours=13, minutes=59, seconds=30)
>> dt:time()
= "1:59pm"
@ -710,17 +711,17 @@ dt := DateTime(2024, 9, 29, hours=13, minutes=59, seconds=30)
### `unix_timestamp`
**Description:**
Get the UNIX timestamp of the given datetime (seconds since the UNIX epoch:
Get the UNIX timestamp of the given moment (seconds since the UNIX epoch:
January 1, 1970 UTC).
**Signature:**
```tomo
func unix_timestamp(datetime:DateTime->Int64)
func unix_timestamp(moment:Moment->Int64)
```
**Parameters:**
`datetime`: The datetime whose UNIX timestamp you want to get.
`moment`: The moment whose UNIX timestamp you want to get.
**Returns:**
A 64-bit integer representation of the UNIX timestamp.

View File

@ -53,7 +53,7 @@ env_t *new_compilation_unit(CORD libname)
.ret=Type(AbortType))}},
{"fail", {.code="fail", .type=Type(FunctionType, .args=new(arg_t, .name="message", .type=Type(CStringType)), .ret=Type(AbortType))}},
{"sleep", {.code="sleep_num", .type=Type(FunctionType, .args=new(arg_t, .name="seconds", .type=Type(NumType, .bits=TYPE_NBITS64)), .ret=Type(VoidType))}},
{"now", {.code="DateTime$now", .type=Type(FunctionType, .args=NULL, .ret=Type(DateTimeType))}},
{"now", {.code="Moment$now", .type=Type(FunctionType, .args=NULL, .ret=Type(MomentType))}},
{"USE_COLOR", {.code="USE_COLOR", .type=Type(BoolType)}},
};
@ -287,33 +287,33 @@ env_t *new_compilation_unit(CORD libname)
{"escape_int", "Int$value_as_text", "func(i:Int -> Pattern)"},
{"escape_text", "Pattern$escape_text", "func(text:Text -> Pattern)"},
)},
{"DateTime", Type(DateTimeType), "DateTime_t", "DateTime", TypedArray(ns_entry_t,
{"Moment", Type(MomentType), "Moment_t", "Moment", TypedArray(ns_entry_t,
// Used as a default for functions below:
{"now", "DateTime$now", "func(->DateTime)"},
{"now", "Moment$now", "func(->Moment)"},
{"after", "DateTime$after", "func(dt:DateTime,seconds,minutes,hours=0.0,days,weeks,months,years=0,timezone=!Text -> DateTime)"},
{"date", "DateTime$date", "func(dt:DateTime,timezone=!Text -> Text)"},
{"day_of_month", "DateTime$day_of_month", "func(dt:DateTime,timezone=!Text -> Int)"},
{"day_of_week", "DateTime$day_of_week", "func(dt:DateTime,timezone=!Text -> Int)"},
{"day_of_year", "DateTime$day_of_year", "func(dt:DateTime,timezone=!Text -> Int)"},
{"format", "DateTime$format", "func(dt:DateTime,format=\"%Y-%m-%dT%H:%M:%S%z\",timezone=!Text -> Text)"},
{"from_unix_timestamp", "DateTime$from_unix_timestamp", "func(timestamp:Int64 -> DateTime)"},
{"get_local_timezone", "DateTime$get_local_timezone", "func(->Text)"},
{"hour", "DateTime$hour", "func(dt:DateTime,timezone=!Text -> Int)"},
{"hours_till", "DateTime$hours_till", "func(now,then:DateTime -> Num)"},
{"minute", "DateTime$minute", "func(dt:DateTime,timezone=!Text -> Int)"},
{"minutes_till", "DateTime$minutes_till", "func(now,then:DateTime -> Num)"},
{"month", "DateTime$month", "func(dt:DateTime,timezone=!Text -> Int)"},
{"nanosecond", "DateTime$nanosecond", "func(dt:DateTime,timezone=!Text -> Int)"},
{"new", "DateTime$new", "func(year,month,day:Int,hour,minute=0,second=0.0,timezone=!Text -> DateTime)"},
{"parse", "DateTime$parse", "func(text:Text, format=\"%Y-%m-%dT%H:%M:%S%z\" -> DateTime?)"},
{"relative", "DateTime$relative", "func(dt:DateTime,relative_to=DateTime.now(),timezone=!Text -> Text)"},
{"second", "DateTime$second", "func(dt:DateTime,timezone=!Text -> Int)"},
{"seconds_till", "DateTime$seconds_till", "func(now:DateTime,then:DateTime -> Num)"},
{"set_local_timezone", "DateTime$set_local_timezone", "func(timezone=!Text)"},
{"time", "DateTime$time", "func(dt:DateTime,seconds=no,am_pm=yes,timezone=!Text -> Text)"},
{"unix_timestamp", "DateTime$unix_timestamp", "func(dt:DateTime -> Int64)"},
{"year", "DateTime$year", "func(dt:DateTime,timezone=!Text -> Int)"},
{"after", "Moment$after", "func(moment:Moment,seconds,minutes,hours=0.0,days,weeks,months,years=0,timezone=!Text -> Moment)"},
{"date", "Moment$date", "func(moment:Moment,timezone=!Text -> Text)"},
{"day_of_month", "Moment$day_of_month", "func(moment:Moment,timezone=!Text -> Int)"},
{"day_of_week", "Moment$day_of_week", "func(moment:Moment,timezone=!Text -> Int)"},
{"day_of_year", "Moment$day_of_year", "func(moment:Moment,timezone=!Text -> Int)"},
{"format", "Moment$format", "func(moment:Moment,format=\"%Y-%m-%dT%H:%M:%S%z\",timezone=!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=!Text -> Int)"},
{"hours_till", "Moment$hours_till", "func(now,then:Moment -> Num)"},
{"minute", "Moment$minute", "func(moment:Moment,timezone=!Text -> Int)"},
{"minutes_till", "Moment$minutes_till", "func(now,then:Moment -> Num)"},
{"month", "Moment$month", "func(moment:Moment,timezone=!Text -> Int)"},
{"nanosecond", "Moment$nanosecond", "func(moment:Moment,timezone=!Text -> Int)"},
{"new", "Moment$new", "func(year,month,day:Int,hour,minute=0,second=0.0,timezone=!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=!Text -> Text)"},
{"second", "Moment$second", "func(moment:Moment,timezone=!Text -> Int)"},
{"seconds_till", "Moment$seconds_till", "func(now:Moment,then:Moment -> Num)"},
{"set_local_timezone", "Moment$set_local_timezone", "func(timezone=!Text)"},
{"time", "Moment$time", "func(moment:Moment,seconds=no,am_pm=yes,timezone=!Text -> Text)"},
{"unix_timestamp", "Moment$unix_timestamp", "func(moment:Moment -> Int64)"},
{"year", "Moment$year", "func(moment:Moment,timezone=!Text -> Int)"},
)},
{"Path", Type(TextType, .lang="Path", .env=namespace_env(env, "Path")), "Text_t", "Text$info", TypedArray(ns_entry_t,
{"append", "Path$append", "func(path:Path, text:Text, permissions=Int32(0o644))"},
@ -347,9 +347,9 @@ env_t *new_compilation_unit(CORD libname)
{"write_unique", "Path$write_unique", "func(path:Path, text:Text -> Path)"},
{"write_unique_bytes", "Path$write_unique_bytes", "func(path:Path, bytes:[Byte] -> Path)"},
{"modified", "Path$modified", "func(path:Path, follow_symlinks=yes -> DateTime?)"},
{"accessed", "Path$accessed", "func(path:Path, follow_symlinks=yes -> DateTime?)"},
{"changed", "Path$changed", "func(path:Path, follow_symlinks=yes -> DateTime?)"},
{"modified", "Path$modified", "func(path:Path, follow_symlinks=yes -> Moment?)"},
{"accessed", "Path$accessed", "func(path:Path, follow_symlinks=yes -> Moment?)"},
{"changed", "Path$changed", "func(path:Path, follow_symlinks=yes -> Moment?)"},
// Text methods:
{"ends_with", "Text$ends_with", "func(path:Path, suffix:Text -> Bool)"},
@ -657,7 +657,7 @@ binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name)
switch (cls_type->tag) {
case ArrayType: return NULL;
case TableType: return NULL;
case CStringType: case DateTimeType:
case CStringType: case MomentType:
case BoolType: case IntType: case BigIntType: case NumType: case ByteType: {
binding_t *b = get_binding(env, CORD_to_const_char_star(type_to_cord(cls_type)));
assert(b);

20
parse.c
View File

@ -110,7 +110,7 @@ static PARSER(parse_heap_alloc);
static PARSER(parse_if);
static PARSER(parse_inline_c);
static PARSER(parse_int);
static PARSER(parse_datetime);
static PARSER(parse_moment);
static PARSER(parse_lambda);
static PARSER(parse_lang_def);
static PARSER(parse_namespace);
@ -508,7 +508,7 @@ PARSER(parse_int) {
return NewAST(ctx->file, start, pos, Int, .str=str);
}
PARSER(parse_datetime) {
PARSER(parse_moment) {
const char *start = pos;
bool negative = match(&pos, "-");
if (!isdigit(*pos)) return NULL;
@ -530,17 +530,17 @@ PARSER(parse_datetime) {
const char *before_spaces = pos;
spaces(&pos);
DateTime_t dt;
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 datetime timezone");
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();
dt = (DateTime_t){.tv_sec=mktime(&info)};
moment = (Moment_t){.tv_sec=mktime(&info)};
if (old_tz) setenv("TZ", old_tz, 1);
else unsetenv("TZ");
} else if (*pos == 'Z' || *pos == '-' || *pos == '+') {
@ -549,16 +549,16 @@ PARSER(parse_datetime) {
pos = after;
long offset = info.tm_gmtoff; // Need to cache this because mktime() mutates it to local timezone >:(
time_t t = mktime(&info);
dt = (DateTime_t){.tv_sec=t + offset - info.tm_gmtoff};
moment = (Moment_t){.tv_sec=t + offset - info.tm_gmtoff};
} else {
dt = (DateTime_t){.tv_sec=mktime(&info)};
moment = (Moment_t){.tv_sec=mktime(&info)};
}
} else {
pos = before_spaces;
dt = (DateTime_t){.tv_sec=mktime(&info)};
moment = (Moment_t){.tv_sec=mktime(&info)};
}
return NewAST(ctx->file, start, pos, DateTime, .dt=dt);
return NewAST(ctx->file, start, pos, Moment, .moment=moment);
}
type_ast_t *parse_table_type(parse_ctx_t *ctx, const char *pos) {
@ -1553,7 +1553,7 @@ PARSER(parse_term_no_suffix) {
ast_t *term = NULL;
(void)(
false
|| (term=parse_datetime(ctx, pos)) // Must come before num/int
|| (term=parse_moment(ctx, pos)) // Must come before num/int
|| (term=parse_null(ctx, pos))
|| (term=parse_num(ctx, pos)) // Must come before int
|| (term=parse_int(ctx, pos))

View File

@ -90,8 +90,8 @@ typedef struct Text_s {
#define Pattern_t Text_t
#define OptionalPattern_t Text_t
typedef struct timeval DateTime_t;
#define OptionalDateTime_t DateTime_t
typedef struct timeval Moment_t;
#define OptionalMoment_t Moment_t
typedef struct RNGState_t* RNG_t;

View File

@ -1,43 +0,0 @@
#pragma once
// DateTime objects
#include <stdint.h>
#include "datatypes.h"
#include "integers.h"
#include "optionals.h"
#include "types.h"
#include "util.h"
Text_t DateTime$as_text(const DateTime_t *dt, bool colorize, const TypeInfo_t *type);
PUREFUNC int32_t DateTime$compare(const DateTime_t *a, const DateTime_t *b, const TypeInfo_t *type);
DateTime_t DateTime$now(void);
DateTime_t DateTime$new(Int_t year, Int_t month, Int_t day, Int_t hour, Int_t minute, double second, OptionalText_t timezone);
DateTime_t DateTime$after(DateTime_t dt, double seconds, double minutes, double hours, Int_t days, Int_t weeks, Int_t months, Int_t years, OptionalText_t timezone);
CONSTFUNC double DateTime$seconds_till(DateTime_t now, DateTime_t then);
CONSTFUNC double DateTime$minutes_till(DateTime_t now, DateTime_t then);
CONSTFUNC double DateTime$hours_till(DateTime_t now, DateTime_t then);
Int_t DateTime$year(DateTime_t dt, OptionalText_t timezone);
Int_t DateTime$month(DateTime_t dt, OptionalText_t timezone);
Int_t DateTime$day_of_week(DateTime_t dt, OptionalText_t timezone);
Int_t DateTime$day_of_month(DateTime_t dt, OptionalText_t timezone);
Int_t DateTime$day_of_year(DateTime_t dt, OptionalText_t timezone);
Int_t DateTime$hour(DateTime_t dt, OptionalText_t timezone);
Int_t DateTime$minute(DateTime_t dt, OptionalText_t timezone);
Int_t DateTime$second(DateTime_t dt, OptionalText_t timezone);
Int_t DateTime$nanosecond(DateTime_t dt, OptionalText_t timezone);
Text_t DateTime$format(DateTime_t dt, Text_t fmt, OptionalText_t timezone);
Text_t DateTime$date(DateTime_t dt, OptionalText_t timezone);
Text_t DateTime$time(DateTime_t dt, bool seconds, bool am_pm, OptionalText_t timezone);
OptionalDateTime_t DateTime$parse(Text_t text, Text_t format);
Text_t DateTime$relative(DateTime_t dt, DateTime_t relative_to, OptionalText_t timezone);
CONSTFUNC Int64_t DateTime$unix_timestamp(DateTime_t dt);
CONSTFUNC DateTime_t DateTime$from_unix_timestamp(Int64_t timestamp);
void DateTime$set_local_timezone(OptionalText_t timezone);
Text_t DateTime$get_local_timezone(void);
extern const TypeInfo_t DateTime$info;
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -1,4 +1,4 @@
// DateTime methods/type info
// Moment methods/type info
#include <ctype.h>
#include <gc.h>
#include <err.h>
@ -7,7 +7,7 @@
#include <unistd.h>
#include "datatypes.h"
#include "datetime.h"
#include "moments.h"
#include "optionals.h"
#include "patterns.h"
#include "stdlib.h"
@ -18,21 +18,21 @@ static OptionalText_t _local_timezone = NULL_TEXT;
#define WITH_TIMEZONE(tz, body) ({ if (tz.length >= 0) { \
OptionalText_t old_timezone = _local_timezone; \
DateTime$set_local_timezone(tz); \
Moment$set_local_timezone(tz); \
body; \
DateTime$set_local_timezone(old_timezone); \
Moment$set_local_timezone(old_timezone); \
} else { \
body; \
}})
public Text_t DateTime$as_text(const DateTime_t *dt, bool colorize, const TypeInfo_t *type)
public Text_t Moment$as_text(const Moment_t *moment, bool colorize, const TypeInfo_t *type)
{
(void)type;
if (!dt)
return Text("DateTime");
if (!moment)
return Text("Moment");
struct tm info;
struct tm *final_info = localtime_r(&dt->tv_sec, &info);
struct tm *final_info = localtime_r(&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);
@ -41,7 +41,7 @@ public Text_t DateTime$as_text(const DateTime_t *dt, bool colorize, const TypeIn
return text;
}
PUREFUNC public int32_t DateTime$compare(const DateTime_t *a, const DateTime_t *b, const TypeInfo_t *type)
PUREFUNC public int32_t Moment$compare(const Moment_t *a, const Moment_t *b, const TypeInfo_t *type)
{
(void)type;
if (a->tv_sec != b->tv_sec)
@ -49,15 +49,15 @@ PUREFUNC public int32_t DateTime$compare(const DateTime_t *a, const DateTime_t *
return (a->tv_usec > b->tv_usec) - (a->tv_usec < b->tv_usec);
}
public DateTime_t DateTime$now(void)
public Moment_t Moment$now(void)
{
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
fail("Couldn't get the time!");
return (DateTime_t){.tv_sec=ts.tv_sec, .tv_usec=ts.tv_nsec};
return (Moment_t){.tv_sec=ts.tv_sec, .tv_usec=ts.tv_nsec};
}
public DateTime_t DateTime$new(Int_t year, Int_t month, Int_t day, Int_t hour, Int_t minute, double second, OptionalText_t timezone)
public Moment_t Moment$new(Int_t year, Int_t month, Int_t day, Int_t hour, Int_t minute, double second, OptionalText_t timezone)
{
struct tm info = {
.tm_min=Int_to_Int32(minute, false),
@ -70,49 +70,49 @@ public DateTime_t DateTime$new(Int_t year, Int_t month, Int_t day, Int_t hour, I
time_t t;
WITH_TIMEZONE(timezone, t = mktime(&info));
return (DateTime_t){.tv_sec=t + (time_t)second, .tv_usec=(suseconds_t)(fmod(second, 1.0) * 1e9)};
return (Moment_t){.tv_sec=t + (time_t)second, .tv_usec=(suseconds_t)(fmod(second, 1.0) * 1e9)};
}
public DateTime_t DateTime$after(DateTime_t dt, double seconds, double minutes, double hours, Int_t days, Int_t weeks, Int_t months, Int_t years, OptionalText_t timezone)
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 timezone)
{
double offset = seconds + 60.*minutes + 3600.*hours;
dt.tv_sec += (time_t)offset;
moment.tv_sec += (time_t)offset;
struct tm info = {};
WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
WITH_TIMEZONE(timezone, localtime_r(&moment.tv_sec, &info));
info.tm_mday += Int_to_Int32(days, false) + 7*Int_to_Int32(weeks, false);
info.tm_mon += Int_to_Int32(months, false);
info.tm_year += Int_to_Int32(years, false);
time_t t = mktime(&info);
return (DateTime_t){
return (Moment_t){
.tv_sec=t,
.tv_usec=dt.tv_usec + (suseconds_t)(fmod(offset, 1.0) * 1e9),
.tv_usec=moment.tv_usec + (suseconds_t)(fmod(offset, 1.0) * 1e9),
};
}
CONSTFUNC public double DateTime$seconds_till(DateTime_t now, DateTime_t then)
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 DateTime$minutes_till(DateTime_t now, DateTime_t then)
CONSTFUNC public double Moment$minutes_till(Moment_t now, Moment_t then)
{
return DateTime$seconds_till(now, then)/60.;
return Moment$seconds_till(now, then)/60.;
}
CONSTFUNC public double DateTime$hours_till(DateTime_t now, DateTime_t then)
CONSTFUNC public double Moment$hours_till(Moment_t now, Moment_t then)
{
return DateTime$seconds_till(now, then)/3600.;
return Moment$seconds_till(now, then)/3600.;
}
public void DateTime$get(
DateTime_t dt, Int_t *year, Int_t *month, Int_t *day, Int_t *hour, Int_t *minute, Int_t *second,
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 timezone)
{
struct tm info = {};
WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
WITH_TIMEZONE(timezone, localtime_r(&moment.tv_sec, &info));
if (year) *year = I(info.tm_year + 1900);
if (month) *month = I(info.tm_mon + 1);
@ -120,97 +120,97 @@ public void DateTime$get(
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(dt.tv_usec);
if (nanosecond) *nanosecond = I(moment.tv_usec);
if (weekday) *weekday = I(info.tm_wday + 1);
}
public Int_t DateTime$year(DateTime_t dt, OptionalText_t timezone)
public Int_t Moment$year(Moment_t moment, OptionalText_t timezone)
{
struct tm info = {};
WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
WITH_TIMEZONE(timezone, localtime_r(&moment.tv_sec, &info));
return I(info.tm_year + 1900);
}
public Int_t DateTime$month(DateTime_t dt, OptionalText_t timezone)
public Int_t Moment$month(Moment_t moment, OptionalText_t timezone)
{
struct tm info = {};
WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
WITH_TIMEZONE(timezone, localtime_r(&moment.tv_sec, &info));
return I(info.tm_mon + 1);
}
public Int_t DateTime$day_of_week(DateTime_t dt, OptionalText_t timezone)
public Int_t Moment$day_of_week(Moment_t moment, OptionalText_t timezone)
{
struct tm info = {};
WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
WITH_TIMEZONE(timezone, localtime_r(&moment.tv_sec, &info));
return I(info.tm_wday + 1);
}
public Int_t DateTime$day_of_month(DateTime_t dt, OptionalText_t timezone)
public Int_t Moment$day_of_month(Moment_t moment, OptionalText_t timezone)
{
struct tm info = {};
WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
WITH_TIMEZONE(timezone, localtime_r(&moment.tv_sec, &info));
return I(info.tm_mday);
}
public Int_t DateTime$day_of_year(DateTime_t dt, OptionalText_t timezone)
public Int_t Moment$day_of_year(Moment_t moment, OptionalText_t timezone)
{
struct tm info = {};
WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
WITH_TIMEZONE(timezone, localtime_r(&moment.tv_sec, &info));
return I(info.tm_yday);
}
public Int_t DateTime$hour(DateTime_t dt, OptionalText_t timezone)
public Int_t Moment$hour(Moment_t moment, OptionalText_t timezone)
{
struct tm info = {};
WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
WITH_TIMEZONE(timezone, localtime_r(&moment.tv_sec, &info));
return I(info.tm_hour);
}
public Int_t DateTime$minute(DateTime_t dt, OptionalText_t timezone)
public Int_t Moment$minute(Moment_t moment, OptionalText_t timezone)
{
struct tm info = {};
WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
WITH_TIMEZONE(timezone, localtime_r(&moment.tv_sec, &info));
return I(info.tm_min);
}
public Int_t DateTime$second(DateTime_t dt, OptionalText_t timezone)
public Int_t Moment$second(Moment_t moment, OptionalText_t timezone)
{
struct tm info = {};
WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
WITH_TIMEZONE(timezone, localtime_r(&moment.tv_sec, &info));
return I(info.tm_sec);
}
public Int_t DateTime$nanosecond(DateTime_t dt, OptionalText_t timezone)
public Int_t Moment$nanosecond(Moment_t moment, OptionalText_t timezone)
{
(void)timezone;
return I(dt.tv_usec);
return I(moment.tv_usec);
}
public Text_t DateTime$format(DateTime_t dt, Text_t fmt, OptionalText_t timezone)
public Text_t Moment$format(Moment_t moment, Text_t fmt, OptionalText_t timezone)
{
struct tm info;
WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
WITH_TIMEZONE(timezone, 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 DateTime$date(DateTime_t dt, OptionalText_t timezone)
public Text_t Moment$date(Moment_t moment, OptionalText_t timezone)
{
return DateTime$format(dt, Text("%F"), timezone);
return Moment$format(moment, Text("%F"), timezone);
}
public Text_t DateTime$time(DateTime_t dt, bool seconds, bool am_pm, OptionalText_t timezone)
public Text_t Moment$time(Moment_t moment, bool seconds, bool am_pm, OptionalText_t timezone)
{
Text_t text;
if (seconds)
text = DateTime$format(dt, am_pm ? Text("%l:%M:%S%P") : Text("%T"), timezone);
text = Moment$format(moment, am_pm ? Text("%l:%M:%S%P") : Text("%T"), timezone);
else
text = DateTime$format(dt, am_pm ? Text("%l:%M%P") : Text("%H:%M"), timezone);
text = Moment$format(moment, am_pm ? Text("%l:%M%P") : Text("%H:%M"), timezone);
return Text$trim(text, Pattern(" "), true, true);
}
public OptionalDateTime_t DateTime$parse(Text_t text, Text_t format)
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);
@ -220,11 +220,11 @@ public OptionalDateTime_t DateTime$parse(Text_t text, Text_t format)
char *invalid = strptime(str, fmt, &info);
if (!invalid || invalid[0] != '\0')
return NULL_DATETIME;
return NULL_MOMENT;
long offset = info.tm_gmtoff; // Need to cache this because mktime() mutates it to local timezone >:(
time_t t = mktime(&info);
return (DateTime_t){.tv_sec=t + offset - info.tm_gmtoff};
return (Moment_t){.tv_sec=t + offset - info.tm_gmtoff};
}
static INLINE Text_t num_format(long n, const char *unit)
@ -234,16 +234,16 @@ static INLINE Text_t num_format(long n, const char *unit)
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 DateTime$relative(DateTime_t dt, DateTime_t relative_to, OptionalText_t timezone)
public Text_t Moment$relative(Moment_t moment, Moment_t relative_to, OptionalText_t timezone)
{
struct tm info = {};
struct tm relative_info = {};
WITH_TIMEZONE(timezone, {
localtime_r(&dt.tv_sec, &info);
localtime_r(&moment.tv_sec, &info);
localtime_r(&relative_to.tv_sec, &relative_info);
});
double second_diff = DateTime$seconds_till(relative_to, dt);
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.)
@ -266,17 +266,17 @@ public Text_t DateTime$relative(DateTime_t dt, DateTime_t relative_to, OptionalT
}
}
CONSTFUNC public Int64_t DateTime$unix_timestamp(DateTime_t dt)
CONSTFUNC public Int64_t Moment$unix_timestamp(Moment_t moment)
{
return (Int64_t)dt.tv_sec;
return (Int64_t)moment.tv_sec;
}
CONSTFUNC public DateTime_t DateTime$from_unix_timestamp(Int64_t timestamp)
CONSTFUNC public Moment_t Moment$from_unix_timestamp(Int64_t timestamp)
{
return (DateTime_t){.tv_sec=(time_t)timestamp};
return (Moment_t){.tv_sec=(time_t)timestamp};
}
public void DateTime$set_local_timezone(OptionalText_t timezone)
public void Moment$set_local_timezone(OptionalText_t timezone)
{
if (timezone.length >= 0) {
setenv("TZ", Text$as_c_string(timezone), 1);
@ -287,7 +287,7 @@ public void DateTime$set_local_timezone(OptionalText_t timezone)
tzset();
}
public Text_t DateTime$get_local_timezone(void)
public Text_t Moment$get_local_timezone(void)
{
if (_local_timezone.length < 0) {
static char buf[PATH_MAX];
@ -304,13 +304,13 @@ public Text_t DateTime$get_local_timezone(void)
return _local_timezone;
}
public const TypeInfo_t DateTime$info = {
.size=sizeof(DateTime_t),
.align=__alignof__(DateTime_t),
public const TypeInfo_t Moment$info = {
.size=sizeof(Moment_t),
.align=__alignof__(Moment_t),
.tag=CustomInfo,
.CustomInfo={
.as_text=(void*)DateTime$as_text,
.compare=(void*)DateTime$compare,
.as_text=(void*)Moment$as_text,
.compare=(void*)Moment$compare,
},
};

43
stdlib/moments.h Normal file
View File

@ -0,0 +1,43 @@
#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 Moment_t *moment, bool colorize, const TypeInfo_t *type);
PUREFUNC int32_t Moment$compare(const Moment_t *a, const Moment_t *b, const TypeInfo_t *type);
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$nanosecond(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

View File

@ -5,9 +5,9 @@
#include "bools.h"
#include "bytes.h"
#include "datatypes.h"
#include "datetime.h"
#include "integers.h"
#include "metamethods.h"
#include "moments.h"
#include "patterns.h"
#include "text.h"
#include "threads.h"
@ -33,8 +33,8 @@ public PUREFUNC bool is_null(const void *obj, const TypeInfo_t *non_optional_typ
return ((OptionalByte_t*)obj)->is_null;
else if (non_optional_type == &Thread$info)
return *(pthread_t**)obj == NULL;
else if (non_optional_type == &DateTime$info)
return ((OptionalDateTime_t*)obj)->tv_usec < 0;
else if (non_optional_type == &Moment$info)
return ((OptionalMoment_t*)obj)->tv_usec < 0;
else if (non_optional_type == &Match$info)
return ((OptionalMatch_t*)obj)->index.small == 0;

View File

@ -22,7 +22,7 @@
#define NULL_TABLE ((OptionalTable_t){.entries.length=-1})
#define NULL_CLOSURE ((OptionalClosure_t){.fn=NULL})
#define NULL_TEXT ((OptionalText_t){.length=-1})
#define NULL_DATETIME ((OptionalDateTime_t){.tv_usec=-1})
#define NULL_MOMENT ((OptionalMoment_t){.tv_usec=-1})
PUREFUNC bool is_null(const void *obj, const TypeInfo_t *non_optional_type);
Text_t Optional$as_text(const void *obj, bool colorize, const TypeInfo_t *type);

View File

@ -205,28 +205,28 @@ public bool Path$is_symlink(Path_t path)
return (sb.st_mode & S_IFMT) == S_IFLNK;
}
public OptionalDateTime_t Path$modified(Path_t path, bool follow_symlinks)
public OptionalMoment_t Path$modified(Path_t path, bool follow_symlinks)
{
struct stat sb;
int status = path_stat(path, follow_symlinks, &sb);
if (status != 0) return NULL_DATETIME;
return (DateTime_t){.tv_sec=sb.st_mtime};
if (status != 0) return NULL_MOMENT;
return (Moment_t){.tv_sec=sb.st_mtime};
}
public OptionalDateTime_t Path$accessed(Path_t path, bool follow_symlinks)
public OptionalMoment_t Path$accessed(Path_t path, bool follow_symlinks)
{
struct stat sb;
int status = path_stat(path, follow_symlinks, &sb);
if (status != 0) return NULL_DATETIME;
return (DateTime_t){.tv_sec=sb.st_atime};
if (status != 0) return NULL_MOMENT;
return (Moment_t){.tv_sec=sb.st_atime};
}
public OptionalDateTime_t Path$changed(Path_t path, bool follow_symlinks)
public OptionalMoment_t Path$changed(Path_t path, bool follow_symlinks)
{
struct stat sb;
int status = path_stat(path, follow_symlinks, &sb);
if (status != 0) return NULL_DATETIME;
return (DateTime_t){.tv_sec=sb.st_ctime};
if (status != 0) return NULL_MOMENT;
return (Moment_t){.tv_sec=sb.st_ctime};
}
static void _write(Path_t path, Array_t bytes, int mode, int permissions)

View File

@ -27,9 +27,9 @@ bool Path$is_directory(Path_t path, bool follow_symlinks);
bool Path$is_pipe(Path_t path, bool follow_symlinks);
bool Path$is_socket(Path_t path, bool follow_symlinks);
bool Path$is_symlink(Path_t path);
OptionalDateTime_t Path$modified(Path_t path, bool follow_symlinks);
OptionalDateTime_t Path$accessed(Path_t path, bool follow_symlinks);
OptionalDateTime_t Path$changed(Path_t path, bool follow_symlinks);
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);
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);

View File

@ -13,11 +13,11 @@
#include "c_strings.h"
#include "channels.h"
#include "datatypes.h"
#include "datetime.h"
#include "functiontype.h"
#include "integers.h"
#include "memory.h"
#include "metamethods.h"
#include "moments.h"
#include "nums.h"
#include "optionals.h"
#include "paths.h"

View File

@ -2,7 +2,7 @@
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] == DateTime(2024, 1, 1, hour=9, timezone="America/Los_Angeles")
>> 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]
@ -37,7 +37,7 @@ func main():
>> t:unix_timestamp()
= Int64(1704221100)
>> t == DateTime.from_unix_timestamp(1704221100)
>> t == Moment.from_unix_timestamp(1704221100)
= yes
>> t < t:after(minutes=1)

View File

@ -704,7 +704,7 @@ type_t *get_type(env_t *env, ast_t *ast)
if (fn_type_t->tag == TypeInfoType) {
type_t *t = Match(fn_type_t, TypeInfoType)->type;
if (t->tag == StructType || t->tag == IntType || t->tag == BigIntType || t->tag == NumType
|| t->tag == ByteType || t->tag == TextType || t->tag == CStringType || t->tag == DateTimeType)
|| t->tag == ByteType || t->tag == TextType || t->tag == CStringType || t->tag == MomentType)
return t; // Constructor
code_err(call->fn, "This is not a type that has a constructor");
}
@ -1204,7 +1204,7 @@ 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 DateTime: return Type(DateTimeType);
case Moment: return Type(MomentType);
case Unknown: code_err(ast, "I can't figure out the type of: %W", ast);
}
#pragma GCC diagnostic pop

View File

@ -25,7 +25,7 @@ CORD type_to_cord(type_t *t) {
case BoolType: return "Bool";
case ByteType: return "Byte";
case CStringType: return "CString";
case DateTimeType: return "DateTime";
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);
@ -425,7 +425,7 @@ 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 DateTimeType: return sizeof(DateTime_t);
case MomentType: return sizeof(Moment_t);
case BigIntType: return sizeof(Int_t);
case IntType: {
switch (Match(t, IntType)->bits) {
@ -509,7 +509,7 @@ 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 DateTimeType: return __alignof__(DateTime_t);
case MomentType: return __alignof__(Moment_t);
case BigIntType: return __alignof__(Int_t);
case IntType: {
switch (Match(t, IntType)->bits) {

View File

@ -48,7 +48,7 @@ struct type_s {
IntType,
NumType,
CStringType,
DateTimeType,
MomentType,
TextType,
ArrayType,
ChannelType,
@ -78,7 +78,7 @@ struct type_s {
struct {
enum { TYPE_NBITS32=32, TYPE_NBITS64=64 } bits;
} NumType;
struct {} CStringType, DateTimeType;
struct {} CStringType, MomentType;
struct {
const char *lang;
struct env_s *env;