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.
|
# 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
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user