Upgrade the "time" crate to 0.2.x

Version 0.2.x of the "time" crate comes with an API that is quite
different from version 0.1.x. This commit upgrades Inko to this new AP.
As version 0.2.x does not support checking if DST is active, support for
this has been removed. DST support is tricky, as it can vary based on
where you are, politics (e.g. some countries may drop it), etc.

As part of upgrading to time 0.2.x, some of the internals for obtaining
timestamps has been changed. In particular, obtaining a timestamp and
UTC offset has been merged into a single VM instruction. This should
ensure that no funny business happens when obtaining a timestamp but not
obtaining the UTC offset until a later point in time. The downside is
having to allocate an array object, but the overhead of this should be
minor.

This fixes #188
parent d5c10f8b
Pipeline #120808679 passed with stages
in 22 minutes and 40 seconds
......@@ -111,8 +111,6 @@ module Inkoc
FileType
FileTime
TimeSystem
TimeSystemOffset
TimeSystemDst
DirectoryCreate
DirectoryRemove
DirectoryList
......
......@@ -1144,15 +1144,7 @@ module Inkoc
end
def on_raw_time_system(*)
typedb.float_type.new_instance
end
def on_raw_time_system_offset(*)
typedb.integer_type.new_instance
end
def on_raw_time_system_dst(*)
typedb.boolean_type.new_instance
typedb.new_array_of_type(TypeSystem::Dynamic.new)
end
def on_raw_string_to_upper(*)
......@@ -1291,7 +1283,7 @@ module Inkoc
end
def on_raw_file_time(*)
typedb.integer_type.new_instance
typedb.new_array_of_type(TypeSystem::Dynamic.new)
end
def on_raw_directory_create(*)
......
......@@ -995,14 +995,6 @@ module Inkoc
raw_nullary_instruction(:TimeSystem, node, body)
end
def on_raw_time_system_offset(node, body)
raw_nullary_instruction(:TimeSystemOffset, node, body)
end
def on_raw_time_system_dst(node, body)
raw_nullary_instruction(:TimeSystemDst, node, body)
end
def on_raw_string_to_upper(node, body)
raw_unary_instruction(:StringToUpper, node, body)
end
......
......@@ -84,13 +84,14 @@ def exists?(path: String) -> Boolean {
# Returns the creation, modification or access time of a file path.
def file_time(path: String, kind: Integer) !! IOError -> SystemTime {
process.blocking {
let timestamp = try {
let time = try {
_INKOC.file_time(path, kind)
} else (error) {
throw IOError.new(error as String)
}
SystemTime.from_timestamp(timestamp)
SystemTime
.from_timestamp(time: time[0] as Float, utc_offset: time[1] as Integer)
}
}
......
......@@ -5,17 +5,6 @@ import std::operators::(
)
import std::time::constants::*
# Returns the UTC offset of the system's local time.
def utc_offset -> Integer {
_INKOC.time_system_offset
}
# Returns `True` if Daylight Saving SystemTime is in effect in the system's
# timezone.
def dst? -> Boolean {
_INKOC.time_system_dst
}
# A span of time measured in seconds.
#
# A `Duration` can be used to measure the span of time without having to worry
......@@ -278,12 +267,9 @@ object SystemTime {
# The sub seconds of this `SystemTime`.
@sub_second: Float
# The UTC offset (in hours) of this `SystemTime`.
# The UTC offset (in seconds) of this `SystemTime`.
@utc_offset: Integer
# A boolean indicating that Daylight Saving Time (DST) is active.
@dst: Boolean
# Returns a new `SystemTime` representing the current time using the local
# timezone.
#
......@@ -295,7 +281,9 @@ object SystemTime {
#
# SystemTime.now
static def now -> SystemTime {
from_timestamp(_INKOC.time_system)
let time = _INKOC.time_system
from_timestamp(time: time[0] as Float, utc_offset: time[1] as Integer)
}
# Returns a new `SystemTime` representing the current time using UTC as the
......@@ -303,25 +291,21 @@ object SystemTime {
#
# # Examples
#
# Getting the current time:
# Getting the current time in UTC:
#
# import std::time::SystemTime
#
# SystemTime.utc
static def utc -> SystemTime {
from_utc_timestamp(_INKOC.time_system)
let time = _INKOC.time_system
from_utc_timestamp(time[0] as Float)
}
# Returns a new `SystemTime` from a Unix timestamp.
#
# This method defaults to using the system's local timezone unless an
# explicit offset is given. When specifying a custom offset also make sure to
# explicitly specify if DST is in effect, otherwise this will be based on the
# system's local time.
# Returns a new `SystemTime` from a Unix timestamp and UTC offset.
static def from_timestamp(
time: ToFloat,
utc_offset = ThisModule.utc_offset,
dst = ThisModule.dst?
utc_offset: Integer,
) -> SystemTime {
# The algorithm below is based on the following code from the musl standard
# library: http://git.musl-libc.org/cgit/musl/tree/src/time/__secs_to_tm.c.
......@@ -453,14 +437,13 @@ object SystemTime {
minute: minute,
second: second,
sub_second: sub_second,
utc_offset: utc_offset,
dst: dst
utc_offset: utc_offset
)
}
# Returns a new `SystemTime` from a Unix timestamp using UTC as the timezone.
static def from_utc_timestamp(time: ToFloat) -> SystemTime {
from_timestamp(time, utc_offset: 0, dst: False)
from_timestamp(time, utc_offset: 0)
}
def init(
......@@ -471,8 +454,7 @@ object SystemTime {
minute = 0,
second = 0,
sub_second = 0.0,
utc_offset = 0,
dst = False,
utc_offset = 0
) {
@year = year
@month = month
......@@ -482,7 +464,6 @@ object SystemTime {
@second = second
@sub_second = sub_second
@utc_offset = utc_offset
@dst = dst
}
# Returns the hour of the day from 0 to 23.
......@@ -608,11 +589,6 @@ object SystemTime {
(year % 4).zero?.and { (year % 100).positive?.or { (year % 400).zero? } }
}
# Returns `True` if DST is active.
def dst? -> Boolean {
@dst
}
# Returns the offset in seconds relative to UTC.
def utc_offset -> Integer {
@utc_offset
......@@ -677,7 +653,9 @@ impl Add!(Duration) for SystemTime {
utc?.if(
true: { SystemTime.from_utc_timestamp(timestamp) },
false: { SystemTime.from_timestamp(timestamp) }
false: {
SystemTime.from_timestamp(time: timestamp, utc_offset: utc_offset)
}
)
}
}
......@@ -698,7 +676,9 @@ impl Subtract!(Duration) for SystemTime {
utc?.if(
true: { SystemTime.from_utc_timestamp(timestamp) },
false: { SystemTime.from_timestamp(timestamp) }
false: {
SystemTime.from_timestamp(time: timestamp, utc_offset: utc_offset)
}
)
}
}
......
......@@ -122,12 +122,6 @@ test.group('std::time::SystemTime.utc') do (g) {
}
test.group('std::time::SystemTime.from_timestamp') do (g) {
g.test("Creating a SystemTime using the local clock's UTC offset") {
assert.equal(SystemTime.from_timestamp(0).utc_offset, time.utc_offset)
}
# For all the following tests we use a fixed UTC offset of 0, ensuring the
# tests do not fail if the test environment uses a different UTC offset.
g.test('Creating a SystemTime using the Unix timestamp 0') {
assert.equal(
SystemTime.from_timestamp(time: 0, utc_offset: 0),
......@@ -355,13 +349,6 @@ test.group('std::time::SystemTime.leap_year?') do (g) {
}
}
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)
......@@ -684,20 +671,3 @@ test.group('std::time::Instant.==') do (g) {
assert.not_equal(Instant.new(1.0), Instant.new(2.0))
}
}
test.group('std::time.utc_offset') do (g) {
g.test('Obtaining the UTC offset of the system clock') {
let offset = time.utc_offset
assert.true(offset >= MIN_UTC_OFFSET)
assert.true(offset <= MAX_UTC_OFFSET)
}
}
test.group('std::time.dst?') do (g) {
g.test('Checking if Daylight Savings Time is active') {
# There is no consistent, platform independent way of testing the DST flag.
# As a resutl, we'll just check if it actually returns a proper boolean.
assert.true(Array.new(True, False).contains?(time.dst?))
}
}
......@@ -563,6 +563,17 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
[[package]]
name = "proc-macro-hack"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.8"
......@@ -780,6 +791,17 @@ dependencies = [
"semver",
]
[[package]]
name = "rustversion"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "scopeguard"
version = "1.0.0"
......@@ -837,6 +859,17 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a0294dc449adc58bb6592fff1a23d3e5e6e235afc6a0ffca2657d19e7bbffe5"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "termcolor"
version = "1.1.0"
......@@ -866,15 +899,38 @@ dependencies = [
[[package]]
name = "time"
version = "0.1.42"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
checksum = "3043ac959c44dccc548a57417876c8fe241502aed69d880efc91166c02717a93"
dependencies = [
"libc",
"redox_syscall",
"rustversion",
"time-macros",
"winapi",
]
[[package]]
name = "time-macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ae9b6e9f095bc105e183e3cd493d72579be3181ad4004fceb01adbe9eecab2d"
dependencies = [
"proc-macro-hack",
"time-macros-impl",
]
[[package]]
name = "time-macros-impl"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e987cfe0537f575b5fc99909de6185f6c19c3ad8889e2275e686a873d0869ba1"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-width"
version = "0.1.7"
......
......@@ -22,7 +22,7 @@ getopts = "^0.2"
num_cpus = "^1.12"
parking_lot = "^0.10"
fnv = "^1.0"
time = "^0.1"
time = "^0.2"
num-integer = "^0.1"
float-cmp = "^0.6"
num-traits = "^0.2"
......
//! DateTime objects for the virtual machine and runtime.
use std::i32;
use std::i64;
use std::time::{SystemTime, UNIX_EPOCH};
use time::{self, Timespec, Tm};
use std::time::SystemTime;
use time::OffsetDateTime;
/// Object for storing the local or UTC time.
#[derive(Clone)]
pub struct DateTime {
time: Tm,
time: OffsetDateTime,
}
/// The number of nanoseconds in a second.
const NANOS_PER_SEC: f64 = 1_000_000_000.0;
impl DateTime {
pub fn new(tm: Tm) -> Self {
DateTime { time: tm }
}
/// Returns the current system time.
pub fn now() -> Self {
DateTime::new(time::now())
}
/// Returns the current time in UTC.
pub fn now_utc() -> Self {
DateTime::new(time::now_utc())
DateTime {
time: OffsetDateTime::now_local(),
}
}
/// Creates a `DateTime` from a `SystemTime` object.
pub fn from_system_time(time: SystemTime) -> Self {
let (sec, nsec) = match time.duration_since(UNIX_EPOCH) {
Ok(duration) => {
(duration.as_secs() as i64, duration.subsec_nanos() as i32)
}
Err(error) => {
let duration = error.duration();
(
-(duration.as_secs() as i64),
-(duration.subsec_nanos() as i32),
)
}
};
DateTime::new(time::at(Timespec::new(sec, nsec)))
}
/// Returns the nanoseconds after the second.
pub fn nanoseconds(&self) -> i64 {
i64::from(self.time.tm_nsec)
}
/// Returns a flag indicating if Daylight Saving Time is active.
pub fn dst_active(&self) -> bool {
self.time.tm_isdst == 1
DateTime { time: time.into() }
}
/// Returns the offset in seconds relative to UTC.
pub fn utc_offset(&self) -> i64 {
i64::from(self.time.tm_utcoff)
self.time.offset().as_seconds() as i64
}
/// Returns the seconds since the Unix epoch (including sub seconds).
pub fn timestamp(&self) -> f64 {
self.time.to_timespec().sec as f64
+ self.nanoseconds() as f64 / NANOS_PER_SEC
self.time.timestamp() as f64
+ (self.time.nanosecond() as f64 / NANOS_PER_SEC)
}
}
......@@ -109,8 +109,6 @@ pub enum InstructionType {
FileType,
FileTime,
TimeSystem,
TimeSystemOffset,
TimeSystemDst,
DirectoryCreate,
DirectoryRemove,
DirectoryList,
......
......@@ -266,9 +266,16 @@ pub fn file_time(
let path = path_ptr.string_value()?;
let kind = kind_ptr.integer_value()?;
let dt = filesystem::date_time_for_path(path, kind)?;
let timestamp = process
.allocate(object_value::float(dt.timestamp()), state.float_prototype);
Ok(process
.allocate(object_value::float(dt.timestamp()), state.float_prototype))
let offset = ObjectPointer::integer(dt.utc_offset());
let tuple = process.allocate(
object_value::array(vec![timestamp, offset]),
state.array_prototype,
);
Ok(tuple)
}
pub fn options_for_integer(mode: i64) -> Result<OpenOptions, String> {
......
......@@ -1324,18 +1324,6 @@ impl Machine {
context.set_register(reg, res);
}
InstructionType::TimeSystemOffset => {
let reg = instruction.arg(0);
let res = time::system_offset();
context.set_register(reg, res);
}
InstructionType::TimeSystemDst => {
let reg = instruction.arg(0);
let res = time::system_dst(&self.state);
context.set_register(reg, res);
}
InstructionType::DirectoryCreate => {
let reg = instruction.arg(0);
let path = context.get_register(instruction.arg(1));
......
......@@ -14,19 +14,14 @@ pub fn monotonic(state: &RcState, process: &RcProcess) -> ObjectPointer {
}
pub fn system(state: &RcState, process: &RcProcess) -> ObjectPointer {
let timestamp = DateTime::now().timestamp();
let dt = DateTime::now();
let timestamp = process
.allocate(object_value::float(dt.timestamp()), state.float_prototype);
process.allocate(object_value::float(timestamp), state.float_prototype)
}
pub fn system_offset() -> ObjectPointer {
ObjectPointer::integer(DateTime::now().utc_offset())
}
let offset = ObjectPointer::integer(dt.utc_offset());
pub fn system_dst(state: &RcState) -> ObjectPointer {
if DateTime::now().dst_active() {
state.true_object
} else {
state.false_object
}
process.allocate(
object_value::array(vec![timestamp, offset]),
state.array_prototype,
)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment