mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 22:52:22 +00:00
Fixes to event timestamps
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
use crate::{
|
||||
timestamps::{parse_datetime, parse_duration},
|
||||
timestamps::{parse_duration, CalDateTime},
|
||||
Error,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use chrono::{Duration, NaiveDateTime, Timelike};
|
||||
use chrono::Duration;
|
||||
use ical::parser::{ical::component::IcalCalendar, Component};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::io::BufReader;
|
||||
use std::{io::BufReader, str::FromStr};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Event {
|
||||
@@ -87,7 +87,7 @@ impl Event {
|
||||
&self.ics
|
||||
}
|
||||
|
||||
pub fn get_first_occurence(&self) -> Result<NaiveDateTime> {
|
||||
pub fn get_first_occurence(&self) -> Result<CalDateTime> {
|
||||
// This is safe since we enforce the event's existance in the constructor
|
||||
let event = self.cal.events.first().unwrap();
|
||||
let dtstart = event
|
||||
@@ -96,10 +96,10 @@ impl Event {
|
||||
.value
|
||||
.to_owned()
|
||||
.ok_or(anyhow!("DTSTART property has no value!"))?;
|
||||
parse_datetime(&dtstart)
|
||||
Ok(CalDateTime::from_str(&dtstart)?)
|
||||
}
|
||||
|
||||
pub fn get_last_occurence(&self) -> Result<NaiveDateTime> {
|
||||
pub fn get_last_occurence(&self) -> Result<CalDateTime> {
|
||||
// This is safe since we enforce the event's existence in the constructor
|
||||
let event = self.cal.events.first().unwrap();
|
||||
|
||||
@@ -113,7 +113,7 @@ impl Event {
|
||||
.value
|
||||
.to_owned()
|
||||
.ok_or(anyhow!("DTEND property has no value!"))?;
|
||||
return parse_datetime(&dtend);
|
||||
return Ok(CalDateTime::from_str(&dtend)?);
|
||||
}
|
||||
|
||||
if let Some(dtend_prop) = event.get_property("DURATION") {
|
||||
@@ -126,10 +126,9 @@ impl Event {
|
||||
}
|
||||
|
||||
let dtstart = self.get_first_occurence()?;
|
||||
if dtstart.num_seconds_from_midnight() == 0 {
|
||||
// no explicit time given => whole-day event
|
||||
if let CalDateTime::Date(_) = dtstart {
|
||||
return Ok(dtstart + Duration::days(1));
|
||||
};
|
||||
}
|
||||
|
||||
Err(anyhow!("help, couldn't determine any last occurence"))
|
||||
}
|
||||
|
||||
@@ -1,12 +1,66 @@
|
||||
use std::{ops::Add, str::FromStr};
|
||||
|
||||
use crate::Error;
|
||||
use anyhow::Result;
|
||||
use chrono::{Duration, NaiveDateTime};
|
||||
use anyhow::{anyhow, Result};
|
||||
use chrono::{DateTime, Duration, NaiveDate, NaiveDateTime, NaiveTime, Utc};
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
lazy_static! {
|
||||
static ref RE_DURATION: regex::Regex = regex::Regex::new(r"^(?<sign>[+-])?P((?P<W>\d+)W)?((?P<D>\d+)D)?(T((?P<H>\d+)H)?((?P<M>\d+)M)?((?P<S>\d+)S)?)?$").unwrap();
|
||||
}
|
||||
|
||||
const LOCAL_DATE_TIME: &str = "%Y%m%dT%H%M%S";
|
||||
const UTC_DATE_TIME: &str = "%Y%m%dT%H%M%SZ";
|
||||
const LOCAL_DATE: &str = "%Y%m%d";
|
||||
|
||||
pub enum CalDateTime {
|
||||
// Form 1, example: 19980118T230000
|
||||
Local(NaiveDateTime),
|
||||
// Form 2, example: 19980119T070000Z
|
||||
Utc(DateTime<Utc>),
|
||||
// Form 3, example: TZID=America/New_York:19980119T020000
|
||||
// TODO: implement timezone parsing
|
||||
ExplicitTZ((String, NaiveDateTime)),
|
||||
Date(NaiveDate),
|
||||
}
|
||||
|
||||
impl Add<Duration> for CalDateTime {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, duration: Duration) -> Self::Output {
|
||||
match self {
|
||||
Self::Local(datetime) => Self::Local(datetime + duration),
|
||||
Self::Utc(datetime) => Self::Utc(datetime + duration),
|
||||
Self::ExplicitTZ((tz, datetime)) => Self::ExplicitTZ((tz, datetime + duration)),
|
||||
Self::Date(date) => Self::Local(date.and_time(NaiveTime::default()) + duration),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for CalDateTime {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
if let Ok(datetime) = NaiveDateTime::parse_from_str(value, LOCAL_DATE_TIME) {
|
||||
return Ok(CalDateTime::Local(datetime));
|
||||
}
|
||||
if let Ok(datetime) = NaiveDateTime::parse_from_str(value, UTC_DATE_TIME) {
|
||||
return Ok(CalDateTime::Utc(datetime.and_utc()));
|
||||
}
|
||||
if let Ok(date) = NaiveDate::parse_from_str(value, LOCAL_DATE) {
|
||||
return Ok(CalDateTime::Date(date));
|
||||
}
|
||||
Err(Error::Other(anyhow!("Invalid datetime format")))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_cal_datetime() {
|
||||
CalDateTime::from_str("19980118T230000").unwrap();
|
||||
CalDateTime::from_str("19980118T230000Z").unwrap();
|
||||
CalDateTime::from_str("19980118").unwrap();
|
||||
}
|
||||
|
||||
pub fn parse_duration(string: &str) -> Result<Duration, Error> {
|
||||
let captures = RE_DURATION
|
||||
.captures(string)
|
||||
@@ -45,25 +99,3 @@ fn test_parse_duration() {
|
||||
assert_eq!(parse_duration("PT12M").unwrap(), Duration::minutes(12));
|
||||
assert_eq!(parse_duration("PT12S").unwrap(), Duration::seconds(12));
|
||||
}
|
||||
|
||||
pub fn parse_datetime(string: &str) -> Result<NaiveDateTime> {
|
||||
// TODO: respect timezones
|
||||
//
|
||||
// Format: ^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(?P<utc>Z)?$
|
||||
// if Z?
|
||||
// UTC time
|
||||
// else
|
||||
// if TZID given?
|
||||
// time in TZ
|
||||
// else
|
||||
// local time of attendee (can be different actual times for different attendees)
|
||||
// BUT for this implementation will be UTC for now since this case is annoying
|
||||
// (sabre-dav does same)
|
||||
let (datetime, _tz_remainder) = NaiveDateTime::parse_and_remainder(string, "%Y%m%dT%H%M%S")?;
|
||||
Ok(datetime)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_datetime() {
|
||||
dbg!(parse_datetime("19960329T133000Z").unwrap());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user