mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-31 18:32:12 +00:00
[time]: Document all functions
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user