src/tools/systemd/tm_editor.rs: new helper class
This commit is contained in:
parent
99baf7afcc
commit
d1a5ffdf78
|
@ -2,4 +2,5 @@ pub mod types;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
|
||||||
mod parse_time;
|
mod parse_time;
|
||||||
|
mod tm_editor;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use anyhow::{bail, Error};
|
use anyhow::{bail, Error};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
use proxmox::tools::time::*;
|
|
||||||
|
|
||||||
use super::parse_time::*;
|
use super::parse_time::*;
|
||||||
|
use super::tm_editor::*;
|
||||||
|
|
||||||
bitflags!{
|
bitflags!{
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -46,6 +45,10 @@ impl DateTimeValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn list_contains(list: &[DateTimeValue], value: u32) -> bool {
|
||||||
|
list.iter().find(|spec| spec.contains(value)).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
// Find an return an entry greater than value
|
// Find an return an entry greater than value
|
||||||
pub fn find_next(list: &[DateTimeValue], value: u32) -> Option<u32> {
|
pub fn find_next(list: &[DateTimeValue], value: u32) -> Option<u32> {
|
||||||
let mut next: Option<u32> = None;
|
let mut next: Option<u32> = None;
|
||||||
|
@ -93,7 +96,6 @@ pub struct CalendarEvent {
|
||||||
pub hour: Vec<DateTimeValue>,
|
pub hour: Vec<DateTimeValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct TimeSpan {
|
pub struct TimeSpan {
|
||||||
pub nsec: u64,
|
pub nsec: u64,
|
||||||
|
@ -108,13 +110,6 @@ pub struct TimeSpan {
|
||||||
pub years: u64,
|
pub years: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimeSpan {
|
|
||||||
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TimeSpan> for f64 {
|
impl From<TimeSpan> for f64 {
|
||||||
fn from(ts: TimeSpan) -> Self {
|
fn from(ts: TimeSpan) -> Self {
|
||||||
(ts.seconds as f64) +
|
(ts.seconds as f64) +
|
||||||
|
@ -141,75 +136,6 @@ pub fn verify_calendar_event(i: &str) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn is_leap_year(year: libc::c_int) -> bool {
|
|
||||||
if year % 4 != 0 { return false; }
|
|
||||||
if year % 100 != 0 { return true; }
|
|
||||||
if year % 400 != 0 { return false; }
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn days_in_month(mon: libc::c_int, year: libc::c_int) -> libc::c_int {
|
|
||||||
|
|
||||||
let mon = mon % 12;
|
|
||||||
|
|
||||||
static MAP: &[libc::c_int] = &[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
||||||
|
|
||||||
if mon == 1 && is_leap_year(year) { return 29; }
|
|
||||||
|
|
||||||
MAP[mon as usize]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wrap_time(t: &mut libc::tm) {
|
|
||||||
|
|
||||||
// sec: 0..59
|
|
||||||
if t.tm_sec >= 60 {
|
|
||||||
t.tm_min += t.tm_sec / 60;
|
|
||||||
t.tm_sec %= 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
// min: 0..59
|
|
||||||
if t.tm_min >= 60 {
|
|
||||||
t.tm_hour += t.tm_min / 60;
|
|
||||||
t.tm_min %= 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
// hour: 0..23
|
|
||||||
if t.tm_hour >= 24 {
|
|
||||||
t.tm_mday += t.tm_hour / 24;
|
|
||||||
t.tm_wday += t.tm_hour / 24;
|
|
||||||
t.tm_hour %= 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translate to 0..($days_in_mon-1)
|
|
||||||
t.tm_mday -= 1;
|
|
||||||
loop {
|
|
||||||
let days_in_mon = days_in_month(t.tm_mon, t.tm_year);
|
|
||||||
if t.tm_mday < days_in_mon { break; }
|
|
||||||
// Wrap one month
|
|
||||||
t.tm_mday -= days_in_mon;
|
|
||||||
t.tm_wday += 7 - (days_in_mon % 7);
|
|
||||||
t.tm_mon += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translate back to 1..$days_in_mon
|
|
||||||
t.tm_mday += 1;
|
|
||||||
|
|
||||||
// mon: 0..11
|
|
||||||
if t.tm_mon >= 12 {
|
|
||||||
t.tm_year += t.tm_mon / 12;
|
|
||||||
t.tm_mon %= 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
t.tm_wday %= 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn time_add_days(t: &mut libc::tm, days: libc::c_int) {
|
|
||||||
t.tm_mday += days;
|
|
||||||
t.tm_wday += days;
|
|
||||||
wrap_time(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compute_next_event(
|
pub fn compute_next_event(
|
||||||
event: &CalendarEvent,
|
event: &CalendarEvent,
|
||||||
last: i64,
|
last: i64,
|
||||||
|
@ -220,9 +146,7 @@ pub fn compute_next_event(
|
||||||
|
|
||||||
let all_days = event.days.is_empty() || event.days.is_all();
|
let all_days = event.days.is_empty() || event.days.is_all();
|
||||||
|
|
||||||
let mut t = if utc { gmtime(last)? } else { localtime(last)? };
|
let mut t = TmEditor::new(last, utc)?;
|
||||||
t.tm_sec = 0; // we're not interested in seconds, actually
|
|
||||||
t.tm_year += 1900; // real years for clarity
|
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|
||||||
|
@ -234,8 +158,7 @@ pub fn compute_next_event(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !all_days { // match day first
|
if !all_days { // match day first
|
||||||
// Note: tm_wday (0-6, Sunday = 0) => convert to Sunday = 6
|
let day_num = t.day_num();
|
||||||
let day_num = (t.tm_wday + 6) % 7;
|
|
||||||
let day = WeekDays::from_bits(1<<day_num).unwrap();
|
let day = WeekDays::from_bits(1<<day_num).unwrap();
|
||||||
if !event.days.contains(day) {
|
if !event.days.contains(day) {
|
||||||
if let Some(n) = (day_num+1..6)
|
if let Some(n) = (day_num+1..6)
|
||||||
|
@ -243,32 +166,27 @@ pub fn compute_next_event(
|
||||||
.find(|d| event.days.contains(*d))
|
.find(|d| event.days.contains(*d))
|
||||||
{
|
{
|
||||||
// try next day
|
// try next day
|
||||||
t.tm_sec = 0; t.tm_min = 0; t.tm_hour = 0;
|
t.add_days((n.bits() as i32) - day_num, true);
|
||||||
time_add_days(&mut t, (n.bits() as i32) - day_num);
|
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// try next week
|
// try next week
|
||||||
t.tm_sec = 0; t.tm_min = 0; t.tm_hour = 0;
|
t.add_days(7 - day_num, true);
|
||||||
time_add_days(&mut t, 7 - day_num);
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this day
|
// this day
|
||||||
if !event.hour.is_empty() {
|
if !event.hour.is_empty() {
|
||||||
let hour = t.tm_hour as u32;
|
let hour = t.hour() as u32;
|
||||||
if event.hour.iter().find(|hspec| hspec.contains(hour)).is_none() {
|
if !DateTimeValue::list_contains(&event.hour, hour) {
|
||||||
if let Some(n) = DateTimeValue::find_next(&event.hour, hour) {
|
if let Some(n) = DateTimeValue::find_next(&event.hour, hour) {
|
||||||
// test next hour
|
// test next hour
|
||||||
t.tm_sec = 0; t.tm_min = 0; t.tm_hour = n as libc::c_int;
|
t.set_time(n as libc::c_int, 0, 0);
|
||||||
wrap_time(&mut t);
|
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// test next day
|
// test next day
|
||||||
t.tm_sec = 0; t.tm_min = 0; t.tm_hour = 0;
|
t.add_days(1, true);
|
||||||
time_add_days(&mut t, 1);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,24 +194,21 @@ pub fn compute_next_event(
|
||||||
|
|
||||||
// this hour
|
// this hour
|
||||||
if !event.minute.is_empty() {
|
if !event.minute.is_empty() {
|
||||||
let minute = t.tm_min as u32;
|
let minute = t.min() as u32;
|
||||||
if event.minute.iter().find(|hspec| hspec.contains(minute)).is_none() {
|
if !DateTimeValue::list_contains(&event.minute, minute) {
|
||||||
if let Some(n) = DateTimeValue::find_next(&event.minute, minute) {
|
if let Some(n) = DateTimeValue::find_next(&event.minute, minute) {
|
||||||
// test next minute
|
// test next minute
|
||||||
t.tm_sec = 0; t.tm_min = n as libc::c_int;
|
t.set_min_sec(n as libc::c_int, 0);
|
||||||
wrap_time(&mut t);
|
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// test next hour
|
// test next hour
|
||||||
t.tm_sec = 0; t.tm_min = 0; t.tm_hour += 1;
|
t.set_time(t.hour() + 1, 0, 0);
|
||||||
wrap_time(&mut t);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t.tm_year -= 1900;
|
let next = t.into_epoch()?;
|
||||||
let next = if utc { timegm(t)? } else { timelocal(t)? };
|
|
||||||
return Ok(next)
|
return Ok(next)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,6 +217,7 @@ pub fn compute_next_event(
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use proxmox::tools::time::*;
|
||||||
|
|
||||||
fn test_event(v: &'static str) -> Result<(), Error> {
|
fn test_event(v: &'static str) -> Result<(), Error> {
|
||||||
match parse_calendar_event(v) {
|
match parse_calendar_event(v) {
|
||||||
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
use anyhow::Error;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
use proxmox::tools::time::*;
|
||||||
|
|
||||||
|
bitflags!{
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct TMChanges: u8 {
|
||||||
|
const SEC = 1;
|
||||||
|
const MIN = 2;
|
||||||
|
const HOUR = 4;
|
||||||
|
const MDAY = 8;
|
||||||
|
const MON = 16;
|
||||||
|
const YEAR = 32;
|
||||||
|
const WDAY = 64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TmEditor {
|
||||||
|
utc: bool,
|
||||||
|
t: libc::tm,
|
||||||
|
changes: TMChanges,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_leap_year(year: libc::c_int) -> bool {
|
||||||
|
if year % 4 != 0 { return false; }
|
||||||
|
if year % 100 != 0 { return true; }
|
||||||
|
if year % 400 != 0 { return false; }
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn days_in_month(mon: libc::c_int, year: libc::c_int) -> libc::c_int {
|
||||||
|
|
||||||
|
let mon = mon % 12;
|
||||||
|
|
||||||
|
static MAP: &[libc::c_int] = &[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
||||||
|
|
||||||
|
if mon == 1 && is_leap_year(year) { return 29; }
|
||||||
|
|
||||||
|
MAP[mon as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TmEditor {
|
||||||
|
|
||||||
|
pub fn new(epoch: i64, utc: bool) -> Result<Self, Error> {
|
||||||
|
let mut t = if utc { gmtime(epoch)? } else { localtime(epoch)? };
|
||||||
|
t.tm_sec = 0; // we're not interested in seconds, actually
|
||||||
|
t.tm_year += 1900; // real years for clarity
|
||||||
|
Ok(Self { utc, t, changes: TMChanges::all() })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_epoch(mut self) -> Result<i64, Error> {
|
||||||
|
self.t.tm_year -= 1900;
|
||||||
|
let epoch = if self.utc { timegm(self.t)? } else { timelocal(self.t)? };
|
||||||
|
Ok(epoch)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_days(&mut self, days: libc::c_int, reset_time: bool) {
|
||||||
|
if days == 0 { return; }
|
||||||
|
if reset_time {
|
||||||
|
self.t.tm_hour = 0;
|
||||||
|
self.t.tm_min = 0;
|
||||||
|
self.t.tm_sec = 0;
|
||||||
|
self.changes.insert(TMChanges::HOUR|TMChanges::MIN|TMChanges::SEC);
|
||||||
|
}
|
||||||
|
self.t.tm_mday += days;
|
||||||
|
self.t.tm_wday += days;
|
||||||
|
self.changes.insert(TMChanges::MDAY|TMChanges::WDAY);
|
||||||
|
self.wrap_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hour(&self) -> libc::c_int { self.t.tm_hour }
|
||||||
|
pub fn min(&self) -> libc::c_int { self.t.tm_min }
|
||||||
|
pub fn sec(&self) -> libc::c_int { self.t.tm_sec }
|
||||||
|
|
||||||
|
// Note: tm_wday (0-6, Sunday = 0) => convert to Sunday = 6
|
||||||
|
pub fn day_num(&self) -> libc::c_int {
|
||||||
|
(self.t.tm_wday + 6) % 7
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_time(&mut self, hour: libc::c_int, min: libc::c_int, sec: libc::c_int) {
|
||||||
|
self.t.tm_hour = hour;
|
||||||
|
self.t.tm_min = min;
|
||||||
|
self.t.tm_sec = sec;
|
||||||
|
self.changes.insert(TMChanges::HOUR|TMChanges::MIN|TMChanges::SEC);
|
||||||
|
self.wrap_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_min_sec(&mut self, min: libc::c_int, sec: libc::c_int) {
|
||||||
|
self.t.tm_min = min;
|
||||||
|
self.t.tm_sec = sec;
|
||||||
|
self.changes.insert(TMChanges::MIN|TMChanges::SEC);
|
||||||
|
self.wrap_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_time(&mut self) {
|
||||||
|
|
||||||
|
// sec: 0..59
|
||||||
|
if self.t.tm_sec >= 60 {
|
||||||
|
self.t.tm_min += self.t.tm_sec / 60;
|
||||||
|
self.t.tm_sec %= 60;
|
||||||
|
self.changes.insert(TMChanges::SEC|TMChanges::MIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// min: 0..59
|
||||||
|
if self.t.tm_min >= 60 {
|
||||||
|
self.t.tm_hour += self.t.tm_min / 60;
|
||||||
|
self.t.tm_min %= 60;
|
||||||
|
self.changes.insert(TMChanges::MIN|TMChanges::HOUR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// hour: 0..23
|
||||||
|
if self.t.tm_hour >= 24 {
|
||||||
|
self.t.tm_mday += self.t.tm_hour / 24;
|
||||||
|
self.t.tm_wday += self.t.tm_hour / 24;
|
||||||
|
self.t.tm_hour %= 24;
|
||||||
|
self.changes.insert(TMChanges::HOUR|TMChanges::MDAY|TMChanges::WDAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate to 0..($days_in_mon-1)
|
||||||
|
self.t.tm_mday -= 1;
|
||||||
|
loop {
|
||||||
|
let days_in_mon = days_in_month(self.t.tm_mon, self.t.tm_year);
|
||||||
|
if self.t.tm_mday < days_in_mon { break; }
|
||||||
|
// Wrap one month
|
||||||
|
self.t.tm_mday -= days_in_mon;
|
||||||
|
self.t.tm_wday += 7 - (days_in_mon % 7);
|
||||||
|
self.t.tm_mon += 1;
|
||||||
|
self.changes.insert(TMChanges::MDAY|TMChanges::WDAY|TMChanges::MON);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate back to 1..$days_in_mon
|
||||||
|
self.t.tm_mday += 1;
|
||||||
|
|
||||||
|
// mon: 0..11
|
||||||
|
if self.t.tm_mon >= 12 {
|
||||||
|
self.t.tm_year += self.t.tm_mon / 12;
|
||||||
|
self.t.tm_mon %= 12;
|
||||||
|
self.changes.insert(TMChanges::MON|TMChanges::YEAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.t.tm_wday %= 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_sec(&mut self, v: libc::c_int) {
|
||||||
|
self.t.tm_sec = v;
|
||||||
|
self.changes.insert(TMChanges::SEC);
|
||||||
|
self.wrap_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_min(&mut self, v: libc::c_int) {
|
||||||
|
self.t.tm_min = v;
|
||||||
|
self.changes.insert(TMChanges::MIN);
|
||||||
|
self.wrap_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_hour(&mut self, v: libc::c_int) {
|
||||||
|
self.t.tm_hour = v;
|
||||||
|
self.changes.insert(TMChanges::HOUR);
|
||||||
|
self.wrap_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_mday(&mut self, v: libc::c_int) {
|
||||||
|
self.t.tm_mday = v;
|
||||||
|
self.changes.insert(TMChanges::MDAY);
|
||||||
|
self.wrap_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_mon(&mut self, v: libc::c_int) {
|
||||||
|
self.t.tm_mon = v;
|
||||||
|
self.changes.insert(TMChanges::MON);
|
||||||
|
self.wrap_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_year(&mut self, v: libc::c_int) {
|
||||||
|
self.t.tm_year = v;
|
||||||
|
self.changes.insert(TMChanges::YEAR);
|
||||||
|
self.wrap_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_wday(&mut self, v: libc::c_int) {
|
||||||
|
self.t.tm_wday = v;
|
||||||
|
self.changes.insert(TMChanges::WDAY);
|
||||||
|
self.wrap_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue