Compare commits

...

4 Commits

Author SHA1 Message Date
Paul Bender 57ecefccc3 Generate error when century bit is set in month method. 2025-10-10 18:33:41 -07:00
Paul Bender 459dfa9200 Improve date/datetime too_big tests. 2025-10-10 18:12:55 -07:00
Paul Bender 66fee80b62 Generate error when century bit is set in functions already reading the century bit 2025-10-10 13:17:53 -07:00
Paul Bender 6874151dec Limit setting year to 20th century. 2025-10-10 12:57:58 -07:00
3 changed files with 54 additions and 91 deletions

View File

@ -19,11 +19,13 @@ where
let mut data = [0; 8]; let mut data = [0; 8];
self.iface.read_data(&mut data)?; self.iface.read_data(&mut data)?;
let year = year_from_registers( let century = data[Register::MONTH as usize + 1] & BitFlags::CENTURY;
data[Register::MONTH as usize + 1], if century != 0 {
data[Register::YEAR as usize + 1], return Err(Error::InvalidDeviceCentury);
); }
let month = packed_bcd_to_decimal(data[Register::MONTH as usize + 1] & !BitFlags::CENTURY);
let year = 2000 + (packed_bcd_to_decimal(data[Register::YEAR as usize + 1]) as u16);
let month = packed_bcd_to_decimal(data[Register::MONTH as usize + 1]);
let day = packed_bcd_to_decimal(data[Register::DOM as usize + 1]); let day = packed_bcd_to_decimal(data[Register::DOM as usize + 1]);
let hour = hours_from_register(data[Register::HOURS as usize + 1]); let hour = hours_from_register(data[Register::HOURS as usize + 1]);
let minute = packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]); let minute = packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]);
@ -36,10 +38,9 @@ where
} }
fn set_datetime(&mut self, datetime: &NaiveDateTime) -> Result<(), Self::Error> { fn set_datetime(&mut self, datetime: &NaiveDateTime) -> Result<(), Self::Error> {
if datetime.year() < 2000 || datetime.year() > 2100 { if !(2000..=2099).contains(&datetime.year()) {
return Err(Error::InvalidInputData); return Err(Error::InvalidInputData);
} }
let (month, year) = month_year_to_registers(datetime.month() as u8, datetime.year() as u16);
let mut payload = [ let mut payload = [
Register::SECONDS, Register::SECONDS,
decimal_to_packed_bcd(datetime.second() as u8), decimal_to_packed_bcd(datetime.second() as u8),
@ -47,8 +48,8 @@ where
hours_to_register(Hours::H24(datetime.hour() as u8))?, hours_to_register(Hours::H24(datetime.hour() as u8))?,
datetime.weekday().number_from_sunday() as u8, datetime.weekday().number_from_sunday() as u8,
decimal_to_packed_bcd(datetime.day() as u8), decimal_to_packed_bcd(datetime.day() as u8),
month, decimal_to_packed_bcd(datetime.month() as u8),
year, decimal_to_packed_bcd((datetime.year() - 2000) as u8),
]; ];
self.iface.write_data(&mut payload) self.iface.write_data(&mut payload)
} }
@ -92,15 +93,28 @@ where
fn month(&mut self) -> Result<u8, Self::Error> { fn month(&mut self) -> Result<u8, Self::Error> {
let data = self.iface.read_register(Register::MONTH)?; let data = self.iface.read_register(Register::MONTH)?;
let value = data & !BitFlags::CENTURY;
Ok(packed_bcd_to_decimal(value)) let century = data & BitFlags::CENTURY;
if century != 0 {
return Err(Error::InvalidDeviceCentury);
}
Ok(packed_bcd_to_decimal(data))
} }
fn year(&mut self) -> Result<u16, Self::Error> { fn year(&mut self) -> Result<u16, Self::Error> {
let mut data = [0; 3]; let mut data = [0; 3];
data[0] = Register::MONTH; data[0] = Register::MONTH;
self.iface.read_data(&mut data)?; self.iface.read_data(&mut data)?;
Ok(year_from_registers(data[1], data[2]))
let century = data[1] & BitFlags::CENTURY;
if century != 0 {
return Err(Error::InvalidDeviceCentury);
}
let year = 2000 + (packed_bcd_to_decimal(data[2]) as u16);
Ok(year)
} }
fn date(&mut self) -> Result<NaiveDate, Self::Error> { fn date(&mut self) -> Result<NaiveDate, Self::Error> {
@ -109,12 +123,15 @@ where
self.iface.read_data(&mut data)?; self.iface.read_data(&mut data)?;
let offset = Register::DOM as usize; let offset = Register::DOM as usize;
let year = year_from_registers(
data[Register::MONTH as usize + 1 - offset], let century = data[Register::MONTH as usize + 1 - offset] & BitFlags::CENTURY;
data[Register::YEAR as usize + 1 - offset], if century != 0 {
); return Err(Error::InvalidDeviceCentury);
let month = }
packed_bcd_to_decimal(data[Register::MONTH as usize + 1 - offset] & !BitFlags::CENTURY);
let year =
2000 + (packed_bcd_to_decimal(data[Register::YEAR as usize + 1 - offset]) as u16);
let month = packed_bcd_to_decimal(data[Register::MONTH as usize + 1 - offset]);
let day = packed_bcd_to_decimal(data[Register::DOM as usize + 1 - offset]); let day = packed_bcd_to_decimal(data[Register::DOM as usize + 1 - offset]);
let date = NaiveDate::from_ymd_opt(year.into(), month.into(), day.into()); let date = NaiveDate::from_ymd_opt(year.into(), month.into(), day.into());
some_or_invalid_error(date) some_or_invalid_error(date)
@ -174,39 +191,30 @@ where
} }
fn set_year(&mut self, year: u16) -> Result<(), Self::Error> { fn set_year(&mut self, year: u16) -> Result<(), Self::Error> {
if !(2000..=2100).contains(&year) { if !(2000..=2099).contains(&year) {
return Err(Error::InvalidInputData); return Err(Error::InvalidInputData);
} }
let data = self.iface.read_register(Register::MONTH)?; let data = self.iface.read_register(Register::MONTH)?;
let month_bcd = data & !BitFlags::CENTURY; let month_bcd = data & !BitFlags::CENTURY;
if year > 2099 {
let mut data = [ let mut data = [
Register::MONTH, Register::MONTH,
BitFlags::CENTURY | month_bcd, month_bcd,
decimal_to_packed_bcd((year - 2100) as u8), decimal_to_packed_bcd((year - 2000) as u8),
]; ];
self.iface.write_data(&mut data) self.iface.write_data(&mut data)
} else {
let mut data = [
Register::MONTH,
month_bcd,
decimal_to_packed_bcd((year - 2000) as u8),
];
self.iface.write_data(&mut data)
}
} }
fn set_date(&mut self, date: &rtcc::NaiveDate) -> Result<(), Self::Error> { fn set_date(&mut self, date: &rtcc::NaiveDate) -> Result<(), Self::Error> {
if date.year() < 2000 || date.year() > 2100 { if !(2000..=2099).contains(&date.year()) {
return Err(Error::InvalidInputData); return Err(Error::InvalidInputData);
} }
let (month, year) = month_year_to_registers(date.month() as u8, date.year() as u16);
let mut payload = [ let mut payload = [
Register::DOW, Register::DOW,
date.weekday().number_from_sunday() as u8, date.weekday().number_from_sunday() as u8,
decimal_to_packed_bcd(date.day() as u8), decimal_to_packed_bcd(date.day() as u8),
month, decimal_to_packed_bcd(date.month() as u8),
year, decimal_to_packed_bcd((date.year() - 2000) as u8),
]; ];
self.iface.write_data(&mut payload) self.iface.write_data(&mut payload)
} }
@ -241,28 +249,6 @@ fn hours_from_register(data: u8) -> Hours {
} }
} }
fn year_from_registers(month: u8, year: u8) -> u16 {
let century = month & BitFlags::CENTURY;
let year = packed_bcd_to_decimal(year);
if century != 0 {
2100 + u16::from(year)
} else {
2000 + u16::from(year)
}
}
fn month_year_to_registers(month: u8, year: u16) -> (u8, u8) {
if year > 2099 {
let month = BitFlags::CENTURY | decimal_to_packed_bcd(month);
(month, decimal_to_packed_bcd((year - 2100) as u8))
} else {
(
decimal_to_packed_bcd(month),
decimal_to_packed_bcd((year - 2000) as u8),
)
}
}
fn is_24h_format(hours_data: u8) -> bool { fn is_24h_format(hours_data: u8) -> bool {
hours_data & BitFlags::H24_H12 == 0 hours_data & BitFlags::H24_H12 == 0
} }

View File

@ -387,6 +387,11 @@ pub enum Error<E> {
/// It was not possible to read a valid date and/or time. /// It was not possible to read a valid date and/or time.
/// The device is probably missing initialization. /// The device is probably missing initialization.
InvalidDeviceState, InvalidDeviceState,
/// Device century is not the 20th century.
///
/// The device does not produce valid dates for centuries
/// other than the 20th century.
InvalidDeviceCentury,
} }
/// Square-wave output frequency /// Square-wave output frequency

View File

@ -185,12 +185,6 @@ mod month {
get_param_test!(get, month, MONTH, 1, 1); get_param_test!(get, month, MONTH, 1, 1);
read_set_param_test!(set, set_month, MONTH, 12, 0b0000_0010, 0b0001_0010); read_set_param_test!(set, set_month, MONTH, 12, 0b0000_0010, 0b0001_0010);
set_invalid_param_range_test!(invalid, set_month, 0, 13); set_invalid_param_range_test!(invalid, set_month, 0, 13);
mod keeps_century {
use super::*;
get_param_test!(get, month, MONTH, 12, 0b1001_0010);
read_set_param_test!(set, set_month, MONTH, 12, 0b1000_0010, 0b1001_0010);
}
} }
mod year { mod year {
@ -206,18 +200,7 @@ mod year {
0b1001_1001 0b1001_1001
); );
get_param_read_array_test!(century1_get, year, 2100, MONTH, [0b1000_0000, 0], [0, 0]); set_invalid_param_range_test!(invalid, set_year, 1999, 2100);
read_set_param_write_two_test!(
century1_set,
set_year,
2100,
MONTH,
0b0001_0010,
0b1001_0010,
0
);
set_invalid_param_range_test!(invalid, set_year, 1999, 2101);
} }
macro_rules! invalid_dt_test { macro_rules! invalid_dt_test {
@ -233,7 +216,7 @@ macro_rules! invalid_dt_test {
} }
#[test] #[test]
fn datetime_too_big() { fn datetime_too_big() {
let dt = new_datetime(2101, 1, 2, 3, 4, 5); let dt = new_datetime(2100, 1, 2, 3, 4, 5);
let mut dev = $create_method(&[]); let mut dev = $create_method(&[]);
assert_invalid_input_data!(dev.set_datetime(&dt)); assert_invalid_input_data!(dev.set_datetime(&dt));
$destroy_method(dev); $destroy_method(dev);
@ -247,7 +230,7 @@ macro_rules! invalid_dt_test {
} }
#[test] #[test]
fn date_too_big() { fn date_too_big() {
let d = new_date(2101, 1, 2); let d = new_date(2100, 1, 2);
let mut dev = $create_method(&[]); let mut dev = $create_method(&[]);
assert_invalid_input_data!(dev.set_date(&d)); assert_invalid_input_data!(dev.set_date(&d));
$destroy_method(dev); $destroy_method(dev);
@ -339,17 +322,6 @@ macro_rules! dt_test {
$destroy_method(dev); $destroy_method(dev);
} }
#[test]
fn set_date_century() {
let d = new_date(2100, 8, 13);
let mut dev = $create_method(&$mac_trans_write!(
DOW,
[0b0000_0110, 0b0001_0011, 0b1000_1000, 0]
));
dev.set_date(&d).unwrap();
$destroy_method(dev);
}
#[test] #[test]
fn get_time() { fn get_time() {
let t = NaiveTime::from_hms_opt(23, 59, 58).unwrap(); let t = NaiveTime::from_hms_opt(23, 59, 58).unwrap();