Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit706ab45

Browse files
committed
refactor: introduce a builder and simplify the date time composition
1 parent4d9afba commit706ab45

File tree

3 files changed

+372
-326
lines changed

3 files changed

+372
-326
lines changed

‎src/items/builder.rs

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
// For the full copyright and license information, please view the LICENSE
2+
// file that was distributed with this source code.
3+
4+
use chrono::{DateTime,Datelike,FixedOffset,NaiveDate,TimeZone,Timelike};
5+
6+
usesuper::{date, relative, time, weekday};
7+
8+
/// The builder is used to construct a DateTime object from various components.
9+
/// The parser creates a `DateTimeBuilder` object with the parsed components,
10+
/// but without the baseline date and time. So you normally need to set the base
11+
/// date and time using the `set_base()` method before calling `build()`, or
12+
/// leave it unset to use the current date and time as the base.
13+
#[derive(Debug,Default)]
14+
pubstructDateTimeBuilder{
15+
base:Option<DateTime<FixedOffset>>,
16+
timestamp:Option<i32>,
17+
date:Option<date::Date>,
18+
time:Option<time::Time>,
19+
weekday:Option<weekday::Weekday>,
20+
timezone:Option<time::Offset>,
21+
relative:Vec<relative::Relative>,
22+
}
23+
24+
implDateTimeBuilder{
25+
pub(super)fnnew() ->Self{
26+
Self::default()
27+
}
28+
29+
/// Sets the base date and time for the builder. If not set, the current
30+
/// date and time will be used.
31+
pub(super)fnset_base(mutself,base:DateTime<FixedOffset>) ->Self{
32+
self.base =Some(base);
33+
self
34+
}
35+
36+
pub(super)fnset_timestamp(mutself,ts:i32) ->Result<Self,&'staticstr>{
37+
ifself.timestamp.is_some(){
38+
Err("timestamp cannot appear more than once")
39+
}else{
40+
self.timestamp =Some(ts);
41+
Ok(self)
42+
}
43+
}
44+
45+
pub(super)fnset_year(mutself,year:u32) ->Result<Self,&'staticstr>{
46+
ifletSome(date) =self.date.as_mut(){
47+
if date.year.is_some(){
48+
Err("year cannot appear more than once")
49+
}else{
50+
date.year =Some(year);
51+
Ok(self)
52+
}
53+
}else{
54+
self.date =Some(date::Date{
55+
day:1,
56+
month:1,
57+
year:Some(year),
58+
});
59+
Ok(self)
60+
}
61+
}
62+
63+
pub(super)fnset_date(mutself,date: date::Date) ->Result<Self,&'staticstr>{
64+
ifself.date.is_some() ||self.timestamp.is_some(){
65+
Err("date cannot appear more than once")
66+
}else{
67+
self.date =Some(date);
68+
Ok(self)
69+
}
70+
}
71+
72+
pub(super)fnset_time(mutself,time: time::Time) ->Result<Self,&'staticstr>{
73+
ifself.time.is_some() ||self.timestamp.is_some(){
74+
Err("time cannot appear more than once")
75+
}elseifself.timezone.is_some() && time.offset.is_some(){
76+
Err("time offset and timezone are mutually exclusive")
77+
}else{
78+
self.time =Some(time);
79+
Ok(self)
80+
}
81+
}
82+
83+
pub(super)fnset_weekday(mutself,weekday: weekday::Weekday) ->Result<Self,&'staticstr>{
84+
ifself.weekday.is_some(){
85+
Err("weekday cannot appear more than once")
86+
}else{
87+
self.weekday =Some(weekday);
88+
Ok(self)
89+
}
90+
}
91+
92+
pub(super)fnset_timezone(mutself,timezone: time::Offset) ->Result<Self,&'staticstr>{
93+
ifself.timezone.is_some(){
94+
Err("timezone cannot appear more than once")
95+
}elseifself.time.as_ref().and_then(|t| t.offset.as_ref()).is_some(){
96+
Err("time offset and timezone are mutually exclusive")
97+
}else{
98+
self.timezone =Some(timezone);
99+
Ok(self)
100+
}
101+
}
102+
103+
pub(super)fnpush_relative(mutself,relative: relative::Relative) ->Self{
104+
self.relative.push(relative);
105+
self
106+
}
107+
108+
pub(super)fnbuild(self) ->Option<DateTime<FixedOffset>>{
109+
let base =self.base.unwrap_or_else(|| chrono::Local::now().into());
110+
letmut dt =new_date(
111+
base.year(),
112+
base.month(),
113+
base.day(),
114+
0,
115+
0,
116+
0,
117+
0,
118+
*base.offset(),
119+
)?;
120+
121+
ifletSome(ts) =self.timestamp{
122+
dt = chrono::Utc
123+
.timestamp_opt(ts.into(),0)
124+
.unwrap()
125+
.with_timezone(&dt.timezone());
126+
}
127+
128+
ifletSome(date::Date{ year, month, day}) =self.date{
129+
dt =new_date(
130+
year.map(|x| xasi32).unwrap_or(dt.year()),
131+
month,
132+
day,
133+
dt.hour(),
134+
dt.minute(),
135+
dt.second(),
136+
dt.nanosecond(),
137+
*dt.offset(),
138+
)?;
139+
}
140+
141+
ifletSome(time::Time{
142+
hour,
143+
minute,
144+
second,
145+
ref offset,
146+
}) =self.time
147+
{
148+
let offset = offset
149+
.clone()
150+
.and_then(|o| chrono::FixedOffset::try_from(o).ok())
151+
.unwrap_or(*dt.offset());
152+
153+
dt =new_date(
154+
dt.year(),
155+
dt.month(),
156+
dt.day(),
157+
hour,
158+
minute,
159+
secondasu32,
160+
(second.fract()*10f64.powi(9)).round()asu32,
161+
offset,
162+
)?;
163+
}
164+
165+
ifletSome(weekday::Weekday{ offset, day}) =self.weekday{
166+
ifself.time.is_none(){
167+
dt =new_date(dt.year(), dt.month(), dt.day(),0,0,0,0,*dt.offset())?;
168+
}
169+
170+
letmut offset = offset;
171+
let day = day.into();
172+
173+
// If the current day is not the target day, we need to adjust
174+
// the x value to ensure we find the correct day.
175+
//
176+
// Consider this:
177+
// Assuming today is Monday, next Friday is actually THIS Friday;
178+
// but next Monday is indeed NEXT Monday.
179+
if dt.weekday() != day && offset >0{
180+
offset -=1;
181+
}
182+
183+
// Calculate the delta to the target day.
184+
//
185+
// Assuming today is Thursday, here are some examples:
186+
//
187+
// Example 1: last Thursday (x = -1, day = Thursday)
188+
// delta = (3 - 3) % 7 + (-1) * 7 = -7
189+
//
190+
// Example 2: last Monday (x = -1, day = Monday)
191+
// delta = (0 - 3) % 7 + (-1) * 7 = -3
192+
//
193+
// Example 3: next Monday (x = 1, day = Monday)
194+
// delta = (0 - 3) % 7 + (0) * 7 = 4
195+
// (Note that we have adjusted the x value above)
196+
//
197+
// Example 4: next Thursday (x = 1, day = Thursday)
198+
// delta = (3 - 3) % 7 + (1) * 7 = 7
199+
let delta =(day.num_days_from_monday()asi32
200+
- dt.weekday().num_days_from_monday()asi32)
201+
.rem_euclid(7)
202+
+ offset.checked_mul(7)?;
203+
204+
dt =if delta <0{
205+
dt.checked_sub_days(chrono::Days::new((-delta)asu64))?
206+
}else{
207+
dt.checked_add_days(chrono::Days::new(deltaasu64))?
208+
}
209+
}
210+
211+
for relinself.relative{
212+
ifself.timestamp.is_none()
213+
&&self.date.is_none()
214+
&&self.time.is_none()
215+
&&self.weekday.is_none()
216+
{
217+
dt = base;
218+
}
219+
220+
match rel{
221+
relative::Relative::Years(x) =>{
222+
dt = dt.with_year(dt.year() + x)?;
223+
}
224+
relative::Relative::Months(x) =>{
225+
// *NOTE* This is done in this way to conform to
226+
// GNU behavior.
227+
let days =last_day_of_month(dt.year(), dt.month());
228+
if x >=0{
229+
dt += dt
230+
.date_naive()
231+
.checked_add_days(chrono::Days::new((days* xasu32)asu64))?
232+
.signed_duration_since(dt.date_naive());
233+
}else{
234+
dt += dt
235+
.date_naive()
236+
.checked_sub_days(chrono::Days::new((days* -xasu32)asu64))?
237+
.signed_duration_since(dt.date_naive());
238+
}
239+
}
240+
relative::Relative::Days(x) => dt += chrono::Duration::days(x.into()),
241+
relative::Relative::Hours(x) => dt += chrono::Duration::hours(x.into()),
242+
relative::Relative::Minutes(x) =>{
243+
dt += chrono::Duration::try_minutes(x.into())?;
244+
}
245+
// Seconds are special because they can be given as a float
246+
relative::Relative::Seconds(x) =>{
247+
dt += chrono::Duration::try_seconds(xasi64)?;
248+
}
249+
}
250+
}
251+
252+
ifletSome(offset) =self.timezone{
253+
dt =with_timezone_restore(offset, dt)?;
254+
}
255+
256+
Some(dt)
257+
}
258+
}
259+
260+
#[allow(clippy::too_many_arguments)]
261+
fnnew_date(
262+
year:i32,
263+
month:u32,
264+
day:u32,
265+
hour:u32,
266+
minute:u32,
267+
second:u32,
268+
nano:u32,
269+
offset:FixedOffset,
270+
) ->Option<DateTime<FixedOffset>>{
271+
let newdate =NaiveDate::from_ymd_opt(year, month, day)
272+
.and_then(|naive| naive.and_hms_nano_opt(hour, minute, second, nano))?;
273+
274+
Some(DateTime::<FixedOffset>::from_local(newdate, offset))
275+
}
276+
277+
/// Restores year, month, day, etc after applying the timezone
278+
/// returns None if timezone overflows the date
279+
fnwith_timezone_restore(
280+
offset: time::Offset,
281+
at:DateTime<FixedOffset>,
282+
) ->Option<DateTime<FixedOffset>>{
283+
let offset:FixedOffset = chrono::FixedOffset::try_from(offset).ok()?;
284+
let copy = at;
285+
let x = at
286+
.with_timezone(&offset)
287+
.with_day(copy.day())?
288+
.with_month(copy.month())?
289+
.with_year(copy.year())?
290+
.with_hour(copy.hour())?
291+
.with_minute(copy.minute())?
292+
.with_second(copy.second())?
293+
.with_nanosecond(copy.nanosecond())?;
294+
Some(x)
295+
}
296+
297+
fnlast_day_of_month(year:i32,month:u32) ->u32{
298+
NaiveDate::from_ymd_opt(year, month +1,1)
299+
.unwrap_or(NaiveDate::from_ymd_opt(year +1,1,1).unwrap())
300+
.pred_opt()
301+
.unwrap()
302+
.day()
303+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp