从 &str [Rust] 解析日期的 Rustacean 方法
Rustacean way to parse a date from &str [Rust]
我有这个功能,但感觉是重复代码。我想知道这里是否有人分享如何让它变得更多 rustacean
。我仍在学习 Rust,我认为这可能是一个很好的例子来分享。
fn check_and_transform_dates(start_date: &str, end_date: &str) -> (i64, i64) {
let message: String = format!(
"Data could not be downloaded ❌, please make sure your dates
are in the following format YYYY-MM-DD
(ie. 2020-01-01), your dates are Start Date: {}, End Date: {}",
&start_date, &end_date,
);
let start_date_parsed: i64 = Utc
.ymd(
FromStr::from_str(start_date.split('-').collect::<Vec<&str>>()[0]).unwrap_or_else(
|_| {
eprintln!("{}", &message);
process::exit(1);
},
),
FromStr::from_str(start_date.split('-').collect::<Vec<&str>>()[1]).unwrap_or_else(
|_| {
eprintln!("{}", &message);
process::exit(1);
},
),
FromStr::from_str(start_date.split('-').collect::<Vec<&str>>()[2]).unwrap_or_else(
|_| {
eprintln!("{}", &message);
process::exit(1);
},
),
)
.and_hms_milli(0, 0, 1, 0)
.timestamp_millis()
.clamp(
Utc.ymd(2016, 1, 1)
.and_hms_milli(0, 0, 0, 0)
.timestamp_millis(),
Utc::now().timestamp_millis(),
);
let end_date_parsed: i64 = Utc
.ymd(
FromStr::from_str(end_date.split('-').collect::<Vec<&str>>()[0]).unwrap_or_else(|_| {
eprintln!("{}", &message);
process::exit(1);
}),
FromStr::from_str(end_date.split('-').collect::<Vec<&str>>()[1]).unwrap_or_else(|_| {
eprintln!("{}", &message);
process::exit(1);
}),
FromStr::from_str(end_date.split('-').collect::<Vec<&str>>()[2]).unwrap_or_else(|_| {
eprintln!("{}", &message);
process::exit(1);
}),
)
.and_hms_milli(0, 0, 2, 0)
.timestamp_millis()
.clamp(
Utc.ymd(2016, 1, 1)
.and_hms_milli(0, 0, 0, 0)
.timestamp_millis(),
Utc::now().timestamp_millis(),
);
(start_date_parsed, end_date_parsed)
主要是去掉传递给Utc.ymd
的三个参数,因为他们做的是一样的,只是使用了不同的索引,他们正在解析诸如“2021-01-01”之类的日期并以毫秒为单位返回它并且将它夹在地板和天花板上。
我们可以将“不要重新发明轮子”称为 Rustacean 方式吗?大概..
您可以使用 NaiveDate::parse_from_str
,有很多格式选项。
然后,您可以使用 Utc::from_utc_date
或 Utc::from_local_date
获取 Date
并稍后像处理
一样处理它
这将代码缩减为:
use chrono::*;
fn check_and_transform_dates(start_date: &str, end_date: &str) -> (i64, i64) {
let message: String = format!(
"Data could not be downloaded ❌, please make sure your dates
are in the following format YYYY-MM-DD
(ie. 2020-01-01), your dates are Start Date: {}, End Date: {}",
&start_date, &end_date,
);
let start_date = NaiveDate::parse_from_str(start_date, "%Y-%m-%d")
.expect(&message);
let start_date_parsed: i64 = Utc.from_utc_date(&start_date)
.and_hms_milli(0, 0, 1, 0)
.timestamp_millis()
.clamp(
Utc.ymd(2016, 1, 1)
.and_hms_milli(0, 0, 0, 0)
.timestamp_millis(),
Utc::now().timestamp_millis(),
);
let end_date = NaiveDate::parse_from_str(end_date, "%Y-%m-%d")
.expect(&message);
let end_date_parsed: i64 = Utc.from_utc_date(&end_date)
.and_hms_milli(0, 0, 2, 0)
.timestamp_millis()
.clamp(
Utc.ymd(2016, 1, 1)
.and_hms_milli(0, 0, 0, 0)
.timestamp_millis(),
Utc::now().timestamp_millis(),
);
(start_date_parsed, end_date_parsed)
}
这就是代码设计问题的来源,它是基于意见的。你看 - 你重复相同的代码两次来处理开始和结束日期 - 理想情况下,这段代码应该成为一个单独的函数。
use chrono::*;
fn parse_and_transform_date(date_str: &str) -> Result<i64, format::ParseError> {
let date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d")?;
let date_parsed: i64 = Utc.from_utc_date(&date)
.and_hms_milli(0, 0, 1, 0)
.timestamp_millis()
.clamp(
Utc.ymd(2016, 1, 1)
.and_hms_milli(0, 0, 0, 0)
.timestamp_millis(),
Utc::now().timestamp_millis(),
);
Ok(date_parsed)
}
fn check_and_transform_dates(start_date: &str, end_date: &str) -> (i64, i64) {
let start_date_result = parse_and_transform_date(start_date);
let end_date_result = parse_and_transform_date(end_date);
if let (Ok(start_date), Ok(end_date)) = (start_date_result, end_date_result) {
return (start_date, end_date); // success
}
panic!(
"Data could not be downloaded ❌, please make sure your dates
are in the following format YYYY-MM-DD
(ie. 2020-01-01), your dates are Start Date: {}, End Date: {}",
&start_date, &end_date,
);
}
最后也让大家考虑几个问题:
- 用这样的错误信息中止你的整个程序不是一个好主意,我建议
check_and_transform_dates
代替 return a Result
然后考虑这个结果,你的调用代码应正确处理这种情况。
check_and_transform_dates
也应该做额外的检查,例如检查 end_date
不在 start_date
之前,等等
修复示例:
use chrono::*;
fn parse_and_transform_date(date_str: &str) -> Result<i64, format::ParseError> {
let date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d")?;
let date_parsed: i64 = Utc.from_utc_date(&date)
.and_hms_milli(0, 0, 1, 0)
.timestamp_millis()
.clamp(
Utc.ymd(2016, 1, 1)
.and_hms_milli(0, 0, 0, 0)
.timestamp_millis(),
Utc::now().timestamp_millis(),
);
Ok(date_parsed)
}
fn check_and_transform_dates(start_date: &str, end_date: &str) -> Option<(i64, i64)> {
let start_date_result = parse_and_transform_date(start_date);
let end_date_result = parse_and_transform_date(end_date);
match (start_date_result, end_date_result) {
(Ok(s), Ok(e)) if (s <= e) => Some((s, e)),
_ => None
}
}
fn main() {
println!("{:?}", check_and_transform_dates("2020-01-02", "2020-01-03")
.expect("Data could not be downloaded"));
}
我要进行重构
fn check_and_transform_dates(start_date: &str, end_date: &str) -> (i64, i64) {
let message: String = format!(
"Data could not be downloaded ❌, please make sure your dates
are in the following format YYYY-MM-DD
(ie. 2020-01-01), your dates are Start Date: {}, End Date: {}",
&start_date, &end_date,
);
let earliest: NaiveDate = NaiveDate::from_ymd(2016, 1, 1);
let today: NaiveDate = Utc::today().naive_utc();
let parse_date = |date: &str| -> NaiveDate {
let date: NaiveDate = NaiveDate::parse_from_str(date, "%F").unwrap_or_else(|_| {
eprintln!("{}", &message);
process::exit(1);
});
if date < earliest {
earliest
} else if date > today {
today
} else {
date
}
};
(
parse_date(start_date).and_hms(0, 0, 1).timestamp() * 1000,
parse_date(end_date).and_hms(0, 0, 2).timestamp() * 1000,
)
}
做一个检查夹紧的功能
fn check_and_clamp_date()
需要 start_date
和 end_date
extern crate chrono;
use chrono::{Duration, NaiveDate, Utc};
static FMT: &str = "%Y-%m-%d";
fn check_and_clamp_date(date: &str, ceil_date: &str, floor_date: &str) -> Result<i64, String> {
let ceil = NaiveDate::parse_from_str(ceil_date, FMT).expect("start corrupt");
let floor = NaiveDate::parse_from_str(floor_date, FMT).expect("end corrupt");
let r = NaiveDate::parse_from_str(date, FMT);
match r {
Err(_) => Err("invalid date format".to_string()),
Ok(d) => {
let delta: Duration = d.clamp(ceil, floor).signed_duration_since(NaiveDate::from_ymd(1970, 1, 1));
Ok(delta.num_nanoseconds().unwrap() / 1000000)
},
}
}
fn check_and_transform_dates(start_date: &str, end_date: &str) -> (i64, i64) {
let message: String = format!(
"Data could not be downloaded ❌, please make sure your dates
are in the following format YYYY-MM-DD
(ie. 2020-01-01), your dates are Start Date: {}, End Date: {}",
&start_date, &end_date,
);
let now = Utc::now().format(FMT).to_string();
let start = check_and_clamp_date(start_date, "2016-01-01", &now).expect(&message) + 1000;
let end = check_and_clamp_date(end_date, "2016-01-01", &now).expect(&message) + 2000;
(start, end)
}
我有这个功能,但感觉是重复代码。我想知道这里是否有人分享如何让它变得更多 rustacean
。我仍在学习 Rust,我认为这可能是一个很好的例子来分享。
fn check_and_transform_dates(start_date: &str, end_date: &str) -> (i64, i64) {
let message: String = format!(
"Data could not be downloaded ❌, please make sure your dates
are in the following format YYYY-MM-DD
(ie. 2020-01-01), your dates are Start Date: {}, End Date: {}",
&start_date, &end_date,
);
let start_date_parsed: i64 = Utc
.ymd(
FromStr::from_str(start_date.split('-').collect::<Vec<&str>>()[0]).unwrap_or_else(
|_| {
eprintln!("{}", &message);
process::exit(1);
},
),
FromStr::from_str(start_date.split('-').collect::<Vec<&str>>()[1]).unwrap_or_else(
|_| {
eprintln!("{}", &message);
process::exit(1);
},
),
FromStr::from_str(start_date.split('-').collect::<Vec<&str>>()[2]).unwrap_or_else(
|_| {
eprintln!("{}", &message);
process::exit(1);
},
),
)
.and_hms_milli(0, 0, 1, 0)
.timestamp_millis()
.clamp(
Utc.ymd(2016, 1, 1)
.and_hms_milli(0, 0, 0, 0)
.timestamp_millis(),
Utc::now().timestamp_millis(),
);
let end_date_parsed: i64 = Utc
.ymd(
FromStr::from_str(end_date.split('-').collect::<Vec<&str>>()[0]).unwrap_or_else(|_| {
eprintln!("{}", &message);
process::exit(1);
}),
FromStr::from_str(end_date.split('-').collect::<Vec<&str>>()[1]).unwrap_or_else(|_| {
eprintln!("{}", &message);
process::exit(1);
}),
FromStr::from_str(end_date.split('-').collect::<Vec<&str>>()[2]).unwrap_or_else(|_| {
eprintln!("{}", &message);
process::exit(1);
}),
)
.and_hms_milli(0, 0, 2, 0)
.timestamp_millis()
.clamp(
Utc.ymd(2016, 1, 1)
.and_hms_milli(0, 0, 0, 0)
.timestamp_millis(),
Utc::now().timestamp_millis(),
);
(start_date_parsed, end_date_parsed)
主要是去掉传递给Utc.ymd
的三个参数,因为他们做的是一样的,只是使用了不同的索引,他们正在解析诸如“2021-01-01”之类的日期并以毫秒为单位返回它并且将它夹在地板和天花板上。
我们可以将“不要重新发明轮子”称为 Rustacean 方式吗?大概..
您可以使用 NaiveDate::parse_from_str
,有很多格式选项。
然后,您可以使用 Utc::from_utc_date
或 Utc::from_local_date
获取 Date
并稍后像处理
这将代码缩减为:
use chrono::*;
fn check_and_transform_dates(start_date: &str, end_date: &str) -> (i64, i64) {
let message: String = format!(
"Data could not be downloaded ❌, please make sure your dates
are in the following format YYYY-MM-DD
(ie. 2020-01-01), your dates are Start Date: {}, End Date: {}",
&start_date, &end_date,
);
let start_date = NaiveDate::parse_from_str(start_date, "%Y-%m-%d")
.expect(&message);
let start_date_parsed: i64 = Utc.from_utc_date(&start_date)
.and_hms_milli(0, 0, 1, 0)
.timestamp_millis()
.clamp(
Utc.ymd(2016, 1, 1)
.and_hms_milli(0, 0, 0, 0)
.timestamp_millis(),
Utc::now().timestamp_millis(),
);
let end_date = NaiveDate::parse_from_str(end_date, "%Y-%m-%d")
.expect(&message);
let end_date_parsed: i64 = Utc.from_utc_date(&end_date)
.and_hms_milli(0, 0, 2, 0)
.timestamp_millis()
.clamp(
Utc.ymd(2016, 1, 1)
.and_hms_milli(0, 0, 0, 0)
.timestamp_millis(),
Utc::now().timestamp_millis(),
);
(start_date_parsed, end_date_parsed)
}
这就是代码设计问题的来源,它是基于意见的。你看 - 你重复相同的代码两次来处理开始和结束日期 - 理想情况下,这段代码应该成为一个单独的函数。
use chrono::*;
fn parse_and_transform_date(date_str: &str) -> Result<i64, format::ParseError> {
let date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d")?;
let date_parsed: i64 = Utc.from_utc_date(&date)
.and_hms_milli(0, 0, 1, 0)
.timestamp_millis()
.clamp(
Utc.ymd(2016, 1, 1)
.and_hms_milli(0, 0, 0, 0)
.timestamp_millis(),
Utc::now().timestamp_millis(),
);
Ok(date_parsed)
}
fn check_and_transform_dates(start_date: &str, end_date: &str) -> (i64, i64) {
let start_date_result = parse_and_transform_date(start_date);
let end_date_result = parse_and_transform_date(end_date);
if let (Ok(start_date), Ok(end_date)) = (start_date_result, end_date_result) {
return (start_date, end_date); // success
}
panic!(
"Data could not be downloaded ❌, please make sure your dates
are in the following format YYYY-MM-DD
(ie. 2020-01-01), your dates are Start Date: {}, End Date: {}",
&start_date, &end_date,
);
}
最后也让大家考虑几个问题:
- 用这样的错误信息中止你的整个程序不是一个好主意,我建议
check_and_transform_dates
代替 return aResult
然后考虑这个结果,你的调用代码应正确处理这种情况。 check_and_transform_dates
也应该做额外的检查,例如检查end_date
不在start_date
之前,等等
修复示例:
use chrono::*;
fn parse_and_transform_date(date_str: &str) -> Result<i64, format::ParseError> {
let date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d")?;
let date_parsed: i64 = Utc.from_utc_date(&date)
.and_hms_milli(0, 0, 1, 0)
.timestamp_millis()
.clamp(
Utc.ymd(2016, 1, 1)
.and_hms_milli(0, 0, 0, 0)
.timestamp_millis(),
Utc::now().timestamp_millis(),
);
Ok(date_parsed)
}
fn check_and_transform_dates(start_date: &str, end_date: &str) -> Option<(i64, i64)> {
let start_date_result = parse_and_transform_date(start_date);
let end_date_result = parse_and_transform_date(end_date);
match (start_date_result, end_date_result) {
(Ok(s), Ok(e)) if (s <= e) => Some((s, e)),
_ => None
}
}
fn main() {
println!("{:?}", check_and_transform_dates("2020-01-02", "2020-01-03")
.expect("Data could not be downloaded"));
}
我要进行重构
fn check_and_transform_dates(start_date: &str, end_date: &str) -> (i64, i64) {
let message: String = format!(
"Data could not be downloaded ❌, please make sure your dates
are in the following format YYYY-MM-DD
(ie. 2020-01-01), your dates are Start Date: {}, End Date: {}",
&start_date, &end_date,
);
let earliest: NaiveDate = NaiveDate::from_ymd(2016, 1, 1);
let today: NaiveDate = Utc::today().naive_utc();
let parse_date = |date: &str| -> NaiveDate {
let date: NaiveDate = NaiveDate::parse_from_str(date, "%F").unwrap_or_else(|_| {
eprintln!("{}", &message);
process::exit(1);
});
if date < earliest {
earliest
} else if date > today {
today
} else {
date
}
};
(
parse_date(start_date).and_hms(0, 0, 1).timestamp() * 1000,
parse_date(end_date).and_hms(0, 0, 2).timestamp() * 1000,
)
}
做一个检查夹紧的功能
fn check_and_clamp_date()
需要 start_date
和 end_date
extern crate chrono;
use chrono::{Duration, NaiveDate, Utc};
static FMT: &str = "%Y-%m-%d";
fn check_and_clamp_date(date: &str, ceil_date: &str, floor_date: &str) -> Result<i64, String> {
let ceil = NaiveDate::parse_from_str(ceil_date, FMT).expect("start corrupt");
let floor = NaiveDate::parse_from_str(floor_date, FMT).expect("end corrupt");
let r = NaiveDate::parse_from_str(date, FMT);
match r {
Err(_) => Err("invalid date format".to_string()),
Ok(d) => {
let delta: Duration = d.clamp(ceil, floor).signed_duration_since(NaiveDate::from_ymd(1970, 1, 1));
Ok(delta.num_nanoseconds().unwrap() / 1000000)
},
}
}
fn check_and_transform_dates(start_date: &str, end_date: &str) -> (i64, i64) {
let message: String = format!(
"Data could not be downloaded ❌, please make sure your dates
are in the following format YYYY-MM-DD
(ie. 2020-01-01), your dates are Start Date: {}, End Date: {}",
&start_date, &end_date,
);
let now = Utc::now().format(FMT).to_string();
let start = check_and_clamp_date(start_date, "2016-01-01", &now).expect(&message) + 1000;
let end = check_and_clamp_date(end_date, "2016-01-01", &now).expect(&message) + 2000;
(start, end)
}