use crate::error::VBResult;
use crate::value::VBVariant;
fn parse_time_string(s: &str) -> Option<f64> {
let s = s.trim();
let parts: Vec<&str> = s.split(':').collect();
if parts.len() < 2 || parts.len() > 3 {
return None;
}
let hours: i32 = parts[0].parse().ok()?;
let minutes: i32 = parts[1].parse().ok()?;
let seconds: i32 = if parts.len() == 3 {
parts[2].parse().ok()?
} else {
0
};
if !(0..=23).contains(&hours) || !(0..=59).contains(&minutes) || !(0..=59).contains(&seconds) {
return None;
}
let total = hours * 3600 + minutes * 60 + seconds;
Some(total as f64 / 86_400.0)
}
pub fn time_statement(value: &VBVariant) -> VBResult<()> {
let serial = match value {
VBVariant::String(s) => {
if let Some(v) = crate::value::parse_vb_date(s) {
v
} else if let Some(v) = parse_time_string(s) {
v
} else {
return Err(crate::error::VBError::type_mismatch());
}
}
_ => value.as_date_serial()?,
};
let fraction = serial.fract();
let total_seconds = (fraction * 86_400.0).round() as i64;
let hours = (total_seconds / 3600) as i8;
let minutes = ((total_seconds % 3600) / 60) as i8;
let seconds = (total_seconds % 60) as i8;
let time = jiff::civil::Time::new(hours, minutes, seconds, 0)
.map_err(|_| crate::error::VBError::type_mismatch())?;
crate::state::clock::set_time(time);
Ok(())
}
#[cfg(test)]
mod tests {
use super::time_statement;
use crate::state::test_support::TEST_LOCK;
use crate::VBVariant;
#[test]
fn sets_time_from_string() {
let _guard = TEST_LOCK.lock().unwrap();
crate::state::clock::reset();
let input = VBVariant::from_string("14:30:00");
time_statement(&input).unwrap();
let result = crate::library::datetime::time::time().unwrap();
let VBVariant::Date(serial) = result else {
panic!("expected a Date variant");
};
let seconds = serial.fract() * 86_400.0;
let expected = 14.0 * 3600.0 + 30.0 * 60.0;
assert!(
(seconds - expected).abs() < 1.0,
"expected ~{expected}s, got {seconds}s"
);
crate::state::clock::reset();
}
#[test]
fn sets_time_from_variant() {
let _guard = TEST_LOCK.lock().unwrap();
crate::state::clock::reset();
let input = VBVariant::Date(0.5);
time_statement(&input).unwrap();
let result = crate::library::datetime::time::time().unwrap();
let VBVariant::Date(serial) = result else {
panic!("expected a Date variant");
};
let seconds = serial.fract() * 86_400.0;
assert!(
(seconds - 43200.0).abs() < 1.0,
"expected ~43200s (noon), got {seconds}s"
);
crate::state::clock::reset();
}
#[test]
fn rejects_null() {
let _guard = TEST_LOCK.lock().unwrap();
let input = VBVariant::Null;
let result = time_statement(&input);
assert!(result.is_err());
}
}