Skip to content

Commit b745b48

Browse files
committed
docs: add comments for the build function
1 parent 7a32b1e commit b745b48

File tree

1 file changed

+53
-22
lines changed

1 file changed

+53
-22
lines changed

src/items/builder.rs

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -160,31 +160,59 @@ impl DateTimeBuilder {
160160
self.set_time(time)
161161
}
162162

163+
/// Build a `Zoned` object from the pieces accumulated in this builder.
164+
///
165+
/// Resolution order (mirrors GNU `date` semantics):
166+
///
167+
/// 1. Base instant.
168+
/// a. If `self.base` is provided, start with it.
169+
/// b. Else if a `timezone` rule is present, start with "now" in that
170+
/// timezone.
171+
/// c. Else start with current system local time.
172+
///
173+
/// 2. Absolute timestamp override.
174+
/// a. If `self.timestamp` is set, it fully determines the result.
175+
///
176+
/// 3. Time of day truncation.
177+
/// a. If any of date, time, weekday, offset, timezone is set, zero the
178+
/// time of day to 00:00:00 before applying fields.
179+
///
180+
/// 4. Fieldwise resolution (applied to the base instant).
181+
/// a. Apply date. If year is absent in the parsed date, inherit the year
182+
/// from the base instant.
183+
/// b. Apply time. If time carries an explicit numeric offset, apply the
184+
/// offset before setting time.
185+
/// c. Apply weekday (e.g., "next Friday" or "last Monday").
186+
/// d. Apply relative adjustments (e.g., "+3 days", "-2 months").
187+
/// e. Apply final fixed offset if present.
163188
pub(super) fn build(self) -> Result<Zoned, error::Error> {
164-
let base = self.base.unwrap_or(if let Some(tz) = &self.timezone {
165-
jiff::Timestamp::now().to_zoned(tz.clone())
166-
} else {
167-
Zoned::now()
168-
});
189+
// 1. Choose the base instant.
190+
let base = match (self.base, &self.timezone) {
191+
(Some(b), _) => b,
192+
(None, Some(tz)) => jiff::Timestamp::now().to_zoned(tz.clone()),
193+
(None, None) => Zoned::now(),
194+
};
169195

170-
// If a timestamp is set, we use it to build the `Zoned` object.
196+
// 2. Absolute timestamp override everything else.
171197
if let Some(ts) = self.timestamp {
172-
return Ok(jiff::Timestamp::try_from(ts)?.to_zoned(base.offset().to_time_zone()));
198+
let ts = jiff::Timestamp::try_from(ts)?;
199+
return Ok(ts.to_zoned(base.offset().to_time_zone()));
173200
}
174201

175-
// If any of the following items are set, we truncate the time portion
176-
// of the base date to zero; otherwise, we use the base date as is.
177-
let mut dt = if self.date.is_none()
178-
&& self.time.is_none()
179-
&& self.weekday.is_none()
180-
&& self.offset.is_none()
181-
&& self.timezone.is_none()
182-
{
183-
base
184-
} else {
202+
// 3. Determine whether to truncate the time of day.
203+
let need_midnight = self.date.is_some()
204+
|| self.time.is_some()
205+
|| self.weekday.is_some()
206+
|| self.offset.is_some()
207+
|| self.timezone.is_some();
208+
209+
let mut dt = if need_midnight {
185210
base.with().time(civil::time(0, 0, 0, 0)).build()?
211+
} else {
212+
base
186213
};
187214

215+
// 4a. Apply date.
188216
if let Some(date) = self.date {
189217
let d: civil::Date = if date.year.is_some() {
190218
date.try_into()?
@@ -194,6 +222,7 @@ impl DateTimeBuilder {
194222
dt = dt.with().date(d).build()?;
195223
}
196224

225+
// 4b. Apply time.
197226
if let Some(time) = self.time.clone() {
198227
if let Some(offset) = &time.offset {
199228
dt = dt.datetime().to_zoned(offset.try_into()?)?;
@@ -203,21 +232,21 @@ impl DateTimeBuilder {
203232
dt = dt.with().time(t).build()?;
204233
}
205234

206-
if let Some(weekday::Weekday { offset, day }) = self.weekday {
235+
// 4c. Apply weekday.
236+
if let Some(weekday::Weekday { mut offset, day }) = self.weekday {
207237
if self.time.is_none() {
208238
dt = dt.with().time(civil::time(0, 0, 0, 0)).build()?;
209239
}
210240

211-
let mut offset = offset;
212-
let day = day.into();
241+
let target = day.into();
213242

214243
// If the current day is not the target day, we need to adjust
215244
// the x value to ensure we find the correct day.
216245
//
217246
// Consider this:
218247
// Assuming today is Monday, next Friday is actually THIS Friday;
219248
// but next Monday is indeed NEXT Monday.
220-
if dt.date().weekday() != day && offset > 0 {
249+
if dt.date().weekday() != target && offset > 0 {
221250
offset -= 1;
222251
}
223252

@@ -237,14 +266,15 @@ impl DateTimeBuilder {
237266
//
238267
// Example 4: next Thursday (x = 1, day = Thursday)
239268
// delta = (3 - 3) % 7 + (1) * 7 = 7
240-
let delta = (day.since(civil::Weekday::Monday) as i32
269+
let delta = (target.since(civil::Weekday::Monday) as i32
241270
- dt.date().weekday().since(civil::Weekday::Monday) as i32)
242271
.rem_euclid(7)
243272
+ offset.checked_mul(7).ok_or("multiplication overflow")?;
244273

245274
dt = dt.checked_add(Span::new().try_days(delta)?)?;
246275
}
247276

277+
// 4d. Apply relative adjustments.
248278
for rel in self.relative {
249279
dt = dt.checked_add::<Span>(if let relative::Relative::Months(x) = rel {
250280
// *NOTE* This is done in this way to conform to GNU behavior.
@@ -255,6 +285,7 @@ impl DateTimeBuilder {
255285
})?;
256286
}
257287

288+
// 4e. Apply final fixed offset.
258289
if let Some(offset) = self.offset {
259290
let (offset, hour_adjustment) = offset.normalize();
260291
dt = dt.checked_add(Span::new().hours(hour_adjustment))?;

0 commit comments

Comments
 (0)