2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 16:56:54 +00:00

57_Calendar: fix weekly events, get full all, defaultTimeFormat

git-svn-id: https://svn.fhem.de/fhem/trunk@16539 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
borisneubert 2018-04-02 20:09:57 +00:00
parent 7fd25a0306
commit 8dcde68ccf
2 changed files with 174 additions and 138 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it. # Do not insert empty lines here, update check depends on it.
- bugfix: 57_Calendar: fix weekly events, get full all, defaultTimeFormat
- change: 93_DbLog: V3.10.0, addLog considers DbLogExclude in Devices, - change: 93_DbLog: V3.10.0, addLog considers DbLogExclude in Devices,
keyword "!useExcludes" to switch off considering keyword "!useExcludes" to switch off considering
DbLogExclude in addLog, DbLogExclude & DbLogInclude DbLogExclude in addLog, DbLogExclude & DbLogInclude

View File

@ -1316,12 +1316,19 @@ sub addEventLimited($$$) {
return -1 if($event->start()< $t+eventsLimitMinus); return -1 if($event->start()< $t+eventsLimitMinus);
return 1 if($event->start()> $t+eventsLimitPlus); return 1 if($event->start()> $t+eventsLimitPlus);
#main::Debug " addEvent: " . $event->asFull();
$self->addEvent($event); $self->addEvent($event);
#main::Debug " addEventLimited: " . $event->asDebug();
return 0; return 0;
} }
# 0= SU ... 6= SA
sub weekdayOf($$) {
my ($self, $t)= @_;
my (undef, undef, undef, undef, undef, undef, $weekday, undef, undef) = localtime($t);
return $weekday;
}
sub createSingleEvent($$$$) { sub createSingleEvent($$$$) {
my ($self, $nextstart, $onCreateEvent)= @_; my ($self, $nextstart, $onCreateEvent)= @_;
@ -1359,8 +1366,108 @@ sub createSingleEvent($$$$) {
return $event; return $event;
} }
sub excludeByExdate($$) {
my ($self, $event)= @_;
my $skip= 0;
if($self->hasKey('EXDATE')) {
foreach my $exdate (@{$self->values("EXDATE")}) {
if($self->tm($exdate) == $event->start()) {
$skip++;
$event->setNote("EXDATE: $exdate");
$self->addSkippedEvent($event);
last;
}
} # end of foreach exdate
} # end of EXDATE checking
return $skip;
}
sub excludeByReference($$$) {
my ($self, $event, $veventsref)= @_;
my $skip= 0;
# check if superseded by out-of-series event
if($self->hasReferences()) {
foreach my $id (@{$self->references()}) {
my $vevent= $veventsref->{$id};
my $recurrenceid= $vevent->value("RECURRENCE-ID");
my $originalstart= $vevent->tm($recurrenceid);
if($originalstart == $event->start()) {
$event->setNote("RECURRENCE-ID: $recurrenceid");
$self->addSkippedEvent($event);
$skip++;
last;
}
}
}
return $skip;
}
sub excludeByRdate($$) {
my ($self, $event)= @_;
my $skip= 0;
# check if excluded by a duplicate RDATE
# this is only to avoid duplicates from previously added RDATEs
if($self->hasKey('RDATE')) {
foreach my $rdate (@{$self->values("RDATE")}) {
if($self->tm($rdate) == $event->start()) {
$event->setNote("RDATE: $rdate");
$self->addSkippedEvent($event);
$skip++;
last;
}
}
}
return $skip;
}
# we return 0 if the storage limit is exceeded or the number of occurances is reached
# we return 1 else no matter if this evevent was added or skipped
sub addOrSkipSeriesEvent($$$$$$) {
my ($self, $event, $t0, $until, $count, $veventsref)= @_;
#main::Debug " addOrSkipSeriesEvent: " . $event->asDebug();
return if($event->{start} > $until); # return if we are after end of series
my $skip= 0;
# check if superseded by out-of-series event
$skip+= $self->excludeByReference($event, $veventsref);
# RFC 5545 p. 120
# The final recurrence set is generated by gathering all of the
# start DATE-TIME values generated by any of the specified "RRULE"
# and "RDATE" properties, and then excluding any start DATE-TIME
# values specified by "EXDATE" properties. This implies that start
# DATE-TIME values specified by "EXDATE" properties take precedence
# over those specified by inclusion properties (i.e., "RDATE" and
# "RRULE"). Where duplicate instances are generated by the "RRULE"
# and "RDATE" properties, only one recurrence is considered.
# Duplicate instances are ignored.
# check if excluded by EXDATE
$skip+= $self->excludeByExdate($event);
# check if excluded by a duplicate RDATE
# this is only to avoid duplicates from previously added RDATEs
$skip+= $self->excludeByRdate($event);
if(!$skip) {
# add event
# and return if we exceed storage limit
my $x= $self->addEventLimited($t0, $event);
#main::Debug "addEventLimited returned $x";
return 0 if($x> 0);
#return 0 if($self->addEventLimited($t0, $event) > 0);
}
my $occurances= scalar(@{$self->{events}});
#main::Debug("$occurances occurances so far");
return($occurances< $count);
}
sub createEvents($$$%) { sub createEvents($$$%) {
my ($self, $t, $onCreateEvent, %vevents)= @_; my ($self, $t0, $onCreateEvent, %vevents)= @_; # t0 is today (for limits)
$self->clearEvents(); $self->clearEvents();
$self->clearSkippedEvents(); $self->clearSkippedEvents();
@ -1419,9 +1526,8 @@ sub createEvents($$$%) {
} }
if(!$skip) { if(!$skip) {
# add event # add event
# and return if we exceed storage limit
$event->setNote("RDATE: $rdate"); $event->setNote("RDATE: $rdate");
$self->addEventLimited($t, $event); $self->addEventLimited($t0, $event);
} }
} }
} }
@ -1429,156 +1535,81 @@ sub createEvents($$$%) {
# #
# now we build the series # now we build the series
# #
#main::Debug "building series...";
# first event in the series # first event in the series
my $event= $self->createSingleEvent(undef, $onCreateEvent); my $event= $self->createSingleEvent(undef, $onCreateEvent);
my $n= 0; return if(!$self->addOrSkipSeriesEvent($event, $t0, $until, $count, \%vevents));
my $nextstart = $event->{start};
#main::Debug "start: " . $event->ts($nextstart);
if(($freq eq "WEEKLY") && ($byday ne "")) {
# special handling for WEEKLY and BYDAY
while(1) { # BYDAY with prefix (e.g. -1SU or 2MO) is not recognized
my $skip= 0; #main::Debug "weekly event, BYDAY= $byday";
my @bydays= split(',', $byday);
# check if superseded by out-of-series event # we assume a week from MO to SU
if($self->hasReferences()) { # we need to cover situations similar to:
foreach my $id (@{$self->references()}) { # BYDAY= TU,WE,TH and start is WE or end is WE
my $vevent= $vevents{$id};
my $recurrenceid= $vevent->value("RECURRENCE-ID"); # loop over days, skip over weeks
my $originalstart= $vevent->tm($recurrenceid); # e.g. TH, FR, SA, SU / ... / MO, TU, WE
if($originalstart == $event->start()) { while(1) {
$event->setNote("RECURRENCE-ID: $recurrenceid"); # next day
$self->addSkippedEvent($event); $nextstart= plusNSeconds($nextstart, 24*60*60, 1);
$skip++; my $weekday= $self->weekdayOf($nextstart);
last; # if we reach MO, then skip ($interval-1) weeks
} $nextstart= plusNSeconds($nextstart, 7*24*60*60, $interval-1) if($weekday==1);
} #main::Debug "Skip to: start " . $event->ts($nextstart) . " = " . $weekdays[$weekday];
if($weekdays[$weekday] ~~ @bydays) {
my $event= $self->createSingleEvent($nextstart, $onCreateEvent);
return if(!$self->addOrSkipSeriesEvent($event, $t0, $until, $count, \%vevents));
}
} }
} else {
# RFC 5545 p. 120 # handling for events with equal time spacing
# The final recurrence set is generated by gathering all of the while(1) {
# start DATE-TIME values generated by any of the specified "RRULE" # advance to next occurance
# and "RDATE" properties, and then excluding any start DATE-TIME if($freq eq "SECONDLY") {
# values specified by "EXDATE" properties. This implies that start $nextstart = plusNSeconds($nextstart, 1, $interval);
# DATE-TIME values specified by "EXDATE" properties take precedence } elsif($freq eq "MINUTELY") {
# over those specified by inclusion properties (i.e., "RDATE" and $nextstart = plusNSeconds($nextstart, 60, $interval);
# "RRULE"). Where duplicate instances are generated by the "RRULE" } elsif($freq eq "HOURLY") {
# and "RDATE" properties, only one recurrence is considered. $nextstart = plusNSeconds($nextstart, 60*60, $interval);
# Duplicate instances are ignored. } elsif($freq eq "DAILY") {
$nextstart = plusNSeconds($nextstart, 24*60*60, $interval);
# check if excluded by EXDATE } elsif($freq eq "WEEKLY") {
if($self->hasKey('EXDATE')) {
foreach my $exdate (@{$self->values("EXDATE")}) {
if($self->tm($exdate) == $event->start()) {
$event->setNote("EXDATE: $exdate");
$self->addSkippedEvent($event);
$skip++;
last;
}
}
}
# check if excluded by a duplicate RDATE
# this is only to avoid duplicates from previously added RDATEs
if($self->hasKey('RDATE')) {
foreach my $rdate (@{$self->values("RDATE")}) {
if($self->tm($rdate) == $event->start()) {
$event->setNote("RDATE: $rdate");
$self->addSkippedEvent($event);
$skip++;
last;
}
}
}
return if($event->{start} > $until); # return if we are after end of series
if(!$skip) {
# add event
# and return if we exceed storage limit
return if($self->addEventLimited($t, $event) > 0);
}
$n++;
return if($n>= $count); # return if we exceeded occurances
# advance to next occurence
my $nextstart = $event->{start};
if($freq eq "SECONDLY") {
$nextstart = plusNSeconds($nextstart, 1, $interval);
} elsif($freq eq "MINUTELY") {
$nextstart = plusNSeconds($nextstart, 60, $interval);
} elsif($freq eq "HOURLY") {
$nextstart = plusNSeconds($nextstart, 60*60, $interval);
} elsif($freq eq "DAILY") {
$nextstart = plusNSeconds($nextstart, 24*60*60, $interval);
} elsif($freq eq "WEEKLY") {
# special handling for WEEKLY and BYDAY
#main::Debug "weekly event, BYDAY= $byday";
if($byday ne "") {
# BYDAY with prefix (e.g. -1SU or 2MO) is not recognized
my @bydays= split(',', $byday);
# we skip interval-1 weeks
$nextstart = plusNSeconds($nextstart, 7*24*60*60, $interval-1) if ($n > 1);
my ($msec, $mmin, $mhour, $mday, $mmon, $myear, $mwday, $yday, $isdat);
my $currentstart = 0;
($msec, $mmin, $mhour, $mday, $mmon, $myear, $mwday, $yday, $isdat) = localtime($nextstart);
$nextstart = plusNSeconds($nextstart, 24*60*60, 7-$mwday) if ($n > 1 and $mwday > 0); # advance to next start of week
do {
$nextstart = plusNSeconds($nextstart, 24*60*60, 1); # forward day by day
($msec, $mmin, $mhour, $mday, $mmon, $myear, $mwday, $yday, $isdat) =
localtime($nextstart);
#main::Debug "Skip to: start " . $event->ts($nextstart) . " = " . $weekdays[$mwday];
if($weekdays[$mwday] ~~ @bydays) {
if (($currentstart > 0) && ($currentstart<= $until)) {
$skip = 0;
$event = $self->createSingleEvent($currentstart, $onCreateEvent);
if($self->hasKey('EXDATE')) {
foreach my $exdate (@{$self->values("EXDATE")}) {
if($self->tm($exdate) == $event->start()) {
$event->setNote("EXDATE: $exdate");
$self->addSkippedEvent($event);
$skip++;
last;
}
}
} # end of EXDATE
$self->addEventLimited($t, $event) if ($skip == 0); # add current event
}
$currentstart = $nextstart;
}
#main::Debug "weekday= " . $weekdays[$mwday] . "($mwday), smartmatch " . join(" ",@bydays) ."= " . ($weekdays[$mwday] ~~ @bydays ? "yes" : "no");
} until($weekdays[$mwday] eq "SU");
#main::Debug $self->asString();
$nextstart = $currentstart;
} # end of WEKKLY BYDAY
else {
# default WEEKLY handling # default WEEKLY handling
$nextstart = plusNSeconds($nextstart, 7*24*60*60, $interval); $nextstart = plusNSeconds($nextstart, 7*24*60*60, $interval);
} elsif($freq eq "MONTHLY") {
if ( $byday ne "" ) {
$nextstart = getNextMonthlyDateByDay( $nextstart, $byday, $interval );
} else {
# here we ignore BYMONTHDAY as we consider the day of month of $self->{start}
# to be equal to BYMONTHDAY.
$nextstart= plusNMonths($nextstart, $interval);
}
} elsif($freq eq "YEARLY") {
$nextstart= plusNMonths($nextstart, 12*$interval);
} else {
main::Log3 undef, 2, "Calendar: event frequency '$freq' not implemented";
return;
} }
} elsif($freq eq "MONTHLY") { # the next event
if ( $byday ne "" ) { #main::Debug "Skip to: start " . $event->ts($nextstart);
$nextstart = getNextMonthlyDateByDay( $nextstart, $byday, $interval ); $event= $self->createSingleEvent($nextstart, $onCreateEvent);
} return if(!$self->addOrSkipSeriesEvent($event, $t0, $until, $count, \%vevents));
else {
# here we ignore BYMONTHDAY as we consider the day of month of $self->{start}
# to be equal to BYMONTHDAY.
$nextstart= plusNMonths($nextstart, $interval);
}
} elsif($freq eq "YEARLY") {
$nextstart= plusNMonths($nextstart, 12*$interval);
} else {
main::Log3 undef, 2, "Calendar: event frequency '$freq' not implemented";
return;
} }
# the next event
$event= $self->createSingleEvent($nextstart, $onCreateEvent);
} }
} else { } else {
# #
# single event # single event
# #
my $event= $self->createSingleEvent(undef, $onCreateEvent); my $event= $self->createSingleEvent(undef, $onCreateEvent);
$self->addEventLimited($t, $event); $self->addEventLimited($t0, $event);
} }
} }
@ -1887,7 +1918,7 @@ sub Calendar_Get($@) {
# attr myCalendar defaultFormat <format> # attr myCalendar defaultFormat <format>
my $format= AttrVal($name, "defaultFormat", '"$T1 $D $S"'); my $format= AttrVal($name, "defaultFormat", '"$T1 $D $S"');
my $timeFormat= AttrVal($name, "defaultTimeFormat",'%d.%m %H:%M'); my $timeFormat= AttrVal($name, "defaultTimeFormat",'%d.%m.%Y %H:%M');
my @filters= (); my @filters= ();
my $next= undef; my $next= undef;
@ -2018,7 +2049,7 @@ sub Calendar_Get($@) {
my @uids= split(";", $hash->{READINGS}{$filter}{VAL}); my @uids= split(";", $hash->{READINGS}{$filter}{VAL});
$param= \@uids; $param= \@uids;
} elsif($filter eq "all") { } elsif($filter eq "all") {
$filterref= undef; $filterref= \&filter_true;
} elsif($filter eq "next") { } elsif($filter eq "next") {
$filterref= \&filter_notend; $filterref= \&filter_notend;
$param= { }; # reference to anonymous (unnamed) empty hash, thus $ in $param $param= { }; # reference to anonymous (unnamed) empty hash, thus $ in $param
@ -2206,6 +2237,10 @@ sub Calendar_GetSecondsFromTimeSpec($) {
################################### ###################################
# Filters # Filters
sub filter_true($$) {
return 1;
}
sub filter_mode($$) { sub filter_mode($$) {
my ($event,$value)= @_; my ($event,$value)= @_;
my $hit; my $hit;