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:
parent
7fd25a0306
commit
8dcde68ccf
@ -1,5 +1,6 @@
|
||||
# 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.
|
||||
- bugfix: 57_Calendar: fix weekly events, get full all, defaultTimeFormat
|
||||
- change: 93_DbLog: V3.10.0, addLog considers DbLogExclude in Devices,
|
||||
keyword "!useExcludes" to switch off considering
|
||||
DbLogExclude in addLog, DbLogExclude & DbLogInclude
|
||||
|
@ -1316,12 +1316,19 @@ sub addEventLimited($$$) {
|
||||
|
||||
return -1 if($event->start()< $t+eventsLimitMinus);
|
||||
return 1 if($event->start()> $t+eventsLimitPlus);
|
||||
#main::Debug " addEvent: " . $event->asFull();
|
||||
$self->addEvent($event);
|
||||
#main::Debug " addEventLimited: " . $event->asDebug();
|
||||
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($$$$) {
|
||||
|
||||
my ($self, $nextstart, $onCreateEvent)= @_;
|
||||
@ -1359,8 +1366,108 @@ sub createSingleEvent($$$$) {
|
||||
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($$$%) {
|
||||
my ($self, $t, $onCreateEvent, %vevents)= @_;
|
||||
my ($self, $t0, $onCreateEvent, %vevents)= @_; # t0 is today (for limits)
|
||||
|
||||
$self->clearEvents();
|
||||
$self->clearSkippedEvents();
|
||||
@ -1419,9 +1526,8 @@ sub createEvents($$$%) {
|
||||
}
|
||||
if(!$skip) {
|
||||
# add event
|
||||
# and return if we exceed storage limit
|
||||
$event->setNote("RDATE: $rdate");
|
||||
$self->addEventLimited($t, $event);
|
||||
$self->addEventLimited($t0, $event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1429,156 +1535,81 @@ sub createEvents($$$%) {
|
||||
#
|
||||
# now we build the series
|
||||
#
|
||||
#main::Debug "building series...";
|
||||
|
||||
# first event in the series
|
||||
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) {
|
||||
my $skip= 0;
|
||||
# BYDAY with prefix (e.g. -1SU or 2MO) is not recognized
|
||||
#main::Debug "weekly event, BYDAY= $byday";
|
||||
my @bydays= split(',', $byday);
|
||||
|
||||
# check if superseded by out-of-series event
|
||||
if($self->hasReferences()) {
|
||||
foreach my $id (@{$self->references()}) {
|
||||
my $vevent= $vevents{$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;
|
||||
}
|
||||
}
|
||||
# we assume a week from MO to SU
|
||||
# we need to cover situations similar to:
|
||||
# BYDAY= TU,WE,TH and start is WE or end is WE
|
||||
|
||||
# loop over days, skip over weeks
|
||||
# e.g. TH, FR, SA, SU / ... / MO, TU, WE
|
||||
while(1) {
|
||||
# next day
|
||||
$nextstart= plusNSeconds($nextstart, 24*60*60, 1);
|
||||
my $weekday= $self->weekdayOf($nextstart);
|
||||
# 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));
|
||||
}
|
||||
}
|
||||
|
||||
# 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
|
||||
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 {
|
||||
} else {
|
||||
# handling for events with equal time spacing
|
||||
while(1) {
|
||||
# advance to next occurance
|
||||
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") {
|
||||
# default WEEKLY handling
|
||||
$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") {
|
||||
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;
|
||||
# the next event
|
||||
#main::Debug "Skip to: start " . $event->ts($nextstart);
|
||||
$event= $self->createSingleEvent($nextstart, $onCreateEvent);
|
||||
return if(!$self->addOrSkipSeriesEvent($event, $t0, $until, $count, \%vevents));
|
||||
}
|
||||
# the next event
|
||||
$event= $self->createSingleEvent($nextstart, $onCreateEvent);
|
||||
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
#
|
||||
# single event
|
||||
#
|
||||
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>
|
||||
|
||||
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 $next= undef;
|
||||
|
||||
@ -2018,7 +2049,7 @@ sub Calendar_Get($@) {
|
||||
my @uids= split(";", $hash->{READINGS}{$filter}{VAL});
|
||||
$param= \@uids;
|
||||
} elsif($filter eq "all") {
|
||||
$filterref= undef;
|
||||
$filterref= \&filter_true;
|
||||
} elsif($filter eq "next") {
|
||||
$filterref= \&filter_notend;
|
||||
$param= { }; # reference to anonymous (unnamed) empty hash, thus $ in $param
|
||||
@ -2206,6 +2237,10 @@ sub Calendar_GetSecondsFromTimeSpec($) {
|
||||
###################################
|
||||
# Filters
|
||||
|
||||
sub filter_true($$) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub filter_mode($$) {
|
||||
my ($event,$value)= @_;
|
||||
my $hit;
|
||||
|
Loading…
x
Reference in New Issue
Block a user