aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorflysand7 <thebumboni@gmail.com>2024-07-20 16:40:38 +1100
committerflysand7 <thebumboni@gmail.com>2024-07-20 18:48:50 +1100
commitb3ca2d5e0a91be3ef3698aa2d626cdff70c9b487 (patch)
tree85064f07053cb54d9f3abeceab5a91bfb0badbb7 /core
parent7237f9c9f850c8a952037938d2fdfca4daf3caa8 (diff)
[time]: Document all functions
Diffstat (limited to 'core')
-rw-r--r--core/time/iso8601.odin88
-rw-r--r--core/time/perf.odin90
-rw-r--r--core/time/rfc3339.odin92
-rw-r--r--core/time/time.odin284
4 files changed, 511 insertions, 43 deletions
diff --git a/core/time/iso8601.odin b/core/time/iso8601.odin
index 528e0b00a..f00107226 100644
--- a/core/time/iso8601.odin
+++ b/core/time/iso8601.odin
@@ -3,23 +3,62 @@ package time
import dt "core:time/datetime"
-// Parses an ISO 8601 string and returns Time in UTC, with any UTC offset applied to it.
-// Only 4-digit years are accepted.
-// Optional pointer to boolean `is_leap` will return `true` if the moment was a leap second.
-// Leap seconds are smeared into 23:59:59.
+/*
+Parse an ISO 8601 string into a time with UTC offset applied to it.
+
+This procedure parses an ISO 8601 string of roughly the following format:
+
+```text
+YYYY-MM-DD[Tt]HH:mm:ss[.nn][Zz][+-]HH:mm
+```
+
+And returns time, in UTC represented by that string. In case the timezone offset
+is specified in the string, that timezone is applied to time.
+
+**Inputs**:
+- `iso_datetime`: The string to be parsed.
+- `is_leap`: Optional output parameter, specifying if the moment was a leap second.
+
+**Returns**:
+- `res`: The time represented by `iso_datetime`, with UTC offset applied.
+- `consumed`: Number of bytes consumed by parsing the string.
+
+**Notes**:
+- Only 4-digit years are accepted.
+- Leap seconds are smeared into 23:59:59.
+*/
iso8601_to_time_utc :: proc(iso_datetime: string, is_leap: ^bool = nil) -> (res: Time, consumed: int) {
offset: int
-
res, offset, consumed = iso8601_to_time_and_offset(iso_datetime, is_leap)
res._nsec += (i64(-offset) * i64(Minute))
return res, consumed
}
-// Parses an ISO 8601 string and returns Time and a UTC offset in minutes.
-// e.g. 1985-04-12T23:20:50.52Z
-// Note: Only 4-digit years are accepted.
-// Optional pointer to boolean `is_leap` will return `true` if the moment was a leap second.
-// Leap seconds are smeared into 23:59:59.
+/*
+Parse an ISO 8601 string into a time and a UTC offset in minutes.
+
+This procedure parses an ISO 8601 string of roughly the following format:
+
+```text
+YYYY-MM-DD[Tt]HH:mm:ss[.nn][Zz][+-]HH:mm
+```
+
+And returns time, in UTC represented by that string, and the UTC offset, in
+minutes.
+
+**Inputs**:
+- `iso_datetime`: The string to be parsed.
+- `is_leap`: Optional output parameter, specifying if the moment was a leap second.
+
+**Returns**:
+- `res`: The time in UTC.
+- `utc_offset`: The UTC offset of the time, in minutes.
+- `consumed`: Number of bytes consumed by parsing the string.
+
+**Notes**:
+- Only 4-digit years are accepted.
+- Leap seconds are smeared into 23:59:59.
+*/
iso8601_to_time_and_offset :: proc(iso_datetime: string, is_leap: ^bool = nil) -> (res: Time, utc_offset: int, consumed: int) {
moment, offset, leap_second, count := iso8601_to_components(iso_datetime)
if count == 0 {
@@ -37,9 +76,32 @@ iso8601_to_time_and_offset :: proc(iso_datetime: string, is_leap: ^bool = nil) -
}
}
-// Parses an ISO 8601 string and returns Time and a UTC offset in minutes.
-// e.g. 1985-04-12T23:20:50.52Z
-// Performs no validation on whether components are valid, e.g. it'll return hour = 25 if that's what it's given
+/*
+Parse an ISO 8601 string into a datetime and a UTC offset in minutes.
+
+This procedure parses an ISO 8601 string of roughly the following format:
+
+```text
+YYYY-MM-DD[Tt]HH:mm:ss[.nn][Zz][+-]HH:mm
+```
+
+And returns datetime, in UTC represented by that string, and the UTC offset, in
+minutes.
+
+**Inputs**:
+- `iso_datetime`: The string to be parsed
+
+**Returns**:
+- `res`: The parsed datetime, in UTC.
+- `utc_offset`: The UTC offset, in minutes.
+- `is_leap`: Specifies whether the moment was a leap second.
+- `consumed`: The number of bytes consumed by parsing the string.
+
+**Notes**:
+- This procedure performs no validation on whether components are valid,
+ e.g. it'll return hour = 25 if that's what it's given in the specified
+ string.
+*/
iso8601_to_components :: proc(iso_datetime: string) -> (res: dt.DateTime, utc_offset: int, is_leap: bool, consumed: int) {
moment, offset, count, leap_second, ok := _iso8601_to_components(iso_datetime)
if !ok {
diff --git a/core/time/perf.odin b/core/time/perf.odin
index 123d67eca..784d7acd6 100644
--- a/core/time/perf.odin
+++ b/core/time/perf.odin
@@ -3,18 +3,39 @@ package time
import "base:runtime"
import "base:intrinsics"
+/*
+Type representing monotonic time, useful for measuring durations.
+*/
Tick :: struct {
_nsec: i64, // relative amount
}
+
+/*
+Obtain the current tick.
+*/
tick_now :: proc "contextless" () -> Tick {
return _tick_now()
}
+/*
+Obtain the difference between ticks.
+*/
tick_diff :: proc "contextless" (start, end: Tick) -> Duration {
d := end._nsec - start._nsec
return Duration(d)
}
+/*
+Incrementally obtain durations since last tick.
+
+This procedure returns the duration between the current tick and the tick
+stored in `prev` pointer, and then stores the current tick in location,
+specified by `prev`. If the prev pointer contains an zero-initialized tick,
+then the returned duration is 0.
+
+This procedure is meant to be used in a loop, or in other scenarios, where one
+might want to obtain time between multiple ticks at specific points.
+*/
tick_lap_time :: proc "contextless" (prev: ^Tick) -> Duration {
d: Duration
t := tick_now()
@@ -25,17 +46,21 @@ tick_lap_time :: proc "contextless" (prev: ^Tick) -> Duration {
return d
}
+/*
+Obtain the duration since last tick.
+*/
tick_since :: proc "contextless" (start: Tick) -> Duration {
return tick_diff(start, tick_now())
}
-
+/*
+Capture the duration the code in the current scope takes to execute.
+*/
@(deferred_in_out=_tick_duration_end)
SCOPED_TICK_DURATION :: proc "contextless" (d: ^Duration) -> Tick {
return tick_now()
}
-
_tick_duration_end :: proc "contextless" (d: ^Duration, t: Tick) {
d^ = tick_since(t)
}
@@ -62,6 +87,13 @@ when ODIN_OS != .Darwin && ODIN_OS != .Linux && ODIN_OS != .FreeBSD {
}
}
+/*
+Check if the CPU has invariant TSC.
+
+This procedure checks if the CPU contains an invariant TSC (Time stamp counter).
+Invariant TSC is a feature of modern processors that allows them to run their
+TSC at a fixed frequency, independent of ACPI state, and CPU frequency.
+*/
has_invariant_tsc :: proc "contextless" () -> bool {
when ODIN_ARCH == .amd64 {
return x86_has_invariant_tsc()
@@ -70,6 +102,17 @@ has_invariant_tsc :: proc "contextless" () -> bool {
return false
}
+/*
+Obtain the CPU's TSC frequency, in hertz.
+
+This procedure tries to obtain the CPU's TSC frequency in hertz. If the CPU
+doesn't have an invariant TSC, this procedure returns with an error. Otherwise
+an attempt is made to fetch the TSC frequency from the OS. If this fails,
+the frequency is obtained by sleeping for the specified amount of time and
+dividing the readings from TSC by the duration of the sleep.
+
+The duration of sleep can be controlled by `fallback_sleep` parameter.
+*/
tsc_frequency :: proc "contextless" (fallback_sleep := 2 * Second) -> (u64, bool) {
if !has_invariant_tsc() {
return 0, false
@@ -93,37 +136,64 @@ tsc_frequency :: proc "contextless" (fallback_sleep := 2 * Second) -> (u64, bool
return hz, true
}
+// Benchmark helpers
+
/*
- Benchmark helpers
+Errors returned by the `benchmark()` procedure.
*/
-
Benchmark_Error :: enum {
Okay = 0,
Allocation_Error,
}
+/*
+Options for benchmarking.
+*/
Benchmark_Options :: struct {
+ // The initialization procedure. `benchmark()` will call this before taking measurements.
setup: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error),
+ // The procedure to benchmark.
bench: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error),
+ // The deinitialization procedure.
teardown: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error),
-
+ // Field to be used by `bench()` procedure for any purpose.
rounds: int,
+ // Field to be used by `bench()` procedure for any purpose.
bytes: int,
+ // Field to be used by `bench()` procedure for any purpose.
input: []u8,
-
+ // `bench()` writes to specify the count of elements processed.
count: int,
+ // `bench()` writes to specify the number of bytes processed.
processed: int,
+ // `bench()` can write the output slice here.
output: []u8, // Unused for hash benchmarks
+ // `bench()` can write the output hash here.
hash: u128,
-
- /*
- Performance
- */
+ // `benchmark()` procedure will output the duration of benchmark
duration: Duration,
+ // `benchmark()` procedure will output the average count of elements
+ // processed per second, using the `count` field of this struct.
rounds_per_second: f64,
+ // `benchmark()` procedure will output the average number of megabytes
+ // processed per second, using the `processed` field of this struct.
megabytes_per_second: f64,
}
+/*
+Benchmark a procedure.
+
+This procedure produces a benchmark. The procedure specified in the `bench`
+field of the `options` parameter will be benchmarked. The following metrics
+can be obtained:
+
+- Run time of the procedure
+- Number of elements per second processed on average
+- Number of bytes per second this processed on average
+
+In order to obtain these metrics, the `bench()` procedure writes to `options`
+struct the number of elements or bytes it has processed.
+*/
benchmark :: proc(options: ^Benchmark_Options, allocator := context.allocator) -> (err: Benchmark_Error) {
assert(options != nil)
assert(options.bench != nil)
diff --git a/core/time/rfc3339.odin b/core/time/rfc3339.odin
index 0a2d431b7..e4c6565d6 100644
--- a/core/time/rfc3339.odin
+++ b/core/time/rfc3339.odin
@@ -4,10 +4,33 @@ package time
import dt "core:time/datetime"
-// Parses an RFC 3339 string and returns Time in UTC, with any UTC offset applied to it.
-// Only 4-digit years are accepted.
-// Optional pointer to boolean `is_leap` will return `true` if the moment was a leap second.
-// Leap seconds are smeared into 23:59:59.
+/*
+Parse an RFC 3339 string into time with a UTC offset applied to it.
+
+This procedure parses the specified RFC 3339 strings of roughly the following
+format:
+
+```text
+YYYY-MM-DD[Tt]HH:mm:ss[.nn][Zz][+-]HH:mm
+```
+
+And returns the time that was represented by the RFC 3339 string, with the UTC
+offset applied to it.
+
+**Inputs**:
+- `rfc_datetime`: An RFC 3339 string to parse.
+- `is_leap`: Optional output parameter specifying whether the moment was a leap
+ second.
+
+**Returns**:
+- `res`: The time, with UTC offset applied, that was parsed from the RFC 3339
+ string.
+- `consumed`: The number of bytes consumed by parsing the RFC 3339 string.
+
+**Notes**:
+- Only 4-digit years are accepted.
+- Leap seconds are smeared into 23:59:59.
+*/
rfc3339_to_time_utc :: proc(rfc_datetime: string, is_leap: ^bool = nil) -> (res: Time, consumed: int) {
offset: int
@@ -16,11 +39,34 @@ rfc3339_to_time_utc :: proc(rfc_datetime: string, is_leap: ^bool = nil) -> (res:
return res, consumed
}
-// Parses an RFC 3339 string and returns Time and a UTC offset in minutes.
-// e.g. 1985-04-12T23:20:50.52Z
-// Note: Only 4-digit years are accepted.
-// Optional pointer to boolean `is_leap` will return `true` if the moment was a leap second.
-// Leap seconds are smeared into 23:59:59.
+/*
+Parse an RFC 3339 string into a time and a UTC offset in minutes.
+
+This procedure parses the specified RFC 3339 strings of roughly the following
+format:
+
+```text
+YYYY-MM-DD[Tt]HH:mm:ss[.nn][Zz][+-]HH:mm
+```
+
+And returns the time, in UTC and a UTC offset, in minutes, that were represented
+by the RFC 3339 string.
+
+**Inputs**:
+- `rfc_datetime`: The RFC 3339 string to be parsed.
+- `is_leap`: Optional output parameter specifying whether the moment was a
+ leap second.
+
+**Returns**:
+- `res`: The time, in UTC, that was parsed from the RFC 3339 string.
+- `utc_offset`: The UTC offset, in minutes, that was parsed from the RFC 3339
+ string.
+- `consumed`: The number of bytes consumed by parsing the string.
+
+**Notes**:
+- Only 4-digit years are accepted.
+- Leap seconds are smeared into 23:59:59.
+*/
rfc3339_to_time_and_offset :: proc(rfc_datetime: string, is_leap: ^bool = nil) -> (res: Time, utc_offset: int, consumed: int) {
moment, offset, leap_second, count := rfc3339_to_components(rfc_datetime)
if count == 0 {
@@ -38,9 +84,31 @@ rfc3339_to_time_and_offset :: proc(rfc_datetime: string, is_leap: ^bool = nil) -
}
}
-// Parses an RFC 3339 string and returns Time and a UTC offset in minutes.
-// e.g. 1985-04-12T23:20:50.52Z
-// Performs no validation on whether components are valid, e.g. it'll return hour = 25 if that's what it's given
+/*
+Parse an RFC 3339 string into a datetime and a UTC offset in minutes.
+
+This procedure parses the specified RFC 3339 strings of roughly the following
+format:
+
+```text
+YYYY-MM-DD[Tt]HH:mm:ss[.nn][Zz][+-]HH:mm
+```
+
+And returns the datetime, in UTC and the UTC offset, in minutes, that were
+represented by the RFC 3339 string.
+
+**Inputs**:
+- `rfc_datetime`: The RFC 3339 string to parse.
+
+**Returns**:
+- `res`: The datetime, in UTC, that was parsed from the RFC 3339 string.
+- `utc_offset`: The UTC offset, in minutes, that was parsed from the RFC 3339
+ string.
+- `is_leap`: Specifies whether the moment was a leap second.
+- `consumed`: Number of bytes consumed by parsing the string.
+
+Performs no validation on whether components are valid, e.g. it'll return hour = 25 if that's what it's given
+*/
rfc3339_to_components :: proc(rfc_datetime: string) -> (res: dt.DateTime, utc_offset: int, is_leap: bool, consumed: int) {
moment, offset, count, leap_second, ok := _rfc3339_to_components(rfc_datetime)
if !ok {
diff --git a/core/time/time.odin b/core/time/time.odin
index 4ea5afc70..ef2bc0aed 100644
--- a/core/time/time.odin
+++ b/core/time/time.odin
@@ -3,24 +3,72 @@ package time
import "base:intrinsics"
import dt "core:time/datetime"
+/*
+Type representing duration, with nanosecond precision.
+*/
Duration :: distinct i64
+/*
+The duration equal to one nanosecond (1e-9 seconds).
+*/
Nanosecond :: Duration(1)
+
+/*
+The duration equal to one microsecond (1e-6 seconds).
+*/
Microsecond :: 1000 * Nanosecond
+
+/*
+The duration equal to one millisecond (1e-3 seconds).
+*/
Millisecond :: 1000 * Microsecond
+
+/*
+The duration equal to one second.
+*/
Second :: 1000 * Millisecond
+
+/*
+The duration equal to one minute (60 seconds).
+*/
Minute :: 60 * Second
+
+/*
+The duration equal to one hour (3600 seconds).
+*/
Hour :: 60 * Minute
+/*
+Minimum representable duration.
+*/
MIN_DURATION :: Duration(-1 << 63)
+
+/*
+Maximum representable duration.
+*/
MAX_DURATION :: Duration(1<<63 - 1)
+/*
+Value specifying whether the time procedures are supported by the current
+platform.
+*/
IS_SUPPORTED :: _IS_SUPPORTED
+/*
+Specifies time since the UNIX epoch, with nanosecond precision.
+
+Capable of representing any time within the following range:
+
+- `min: 1677-09-21 00:12:44.145224192 +0000 UTC`
+- `max: 2262-04-11 23:47:16.854775807 +0000 UTC`
+*/
Time :: struct {
_nsec: i64, // Measured in UNIX nanonseconds
}
+/*
+Type representing a month.
+*/
Month :: enum int {
January = 1,
February,
@@ -36,6 +84,9 @@ Month :: enum int {
December,
}
+/*
+Type representing a weekday.
+*/
Weekday :: enum int {
Sunday = 0,
Monday,
@@ -46,20 +97,37 @@ Weekday :: enum int {
Saturday,
}
+/*
+Type representing a stopwatch.
+
+The stopwatch is used for measuring the total time in multiple "runs". When the
+stopwatch is started, it starts counting time. When the stopwatch is stopped,
+the difference in time between the last start and the stop is added to the
+total. When the stopwatch resets, the total is reset.
+*/
Stopwatch :: struct {
running: bool,
_start_time: Tick,
_accumulation: Duration,
}
+/*
+Obtain the current time.
+*/
now :: proc "contextless" () -> Time {
return _now()
}
+/*
+Sleep for the specified duration.
+*/
sleep :: proc "contextless" (d: Duration) {
_sleep(d)
}
+/*
+Start the stopwatch.
+*/
stopwatch_start :: proc "contextless" (stopwatch: ^Stopwatch) {
if !stopwatch.running {
stopwatch._start_time = tick_now()
@@ -67,6 +135,9 @@ stopwatch_start :: proc "contextless" (stopwatch: ^Stopwatch) {
}
}
+/*
+Stop the stopwatch.
+*/
stopwatch_stop :: proc "contextless" (stopwatch: ^Stopwatch) {
if stopwatch.running {
stopwatch._accumulation += tick_diff(stopwatch._start_time, tick_now())
@@ -74,11 +145,21 @@ stopwatch_stop :: proc "contextless" (stopwatch: ^Stopwatch) {
}
}
+/*
+Reset the stopwatch.
+*/
stopwatch_reset :: proc "contextless" (stopwatch: ^Stopwatch) {
stopwatch._accumulation = {}
stopwatch.running = false
}
+/*
+Obtain the total time, counted by the stopwatch.
+
+This procedure obtains the total time, counted by the stopwatch. If the stopwatch
+isn't stopped at the time of calling this procedure, the time between the last
+start and the current time is also accounted for.
+*/
stopwatch_duration :: proc "contextless" (stopwatch: Stopwatch) -> Duration {
if !stopwatch.running {
return stopwatch._accumulation
@@ -86,40 +167,92 @@ stopwatch_duration :: proc "contextless" (stopwatch: Stopwatch) -> Duration {
return stopwatch._accumulation + tick_diff(stopwatch._start_time, tick_now())
}
+/*
+Calculate the duration elapsed between two times.
+*/
diff :: proc "contextless" (start, end: Time) -> Duration {
d := end._nsec - start._nsec
return Duration(d)
}
+/*
+Calculate the duration elapsed since a specific time.
+*/
since :: proc "contextless" (start: Time) -> Duration {
return diff(start, now())
}
+/*
+Obtain the number of nanoseconds in a duration.
+*/
duration_nanoseconds :: proc "contextless" (d: Duration) -> i64 {
return i64(d)
}
+
+/*
+Obtain the number of microseconds in a duration.
+*/
duration_microseconds :: proc "contextless" (d: Duration) -> f64 {
return duration_seconds(d) * 1e6
}
+
+/*
+Obtain the number of milliseconds in a duration.
+*/
duration_milliseconds :: proc "contextless" (d: Duration) -> f64 {
return duration_seconds(d) * 1e3
}
+
+/*
+Obtain the number of seconds in a duration.
+*/
duration_seconds :: proc "contextless" (d: Duration) -> f64 {
sec := d / Second
nsec := d % Second
return f64(sec) + f64(nsec)/1e9
}
+
+/*
+Obtain the number of minutes in a duration.
+*/
duration_minutes :: proc "contextless" (d: Duration) -> f64 {
min := d / Minute
nsec := d % Minute
return f64(min) + f64(nsec)/(60*1e9)
}
+
+/*
+Obtain the number of hours in a duration.
+*/
duration_hours :: proc "contextless" (d: Duration) -> f64 {
hour := d / Hour
nsec := d % Hour
return f64(hour) + f64(nsec)/(60*60*1e9)
}
+/*
+Round a duration to a specific unit.
+
+This procedure rounds the duration to a specific unit.
+
+**Inputs**:
+- `d`: The duration to round.
+- `m`: The unit to round to.
+
+**Returns**:
+- The duration `d`, rounded to the unit specified by `m`.
+
+**Example**:
+
+In order to obtain the rough amount of seconds in a duration, the following call
+can be used:
+
+```
+time.duration_round(my_duration, time.Second)
+```
+
+**Note**: Any duration can be supplied as a unit.
+*/
duration_round :: proc "contextless" (d, m: Duration) -> Duration {
_less_than_half :: #force_inline proc "contextless" (x, y: Duration) -> bool {
return u64(x)+u64(x) < u64(y)
@@ -149,50 +282,103 @@ duration_round :: proc "contextless" (d, m: Duration) -> Duration {
return MAX_DURATION
}
+/*
+Truncate the duration to the specified unit.
+
+This procedure truncates the duration `d` to the unit specified by `m`.
+
+**Inputs**:
+- `d`: The duration to truncate.
+- `m`: The unit to truncate to.
+
+**Returns**:
+- The duration `d`, truncated to the unit specified by `m`.
+
+**Example**:
+
+In order to obtain the amount of whole seconds in a duration, the following call
+can be used:
+
+```
+time.duration_round(my_duration, time.Second)
+```
+
+**Note**: Any duration can be supplied as a unit.
+*/
duration_truncate :: proc "contextless" (d, m: Duration) -> Duration {
return d if m <= 0 else d - d%m
}
+/*
+Parse time into date components.
+*/
date :: proc "contextless" (t: Time) -> (year: int, month: Month, day: int) {
year, month, day, _ = _abs_date(_time_abs(t), true)
return
}
+/*
+Obtain the year of the date specified by time.
+*/
year :: proc "contextless" (t: Time) -> (year: int) {
year, _, _, _ = _date(t, true)
return
}
+/*
+Obtain the month of the date specified by time.
+*/
month :: proc "contextless" (t: Time) -> (month: Month) {
_, month, _, _ = _date(t, true)
return
}
+/*
+Obtain the day of the date specified by time.
+*/
day :: proc "contextless" (t: Time) -> (day: int) {
_, _, day, _ = _date(t, true)
return
}
+/*
+Obtain the week day of the date specified by time.
+*/
weekday :: proc "contextless" (t: Time) -> (weekday: Weekday) {
abs := _time_abs(t)
sec := (abs + u64(Weekday.Monday) * SECONDS_PER_DAY) % SECONDS_PER_WEEK
return Weekday(int(sec) / SECONDS_PER_DAY)
}
+/*
+Obtain the time components from a time, a duration or a stopwatch's total.
+*/
clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
+/*
+Obtain the time components from a time.
+*/
clock_from_time :: proc "contextless" (t: Time) -> (hour, min, sec: int) {
return clock_from_seconds(_time_abs(t))
}
+/*
+Obtain the time components from a duration.
+*/
clock_from_duration :: proc "contextless" (d: Duration) -> (hour, min, sec: int) {
return clock_from_seconds(u64(d/1e9))
}
+/*
+Obtain the time components from a stopwatch's total.
+*/
clock_from_stopwatch :: proc "contextless" (s: Stopwatch) -> (hour, min, sec: int) {
return clock_from_duration(stopwatch_duration(s))
}
+/*
+Obtain the time components from the number of seconds.
+*/
clock_from_seconds :: proc "contextless" (nsec: u64) -> (hour, min, sec: int) {
sec = int(nsec % SECONDS_PER_DAY)
hour = sec / SECONDS_PER_HOUR
@@ -202,10 +388,16 @@ clock_from_seconds :: proc "contextless" (nsec: u64) -> (hour, min, sec: int) {
return
}
+/*
+Read the timestamp counter of the CPU.
+*/
read_cycle_counter :: proc "contextless" () -> u64 {
return u64(intrinsics.read_cycle_counter())
}
+/*
+Obtain time from unix seconds and unix nanoseconds.
+*/
unix :: proc "contextless" (sec: i64, nsec: i64) -> Time {
sec, nsec := sec, nsec
if nsec < 0 || nsec >= 1e9 {
@@ -220,31 +412,59 @@ unix :: proc "contextless" (sec: i64, nsec: i64) -> Time {
return Time{(sec*1e9 + nsec)}
}
+/*
+Obtain time from unix nanoseconds.
+*/
from_nanoseconds :: #force_inline proc "contextless" (nsec: i64) -> Time {
return Time{nsec}
}
+/*
+Alias for `time_to_unix`.
+*/
to_unix_seconds :: time_to_unix
+
+/*
+Obtain the unix seconds from a time.
+*/
time_to_unix :: proc "contextless" (t: Time) -> i64 {
return t._nsec/1e9
}
+/*
+Alias for `time_to_unix_nano`.
+*/
to_unix_nanoseconds :: time_to_unix_nano
+
+/*
+Obtain the unix nanoseconds from a time.
+*/
time_to_unix_nano :: proc "contextless" (t: Time) -> i64 {
return t._nsec
}
+/*
+Add duration to a time.
+*/
time_add :: proc "contextless" (t: Time, d: Duration) -> Time {
return Time{t._nsec + i64(d)}
}
-// Accurate sleep borrowed from: https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/
-//
-// Accuracy seems to be pretty good out of the box on Linux, to within around 4µs worst case.
-// On Windows it depends but is comparable with regular sleep in the worst case.
-// To get the same kind of accuracy as on Linux, have your program call `windows.timeBeginPeriod(1)` to
-// tell Windows to use a more accurate timer for your process.
-// Additionally your program should call `windows.timeEndPeriod(1)` once you're done with `accurate_sleep`.
+/*
+Accurate sleep
+
+This procedure sleeps for the duration specified by `d`, very accurately.
+
+**Note**: Implementation borrowed from: [this source](https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/)
+
+**Note(linux)**: The accuracy is within around 4µs (microseconds), in the worst case.
+
+**Note(windows)**: The accuracy depends but is comparable with regular sleep in
+the worst case. To get the same kind of accuracy as on Linux, have your program
+call `windows.timeBeginPeriod(1)` to tell Windows to use a more accurate timer
+for your process. Additionally your program should call `windows.timeEndPeriod(1)`
+once you're done with `accurate_sleep`.
+*/
accurate_sleep :: proc "contextless" (d: Duration) {
to_sleep, estimate, mean, m2, count: Duration
@@ -362,6 +582,13 @@ _abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Mon
return
}
+/*
+Convert datetime components into time.
+
+This procedure calculates the time from datetime components supplied in the
+arguments to this procedure. If the datetime components don't represent a valid
+datetime, the function returns `false` in the second argument.
+*/
components_to_time :: proc "contextless" (#any_int year, #any_int month, #any_int day, #any_int hour, #any_int minute, #any_int second: i64, #any_int nsec := i64(0)) -> (t: Time, ok: bool) {
this_date, err := dt.components_to_datetime(year, month, day, hour, minute, second, nsec)
if err != .None {
@@ -370,6 +597,12 @@ components_to_time :: proc "contextless" (#any_int year, #any_int month, #any_in
return compound_to_time(this_date)
}
+/*
+Convert datetime into time.
+
+If the datetime represents a time outside of a valid range, `false` is returned
+as the second return value. See `Time` for the representable range.
+*/
compound_to_time :: proc "contextless" (datetime: dt.DateTime) -> (t: Time, ok: bool) {
unix_epoch := dt.DateTime{{1970, 1, 1}, {0, 0, 0, 0}}
delta, err := dt.sub(datetime, unix_epoch)
@@ -387,12 +620,21 @@ compound_to_time :: proc "contextless" (datetime: dt.DateTime) -> (t: Time, ok:
return Time{_nsec=i64(nanoseconds)}, true
}
+/*
+Convert datetime components into time.
+*/
datetime_to_time :: proc{components_to_time, compound_to_time}
+/*
+Check if a year is a leap year.
+*/
is_leap_year :: proc "contextless" (year: int) -> (leap: bool) {
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
}
+/*
+Days before each month in a year, not counting the leap day on february 29th.
+*/
@(rodata)
days_before := [?]i32{
0,
@@ -410,11 +652,37 @@ days_before := [?]i32{
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
}
-
+/*
+Number of seconds in a minute (without leap seconds).
+*/
SECONDS_PER_MINUTE :: 60
+
+/*
+Number of seconds in an hour (without leap seconds).
+*/
SECONDS_PER_HOUR :: 60 * SECONDS_PER_MINUTE
+
+/*
+Number of seconds in a day (without leap seconds).
+*/
SECONDS_PER_DAY :: 24 * SECONDS_PER_HOUR
+
+/*
+Number of seconds in a week (without leap seconds).
+*/
SECONDS_PER_WEEK :: 7 * SECONDS_PER_DAY
+
+/*
+Days in 400 years, with leap days.
+*/
DAYS_PER_400_YEARS :: 365*400 + 97
+
+/*
+Days in 100 years, with leap days.
+*/
DAYS_PER_100_YEARS :: 365*100 + 24
+
+/*
+Days in 4 years, with leap days.
+*/
DAYS_PER_4_YEARS :: 365*4 + 1