Skip to content

Commit 4e58683

Browse files
authored
Merge pull request #78 from sunng87/master
feat: merge my pg_interval_2 fork into mainline
2 parents e7d6dd6 + 98065ba commit 4e58683

11 files changed

Lines changed: 1850 additions & 104 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "pg_interval"
33
version = "0.4.4"
44
edition = "2021"
5-
authors = ["Ryan Piper <[email protected]>"]
5+
authors = ["Ryan Piper <[email protected]>", "Ning Sun <[email protected]>"]
66
license = "MIT"
77
description = "A native PostgreSQL interval type"
88
repository = "https://github.com/piperRyan/rust-postgres-interval"

README.md

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
[![Build Status](https://travis-ci.org/piperRyan/rust-postgres-interval.svg?branch=master)](https://travis-ci.org/piperRyan/rust-postgres-interval) [![codecov](https://codecov.io/gh/piperRyan/rust-postgres-interval/branch/master/graph/badge.svg)](https://codecov.io/gh/piperRyan/rust-postgres-interval)
2-
31
# Rust-Postgres-Interval
42
A interval type for the postgres driver.
53

@@ -23,18 +21,3 @@ fn main() {
2321
assert_eq!(String::from("P1Y1M1DT1H"), output);
2422
}
2523
```
26-
27-
## Requirements
28-
- rust 1.22
29-
30-
## Roadmap to 1.0.0
31-
32-
- [x] Convert Interval Into Formated String
33-
- [x] Iso 8601
34-
- [x] Postgres
35-
- [x] Sql
36-
- [ ] Parse Formated Strings Into The Interval Type
37-
- [x] Iso 8601
38-
- [x] Postgres
39-
- [ ] Sql
40-
- [x] Chrono Integrations

src/integrations/duration.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const NANOS_PER_MICRO: i64 = 1000;
77
impl Interval {
88
/// Tries to convert from the `Duration` type to a `Interval`. Will
99
/// return `None` on a overflow. This is a lossy conversion in that
10-
/// any units smaller than a microsecond will be lost.
10+
/// any units smaller than a microsecond will be lost.
1111
pub fn from_duration(duration: Duration) -> Option<Interval> {
1212
let mut days = duration.num_days();
1313
let mut new_dur = duration - Duration::days(days);

src/interval_fmt/iso_8601.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,19 @@ impl IntervalNorm {
1717
if self.minutes != 0 {
1818
time_interval.push_str(&format!("{}M", self.minutes));
1919
}
20-
if self.seconds != 0 {
21-
time_interval.push_str(&format!("{}S", self.seconds));
22-
}
23-
if self.microseconds != 0 {
24-
let ms = super::safe_abs_u64(self.microseconds);
25-
time_interval.push_str(&format!(".{:06}", ms));
20+
if self.seconds != 0 || self.microseconds != 0 {
21+
let secs_with_micros = if self.seconds != 0 && self.microseconds != 0 {
22+
format!(
23+
"{}.{:06}",
24+
self.seconds,
25+
super::safe_abs_u64(self.microseconds)
26+
)
27+
} else if self.microseconds != 0 {
28+
format!(".{:06}", super::safe_abs_u64(self.microseconds))
29+
} else {
30+
format!("{}", self.seconds)
31+
};
32+
time_interval.push_str(&format!("{}S", secs_with_micros));
2633
}
2734
} else {
2835
time_interval = "".to_owned();

src/interval_fmt/postgres.rs

Lines changed: 148 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,53 @@
11
use crate::interval_norm::IntervalNorm;
22

3+
fn get_year_suffix(value: i32) -> &'static str {
4+
if value == 1 {
5+
"year"
6+
} else {
7+
"years"
8+
}
9+
}
10+
11+
fn get_mon_suffix(value: i32) -> &'static str {
12+
if value == 1 {
13+
"mon"
14+
} else {
15+
"mons"
16+
}
17+
}
18+
19+
fn get_day_suffix(value: i32) -> &'static str {
20+
if value == 1 {
21+
"day"
22+
} else {
23+
"days"
24+
}
25+
}
26+
27+
fn get_hour_suffix(value: i64) -> &'static str {
28+
if value == 1 {
29+
"hour"
30+
} else {
31+
"hours"
32+
}
33+
}
34+
35+
fn get_min_suffix(value: i64) -> &'static str {
36+
if value == 1 {
37+
"min"
38+
} else {
39+
"mins"
40+
}
41+
}
42+
43+
fn get_sec_suffix(seconds: i64, microseconds: i64) -> &'static str {
44+
if seconds == 1 && microseconds == 0 {
45+
"sec"
46+
} else {
47+
"secs"
48+
}
49+
}
50+
351
impl IntervalNorm {
452
/// Produces a postgres compliant interval string.
553
pub fn into_postgres(self) -> String {
@@ -10,14 +58,18 @@ impl IntervalNorm {
1058
let mut day_interval = "".to_owned();
1159
let time_interval = self.get_postgres_time_interval();
1260
if self.is_day_present() {
13-
day_interval = format!("{:#?} days ", self.days)
61+
day_interval = format!("{} {} ", self.days, get_day_suffix(self.days))
1462
}
1563
if self.is_year_month_present() {
1664
if self.years != 0 {
17-
year_interval.push_str(&format!("{:#?} year ", self.years))
65+
year_interval.push_str(&format!("{} {} ", self.years, get_year_suffix(self.years)))
1866
}
1967
if self.months != 0 {
20-
year_interval.push_str(&format!("{:#?} mons ", self.months));
68+
year_interval.push_str(&format!(
69+
"{} {} ",
70+
self.months,
71+
get_mon_suffix(self.months)
72+
));
2173
}
2274
}
2375
year_interval.push_str(&day_interval);
@@ -48,4 +100,97 @@ impl IntervalNorm {
48100
}
49101
time_interval
50102
}
103+
104+
/// Produces a postgres_verbose compliant interval string.
105+
pub fn into_postgres_verbose(self) -> String {
106+
let is_negative = !self.is_time_interval_pos()
107+
&& (self.years < 0
108+
|| self.months < 0
109+
|| self.days < 0
110+
|| self.hours < 0
111+
|| self.minutes < 0
112+
|| self.seconds < 0
113+
|| self.microseconds < 0);
114+
115+
let mut parts = Vec::new();
116+
117+
if self.years != 0 {
118+
let abs_years = if self.years < 0 {
119+
-self.years
120+
} else {
121+
self.years
122+
};
123+
parts.push(format!("{} {}", abs_years, get_year_suffix(abs_years)));
124+
}
125+
126+
if self.months != 0 {
127+
let abs_months = if self.months < 0 {
128+
-self.months
129+
} else {
130+
self.months
131+
};
132+
parts.push(format!("{} {}", abs_months, get_mon_suffix(abs_months)));
133+
}
134+
135+
if self.days != 0 {
136+
let abs_days = if self.days < 0 { -self.days } else { self.days };
137+
parts.push(format!("{} {}", abs_days, get_day_suffix(abs_days)));
138+
}
139+
140+
if self.hours != 0 {
141+
let abs_hours = if self.hours < 0 {
142+
-self.hours
143+
} else {
144+
self.hours
145+
};
146+
parts.push(format!("{} {}", abs_hours, get_hour_suffix(abs_hours)));
147+
}
148+
149+
if self.minutes != 0 {
150+
let abs_minutes = if self.minutes < 0 {
151+
-self.minutes
152+
} else {
153+
self.minutes
154+
};
155+
parts.push(format!("{} {}", abs_minutes, get_min_suffix(abs_minutes)));
156+
}
157+
158+
if self.seconds != 0 || self.microseconds != 0 {
159+
let abs_seconds = if self.seconds < 0 {
160+
-self.seconds
161+
} else {
162+
self.seconds
163+
};
164+
let abs_micros = if self.microseconds < 0 {
165+
-self.microseconds
166+
} else {
167+
self.microseconds
168+
};
169+
if abs_micros != 0 {
170+
let secs_with_micros = abs_seconds as f64 + abs_micros as f64 / 1_000_000.0;
171+
parts.push(format!(
172+
"{} {}",
173+
secs_with_micros,
174+
get_sec_suffix(abs_seconds, abs_micros)
175+
));
176+
} else {
177+
parts.push(format!(
178+
"{} {}",
179+
abs_seconds,
180+
get_sec_suffix(abs_seconds, abs_micros)
181+
));
182+
}
183+
}
184+
185+
if parts.is_empty() {
186+
return "@ 0".to_owned();
187+
}
188+
189+
let result = format!("@ {}", parts.join(" "));
190+
if is_negative {
191+
format!("{} ago", result)
192+
} else {
193+
result
194+
}
195+
}
51196
}

src/interval_fmt/sql.rs

Lines changed: 91 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,104 @@
11
use crate::interval_norm::IntervalNorm;
22

33
impl IntervalNorm {
4+
fn format_time(
5+
hours: i64,
6+
minutes: i64,
7+
seconds: i64,
8+
microseconds: i64,
9+
sign: bool,
10+
) -> String {
11+
let sign_str = if sign { "-" } else { "" };
12+
let time_str = format!(
13+
"{}{}:{:02}:{:02}",
14+
sign_str,
15+
super::safe_abs_u64(hours),
16+
super::safe_abs_u64(minutes),
17+
super::safe_abs_u64(seconds)
18+
);
19+
20+
if microseconds != 0 {
21+
format!("{}.{:06}", time_str, super::safe_abs_u64(microseconds))
22+
} else {
23+
time_str
24+
}
25+
}
26+
427
pub fn into_sql(self) -> String {
5-
if self.is_zeroed() {
6-
"0".to_owned()
7-
} else if !self.is_time_present() && !self.is_day_present() {
8-
get_year_month(self.months, self.years, true)
9-
} else if !self.is_time_present() && !self.is_year_month_present() {
10-
format!("{} 0:00:00", self.days)
11-
} else if !self.is_year_month_present() && !self.is_day_present() {
12-
get_time_interval(
13-
self.hours,
14-
self.minutes,
15-
self.seconds,
16-
self.microseconds,
17-
self.is_time_interval_pos(),
18-
true,
28+
let has_negative = self.has_negative();
29+
let has_positive = self.has_positive();
30+
31+
let has_year_month = self.is_year_month_present();
32+
let has_day_time = self.is_day_present() || self.is_time_present();
33+
34+
let sql_standard_value = !(has_negative && has_positive || has_year_month && has_day_time);
35+
36+
if !has_negative && !has_positive {
37+
return "0".to_owned();
38+
}
39+
40+
if !sql_standard_value {
41+
let year_sign = if self.years < 0 || self.months < 0 {
42+
'-'
43+
} else {
44+
'+'
45+
};
46+
let day_sign = if self.days < 0 { '-' } else { '+' };
47+
let sec_sign = if self.hours < 0
48+
|| self.minutes < 0
49+
|| self.seconds < 0
50+
|| self.microseconds < 0
51+
{
52+
'-'
53+
} else {
54+
'+'
55+
};
56+
57+
let time_str = format!(
58+
"{}{}:{:02}:{:02}",
59+
sec_sign,
60+
super::safe_abs_u64(self.hours),
61+
super::safe_abs_u64(self.minutes),
62+
super::safe_abs_u64(self.seconds)
63+
);
64+
65+
let time_str = if self.microseconds != 0 {
66+
format!("{}.{:06}", time_str, super::safe_abs_u64(self.microseconds))
67+
} else {
68+
time_str
69+
};
70+
71+
format!(
72+
"{}{}-{} {}{} {}",
73+
year_sign,
74+
self.years.abs(),
75+
self.months.abs(),
76+
day_sign,
77+
self.days.abs(),
78+
time_str
79+
)
80+
} else if has_year_month {
81+
format!("{}-{}", self.years, super::safe_abs_u32(self.months))
82+
} else if self.days != 0 {
83+
format!(
84+
"{} {}",
85+
self.days,
86+
Self::format_time(
87+
self.hours,
88+
self.minutes,
89+
self.seconds,
90+
self.microseconds,
91+
false
92+
)
1993
)
2094
} else {
21-
let year_month = get_year_month(self.months, self.years, false);
22-
let time_interval = get_time_interval(
95+
Self::format_time(
2396
self.hours,
2497
self.minutes,
2598
self.seconds,
2699
self.microseconds,
27-
self.is_time_interval_pos(),
28-
false,
29-
);
30-
format!("{} {:+} {}", year_month, self.days, time_interval)
100+
has_negative,
101+
)
31102
}
32103
}
33104
}
34-
35-
fn get_year_month(mons: i32, years: i32, is_only_year_month: bool) -> String {
36-
let months = super::safe_abs_u32(mons);
37-
if years == 0 || is_only_year_month {
38-
format!("{}-{}", years, months)
39-
} else {
40-
format!("{:+}-{}", years, months)
41-
}
42-
}
43-
44-
fn get_time_interval(
45-
hours: i64,
46-
mins: i64,
47-
secs: i64,
48-
micros: i64,
49-
is_time_interval_pos: bool,
50-
is_only_time: bool,
51-
) -> String {
52-
let mut interval = "".to_owned();
53-
if is_time_interval_pos && is_only_time {
54-
interval.push_str(&format!("{}:{:02}:{:02}", hours, mins, secs));
55-
} else {
56-
let minutes = super::safe_abs_u64(mins);
57-
let seconds = super::safe_abs_u64(secs);
58-
interval.push_str(&format!("{:+}:{:02}:{:02}", hours, minutes, seconds));
59-
}
60-
if micros != 0 {
61-
let microseconds = format!(".{:06}", super::safe_abs_u64(micros));
62-
interval.push_str(&microseconds);
63-
}
64-
interval
65-
}

0 commit comments

Comments
 (0)