Browse Source

Merge pull request #3948 from flysand7/docs-time

[time]: Document all functions
Jeroen van Rijn 1 year ago
parent
commit
cdd2c98b8d
4 changed files with 511 additions and 43 deletions
  1. 75 13
      core/time/iso8601.odin
  2. 80 10
      core/time/perf.odin
  3. 80 12
      core/time/rfc3339.odin
  4. 276 8
      core/time/time.odin

+ 75 - 13
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 {

+ 80 - 10
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)

+ 80 - 12
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 {

+ 276 - 8
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