Files
IronCalc/base/src/functions/date_and_time.rs

311 lines
11 KiB
Rust

use chrono::DateTime;
use chrono::Datelike;
use chrono::Months;
use chrono::Timelike;
use crate::constants::MAXIMUM_DATE_SERIAL_NUMBER;
use crate::constants::MINIMUM_DATE_SERIAL_NUMBER;
use crate::expressions::types::CellReferenceIndex;
use crate::formatter::dates::date_to_serial_number;
use crate::formatter::dates::permissive_date_to_serial_number;
use crate::model::get_milliseconds_since_epoch;
use crate::{
calc_result::CalcResult, constants::EXCEL_DATE_BASE, expressions::parser::Node,
expressions::token::Error, formatter::dates::from_excel_date, model::Model,
};
impl Model {
pub(crate) fn fn_day(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
let args_count = args.len();
if args_count != 1 {
return CalcResult::new_args_number_error(cell);
}
let serial_number = match self.get_number(&args[0], cell) {
Ok(c) => c.floor() as i64,
Err(s) => return s,
};
let date = match from_excel_date(serial_number) {
Ok(date) => date,
Err(_) => {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "Out of range parameters for date".to_string(),
}
}
};
let day = date.day() as f64;
CalcResult::Number(day)
}
pub(crate) fn fn_month(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
let args_count = args.len();
if args_count != 1 {
return CalcResult::new_args_number_error(cell);
}
let serial_number = match self.get_number(&args[0], cell) {
Ok(c) => c.floor() as i64,
Err(s) => return s,
};
let date = match from_excel_date(serial_number) {
Ok(date) => date,
Err(_) => {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "Out of range parameters for date".to_string(),
}
}
};
let month = date.month() as f64;
CalcResult::Number(month)
}
pub(crate) fn fn_eomonth(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
let args_count = args.len();
if args_count != 2 {
return CalcResult::new_args_number_error(cell);
}
let serial_number = match self.get_number(&args[0], cell) {
Ok(c) => {
let t = c.floor() as i64;
if t < 0 {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "Function EOMONTH parameter 1 value is negative. It should be positive or zero.".to_string(),
};
}
t
}
Err(s) => return s,
};
let date = match from_excel_date(serial_number) {
Ok(date) => date,
Err(_) => {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "Out of range parameters for date".to_string(),
}
}
};
if serial_number > MAXIMUM_DATE_SERIAL_NUMBER as i64 {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "Function DAY parameter 1 value is too large.".to_string(),
};
}
let months = match self.get_number_no_bools(&args[1], cell) {
Ok(c) => {
let t = c.trunc();
t as i32
}
Err(s) => return s,
};
let months_abs = months.unsigned_abs();
let native_date = if months > 0 {
date + Months::new(months_abs)
} else {
date - Months::new(months_abs)
};
// Instead of calculating the end of month we compute the first day of the following month
// and take one day.
let mut month = native_date.month() + 1;
let mut year = native_date.year();
if month == 13 {
month = 1;
year += 1;
}
match date_to_serial_number(1, month, year) {
Ok(serial_number) => CalcResult::Number(serial_number as f64 - 1.0),
Err(message) => CalcResult::Error {
error: Error::NUM,
origin: cell,
message,
},
}
}
// year, month, day
pub(crate) fn fn_date(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
let args_count = args.len();
if args_count != 3 {
return CalcResult::new_args_number_error(cell);
}
let year = match self.get_number(&args[0], cell) {
Ok(c) => {
let t = c.floor() as i32;
if t < 0 {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "Out of range parameters for date".to_string(),
};
}
t
}
Err(s) => return s,
};
let month = match self.get_number(&args[1], cell) {
Ok(c) => {
let t = c.floor();
t as i32
}
Err(s) => return s,
};
let day = match self.get_number(&args[2], cell) {
Ok(c) => {
let t = c.floor();
t as i32
}
Err(s) => return s,
};
match permissive_date_to_serial_number(day, month, year) {
Ok(serial_number) => CalcResult::Number(serial_number as f64),
Err(message) => CalcResult::Error {
error: Error::NUM,
origin: cell,
message,
},
}
}
pub(crate) fn fn_year(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
let args_count = args.len();
if args_count != 1 {
return CalcResult::new_args_number_error(cell);
}
let serial_number = match self.get_number(&args[0], cell) {
Ok(c) => c.floor() as i64,
Err(s) => return s,
};
let date = match from_excel_date(serial_number) {
Ok(date) => date,
Err(_) => {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "Out of range parameters for date".to_string(),
}
}
};
let year = date.year() as f64;
CalcResult::Number(year)
}
// date, months
pub(crate) fn fn_edate(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
let args_count = args.len();
if args_count != 2 {
return CalcResult::new_args_number_error(cell);
}
let serial_number = match self.get_number(&args[0], cell) {
Ok(c) => c.floor() as i64,
Err(s) => return s,
};
let date = match from_excel_date(serial_number) {
Ok(date) => date,
Err(_) => {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "Out of range parameters for date".to_string(),
}
}
};
let months = match self.get_number(&args[1], cell) {
Ok(c) => {
let t = c.trunc();
t as i32
}
Err(s) => return s,
};
let months_abs = months.unsigned_abs();
let native_date = if months > 0 {
date + Months::new(months_abs)
} else {
date - Months::new(months_abs)
};
let serial_number = native_date.num_days_from_ce() - EXCEL_DATE_BASE;
if serial_number < MINIMUM_DATE_SERIAL_NUMBER {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "EDATE out of bounds".to_string(),
};
}
CalcResult::Number(serial_number as f64)
}
pub(crate) fn fn_today(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
let args_count = args.len();
if args_count != 0 {
return CalcResult::Error {
error: Error::ERROR,
origin: cell,
message: "Wrong number of arguments".to_string(),
};
}
// milliseconds since January 1, 1970 00:00:00 UTC.
let milliseconds = get_milliseconds_since_epoch();
let seconds = milliseconds / 1000;
let local_time = match DateTime::from_timestamp(seconds, 0) {
Some(dt) => dt.with_timezone(&self.tz),
None => {
return CalcResult::Error {
error: Error::ERROR,
origin: cell,
message: "Invalid date".to_string(),
}
}
};
// 693_594 is computed as:
// NaiveDate::from_ymd(1900, 1, 1).num_days_from_ce() - 2
// The 2 days offset is because of Excel 1900 bug
let days_from_1900 = local_time.num_days_from_ce() - 693_594;
CalcResult::Number(days_from_1900 as f64)
}
pub(crate) fn fn_now(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
let args_count = args.len();
if args_count != 0 {
return CalcResult::Error {
error: Error::ERROR,
origin: cell,
message: "Wrong number of arguments".to_string(),
};
}
// milliseconds since January 1, 1970 00:00:00 UTC.
let milliseconds = get_milliseconds_since_epoch();
let seconds = milliseconds / 1000;
let local_time = match DateTime::from_timestamp(seconds, 0) {
Some(dt) => dt.with_timezone(&self.tz),
None => {
return CalcResult::Error {
error: Error::ERROR,
origin: cell,
message: "Invalid date".to_string(),
}
}
};
// 693_594 is computed as:
// NaiveDate::from_ymd(1900, 1, 1).num_days_from_ce() - 2
// The 2 days offset is because of Excel 1900 bug
let days_from_1900 = local_time.num_days_from_ce() - 693_594;
let days = (local_time.num_seconds_from_midnight() as f64) / (60.0 * 60.0 * 24.0);
CalcResult::Number(days_from_1900 as f64 + days.fract())
}
}