Skip to content

Commit 0ab3965

Browse files
committed
Add time.gmtime function.
1 parent 5edcecb commit 0ab3965

File tree

6 files changed

+191
-9
lines changed

6 files changed

+191
-9
lines changed

Cargo.lock

+24
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/snippets/test_re.py

-5
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,3 @@
2626
# TODO:
2727
# assert mo.group(0) == '_boe0'
2828

29-
from string import Template
30-
s = Template('$who likes $what')
31-
# TODO:
32-
# r = s.substitute(who='tim', what='kung pow')
33-
# print(r)

tests/snippets/test_string.py

+12
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,15 @@
1111
# FIXME
1212
#assert string.whitespace == ' \t\n\r\x0b\x0c', string.whitespace
1313
#assert string.printable == '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'
14+
15+
assert string.capwords('bla bla', ' ') == 'Bla Bla'
16+
17+
from string import Template
18+
s = Template('$who likes $what')
19+
# TODO:
20+
# r = s.substitute(who='tim', what='kung pow')
21+
# print(r)
22+
23+
from string import Formatter
24+
25+
f = Formatter()

tests/snippets/test_time.py

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
3+
import time
4+
5+
x = time.gmtime(1000)
6+
7+
assert x.tm_year == 1970
8+
assert x.tm_min == 16
9+
assert x.tm_sec == 40
10+

vm/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ regex = "1"
4040
rustc_version_runtime = "0.1.*"
4141
statrs = "0.10.0"
4242
caseless = "0.2.1"
43+
chrono = "0.4.7"
4344
unicode-segmentation = "1.2.1"
4445
unicode-xid = "0.1.0"
4546
lazy_static = "^1.0.1"

vm/src/stdlib/time_module.rs

+144-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
//! The python `time` module.
2-
2+
/// See also:
3+
/// https://docs.python.org/3/library/time.html
4+
use std::fmt;
35
use std::thread;
46
use std::time::{Duration, SystemTime, UNIX_EPOCH};
57

6-
use crate::function::PyFuncArgs;
7-
use crate::obj::objfloat;
8-
use crate::pyobject::{PyObjectRef, PyResult};
8+
use crate::function::{OptionalArg, PyFuncArgs};
9+
use crate::obj::objtype::PyClassRef;
10+
use crate::obj::{objfloat, objint, objtype};
11+
use crate::pyobject::{PyClassImpl, PyObjectRef, PyResult, PyValue};
912
use crate::vm::VirtualMachine;
1013

14+
use num_traits::cast::ToPrimitive;
15+
16+
use chrono::naive::NaiveDateTime;
17+
use chrono::{Datelike, Timelike};
18+
1119
fn time_sleep(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
1220
arg_check!(vm, args, required = [(seconds, Some(vm.ctx.float_type()))]);
1321
let seconds = objfloat::get_value(seconds);
@@ -32,11 +40,143 @@ fn time_time(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
3240
Ok(value)
3341
}
3442

43+
fn time_monotonic(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
44+
arg_check!(vm, args);
45+
// TODO: implement proper monotonic time!
46+
let x = match SystemTime::now().duration_since(UNIX_EPOCH) {
47+
Ok(v) => duration_to_f64(v),
48+
Err(err) => panic!("Error: {:?}", err),
49+
};
50+
let value = vm.ctx.new_float(x);
51+
Ok(value)
52+
}
53+
54+
fn pyfloat_to_secs_and_nanos(seconds: &PyObjectRef) -> (i64, u32) {
55+
let seconds = objfloat::get_value(seconds);
56+
let secs: i64 = seconds.trunc() as i64;
57+
let nanos: u32 = (seconds.fract() * 1e9) as u32;
58+
(secs, nanos)
59+
}
60+
61+
fn pyobj_to_naive_date_time(
62+
value: &PyObjectRef,
63+
vm: &VirtualMachine,
64+
) -> PyResult<Option<NaiveDateTime>> {
65+
if objtype::isinstance(value, &vm.ctx.float_type()) {
66+
let (seconds, nanos) = pyfloat_to_secs_and_nanos(&value);
67+
let dt = NaiveDateTime::from_timestamp(seconds, nanos);
68+
Ok(Some(dt))
69+
} else if objtype::isinstance(&value, &vm.ctx.int_type()) {
70+
let seconds = objint::get_value(&value).to_i64().unwrap();
71+
let dt = NaiveDateTime::from_timestamp(seconds, 0);
72+
Ok(Some(dt))
73+
} else {
74+
Err(vm.new_type_error("Expected float, int or None".to_string()))
75+
}
76+
}
77+
78+
/// https://docs.python.org/3/library/time.html?highlight=gmtime#time.gmtime
79+
fn time_gmtime(secs: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult<PyStructTime> {
80+
let default = chrono::offset::Utc::now().naive_utc();
81+
let instant = match secs {
82+
OptionalArg::Present(secs) => pyobj_to_naive_date_time(&secs, vm)?.unwrap_or(default),
83+
OptionalArg::Missing => default,
84+
};
85+
let value = PyStructTime::new(instant);
86+
Ok(value)
87+
}
88+
89+
fn time_localtime(secs: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult<PyStructTime> {
90+
let default = chrono::offset::Local::now().naive_local();
91+
let instant = match secs {
92+
OptionalArg::Present(secs) => pyobj_to_naive_date_time(&secs, vm)?.unwrap_or(default),
93+
OptionalArg::Missing => default,
94+
};
95+
let value = PyStructTime::new(instant);
96+
Ok(value)
97+
}
98+
99+
#[pyclass(name = "struct_time")]
100+
struct PyStructTime {
101+
tm: NaiveDateTime,
102+
}
103+
104+
impl fmt::Debug for PyStructTime {
105+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106+
write!(f, "struct_time()")
107+
}
108+
}
109+
110+
impl PyValue for PyStructTime {
111+
fn class(vm: &VirtualMachine) -> PyClassRef {
112+
vm.class("time", "struct_time")
113+
}
114+
}
115+
116+
#[pyimpl]
117+
impl PyStructTime {
118+
fn new(tm: NaiveDateTime) -> Self {
119+
PyStructTime { tm }
120+
}
121+
122+
#[pymethod(name = "__repr__")]
123+
fn repr(&self, _vm: &VirtualMachine) -> String {
124+
// TODO: extract year day and isdst somehow..
125+
format!(
126+
"time.struct_time(tm_year={}, tm_mon={}, tm_mday={}, tm_hour={}, tm_min={}, tm_sec={}, tm_wday={})",
127+
self.tm.date().year(), self.tm.date().month(), self.tm.date().day(),
128+
self.tm.time().hour(), self.tm.time().minute(), self.tm.time().second(),
129+
self.tm.date().weekday().num_days_from_monday()
130+
)
131+
}
132+
133+
#[pyproperty(name = "tm_year")]
134+
fn tm_year(&self, _vm: &VirtualMachine) -> i32 {
135+
self.tm.date().year()
136+
}
137+
138+
#[pyproperty(name = "tm_mon")]
139+
fn tm_mon(&self, _vm: &VirtualMachine) -> u32 {
140+
self.tm.date().month()
141+
}
142+
143+
#[pyproperty(name = "tm_mday")]
144+
fn tm_mday(&self, _vm: &VirtualMachine) -> u32 {
145+
self.tm.date().day()
146+
}
147+
148+
#[pyproperty(name = "tm_hour")]
149+
fn tm_hour(&self, _vm: &VirtualMachine) -> u32 {
150+
self.tm.time().hour()
151+
}
152+
153+
#[pyproperty(name = "tm_min")]
154+
fn tm_min(&self, _vm: &VirtualMachine) -> u32 {
155+
self.tm.time().minute()
156+
}
157+
158+
#[pyproperty(name = "tm_sec")]
159+
fn tm_sec(&self, _vm: &VirtualMachine) -> u32 {
160+
self.tm.time().second()
161+
}
162+
163+
#[pyproperty(name = "tm_wday")]
164+
fn tm_wday(&self, _vm: &VirtualMachine) -> u32 {
165+
self.tm.date().weekday().num_days_from_monday()
166+
}
167+
}
168+
35169
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
36170
let ctx = &vm.ctx;
37171

172+
let struct_time_type = PyStructTime::make_class(ctx);
173+
38174
py_module!(vm, "time", {
175+
"gmtime" => ctx.new_rustfunc(time_gmtime),
176+
"localtime" => ctx.new_rustfunc(time_localtime),
177+
"monotonic" => ctx.new_rustfunc(time_monotonic),
39178
"sleep" => ctx.new_rustfunc(time_sleep),
179+
"struct_time" => struct_time_type,
40180
"time" => ctx.new_rustfunc(time_time)
41181
})
42182
}

0 commit comments

Comments
 (0)