Skip to content

Commit 249c079

Browse files
committed
parse time only strings
1 parent cdc1b64 commit 249c079

File tree

2 files changed

+126
-0
lines changed

2 files changed

+126
-0
lines changed

src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::fmt::{self, Display};
1616
mod parse_relative_time;
1717
mod parse_timestamp;
1818

19+
mod parse_time_only_str;
1920
mod parse_weekday;
2021

2122
use chrono::{
@@ -237,6 +238,11 @@ pub fn parse_datetime_at_date<S: AsRef<str> + Clone>(
237238
}
238239
}
239240

241+
// parse time only dates
242+
if let Some(date_time) = parse_time_only_str::parse_time_only(date, s.as_ref()) {
243+
return Ok(date_time);
244+
}
245+
240246
// Default parse and failure
241247
s.as_ref()
242248
.parse()

src/parse_time_only_str.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
use chrono::{DateTime, FixedOffset, Local, NaiveTime, TimeZone};
2+
use regex::Regex;
3+
4+
mod time_only_formats {
5+
pub const HH_MM: &str = "%R";
6+
pub const HH_MM_SS: &str = "%T";
7+
pub const TWELVEHOUR: &str = "%r";
8+
}
9+
10+
pub(crate) fn parse_time_only(date: DateTime<Local>, s: &str) -> Option<DateTime<FixedOffset>> {
11+
let re =
12+
Regex::new(r"^(?<time>.*?)(?:(?<sign>\+|-)(?<h>[0-9]{1,2}):?(?<m>[0-9]{0,2}))?$").unwrap();
13+
let captures = re.captures(s)?;
14+
15+
let parsed_offset = match captures.name("h") {
16+
Some(hours) if !(hours.as_str().is_empty()) => {
17+
let mut offset_in_sec = hours.as_str().parse::<i32>().unwrap() * 3600;
18+
match captures.name("m") {
19+
Some(minutes) if !(minutes.as_str().is_empty()) => {
20+
offset_in_sec += minutes.as_str().parse::<i32>().unwrap() * 60;
21+
}
22+
_ => (),
23+
};
24+
offset_in_sec *= if &captures["sign"] == "-" { -1 } else { 1 };
25+
FixedOffset::east_opt(offset_in_sec)
26+
}
27+
_ => None,
28+
};
29+
30+
for fmt in [
31+
time_only_formats::HH_MM,
32+
time_only_formats::HH_MM_SS,
33+
time_only_formats::TWELVEHOUR,
34+
] {
35+
if let Ok(parsed) = NaiveTime::parse_from_str(captures["time"].trim(), fmt) {
36+
let parsed_dt = date.date_naive().and_time(parsed);
37+
let offset = match parsed_offset {
38+
Some(offset) => offset,
39+
None => *date.offset(),
40+
};
41+
return offset.from_local_datetime(&parsed_dt).single();
42+
}
43+
}
44+
45+
None
46+
}
47+
48+
#[cfg(test)]
49+
mod tests {
50+
use crate::parse_time_only_str::parse_time_only;
51+
use chrono::{DateTime, Local, TimeZone};
52+
use std::env;
53+
54+
fn get_test_date() -> DateTime<Local> {
55+
Local.with_ymd_and_hms(2024, 03, 03, 0, 0, 0).unwrap()
56+
}
57+
58+
#[test]
59+
fn test_time_only() {
60+
env::set_var("TZ", "UTC");
61+
let parsed_time = parse_time_only(get_test_date(), "21:04")
62+
.unwrap()
63+
.timestamp();
64+
assert_eq!(parsed_time, 1709499840)
65+
}
66+
67+
#[test]
68+
fn test_time_with_offset() {
69+
env::set_var("TZ", "UTC");
70+
let parsed_time = parse_time_only(get_test_date(), "21:04 +0530")
71+
.unwrap()
72+
.timestamp();
73+
assert_eq!(parsed_time, 1709480040);
74+
}
75+
76+
#[test]
77+
fn test_time_with_hour_only_offset() {
78+
env::set_var("TZ", "UTC");
79+
let parsed_time = parse_time_only(get_test_date(), "22:04 +01")
80+
.unwrap()
81+
.timestamp();
82+
assert_eq!(parsed_time, 1709499840);
83+
}
84+
85+
#[test]
86+
fn test_time_with_hour_only_neg_offset() {
87+
env::set_var("TZ", "UTC");
88+
let parsed_time = parse_time_only(get_test_date(), "17:04 -04")
89+
.unwrap()
90+
.timestamp();
91+
assert_eq!(parsed_time, 1709499840);
92+
}
93+
94+
#[test]
95+
fn test_time_with_seconds() {
96+
env::set_var("TZ", "UTC");
97+
let parsed_time = parse_time_only(get_test_date(), "21:04:30")
98+
.unwrap()
99+
.timestamp();
100+
assert_eq!(parsed_time, 1709499870)
101+
}
102+
103+
#[test]
104+
fn test_time_with_seconds_with_offset() {
105+
env::set_var("TZ", "UTC");
106+
let parsed_time = parse_time_only(get_test_date(), "21:04:30 +0530")
107+
.unwrap()
108+
.timestamp();
109+
assert_eq!(parsed_time, 1709480070)
110+
}
111+
112+
#[test]
113+
fn test_twelve_hour_time() {
114+
env::set_var("TZ", "UTC");
115+
let parsed_time = parse_time_only(get_test_date(), "9:04:00 PM")
116+
.unwrap()
117+
.timestamp();
118+
assert_eq!(parsed_time, 1709499840)
119+
}
120+
}

0 commit comments

Comments
 (0)