Skip to content

Commit 98e6486

Browse files
Merge pull request RustPython#1128 from RustPython/re-kung-fu
Add time.gmtime function.
2 parents dd20263 + fb23cc6 commit 98e6486

File tree

6 files changed

+269
-9
lines changed

6 files changed

+269
-9
lines changed

Cargo.lock

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/snippets/test_re.py

Lines changed: 0 additions & 5 deletions
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

Lines changed: 12 additions & 0 deletions
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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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+
11+
s = time.strftime('%Y-%m-%d-%H-%M-%S', x)
12+
# print(s)
13+
assert s == '1970-01-01-00-16-40'
14+
15+
x2 = time.strptime(s, '%Y-%m-%d-%H-%M-%S')
16+
assert x2.tm_min == 16
17+
18+
s = time.asctime(x)
19+
# print(s)
20+
assert s == 'Thu Jan 1 00:16:40 1970'
21+

vm/Cargo.toml

Lines changed: 1 addition & 0 deletions
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

Lines changed: 211 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
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::objstr::PyStringRef;
10+
use crate::obj::objtype::PyClassRef;
11+
use crate::obj::{objfloat, objint, objtype};
12+
use crate::pyobject::{PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue};
913
use crate::vm::VirtualMachine;
1014

15+
use num_traits::cast::ToPrimitive;
16+
17+
use chrono::naive::NaiveDateTime;
18+
use chrono::{Datelike, Timelike};
19+
1120
fn time_sleep(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
1221
arg_check!(vm, args, required = [(seconds, Some(vm.ctx.float_type()))]);
1322
let seconds = objfloat::get_value(seconds);
@@ -32,11 +41,209 @@ fn time_time(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
3241
Ok(value)
3342
}
3443

44+
fn time_monotonic(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
45+
arg_check!(vm, args);
46+
// TODO: implement proper monotonic time!
47+
let x = match SystemTime::now().duration_since(UNIX_EPOCH) {
48+
Ok(v) => duration_to_f64(v),
49+
Err(err) => panic!("Error: {:?}", err),
50+
};
51+
let value = vm.ctx.new_float(x);
52+
Ok(value)
53+
}
54+
55+
fn pyfloat_to_secs_and_nanos(seconds: &PyObjectRef) -> (i64, u32) {
56+
let seconds = objfloat::get_value(seconds);
57+
let secs: i64 = seconds.trunc() as i64;
58+
let nanos: u32 = (seconds.fract() * 1e9) as u32;
59+
(secs, nanos)
60+
}
61+
62+
fn pyobj_to_naive_date_time(
63+
value: &PyObjectRef,
64+
vm: &VirtualMachine,
65+
) -> PyResult<Option<NaiveDateTime>> {
66+
if objtype::isinstance(value, &vm.ctx.float_type()) {
67+
let (seconds, nanos) = pyfloat_to_secs_and_nanos(&value);
68+
let dt = NaiveDateTime::from_timestamp(seconds, nanos);
69+
Ok(Some(dt))
70+
} else if objtype::isinstance(&value, &vm.ctx.int_type()) {
71+
let seconds = objint::get_value(&value).to_i64().unwrap();
72+
let dt = NaiveDateTime::from_timestamp(seconds, 0);
73+
Ok(Some(dt))
74+
} else {
75+
Err(vm.new_type_error("Expected float, int or None".to_string()))
76+
}
77+
}
78+
79+
/// https://docs.python.org/3/library/time.html?highlight=gmtime#time.gmtime
80+
fn time_gmtime(secs: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult<PyStructTime> {
81+
let default = chrono::offset::Utc::now().naive_utc();
82+
let instant = match secs {
83+
OptionalArg::Present(secs) => pyobj_to_naive_date_time(&secs, vm)?.unwrap_or(default),
84+
OptionalArg::Missing => default,
85+
};
86+
let value = PyStructTime::new(instant);
87+
Ok(value)
88+
}
89+
90+
fn time_localtime(secs: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult<PyStructTime> {
91+
let instant = optional_or_localtime(secs, vm)?;
92+
let value = PyStructTime::new(instant);
93+
Ok(value)
94+
}
95+
96+
/// Construct a localtime from the optional seconds, or get the current local time.
97+
fn optional_or_localtime(
98+
secs: OptionalArg<PyObjectRef>,
99+
vm: &VirtualMachine,
100+
) -> PyResult<NaiveDateTime> {
101+
let default = chrono::offset::Local::now().naive_local();
102+
let instant = match secs {
103+
OptionalArg::Present(secs) => pyobj_to_naive_date_time(&secs, vm)?.unwrap_or(default),
104+
OptionalArg::Missing => default,
105+
};
106+
Ok(instant)
107+
}
108+
109+
const CFMT: &str = "%a %b %e %H:%M:%S %Y";
110+
111+
fn time_asctime(t: OptionalArg<PyStructTimeRef>, vm: &VirtualMachine) -> PyResult {
112+
let default = chrono::offset::Local::now().naive_local();
113+
let instant = match t {
114+
OptionalArg::Present(t) => t.get_date_time(),
115+
OptionalArg::Missing => default,
116+
};
117+
let formatted_time = instant.format(&CFMT).to_string();
118+
Ok(vm.ctx.new_str(formatted_time))
119+
}
120+
121+
fn time_ctime(secs: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult<String> {
122+
let instant = optional_or_localtime(secs, vm)?;
123+
let formatted_time = instant.format(&CFMT).to_string();
124+
Ok(formatted_time)
125+
}
126+
127+
fn time_strftime(
128+
format: PyStringRef,
129+
t: OptionalArg<PyStructTimeRef>,
130+
vm: &VirtualMachine,
131+
) -> PyResult {
132+
let default = chrono::offset::Local::now().naive_local();
133+
let instant = match t {
134+
OptionalArg::Present(t) => t.get_date_time(),
135+
OptionalArg::Missing => default,
136+
};
137+
let formatted_time = instant.format(&format.value).to_string();
138+
Ok(vm.ctx.new_str(formatted_time))
139+
}
140+
141+
fn time_strptime(
142+
string: PyStringRef,
143+
format: OptionalArg<PyStringRef>,
144+
vm: &VirtualMachine,
145+
) -> PyResult<PyStructTime> {
146+
let format: String = match format {
147+
OptionalArg::Present(format) => format.value.clone(),
148+
OptionalArg::Missing => "%a %b %H:%M:%S %Y".to_string(),
149+
};
150+
let instant = NaiveDateTime::parse_from_str(&string.value, &format)
151+
.map_err(|e| vm.new_value_error(format!("Parse error: {:?}", e)))?;
152+
let struct_time = PyStructTime::new(instant);
153+
Ok(struct_time)
154+
}
155+
156+
#[pyclass(name = "struct_time")]
157+
struct PyStructTime {
158+
tm: NaiveDateTime,
159+
}
160+
161+
type PyStructTimeRef = PyRef<PyStructTime>;
162+
163+
impl fmt::Debug for PyStructTime {
164+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
165+
write!(f, "struct_time()")
166+
}
167+
}
168+
169+
impl PyValue for PyStructTime {
170+
fn class(vm: &VirtualMachine) -> PyClassRef {
171+
vm.class("time", "struct_time")
172+
}
173+
}
174+
175+
#[pyimpl]
176+
impl PyStructTime {
177+
fn new(tm: NaiveDateTime) -> Self {
178+
PyStructTime { tm }
179+
}
180+
181+
#[pymethod(name = "__repr__")]
182+
fn repr(&self, _vm: &VirtualMachine) -> String {
183+
// TODO: extract year day and isdst somehow..
184+
format!(
185+
"time.struct_time(tm_year={}, tm_mon={}, tm_mday={}, tm_hour={}, tm_min={}, tm_sec={}, tm_wday={})",
186+
self.tm.date().year(), self.tm.date().month(), self.tm.date().day(),
187+
self.tm.time().hour(), self.tm.time().minute(), self.tm.time().second(),
188+
self.tm.date().weekday().num_days_from_monday()
189+
)
190+
}
191+
192+
fn get_date_time(&self) -> NaiveDateTime {
193+
self.tm
194+
}
195+
196+
#[pyproperty(name = "tm_year")]
197+
fn tm_year(&self, _vm: &VirtualMachine) -> i32 {
198+
self.tm.date().year()
199+
}
200+
201+
#[pyproperty(name = "tm_mon")]
202+
fn tm_mon(&self, _vm: &VirtualMachine) -> u32 {
203+
self.tm.date().month()
204+
}
205+
206+
#[pyproperty(name = "tm_mday")]
207+
fn tm_mday(&self, _vm: &VirtualMachine) -> u32 {
208+
self.tm.date().day()
209+
}
210+
211+
#[pyproperty(name = "tm_hour")]
212+
fn tm_hour(&self, _vm: &VirtualMachine) -> u32 {
213+
self.tm.time().hour()
214+
}
215+
216+
#[pyproperty(name = "tm_min")]
217+
fn tm_min(&self, _vm: &VirtualMachine) -> u32 {
218+
self.tm.time().minute()
219+
}
220+
221+
#[pyproperty(name = "tm_sec")]
222+
fn tm_sec(&self, _vm: &VirtualMachine) -> u32 {
223+
self.tm.time().second()
224+
}
225+
226+
#[pyproperty(name = "tm_wday")]
227+
fn tm_wday(&self, _vm: &VirtualMachine) -> u32 {
228+
self.tm.date().weekday().num_days_from_monday()
229+
}
230+
}
231+
35232
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
36233
let ctx = &vm.ctx;
37234

235+
let struct_time_type = PyStructTime::make_class(ctx);
236+
38237
py_module!(vm, "time", {
238+
"asctime" => ctx.new_rustfunc(time_asctime),
239+
"ctime" => ctx.new_rustfunc(time_ctime),
240+
"gmtime" => ctx.new_rustfunc(time_gmtime),
241+
"localtime" => ctx.new_rustfunc(time_localtime),
242+
"monotonic" => ctx.new_rustfunc(time_monotonic),
243+
"strftime" => ctx.new_rustfunc(time_strftime),
244+
"strptime" => ctx.new_rustfunc(time_strptime),
39245
"sleep" => ctx.new_rustfunc(time_sleep),
246+
"struct_time" => struct_time_type,
40247
"time" => ctx.new_rustfunc(time_time)
41248
})
42249
}

0 commit comments

Comments
 (0)