Skip to content

Commit

Permalink
add Duration::parse str
Browse files Browse the repository at this point in the history
  • Loading branch information
ritchie46 committed Dec 17, 2021
1 parent de91c14 commit 4dfb67d
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 17 deletions.
17 changes: 10 additions & 7 deletions polars/polars-time/src/calendar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,20 @@ pub(crate) const fn last_day_of_month(month: i32) -> u32 {
pub(crate) const fn is_leap_year(year: i32) -> bool {
year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)
}
/// nanoseconds per second
pub const NS_SECONDS: i64 = 1_000_000_000;

/// nanosecs per minute
pub const NS_MINUTE: i64 = 60 * NS_SECONDS;
/// nanoseconds per unit
pub const NS_MICROSECOND: i64 = 1_000;
pub const NS_MILLISECOND: i64 = 1_000_000;
pub const NS_SECOND: i64 = 1_000_000_000;
pub const NS_MINUTE: i64 = 60 * NS_SECOND;
pub const NS_HOUR: i64 = 60 * NS_MINUTE;
pub const NS_DAY: i64 = 24 * NS_HOUR;
pub const NS_WEEK: i64 = 7 * NS_DAY;

pub fn timestamp_ns_to_datetime(v: i64) -> NaiveDateTime {
NaiveDateTime::from_timestamp(
// extract seconds from nanoseconds
v / NS_SECONDS,
v / NS_SECOND,
// discard extracted seconds
(v % NS_SECONDS) as u32,
(v % NS_SECOND) as u32,
)
}
91 changes: 88 additions & 3 deletions polars/polars-time/src/duration.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::calendar::{
is_leap_year, last_day_of_month, timestamp_ns_to_datetime, NS_MINUTE, NS_SECONDS,
is_leap_year, last_day_of_month, timestamp_ns_to_datetime, NS_DAY, NS_HOUR, NS_MICROSECOND,
NS_MILLISECOND, NS_MINUTE, NS_SECOND, NS_WEEK,
};
use crate::unit::TimeNanoseconds;
use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
Expand All @@ -16,6 +17,73 @@ pub struct Duration {
}

impl Duration {
/// 1ns // 1 nanosecond
/// 1us // 1 microsecond
/// 1ms // 1 millisecond
/// 1s // 1 second
/// 1m // 1 minute
/// 1h // 1 hour
/// 1d // 1 day
/// 1w // 1 week
/// 1mo // 1 calendar month
/// 1y // 1 calendar year
///
/// 3d12h4m25s // 3 days, 12 hours, 4 minutes, and 25 seconds
///
/// # Panics if given str is incorrect
pub fn parse(duration: &str) -> Self {
let mut nsecs = 0;
let mut months = 0;
let negative = duration.chars().next().unwrap() == '-';

let mut iter = duration.char_indices();
let mut start = 0;

let mut unit = String::with_capacity(2);
while let Some((i, mut ch)) = iter.next() {
if !ch.is_ascii_digit() {
let n = duration[start..i].parse::<i64>().unwrap();

loop {
if ch.is_ascii_alphabetic() {
unit.push(ch)
} else {
break;
}
match iter.next() {
Some((i, ch_)) => {
ch = ch_;
start = i
}
None => {
break;
}
}
}

match &*unit {
"ns" => nsecs += n,
"us" => nsecs += n * NS_MICROSECOND,
"ms" => nsecs += n * NS_MILLISECOND,
"s" => nsecs += n * NS_SECOND,
"m" => nsecs += n * NS_MINUTE,
"h" => nsecs += n * NS_HOUR,
"d" => nsecs += n * NS_DAY,
"w" => nsecs += n * NS_WEEK,
"mo" => months += n,
"y" => months += n * 12,
unit => panic!("unit: '{}' not supported", unit),
}
unit.clear();
}
}
Duration {
nsecs,
months,
negative,
}
}

fn to_positive(v: i64) -> (bool, i64) {
if v < 0 {
(true, -v)
Expand All @@ -35,7 +103,7 @@ impl Duration {
}

pub fn from_seconds(v: i64) -> Self {
Self::from_nsecs(v * NS_SECONDS)
Self::from_nsecs(v * NS_SECOND)
}

pub fn from_minutes(v: i64) -> Self {
Expand Down Expand Up @@ -67,7 +135,7 @@ impl Duration {

/// Estimated duration of the window duration. Not a very good one if months != 0.
pub fn duration(&self) -> TimeNanoseconds {
(self.months * 30 * 24 * 3600 * NS_SECONDS + self.nsecs).into()
(self.months * 30 * 24 * 3600 * NS_SECOND + self.nsecs).into()
}

// Truncate the given nanoseconds timestamp by the window boundary.
Expand Down Expand Up @@ -205,3 +273,20 @@ fn new_datetime(

NaiveDateTime::new(date, time)
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_parse() {
let out = Duration::parse("1ns");
assert_eq!(out.nsecs, 1);
let out = Duration::parse("1ns1ms");
assert_eq!(out.nsecs, NS_MILLISECOND + 1);
let out = Duration::parse("123ns40ms");
assert_eq!(out.nsecs, 40 * NS_MILLISECOND + 123);
let out = Duration::parse("123ns40ms1w");
assert_eq!(out.nsecs, 40 * NS_MILLISECOND + 123 + NS_WEEK);
}
}
5 changes: 4 additions & 1 deletion polars/polars-time/src/groupby.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ mod test {
Duration::from_seconds(30),
Duration::from_seconds(0),
);
let gt = groupby(window, &ts).into_iter().map(|g| g.1).collect::<Vec<_>>();
let gt = groupby(window, &ts)
.into_iter()
.map(|g| g.1)
.collect::<Vec<_>>();

let expected = &[[0, 1, 2], [2, 3, 4], [4, 5, 6]];
assert_eq!(gt, expected);
Expand Down
5 changes: 1 addition & 4 deletions polars/polars-time/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,4 @@ mod test;
mod unit;
mod window;

pub use {
duration::Duration,
window::Window
};
pub use {duration::Duration, window::Window};
4 changes: 2 additions & 2 deletions polars/polars-time/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fn test_window_boundaries() {
let hm_start = overlapping_bounds
.iter()
.map(|b| {
let dt = timestamp_ns_to_datetime(*b.start);
let dt = timestamp_ns_to_datetime(b.start);
(dt.hour(), dt.minute())
})
.collect::<Vec<_>>();
Expand All @@ -54,7 +54,7 @@ fn test_window_boundaries() {
let hm_stop = overlapping_bounds
.iter()
.map(|b| {
let dt = timestamp_ns_to_datetime(*b.stop);
let dt = timestamp_ns_to_datetime(b.stop);
(dt.hour(), dt.minute())
})
.collect::<Vec<_>>();
Expand Down

0 comments on commit 4dfb67d

Please sign in to comment.