Skip to content
6 changes: 3 additions & 3 deletions core/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -867,9 +867,9 @@ fn resolve_builtin_identifier<I: Interrupt>(
]),
"print" => Value::BuiltInFunction(BuiltInFunction::Print),
"println" => Value::BuiltInFunction(BuiltInFunction::Println),
"today" => Value::Date(crate::date::Date::today(context)?),
"tomorrow" => Value::Date(crate::date::Date::today(context)?.next()),
"yesterday" => Value::Date(crate::date::Date::today(context)?.prev()),
"today" => Value::Date(crate::date::Date::today(context, int)?),
"tomorrow" => Value::Date(crate::date::Date::today(context, int)?.next()?),
"yesterday" => Value::Date(crate::date::Date::today(context, int)?.prev()?),
"trans" => Value::String(Cow::Borrowed("🏳️‍⚧️")),
_ => return Err(FendError::IdentifierNotFound(ident.clone())),
})
Expand Down
201 changes: 135 additions & 66 deletions core/src/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub(crate) use day_of_week::DayOfWeek;
pub(crate) use month::Month;
use year::Year;

use crate::interrupt::test_int;
use crate::{Interrupt, error::FendError, ident::Ident, result::FResult, value::Value};

#[derive(Copy, Clone, Eq, PartialEq)]
Expand All @@ -21,36 +22,31 @@ pub(crate) struct Date {
}

impl Date {
pub(crate) fn today(context: &crate::Context) -> FResult<Self> {
pub(crate) fn today<I: Interrupt>(context: &crate::Context, int: &I) -> FResult<Self> {
let Some(current_time_info) = &context.current_time else {
return Err(FendError::UnableToGetCurrentDate);
};
let mut ms_since_epoch: i64 = current_time_info.elapsed_unix_time_ms.try_into().unwrap();
ms_since_epoch -= current_time_info.timezone_offset_secs * 1000;
let mut days = ms_since_epoch / 86_400_000; // no leap seconds
let mut year = Year::new(1970);
while days >= year.number_of_days().into() {
year = year.next();
days -= i64::from(year.number_of_days());
}
let mut month = Month::January;
while days >= month.number_of_days(year).into() {
month = month.next();
days -= i64::from(month.number_of_days(year));
}
Ok(Self {
year,
month,
day: Day::new(days.try_into().unwrap()),
})
let mut seconds_since_epoch = current_time_info.elapsed_unix_time_ms / 1000;
seconds_since_epoch = seconds_since_epoch
.checked_sub_signed(current_time_info.timezone_offset_secs)
.ok_or(FendError::ValueTooLarge)?;
let days = seconds_since_epoch / (60 * 60 * 24); // no leap seconds

let date = Self {
year: Year::new(1970),
day: Day::new(1),
month: Month::January,
};

date.add_days(days.try_into().map_err(|_| FendError::ValueTooLarge)?, int)
}

fn day_of_week(self) -> DayOfWeek {
let d1 = (1
+ 5 * ((self.year.value() - 1) % 4)
+ 4 * ((self.year.value() - 1) % 100)
+ 6 * ((self.year.value() - 1) % 400))
% 7;
let d1 = ((1
+ 5 * ((i64::from(self.year.value()) - 1) % 4)
+ 4 * ((i64::from(self.year.value()) - 1) % 100)
+ 6 * ((i64::from(self.year.value()) - 1) % 400))
% 7) as i32;
let ms = match self.month {
Month::January => (0, 0),
Month::February => (3, 3),
Expand All @@ -63,7 +59,7 @@ impl Date {
Month::October => (0, 1),
};
let m = if self.year.is_leap_year() { ms.1 } else { ms.0 };
match (d1 + m + i32::from(self.day.value() - 1)) % 7 {
match ((d1 + m + i32::from(self.day.value() - 1)) % 7 + 7) % 7 {
0 => DayOfWeek::Sunday,
1 => DayOfWeek::Monday,
2 => DayOfWeek::Tuesday,
Expand All @@ -75,65 +71,66 @@ impl Date {
}
}

pub(crate) fn next(self) -> Self {
pub(crate) fn next(self) -> FResult<Self> {
if self.day.value() < Month::number_of_days(self.month, self.year) {
Self {
Ok(Self {
day: Day::new(self.day.value() + 1),
month: self.month,
year: self.year,
}
})
} else if self.month == Month::December {
Self {
Ok(Self {
day: Day::new(1),
month: Month::January,
year: self.year.next(),
}
year: self.year.next()?,
})
} else {
Self {
Ok(Self {
day: Day::new(1),
month: self.month.next(),
year: self.year,
}
})
}
}

pub(crate) fn prev(self) -> Self {
pub(crate) fn prev(self) -> FResult<Self> {
if self.day.value() > 1 {
Self {
Ok(Self {
day: Day::new(self.day.value() - 1),
month: self.month,
year: self.year,
}
})
} else if self.month == Month::January {
Self {
Ok(Self {
day: Day::new(31),
month: Month::December,
year: self.year.prev(),
}
year: self.year.prev()?,
})
} else {
let month = self.month.prev();
Self {
Ok(Self {
day: Day::new(Month::number_of_days(month, self.year)),
month,
year: self.year,
}
})
}
}

pub(crate) fn diff_months(self, mut months: i64) -> FResult<Self> {
let mut result = self;
while months >= 12 {
result.year = result.year.next();
months -= 12;
}
while months <= -12 {
result.year = result.year.prev();
months += 12;
if months >= 12 {
let years = months / 12;
months %= 12;
result.year = result.year.add(years)?;
} else if months <= -12 {
let years = (months / 12).unsigned_abs();
months %= 12;
result.year = result.year.sub(years)?;
}
while months > 0 {
if result.month == Month::December {
result.month = Month::January;
result.year = result.year.next();
result.year = result.year.next()?;
} else {
result.month = result.month.next();
}
Expand All @@ -142,7 +139,7 @@ impl Date {
while months < 0 {
if result.month == Month::January {
result.month = Month::December;
result.year = result.year.prev();
result.year = result.year.prev()?;
} else {
result.month = result.month.prev();
}
Expand All @@ -154,7 +151,7 @@ impl Date {
let mut after = result;
if after.month == Month::December {
after.month = Month::January;
after.year = after.year.next();
after.year = after.year.next()?;
} else {
after.month = after.month.next();
}
Expand Down Expand Up @@ -197,38 +194,110 @@ impl Date {
})
}

fn add_days<I: Interrupt>(self, mut num_days: usize, int: &I) -> FResult<Self> {
let mut result = self;

// make sure to be before 29th February to make skipping years work
while num_days > 0 && result.month != Month::January {
result = result.next()?;
num_days -= 1;
}

while let days_in_year = result.year.number_of_days().into()
&& num_days >= days_in_year
{
num_days -= days_in_year;

result.year = result.year.next()?;

test_int(int)?;
}

for _ in 0..num_days {
test_int(int)?;
result = result.next()?;
}

Ok(result)
}

pub(crate) fn add<I: Interrupt>(self, rhs: Value, int: &I) -> FResult<Value> {
let rhs = rhs.expect_num()?;
if rhs.unit_equal_to("day", int)? {
let num_days = rhs.try_as_usize_unit(int)?;
let mut result = self;
for _ in 0..num_days {
result = result.next();
}

let result = self.add_days(num_days, int)?;

Ok(Value::Date(result))
} else if rhs.unit_equal_to("week", int)? {
let num_weeks = rhs.try_as_usize_unit(int)?;

let num_days = num_weeks.checked_mul(7).ok_or(FendError::ValueTooLarge)?;

let result = self.add_days(num_days, int)?;

debug_assert_eq!(self.day_of_week(), result.day_of_week());

Ok(Value::Date(result))
} else if rhs.unit_equal_to("month", int)? {
let num_months = rhs.try_as_usize_unit(int)?;
let result =
self.diff_months(i64::try_from(num_months).map_err(|_| FendError::ValueTooLarge)?)?;
Ok(Value::Date(result))
} else if rhs.unit_equal_to("year", int)? {
let num_years = rhs.try_as_usize_unit(int)?;
let num_months = num_years.checked_mul(12).ok_or(FendError::ValueTooLarge)?;
let result =
self.diff_months(i64::try_from(num_months).map_err(|_| FendError::ValueTooLarge)?)?;
Ok(Value::Date(result))
} else {
Err(FendError::ExpectedANumber)
}
}

fn sub_days<I: Interrupt>(self, mut num_days: usize, int: &I) -> FResult<Self> {
let mut result = self;

// make sure to be after 29th February to make skipping years work
while num_days > 0 && result.month.as_u8() < Month::March.as_u8() {
result = result.prev()?;
num_days -= 1;
}

while let days_in_year = result.year.number_of_days().into()
&& num_days >= days_in_year
{
num_days -= days_in_year;

result.year = result.year.prev()?;

test_int(int)?;
}

for _ in 0..num_days {
result = result.prev()?;
test_int(int)?;
}

Ok(result)
}

pub(crate) fn sub<I: Interrupt>(self, rhs: Value, int: &I) -> FResult<Value> {
let rhs = rhs.expect_num()?;

if rhs.unit_equal_to("day", int)? {
let num_days = rhs.try_as_usize_unit(int)?;
let mut result = self;
for _ in 0..num_days {
result = result.prev();
}

let result = self.sub_days(num_days, int)?;

Ok(Value::Date(result))
} else if rhs.unit_equal_to("week", int)? {
let num_weeks = rhs.try_as_usize_unit(int)?;
let mut result = self;
for _ in 0..num_weeks {
for _ in 0..7 {
result = result.prev();
}
}

let num_days = num_weeks.checked_mul(7).ok_or(FendError::ValueTooLarge)?;

let result = self.sub_days(num_days, int)?;

Ok(Value::Date(result))
} else if rhs.unit_equal_to("month", int)? {
let num_months = rhs.try_as_usize_unit(int)?;
Expand All @@ -237,7 +306,7 @@ impl Date {
Ok(Value::Date(result))
} else if rhs.unit_equal_to("year", int)? {
let num_years = rhs.try_as_usize_unit(int)?;
let num_months = num_years * 12;
let num_months = num_years.checked_mul(12).ok_or(FendError::ValueTooLarge)?;
let result = self
.diff_months(-i64::try_from(num_months).map_err(|_| FendError::ValueTooLarge)?)?;
Ok(Value::Date(result))
Expand Down
2 changes: 1 addition & 1 deletion core/src/date/month.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl Month {
.map_err(|_| FendError::DeserializationError("month is out of range"))
}

fn as_u8(self) -> u8 {
pub(crate) fn as_u8(self) -> u8 {
match self {
Self::January => 1,
Self::February => 2,
Expand Down
Loading
Loading