Reorganise and test std::time

std::time is now broken up into two modules:

1. std::time, which provides time related types.
2. std::time::duration, which provides the Duration type.

This separation allows for the use of module methods to construct a
Duration using different time units, instead of using methods defined on
Duration itself. This means that instead of this:

    import std::time::Duration

    Duration.from_seconds(5)

You now write this:

    import std::time::duration

    duration.from_seconds(5)

== Renaming MonotonicTime to Instant

MonotonicTime has been renamed to "Instant", which is a bit shorter and
much easier to type. A new method has been added as well: "elapsed".
This method can be used to measure the time that has elapsed since the
Instant was created.

== Adding and subtracting SystemTime and Instant

The methods + and - for SystemTime and Instant no longer accept a
ToFloat, instead they require a Duration. Accepting a ToFloat would
allow you to add a SystemTime to another SystemTime, or add an Instant
to an Instant. Neither make sense, so instead we now require the use of
a Duration. This means that instead of this:

    import std::time

    time.now + 5

You now have to write this:

    import std::time
    import std::time::duration

    time.now + duration.from_seconds(5)

== Tests

Finally, both std::time and std::time::duration have tests. These tests
uncovered a few bugs in the implementation of SystemTime, which have
been resolved.
parent f9c07ef6
Pipeline #41277834 passed with stages
in 15 minutes and 11 seconds
......@@ -3,7 +3,7 @@
#! The `Formatter` trait is used by a `Runner` to display test results as they
#! finish, and to display a summary once all tests have been executed.
import std::test::test::Test
import std::time::Duration
import std::time::duration::Duration
## A type for formatting the results of tests.
trait Formatter {
......
......@@ -10,7 +10,7 @@ import std::string_buffer::StringBuffer
import std::test::error::TestFailure
import std::test::formatter::Formatter
import std::test::test::Test
import std::time::Duration
import std::time::duration::Duration
## The minimum amount of time (in seconds) the test suite should run, before
## displaying the execution time in seconds.
......
......@@ -21,7 +21,8 @@ import std::process::(self, Receiver, Sender)
import std::test::config::Configuration
import std::test::formatter::Formatter
import std::test::test::Test
import std::time::(self, Duration)
import std::time
import std::time::duration
import std::vm
## The exit status to use if one or more tests failed to run.
......@@ -119,7 +120,7 @@ object RunTests impl Command {
## Once this method has been called, no new tests can be registered.
def run(state: RunnerState) {
let mut pending = 0
let start_time = time.monotonic
let start_time = time.instant
let last_index = state.tests.length - 1
let executed = []
let failed = []
......@@ -152,7 +153,7 @@ object RunTests impl Command {
}
}
let duration = Duration.from_seconds((time.monotonic - start_time).to_float)
let duration = start_time.elapsed
failed.empty?.if_false {
state.formatter.failures(failed)
......
This diff is collapsed.
#! Types for measuring the duration between two points in time.
import std::conversion::(ToFloat, ToInteger)
import std::operators::(Add, Subtract)
import std::operators::(Smaller, Greater, GreaterOrEqual, SmallerOrEqual, Equal)
import std::time::constants::*
## A span of time measured in seconds.
##
## A `Duration` can be used to measure the span of time without having to worry
## about casting the time to different scales yourself. A `Duration` can be
## created using various scales such as seconds and milliseconds.
object Duration impl
ToInteger,
ToFloat,
Add!(Self),
Subtract!(Self),
Smaller,
Greater,
SmallerOrEqual,
GreaterOrEqual {
def init(seconds: Float) {
let @seconds = seconds
}
## Returns the duration in seconds.
##
## # Examples
##
## Getting the seconds in a `Duration`:
##
## import std::time::duration
##
## duration.from_seconds(5).as_seconds # => 5.0
def as_seconds -> Float {
@seconds
}
## Returns the duration in milliseconds.
##
## # Examples
##
## Getting the milliseconds in a `Duration`:
##
## import std::time::duration
##
## duration.from_seconds(5).as_milliseconds # => 5000.0
def as_milliseconds -> Float {
@seconds * MILLISEC_TO_SEC
}
## Returns the duration in microseconds.
##
## # Examples
##
## Getting the microseconds in a `Duration`:
##
## import std::time::duration
##
## duration.from_seconds(5).as_microseconds # => 5000000.0
def as_microseconds -> Float {
@seconds * MICROSEC_TO_SEC
}
## Returns the duration in nanoseconds.
##
## # Examples
##
## Getting the nanoseconds in a `Duration`:
##
## import std::time::duration
##
## duration.from_seconds(5).as_nanoseconds # => 5000000000.0
def as_nanoseconds -> Float {
@seconds * NANOSEC_TO_SEC
}
## Returns the number of seconds in the `Duration`.
def to_integer -> Integer {
@seconds.to_integer
}
## Returns the number of seconds in the `Duration`.
def to_float -> Float {
@seconds
}
## Adds two `Duration` objects together.
##
## # Examples
##
## Adding two `Duration` objects:
##
## import std::time::duration
##
## let sum = duration.from_seconds(5) + duration.from_seconds(2)
##
## sum.as_seconds # => 7.0
def +(other: Self) -> Self {
Duration.new(@seconds + other.as_seconds)
}
## Subtracts the given `Duration` from `self`.
##
## # Examples
##
## Subtracting a `Duration` from another `Duration`
##
## import std::time::duration
##
## let diff = duration.from_seconds(5) - duration.from_seconds(2)
##
## diff.as_seconds # => 3.0
def -(other: Self) -> Self {
Duration.new(@seconds - other.as_seconds)
}
## Returns `True` if `self` is smaller than the given `Duration`.
##
## # Examples
##
## Comparing two `Duration` objects:
##
## import std::time::duration
##
## duration.from_seconds(5) < duration.from_seconds(10) # => True
def <(other: Self) -> Boolean {
@seconds < other.as_seconds
}
## Returns `True` if `self` is greater than the given `Duration`.
##
## # Examples
##
## Comparing two `Duration` objects:
##
## import std::time::duration
##
## duration.from_seconds(5) > duration.from_seconds(10) # => False
def >(other: Self) -> Boolean {
@seconds > other.as_seconds
}
## Returns `True` if `self` is smaller than or equal to the given `Duration`.
##
## # Examples
##
## Comparing two `Duration` objects:
##
## import std::time::duration
##
## duration.from_seconds(5) <= duration.from_seconds(10) # => True
## duration.from_seconds(5) <= duration.from_seconds(5) # => True
def <=(other: Self) -> Boolean {
@seconds <= other.as_seconds
}
## Returns `True` if `self` is greater than or equal to the given `Duration`.
##
## # Examples
##
## Comparing two `Duration` objects:
##
## import std::time::duration
##
## duration.from_seconds(5) >= duration.from_seconds(10) # => False
## duration.from_seconds(5) >= duration.from_seconds(5) # => True
def >=(other: Self) -> Boolean {
@seconds >= other.as_seconds
}
}
## Creates a new `Duration` from the given number of seconds.
##
## # Examples
##
## Creating a `Duration` using an `Integer`:
##
## import std::time::duration
##
## duration.from_seconds(10)
##
## Creating a `Duration` using a `Float`:
##
## import std::time::duration
##
## duration.from_seconds(10.5)
def from_seconds(seconds: ToFloat) -> Duration {
Duration.new(seconds.to_float)
}
## Creates a new `Duration` from the given number of milliseconds.
##
## # Examples
##
## Creating a `Duration` using an `Integer`:
##
## import std::time::duration
##
## duration.from_milliseconds(10)
##
## Creating a `Duration` using a `Float`:
##
## import std::time::duration
##
## duration.from_milliseconds(10.5)
def from_milliseconds(milliseconds: ToFloat) -> Duration {
Duration.new(milliseconds.to_float / MILLISEC_TO_SEC)
}
## Creates a new `Duration` from the given number of nanoseconds.
##
## # Examples
##
## Creating a `Duration` using an `Integer`:
##
## import std::time::duration
##
## duration.from_nanoseconds(10)
##
## Creating a `Duration` using a `Float`:
##
## import std::time::duration
##
## duration.from_nanoseconds(10.5)
def from_nanoseconds(nanoseconds: ToFloat) -> Duration {
Duration.new(nanoseconds.to_float / NANOSEC_TO_SEC)
}
......@@ -32,6 +32,8 @@ import test::std::test_process
import test::std::test_range
import test::std::test_string
import test::std::test_string_buffer
import test::std::test_time
import test::std::test_trait
import test::std::time::test_duration
test.run
import std::process::(self, Receiver, Sender)
import std::test
import std::test::assert
import std::time::MonotonicTime
import std::time::Instant
test.group('std::process::Sender.send') do (g) {
g.test('Sending a message to a process') {
......@@ -108,12 +108,12 @@ test.group('std::process.suspend') do (g) {
}
g.test('Suspending a process for a minimum amount of time') {
let start = MonotonicTime.new
let start = Instant.new
let wait = 0.01
process.suspend(wait)
let duration = (MonotonicTime.new - start).to_float
let duration = start.elapsed.to_float
assert.true(duration >= wait)
}
......
import std::process
import std::test
import std::test::assert
import std::time::(self, Instant, SystemTime)
import std::time::duration
let MIN_UTC_OFFSET = -12 * 3600
let MAX_UTC_OFFSET = 14 * 3600
test.group('std::time::SystemTime.hour') do (g) {
g.test('Obtaining the hour') {
assert.equal(SystemTime.new(hour: 4).hour, 4)
}
}
test.group('std::time::SystemTime.minute') do (g) {
g.test('Obtaining the minute') {
assert.equal(SystemTime.new(minute: 4).minute, 4)
}
}
test.group('std::time::SystemTime.second') do (g) {
g.test('Obtaining the second') {
assert.equal(SystemTime.new(second: 4).second, 4)
}
}
test.group('std::time::SystemTime.sub_second') do (g) {
g.test('Obtaining the sub second') {
assert.equal(SystemTime.new(sub_second: 4.0).sub_second, 4.0)
}
}
test.group('std::time::SystemTime.year') do (g) {
g.test('Obtaining the year') {
assert.equal(SystemTime.new(year: 4).year, 4)
}
}
test.group('std::time::SystemTime.month') do (g) {
g.test('Obtaining the month') {
assert.equal(SystemTime.new(month: 4).month, 4)
}
}
test.group('std::time::SystemTime.day') do (g) {
g.test('Obtaining the day') {
assert.equal(SystemTime.new(day: 4).day, 4)
}
}
test.group('std::time::SystemTime.day_of_week') do (g) {
g.test('Obtaining the day of the week') {
assert.equal(SystemTime.new(year: 2018, month: 12, day: 10).day_of_week, 1)
assert.equal(SystemTime.new(year: 2018, month: 12, day: 11).day_of_week, 2)
assert.equal(SystemTime.new(year: 2018, month: 12, day: 12).day_of_week, 3)
assert.equal(SystemTime.new(year: 2018, month: 12, day: 13).day_of_week, 4)
assert.equal(SystemTime.new(year: 2018, month: 12, day: 14).day_of_week, 5)
assert.equal(SystemTime.new(year: 2018, month: 12, day: 15).day_of_week, 6)
assert.equal(SystemTime.new(year: 2018, month: 12, day: 16).day_of_week, 7)
assert.equal(SystemTime.new(year: 2018, month: 12, day: 17).day_of_week, 1)
assert.equal(SystemTime.new(year: 2018, month: 12, day: 18).day_of_week, 2)
assert.equal(SystemTime.new(year: 2018, month: 12, day: 19).day_of_week, 3)
assert.equal(SystemTime.new(year: 2018, month: 12, day: 20).day_of_week, 4)
assert.equal(SystemTime.new(year: 2018, month: 12, day: 21).day_of_week, 5)
assert.equal(SystemTime.new(year: 2018, month: 12, day: 22).day_of_week, 6)
assert.equal(SystemTime.new(year: 2018, month: 12, day: 23).day_of_week, 7)
}
g.test('Obtaining the day of the week for a date before the Unix epoch') {
assert.equal(SystemTime.new(year: 1969, month: 12, day: 29).day_of_week, 1)
assert.equal(SystemTime.new(year: 1969, month: 12, day: 30).day_of_week, 2)
assert.equal(SystemTime.new(year: 1969, month: 12, day: 31).day_of_week, 3)
}
g.test('Obtaining the day of the week for the Unix epoch') {
assert.equal(SystemTime.new(year: 1970, month: 1, day: 1).day_of_week, 4)
}
}
test.group('std::time::SystemTime.day_of_year') do (g) {
g.test('Obtaining the day of the year for a date before the Unix epoch') {
assert.equal(SystemTime.new(year: 1969, month: 12, day: 29).day_of_year, 363)
assert.equal(SystemTime.new(year: 1969, month: 12, day: 30).day_of_year, 364)
assert.equal(SystemTime.new(year: 1969, month: 12, day: 31).day_of_year, 365)
}
g.test('Obtaining the day of the year for a date on the Unix epoch') {
assert.equal(SystemTime.new(year: 1970, month: 1, day: 1).day_of_year, 1)
assert.equal(SystemTime.new(year: 1970, month: 1, day: 2).day_of_year, 2)
assert.equal(SystemTime.new(year: 1970, month: 4, day: 1).day_of_year, 91)
}
g.test('Obtaining the day of the year for a leap year') {
assert.equal(SystemTime.new(year: 2016, month: 12, day: 31).day_of_year, 366)
}
g.test('Obtaining the day of the year for a date after the Unix epoch') {
assert.equal(SystemTime.new(year: 2018, month: 12, day: 31).day_of_year, 365)
}
}
test.group('std::time::SystemTime.days_since_unix_epoch') do (g) {
g.test('Obtaining the days leading up to the Unix epoch') {
assert.equal(
SystemTime.new(year: 1969, month: 12, day: 31).days_since_unix_epoch,
-1
)
assert.equal(
SystemTime.new(year: 1969, month: 12, day: 30).days_since_unix_epoch,
-2
)
assert.equal(SystemTime.new(year: 1969, day: 1).days_since_unix_epoch, -365)
}
g.test('Obtaining the days since the Unix epoch') {
assert.equal(SystemTime.new(year: 1970, day: 1).days_since_unix_epoch, 0)
assert.equal(SystemTime.new(year: 1970, day: 2).days_since_unix_epoch, 1)
assert.equal(SystemTime.new(year: 1970, day: 3).days_since_unix_epoch, 2)
}
g.test('Obtaining the days since the Unix epoch for a leap year') {
assert.equal(
SystemTime.new(year: 2016, day: 3).days_since_unix_epoch,
16803
)
}
}
test.group('std::time::SystemTime.before_unix_epoch?') do (g) {
g.test('Checking if a SystemTime is before the Unix epoch') {
assert.true(SystemTime.new(year: 1950).before_unix_epoch?)
assert.false(SystemTime.new(year: 1970).before_unix_epoch?)
assert.false(SystemTime.new(year: 1971).before_unix_epoch?)
}
}
test.group('std::time::SystemTime.leap_year?') do (g) {
g.test('Checking if a SystemTime is in a leap year') {
assert.true(SystemTime.new(year: 2000).leap_year?)
assert.true(SystemTime.new(year: 2016).leap_year?)
assert.true(SystemTime.new(year: 2020).leap_year?)
assert.true(SystemTime.new(year: 2024).leap_year?)
assert.true(SystemTime.new(year: 2400).leap_year?)
assert.false(SystemTime.new(year: 1700).leap_year?)
assert.false(SystemTime.new(year: 1800).leap_year?)
assert.false(SystemTime.new(year: 1900).leap_year?)
assert.false(SystemTime.new(year: 2017).leap_year?)
assert.false(SystemTime.new(year: 2018).leap_year?)
assert.false(SystemTime.new(year: 2019).leap_year?)
assert.false(SystemTime.new(year: 2100).leap_year?)
}
}
test.group('std::time::SystemTime.dst?') do (g) {
g.test('Checking if Daylight Savings Time is active') {
assert.false(SystemTime.new.dst?)
assert.true(SystemTime.new(dst: True).dst?)
}
}
test.group('std::time::SystemTime.utc_offset') do (g) {
g.test('Obtaining the offset relative to UTC') {
assert.equal(SystemTime.new(utc_offset: 5).utc_offset, 5)
}
}
test.group('std::time::SystemTime.utc?') do (g) {
g.test('Checking if a SystemTime uses UTC') {
assert.true(SystemTime.new.utc?)
assert.false(SystemTime.new(utc_offset: 3600).utc?)
}
}
test.group('std::time::SystemTime.seconds_since_unix_epoch') do (g) {
g.test('Obtaining the number of seconds leading up to the Unix epoch') {
assert.equal(
SystemTime.new(year: 1969, month: 12, day: 31).seconds_since_unix_epoch,
-86400
)
}
g.test('Obtaining the number of seconds since the Unix epoch') {
assert.equal(SystemTime.new(year: 1970).seconds_since_unix_epoch, 0)
assert.equal(
SystemTime.new(year: 1970, hour: 1).seconds_since_unix_epoch,
3600
)
}
g.test('Obtaining the number of seconds since the Unix epoch with a UTC offset') {
let time = SystemTime.new(year: 1970, hour: 1, utc_offset: 3600)
assert.equal(time.seconds_since_unix_epoch, 0)
}
}
test.group('std::time::SystemTime.to_utc') do (g) {
g.test('Converting a SystemTime to a UTC based SystemTime') {
let utc = SystemTime.new(year: 1970, hour: 1, utc_offset: 3600).to_utc
assert.equal(utc.utc_offset, 0)
assert.equal(utc, SystemTime.new(year: 1970, hour: 0))
}
}
test.group('std::time::SystemTime.to_integer') do (g) {
g.test('Converting a SystemTime to an Integer') {
assert.equal(
SystemTime.new(year: 1969, month: 12, day: 31).to_integer,
-86400
)
assert.equal(SystemTime.new(year: 1970).to_integer, 0)
assert.equal(SystemTime.new(year: 1970, hour: 1).to_integer, 3600)
}
}
test.group('std::time::SystemTime.to_float') do (g) {
g.test('Converting a SystemTime to a Float without sub second precision') {
assert.equal(
SystemTime.new(year: 1969, month: 12, day: 31).to_float,
-86400.0
)
assert.equal(SystemTime.new(year: 1970).to_float, 0.0)
assert.equal(SystemTime.new(year: 1970, hour: 1).to_float, 3600.0)
}
g.test('Converting a SystemTime to a Float with sub second precision') {
assert.equal(
SystemTime.new(year: 1969, month: 12, day: 31, sub_second: 0.1).to_float,
-86399.9
)
assert.equal(SystemTime.new(year: 1970, sub_second: 0.1).to_float, 0.1)
}
}
test.group('std::time::SystemTime.+') do (g) {
g.test('Adding a Duration to a SystemTime using UTC') {
let time = SystemTime.new(year: 1970) + duration.from_seconds(3600)
assert.equal(time, SystemTime.new(year: 1970, hour: 1))
}
g.test('Adding a Duration to a SystemTime using local time') {
let time = SystemTime.new(year: 1970, utc_offset: 3600)
+ duration.from_seconds(3600)
assert.equal(time, SystemTime.new(year: 1970, hour: 1, utc_offset: 3600))
}
}
test.group('std::time::SystemTime.-') do (g) {
g.test('Subtracting a Duration to a SystemTime using UTC') {
let time = SystemTime.new(year: 1970, hour: 1) - duration.from_seconds(3600)
assert.equal(time, SystemTime.new(year: 1970))
}
g.test('Subtracting a Duration to a SystemTime using local time') {
let time = SystemTime.new(year: 1970, hour: 1, utc_offset: 3600)
- duration.from_seconds(3600)
assert.equal(time, SystemTime.new(year: 1970, utc_offset: 3600))
}
}
test.group('std::time::SystemTime.<') do (g) {
g.test('Comparing an older SystemTime with a newer SystemTime') {
assert.true(SystemTime.new(year: 1970) < SystemTime.new(year: 1971))
assert.true(SystemTime.new(year: 1970, hour: 2) < SystemTime.new(year: 1971))
}
g.test('Comparing two identical SystemTime objects') {
assert.false(SystemTime.new(year: 1970) < SystemTime.new(year: 1970))
}
g.test('Comparing a newer SystemTime with an older SystemTime') {
assert.false(SystemTime.new(year: 1970) < SystemTime.new(year: 1969))
assert.false(
SystemTime.new(year: 1970, hour: 2) < SystemTime.new(year: 1970, hour: 1)
)
}
}
test.group('std::time::SystemTime.<=') do (g) {
g.test('Comparing an older SystemTime with a newer SystemTime') {
assert.true(SystemTime.new(year: 1970) <= SystemTime.new(year: 1971))
assert.true(SystemTime.new(year: 1970) <= SystemTime.new(year: 1970))
assert.true(
SystemTime.new(year: 1970, hour: 2) <= SystemTime.new(year: 1971)
)
}
g.test('Comparing a newer SystemTime with an older SystemTime') {
assert.false(SystemTime.new(year: 1970) <= SystemTime.new(year: 1969))
assert.false(
SystemTime.new(year: 1970, hour: 2) <= SystemTime.new(year: 1970, hour: 1)
)
}
}
test.group('std::time::SystemTime.>') do (g) {
g.test('Comparing a newer SystemTime with an older SystemTime') {
assert.true(SystemTime.new(year: 1971) > SystemTime.new(year: 1970))
assert.true(SystemTime.new(year: 1970, hour: 2) > SystemTime.new(year: 1970))
}
g.test('Comparing two SystemTime objects in the same year') {
assert.false(SystemTime.new(year: 1970) > SystemTime.new(year: 1970))
}