Giter VIP home page Giter VIP logo

when's Introduction

When

Date/Calendar recursion library for PHP 7.1+

Build Status Total Downloads Latest Stable Version License

Author: Tom Planer

Installation

composer require tplaner/When

Current Features

When offers full support for RFC5455 Recurrence Rule features (and some bonus features). Please check the unit tests for information and examples about how to use it.

Here are some basic examples.

// friday the 13th for the next 5 occurrences
$r = new When();
$r->startDate(new DateTime("19980213T090000"))
  ->freq("monthly")
  ->count(5)
  ->byday("fr")
  ->bymonthday(13)
  ->generateOccurrences();

print_r($r->occurrences);
// friday the 13th for the next 5 occurrences rrule
$r = new When();
$r->startDate(new DateTime("19980213T090000"))
  ->rrule("FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13;COUNT=5")
  ->generateOccurrences();

print_r($r->occurrences);
// friday the 13th for the next 5 occurrences, skipping known friday the 13ths
$r = new When();
$r->startDate(new DateTime("19980213T090000"))
  ->freq("monthly")
  ->count(5)
  ->byday("fr")
  ->bymonthday(13)
  ->exclusions('19990813T090000,20001013T090000')
  ->generateOccurrences();

print_r($r->occurrences);
// friday the 13th forever; see which ones occur in 2018
$r = new When();
$r->startDate(new DateTime("19980213T090000"))
  ->rrule("FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13");

$occurrences = $r->getOccurrencesBetween(new DateTime('2018-01-01 09:00:00'),
                                         new DateTime('2019-01-01 09:00:00'));
print_r($occurrences);

InvalidStartDate Exception: The start date must be the first occurrence

According to the specification the starting date should be the first recurring date. This can often be troublesome, especially if you're generating the recurring dates from user input as it will throw an exception. You can disable this functionality easily:

$r = new When();
$r->RFC5545_COMPLIANT = When::IGNORE;

// get the last Friday of the month for the next 5 occurrences
$r->startDate(new DateTime())
  ->rrule("FREQ=MONTHLY;BYDAY=-1FR;COUNT=5")
  ->generateOccurrences();

print_r($r->occurrences);

Additional examples:

$r = new When();
$r->RFC5545_COMPLIANT = When::IGNORE;

// second to last day of the month
$r->startDate(new DateTime())
  ->rrule("FREQ=MONTHLY;BYMONTHDAY=-2;COUNT=5")
  ->generateOccurrences();

print_r($r->occurrences);
$r = new When();
$r->RFC5545_COMPLIANT = When::IGNORE;

// every other week
$r->startDate(new DateTime())
    ->rrule("FREQ=WEEKLY;INTERVAL=2;COUNT=10")
    ->generateOccurrences();

print_r($r->occurrences);
$r1 = new When();
$r2 = new When();
$r1->RFC5545_COMPLIANT = When::IGNORE;
$r2->RFC5545_COMPLIANT = When::IGNORE;

// complex example of a payment schedule
// borrowed from: https://www.mikeyroy.com/2019/10/25/google-calendar-recurring-event-for-twice-monthly-payroll-only-on-weekdays/
//
// you're paid on the 15th, (or closest to it, but only on a weekday)
$r1->startDate(new DateTime())
   ->rrule("FREQ=MONTHLY;INTERVAL=1;BYSETPOS=-1;BYDAY=MO,TU,WE,TH,FR;BYMONTHDAY=13,14,15;COUNT=12")
   ->generateOccurrences();

// you're also paid on the last weekday of the month
$r2->startDate(new DateTime())
    ->rrule("FREQ=MONTHLY;INTERVAL=1;BYSETPOS=-1;BYDAY=MO,TU,WE,TH,FR;BYMONTHDAY=26,27,28,29,30,31;COUNT=12")
    ->generateOccurrences();

$totalPaydays = count($r1->occurrences);
for ($i = 0; $i < $totalPaydays; $i++)
{
    echo "You'll be paid on: " . $r1->occurrences[$i]->format('F d, Y') . "\n";
    echo "You'll be paid on: " . $r2->occurrences[$i]->format('F d, Y') . "\n";
}

Performance

When is pretty fast, and shouldn't be able to loop infinitely. This is because the gregorian calendar actually repeats fully every 400 years. Therefore, this is an imposed upper limit to When, it will not generate occurrences more than 400 years into the future, and if it can't find a match in the next 400 years the pattern just doesn't exist.

By default, we do not generate more than 200 occurrences, though this can be configured simply by specifying a higher COUNT or by modifying the $rangeLimit prior to calling generateOccurrences():

$r = new When();
$r->RFC5545_COMPLIANT = When::IGNORE;

$r->startDate(new DateTime())
  ->rrule("FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13")
  ->generateOccurrences();

// will generate an array of 200
print_r($r->occurrences);

The following is a pretty intensive benchmark the final occurrence is in the year 2254. On my machine this generates the results in about 0.28s.

$r = new When();
$r->RFC5545_COMPLIANT = When::IGNORE;

$r->startDate(new DateTime(20210101))
  ->rrule("FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13;COUNT=400")
  ->generateOccurrences();

// will generate an array of 400
print_r($r->occurrences);

COUNT with an UNTIL, only 5 Friday the 13ths from 2021 to 2025.

$r = new When();
$r->RFC5545_COMPLIANT = When::IGNORE;

$r->startDate(new DateTime(20210101))
  ->rrule("FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13;COUNT=400;UNTIL=20250101")
  ->generateOccurrences();

// will generate until 2023-01-01 or 400
print_r($r->occurrences);

Limiting by $rangeLimit:

$r = new When();
$r->RFC5545_COMPLIANT = When::IGNORE;

$r->rangeLimit = 400;

$r->startDate(new DateTime())
  ->rrule("FREQ=MONTHLY;BYDAY=-1FR")
  ->generateOccurrences();

// 400 occurrences, limited by the rangeLimit
print_r($r->occurrences);

when's People

Contributors

akf avatar barrydam avatar blindmikey avatar boryn avatar deldreth avatar donaldgoose avatar hikari-no-yume avatar huebs avatar jacktuck avatar jamespaulmuir avatar justinethier avatar kevtainer avatar martinknor avatar pospi avatar remicollet avatar tplaner avatar trentontrama avatar ultrasimplified avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

when's Issues

Timezone not preserved from constructor

In the following code, the timezone parameter seems to get ignored, and all recurrences inherit the system's default timezone (Europe/Paris in this example):

$timezone = new DateTimeZone('Asia/Tokyo');
$recurrence = new \When\When("today 10:00:00", $timezone);
$recurrence
        ->freq("daily")
        ->byday("mo,tu,we,th")
        ->byhour("10,11,12,13,14,15,16,17,18")
        ->byminute('00')
        ->bysecond('00')
        ->count(50)
        ->generateOccurrences();
var_dump($recurrence->occurrences);

Output:

array (size=9)
0 => 
    object(DateTime)[8]
        public 'date' => string '2015-08-25 10:00:00' (length=19)
        public 'timezone_type' => int 3
        public 'timezone' => string 'Europe/Paris' (length=12)
1 => 
    object(DateTime)[9]
        public 'date' => string '2015-08-25 11:00:00' (length=19)
        public 'timezone_type' => int 3
        public 'timezone' => string 'Europe/Paris' (length=12)
...

Make it possible to use other time objects than \DateTime

The lib has hard dependencies on a few base classes:

$this->until(new \DateTime($param));
$this->until->add(new \DateInterval('P400Y'));

It should be possible to change them. Either by providing a method than can be overloaded or a getter and setter that will allow me to define the interval and date objects.

Calling the classes directly in the code makes it impossible to work with custom date objects.

Unbounded series support -- interested?

I started working with When and wound up needing to add some things. The changes are proving to be pretty sizable, so I thought I'd ask whether you're interested in them before just making a pull request. It might not be a direction you want to go with your project. (Also, I think I have some loose ends I should tie up before making a pull request!)

My initial goal was to eliminate the need for an arbitrary boundary on the recurrence series, provided that the user doesn't call generateOccurrences() and instead uses my new function getOccurrencesBetween($startDate, $endDate). This necessitated making occursOn a bit smarter; adding a check for whether the date offered matches the INTERVAL, if >1. So far, progress is promising but I am definitely having to clarify my understanding of RFC 5545 as I go.

Users could still call generateOccurrences as usual. The one thing I don't like about this approach is that there's a hidden side-effect if you use both generateOccurrences and getOccurencesBetween in the same code (so you shouldn't). I can see a way to eliminate the side-effect, though.

If you want to take a look at what I have so far, it's on the "unbounded" branch of the lucidmeetings fork: https://github.com/lucidmeetings/When/tree/unbounded

Another thing I'm working on is support for EXDATEs and RDATEs. I can easily handle that in a wrapper around When, but it would make more sense to integrate it in.

Exceptions

Would be nice to have a possibility to be able to define explicit exceptions in a pattern before the generation of occurrences. This could be done by datetimeobjects or even patterns. See example of use down below

Example:

// friday the 13th for the next 5 occurrences
$r = new When();
$r->startDate(new DateTime("19980213T090000"))
  ->freq("monthly")
  ->count(5)
  ->byday("fr")
  ->bymonthday(13)
  ->except([new Datetime(""), new Datetime("")])
  ->generateOccurrences();

print_r($r->occurrences);

$r->occurrences would of course return friday the 13th for the next 5 occurrences without the defined exeptions

Documentation does not match the version

The documentation displayed on the front page does not seem to match the contents of the package i.e. it says that you can use getOccurrencesBetween in 2.0, however, when you install it, the library does not contain this method.

New version code

Hello there thank you for the nice library very handy. As there isn't any documentation can you please help me convert this (found on Stackoverflow) to the new version so it works.

$r = new When();
$r->recur(<start date here>, 'weekly')
->until(<end date here>)
->wkst('SU')
->byday(array('MO', 'TU', 'WE', 'TH', 'FR'));

while($result = $r->next())
{
echo $result->format('c') . '<br />';
}

And what format does the date need to be in?

Wrong Occurrences using Google Calendar RRULE

Why I get a strange result when I try to get the Google Calendar RRULE?
This is the code:

$r = new \When\When();
$r->startDate($date)
    ->rrule($recurrence)
    ->generateOccurrences();

These are the parameters:

object(DateTime)#1038 (3) {
  ["date"] => string(26) "2014-01-12 13:30:00.000000"
  ["timezone_type"] => int(3)
  ["timezone"] => string(11) "Europe/Rome"
}
string(45) "FREQ=MONTHLY;UNTIL=20151212T235959Z;BYDAY=2SU"

This is the When dump class with data:

object(When\When)#1126 (19) {
  ["startDate"] => object(DateTime)#1125 (3) {
    ["date"] => string(26) "2014-01-12 13:30:00.000000"
    ["timezone_type"] => int(3)
    ["timezone"] => string(11) "Europe/Rome"
  }
  ["freq"] => string(7) "monthly"
  ["until"] => object(DateTime)#1110 (3) {
    ["date"] => string(26) "2015-12-12 23:59:59.000000"
    ["timezone_type"] => int(2)
    ["timezone"] => string(1) "Z"
  }
  ["count"] => int(200)
  ["interval"] => int(1)
  ["byseconds"] => array(1) {
    [0] => int(0)
  }
  ["byminutes"] => array(1) {
    [0] => int(30)
  }
  ["byhours"] => array(1) {
    [0] => int(13)
  }
  ["bydays"] => array(1) {
    [0] => string(3) "2su"
  }
  ["bymonthdays"] => NULL
  ["byyeardays"] => NULL
  ["byweeknos"] => NULL
  ["bymonths"] => NULL
  ["bysetpos"] => NULL
  ["wkst"] => string(2) "mo"
  ["occurrences"] => array(23) {
    [0] => object(DateTime)#1113 (3) {
      ["date"] => string(26) "2014-01-12 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [1] => object(DateTime)#1118 (3) {
      ["date"] => string(26) "2014-02-09 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [2] => object(DateTime)#1120 (3) {
      ["date"] => string(26) "2014-03-09 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [3] => object(DateTime)#1122 (3) {
      ["date"] => string(26) "2014-04-13 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [4] => object(DateTime)#1119 (3) {
      ["date"] => string(26) "2014-05-11 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [5] => object(DateTime)#1117 (3) {
      ["date"] => string(26) "2014-06-08 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [6] => object(DateTime)#1114 (3) {
      ["date"] => string(26) "2014-07-13 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [7] => object(DateTime)#1116 (3) {
      ["date"] => string(26) "2014-08-10 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [8] => object(DateTime)#1115 (3) {
      ["date"] => string(26) "2014-09-14 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [9] => object(DateTime)#1112 (3) {
      ["date"] => string(26) "2014-10-12 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [10] => object(DateTime)#1111 (3) {
      ["date"] => string(26) "2014-11-09 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [11] => object(DateTime)#1098 (3) {
      ["date"] => string(26) "2014-12-14 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [12] => object(DateTime)#1102 (3) {
      ["date"] => string(26) "2015-01-11 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [13] => object(DateTime)#1107 (3) {
      ["date"] => string(26) "2015-02-08 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [14] => object(DateTime)#1109 (3) {
      ["date"] => string(26) "2015-03-08 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [15] => object(DateTime)#1108 (3) {
      ["date"] => string(26) "2015-04-12 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [16] => object(DateTime)#1106 (3) {
      ["date"] => string(26) "2015-05-10 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [17] => object(DateTime)#1103 (3) {
      ["date"] => string(26) "2015-06-14 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [18] => object(DateTime)#1105 (3) {
      ["date"] => string(26) "2015-07-12 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [19] => object(DateTime)#1104 (3) {
      ["date"] => string(26) "2015-08-09 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [20] => object(DateTime)#1101 (3) {
      ["date"] => string(26) "2015-09-13 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [21] => object(DateTime)#1100 (3) {
      ["date"] => string(26) "2015-10-11 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
    [22] => object(DateTime)#1099 (3) {
      ["date"] => string(26) "2015-11-08 13:30:00.000000"
      ["timezone_type"] => int(3)
      ["timezone"] => string(11) "Europe/Rome"
    }
  }
  ["date"] => string(26) "2015-10-08 00:10:42.000000"
  ["timezone_type"] => int(3)
  ["timezone"] => string(11) "Europe/Rome"
}

The next occurrence must be the 18th October 2015

[BUG] tuesday not generated with rrule

Seems to skip on Tuesday with some rules.

My testing code:

<?
date_default_timezone_set('Europe/Paris');
require_once('Valid.php');
require_once('When.php');

use \When\When;

$rule = "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR,SU"";
// Initialize When Class
$r = new When();
$startDate = 1402869600;
$r->startDate(new DateTime("@$startDate"))->rrule($rule)->count(10);
$r->generateOccurrences();

print_r($r->occurrences);
?>

Result

Array
(
    [0] => DateTime Object
        (
            [date] => 2014-06-15 22:00:00
            [timezone_type] => 1
            [timezone] => +00:00
        )

    [1] => DateTime Object
        (
            [date] => 2014-06-16 22:00:00
            [timezone_type] => 1
            [timezone] => +00:00
        )

    [2] => DateTime Object
        (
            [date] => 2014-06-18 22:00:00
            [timezone_type] => 1
            [timezone] => +00:00
        )

    [3] => DateTime Object
        (
            [date] => 2014-06-19 22:00:00
            [timezone_type] => 1
            [timezone] => +00:00
        )

    [4] => DateTime Object
        (
            [date] => 2014-06-20 22:00:00
            [timezone_type] => 1
            [timezone] => +00:00
        )

)

[1] = 2014-06-16 22:00:00
[2] = 2014-06-18 22:00:00

There isn't 2014-06-17 22:00:00

How to include the module in my ZF2 project?

This is a stupid question but I have found some difficulty to use this module. I have installed the module in the vendor folder by the composer. Then, in my "application.config.php" file I have added the: "When" within the array, in this way:

<?php
    return array(
        'modules' => array(
            'When',
        )
    );

but I get this error:

Fatal error: Uncaught exception 'Zend\ModuleManager\Exception\RuntimeException' with message 'Module (When) could not be initialized.'

UPDATE: Solved! I must instantiate it without add it in the modules list!

Thanks

`bysetpos` excludes the first occurence

Hi @tplaner,

Thanks a lot for this awesome package. It's been working wonders for me, so kudo's!

I'm trying to implement a recurring item, that occurs every first Thursday of the month. Sounds easy enough; but I'm getting some inconsistent results.

I've attached an example that goes wrong. When you set $start to 2018-31-12, I get the correct results. Saw that this has to do with the $count > 0 in prepareOccurrences. The count always starts with 0 so, the first result (jaunary) will be stripped of. Not sure why this is.

Hope you can help. Thanks!

$start = \DateTime::createFromFormat('Y-m-d', '2019-01-01');
$end = \DateTime::createFromFormat('Y-m-d', '2019-03-01');

$when = new \When\When();
$when->RFC5545_COMPLIANT = $when::IGNORE;

$when->startDate($start)
    ->until($end)
    ->byday('th')
    ->freq('monthly')
    ->bysetpos(1); // every first thursday of the month

var_dump($when->getOccurrencesBetween($start, $end));
// returns [2019-02-07], missing 2019-01-03

No 2weekly option

Can you add 2weekly or explain how it's possible to generate events every 2 weeks.

For example, The event will start on 1st April, then won't reoccur until again until the 15 April, then again on 29th April.

Cheers.

Dan

Safe to use with PHP 5.2?

Is When safe to use with PHP 5.2.4? This is the minimum version for WordPress and since I'm looking at using this in a WordPress plugin, there will likely be users on old versions of PHP.

I ask because I see on master it says PHP 5.2+ but looking into When.php on master (and old releases) I see at the top, "Requirements: PHP 5.3+ - makes extensive use of the Date and Time library"

Thank you.

Problem causing infinitie loop

In my version of PHP (5.2.5) a infinite loop was occurring due to code around line 563.

$tmp_month = $month+1;
$tmp_date = new DateTime($year . '-' . $tmp_month . '-' . $overflow_count . ' ' . $timestamp);
$overflow_count++;

I don't know if I did the right thing, but my fix was to add this before the new object is created:

if($tmp_month ==13){$tmp_month=1;$year++;}

This explicit resolution of the month=13 state solved the hang.

Branch recommendation

Hey there!

If you are pushing people to push the develop branch...you should just make that the default branch. :)

Getting the next occurance or rule from NOW

The start date must be the first occurrence. So given a specific rule, say: FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13 how would I calculate the next occurrence after NOW?

I've tried: $r->RFC5545_COMPLIANT = When::IGNORE; but this just gives an empty array, as expected I think.

This looks interesting #19 but I cant get it to work, even after calling adjustStartDate() I still get the The start date must be the first occurrence exception.

Is this possible? I'm planning on using this to calculate when a specific rule would occur next (relative to today/now) without knowing any occurrences.

Ideas? Thanks.

->rrule Not Working in Develop Branch

Looks like the function was gutted in this new version, is the develop branch not ready for use yet?

Also, was wondering if there were plans for the reverse of rrule. Would make database storage really simple if we could seed the When class using your simple methods and then have another method to create an rrule based on the settings input.

New version occurences

Hello,

I like how in the older version (Master branch) you can keep on looping through the data until you get to a point you want to stop (endless events).

In the newer version (Development branch) it preloads up to 200 events into the occurrences array. I think it would be nice to be able to not preload the events (if you choose to) and just endlessly loop through the data (until you want it to stop).

Otherwise, I think it works pretty good ๐Ÿ‘

Does not work properly when event is on 31th and Occorance is monthly

When we have an event on 31th of December then it does not generate the next event on month when the day is less then 31 i.e. when days in month are 30 or less.

$r = new When();
$r->startDate(new DateTime("2015-12-31 01:00:00"))
->freq("monthly")
->count(30)
->generateOccurrences();

Daylight Savings Time

I hate DST with a passion but I have to support it in our app (we just switched over to RRules from a custom solution). Does your library handle DST at all or do you have any plans to support it? If not do you know of any workarounds people use to do this? Thanks! Love the library!

Use with Laravel

When looks awesome for what I need, but I'm having an issue getting it working with Laravel 4.1.

I've added the following to my composer.json file and have run composer update:

"mynameiszanders/when": "dev-develop"

I've also added this to the top of my controller:

use When\When;

And I'm attempting to use it with:

$recurrences = new When();

However, I'm getting Class 'When\When' not found on page load. Am I including it incorrectly? Apologies for the noob question, I couldn't find anyone else with a similar issue.

Many thanks.

Implement a prev() function

Implement a previous function. Most likely this will be done by storing an array of all previously returned values.

Yearly recur increments by date not year

I'm trying to recur every 1 year for 5 years but it's incrementing the date instead of year.

public function test() {
        $r = new \When\When();
        $r->startDate(new DateTime('2015-09-17 00:00:00'));
        $r->freq('yearly');
        $r->interval(1);
        $r->count(5);
        $r->generateOccurrences();      

        print_r($r->occurrences);

    }

Results

Array
(
    [0] => DateTime Object
        (
            [date] => 2015-09-17 00:00:00
            [timezone_type] => 3
            [timezone] => UTC
        )

    [1] => DateTime Object
        (
            [date] => 2015-09-18 00:00:00
            [timezone_type] => 3
            [timezone] => UTC
        )

    [2] => DateTime Object
        (
            [date] => 2015-09-19 00:00:00
            [timezone_type] => 3
            [timezone] => UTC
        )

    [3] => DateTime Object
        (
            [date] => 2015-09-20 00:00:00
            [timezone_type] => 3
            [timezone] => UTC
        )

    [4] => DateTime Object
        (
            [date] => 2015-09-21 00:00:00
            [timezone_type] => 3
            [timezone] => UTC
        )

)

Whole Year Skipped for monthly recurring.

if you set monthly recurring for Dec 5 2015. Which's End Date is Dec 5 2025. Event should display in All Month's 5th Date. it's not being display in any month of 2016 and re-appearing from 5-Jan-2017.

It's happening for November also. This time Event is skipped in 2018 and so on.
Can any body help on this? Waiting for your positive response.

Reference to non-existent object variable ($thisClone->limit)

I've been doing some testing on the getOccurrencesBetween() function and found a line that appears to reference an object variable that doesn't exist. Here is the code in question:

Line 525 of When.php (introduced in de0cd0d):

            if (isset($thisClone->limit)) {
                $thisClone->limit = $thisClone->limit - 200;
            }

Under what condition would $thisClone->limit be set?

I am wondering if what was meant here was a reference to the $limit variable set while calling the getOccurrencesBetween() function. If so, $limit isn't an object variable and the check on $thisClone->limit will return false even if getOccurrencesBetween() is called with a limit.

Am I missing something?

Every three months by first Monday starting January 7 2019 misses occurrences

I'm sure there's a more general description of this, but I don't have it yet! I stumbled upon this problem by creating a recurrence like so (the end date is fairly arbitrary):

  • Every three months (quarterly) on the first Monday of the month,
  • starting January 7 2019, until February 2, 2021 (issue #TODO)
  • DTSTART;TZID=America/New_York:20190107T170000

What I'm getting is the first occurrence on January 7, and then none until October 7 2019. There should be occurrences on 4/1 and 7/1.

I'm working on figuring this one out, but wanted to get an issue in the system in case anyone has thoughts on it.

As a side note, my initial test case didn't explicitly set the time zone for the start date and expected results, and the test passed! I know the time zone offset has played into some other bugs in this library.

(It's awfully quiet here. Have folks moved on to something else?)

Incorrect date generated throws exception

When having the start date and recur rule as below:

$r->recur(new DateTime('2011-03-23 13:00:00'))
  ->rrule('INTERVAL=1;FREQ=WEEKLY;BYDAY=MO,TU,TH,FR;UNTIL=2013-03-25T13:00:00+0000');

it throws the following exception:

Fatal error:  Uncaught exception 'Exception' with message 'DateTime::__construct() [datetime.--construct]: Failed to parse time string (2011-13-1 13:00:00) at position 6 (3): Unexpected character' in /Volumes/cwisnewdev/WWW/tests/wtf/when/When.php:561
Stack trace:
#0 /Volumes/cwisnewdev/WWW/tests/wtf/when/When.php(561): DateTime->__construct('2011-13-1 13:00...')
#1 /Volumes/cwisnewdev/WWW/tests/wtf/when/When.php(719): When->create_suggestions()
#2 /Volumes/cwisnewdev/WWW/tests/wtf/when/When_Iterator.php(97): When->next()
#3 /Volumes/cwisnewdev/WWW/tests/wtf/when/rrule.php(33): When_Iterator->valid()
#4 {main}
  thrown in /Volumes/cwisnewdev/WWW/tests/wtf/when/When.php on line 561

As you can see, it's tried to create a datetime object with the date of '2011-13-1' and an exception is thrown because there aren't 13 months in the year (last time I checked, anyway ;-) ).

The relevant code is:

$tmp_month = $month+1;
$tmp_date = new DateTime($year . '-' . $tmp_month . '-' . $overflow_count . ' ' . $timestamp);
$overflow_count++;

One possible fix would be to make the code:

$tmp_date = new DateTime($year . '-' . $month . '-' . $overflow_count . ' ' . $timestamp);
$tmp_date->modify('+1 month');
$overflow_count++;

Frequency 'Secondly' issue if interval > 60

Hello,
Great Library, but in frequency secondly if interval is greater than 59 (i.e 60), it is giving wrong output in next occurances.

Kindly let me know how to fix it.

Thanks,
Zeeshan.

InvalidStartDate exception

There is no way to set a between constraint for a weekly recurrence, e.g. I want to have a 18:30 event each Monday starting from 1st of December and ending 31st of December (or any other random period).

Exception causing trouble in PHP 5.2.13

With an RRULE such as:

FREQ=MONTHLY;

my production version of PHP (5.2.13) was throwing an exception because of code at around line 630. The message was:
Warning: DateTime::modify() [datetime.modify]: Failed to parse time string (last day of 1 month) at position 9 (o):

The offending line of code was:
$this->try_date->modify('last day of ' . $this->interval . ' ' . $interval);

I believe this version of PHP could not understand 'last day of'.

Now, I'm not sure what the intent of the code was here, but I'm assuming you are wanting to move the date ahead a certain (interval) number of months, landing on the last day of the month. My solution was:

for ($i=0; $i< $this->interval; $i++)
{
$this->try_date->modify('+ 28 days');
$this->try_date->setDate($this->try_date->format('Y'), $this->try_date->format('m'), $this->try_date->format('t'));
}

The +28 days ensure we dont skip a smaller month, and the format('t') gives us the last date of the month in question.

UNTIL issue

$rrule = new When_Iterator();
$rrule->recur('20120206T150000','weekly')->rrule("FREQ=WEEKLY;UNTIL=20130101T000000Z;BYDAY=MO");

Exception: DateTime::__construct() [http://php.net/datetime.--construct]: Failed to parse time string (2012-13-1 15:00:00) at position 6 (3): Unexpected character

(Unwanted) Extra occurrence on weekly intervals if startDate matches weekStart

I'm trying to generate events that occur every 2 (or more) weeks using interval:

        $startDate = new \DateTime('2015-10-26');
        $endDate = new \DateTime('2016-01-31');

        $eventGenerator = new When();
        $eventGenerator->startDate($startDate);
        $eventGenerator->until($endDate);

        $eventGenerator->freq('weekly');
        $eventGenerator->interval(2);

        $eventGenerator->generateOccurrences();

        var_dump($eventGenerator->occurrences);

But the first three events are recurred weekly, before the interval is working as expected:

array (size=8)
  0 => 
    object(DateTime)[994]
      public 'date' => string '2015-10-26 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  1 => 
    object(DateTime)[996]
      public 'date' => string '2015-11-02 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  2 => 
    object(DateTime)[993]
      public 'date' => string '2015-11-09 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  3 => 
    object(DateTime)[997]
      public 'date' => string '2015-11-23 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  4 => 
    object(DateTime)[998]
      public 'date' => string '2015-12-07 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  5 => 
    object(DateTime)[999]
      public 'date' => string '2015-12-21 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  6 => 
    object(DateTime)[1000]
      public 'date' => string '2016-01-04 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  7 => 
    object(DateTime)[1001]
      public 'date' => string '2016-01-18 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)

If I set the interval to 3, I get a 1-week and a 2-week interval before 3-week intervals are computed:

array (size=6)
  0 => 
    object(DateTime)[994]
      public 'date' => string '2015-10-26 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  1 => 
    object(DateTime)[996]
      public 'date' => string '2015-11-02 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  2 => 
    object(DateTime)[993]
      public 'date' => string '2015-11-16 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  3 => 
    object(DateTime)[997]
      public 'date' => string '2015-12-07 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  4 => 
    object(DateTime)[998]
      public 'date' => string '2015-12-28 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  5 => 
    object(DateTime)[999]
      public 'date' => string '2016-01-18 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)

Funny enough, this only happens if the start date is a monday - for any other start date, the interval is working as expected (startdate="2015-10-25", interval=3):

array (size=5)
  0 => 
    object(DateTime)[994]
      public 'date' => string '2015-10-25 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  1 => 
    object(DateTime)[993]
      public 'date' => string '2015-11-15 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  2 => 
    object(DateTime)[996]
      public 'date' => string '2015-12-06 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  3 => 
    object(DateTime)[997]
      public 'date' => string '2015-12-27 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)
  4 => 
    object(DateTime)[998]
      public 'date' => string '2016-01-17 00:00:00.000000' (length=26)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Berlin' (length=13)

It seems that When() just doesn't like mondays...

I suspect that it may have something to do with l.550 of When(), the block that is commented with

// not very happy with this

Could you please have a look into this?

best regards,
ยต.

recur yearly without using 'byday'

Hi, firstly thanks for releasing this library, it has proved very useful.

I have however come across a problem, which I'm not sure whether it is a bug or not. I have created the following to return the next 10 dates on the 14th June.

$r = new When();
$r->recur('2012-04-24', 'yearly')
  ->count(10)
      ->bymonthday(array(14))
      ->bymonth(array(6));

while($result = $r->next())
{
    echo $result->format('c') . '<br />';
}

This gives an error.

If I add

->byday(array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA))

then it works, but I don't understand why it would be necessary to do this.

Many Thanks

Dave

Can't traverse year boundary using WEEKLY interval

I create a When object and set the recur date to a date in 2017 and define a repetition rule with a Weekly interval and then cycle through getting the next date in the cycle. When I go past the new year it fails.

$begdatetime is a PHP DateTime structure whose date is:
date - "2017-08-24 08:00:00.000000"
timezone - "America/Chicago"
$reprule - "FREQ=WEEKLY;INTERVAL=1;BYDAY=SU"

    $when = new When();
    $when->recur($begdatetime)->rrule($reprule);
    while ( $nxtdatetime = $when->next()) {
         <unimportant code that breaks on a condition after the next would go into 2018>
    }

The error is:
PHP Fatal error: Uncaught Exception: DateTime::__construct(): Failed to parse time string (2017-13-1 08:00:00) at position 6 (3): Unexpected character in /usr/share/php/when/When.php:561
Stack trace:
#0 /usr/share/php/when/When.php(561): DateTime->__construct('2017-13-1 08:00...')
#1 /usr/share/php/when/When.php(719): When->create_suggestions()
#2 /var/www/html/mhscheduler/mhscheduler_lib.php(3327): When->next()
#3 /var/www/html/mhscheduler/mhscheduler_lib.php(3251): Schedule->nextoccurrence()
#4 /var/www/html/mhscheduler/mhscheduler_lib.php(344): Schedule->nexteventdatetime()
#5 /var/www/html/mhscheduler/doscheduling.php(227): scheduleevent(Object(Account), Object(Schedule))
#6 {main}
thrown in /usr/share/php/when/When.php on line 561

Clearly it's a year roll over issue as there are not 13 months in 2017. Here is the cuplrit:

554				if($_day <= $days_in_month)
555			        {
556					$tmp_date = new DateTime($year . '-' . $month . '-' . $_day . ' ' . $timestamp);
557				}
558				else
559				{
560					$tmp_month = $month+1;
561					$tmp_date = new DateTime($year . '-' . $tmp_month . '-' . $overflow_count . ' ' . $timestamp);
562					$overflow_count++;
563				}
564				
565				$week_day = $tmp_date->format('w');

When I get to line 560 when $month = 12, $tmp_month gets set to 13 and that is used to determine the month. PHP's DateTime __construct chokes on that. Clearly when $month is 12 $tmp_month should go to 1 and $year or some variation of it should be incremented.

I am just not sure of the fix. Don't know if it's as simple as:

if ($month == 12 ) {
$tmp_month = 1;
$year++;
} else {
$tmp_month = month +1;
}

Not sure it's that simple as I haven't had time to look at how all the incrementing gets done, what all the $tmp_* are about. I don't want to fix one problem and create another.

Anyone seen this before and is there a fix ?

I am running on Fedora25 and have this RPM installed:

$ rpm -qi php-when
Name : php-when
Version : 0.3
Release : 7.fc24
Architecture: noarch
Install Date: Wed 15 Feb 2017 09:19:50 PM CST
Group : Unspecified
Size : 22604
License : MIT
Signature : RSA/SHA256, Thu 31 Mar 2016 06:45:46 PM CDT, Key ID 4089d8f2fdb19c98
Source RPM : php-when-0.3-7.fc24.src.rpm
Build Date : Thu 04 Feb 2016 06:49:53 PM CST
Build Host : bvirthost02-nfs.phx2.fedoraproject.org
Relocations : (not relocatable)
Packager : Fedora Project
Vendor : Fedora Project
URL : https://github.com/tplaner/When
Summary : Date/Calendar recursion library for PHP
Description :
PHP library that handles recursive dates: It determines the next date of
recursion given an iCalendar "rrule" like pattern.

Logic error when trying to create weekly recurrences with a byDay before the current start date day

Hello Tom,

Firstly, thank-you for this great library! It is really amazing!

I've come across an error when I try and create weekly recurrences that have a BYDAY value before the day of the start date.

For example, if I try and generate recurrences with the following rule and a start date day of Wednesday, I get an exception The start date must be the first occurrence.

FREQ=WEEKLY;BYDAY=Mo;COUNT=4;

If I change BYDAY to We, Th, Fr, Sa or Su it works correctly.

Is there anyway that When can deal with this sort of scenario and continue to create the event on the following Monday?

Tim

Duration ... HowTo set ?

Hi,
Congratz with the development of this library. Really great work !

If I can ask ...
How would you suggest to set the duration for repeating events ?
E.g. I have weekly meetings on Monday, Wednesday and Friday starting at 08:AM and a duration of 2 hrs...
Startday is June 1st, and Endday is June 29th...
I'm getting a bit confused with all the options I can set ...

Thanks in advance for your reply,

Johnny

Bug with weekly RRules which start on a monday

When for instance my RRule is: 'FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,TU;'.
With a start date of 10-02-2014 and a count of 5 occurrences.

The resulting dates i get are:

  1. monday 10-02-2014
  2. monday 17-02-2014
  3. tuesday 18-02-2014
  4. monday 03-03-2014
  5. tuesday 04-03-2014

The difference between 1 and 2 is only one week while the interval is set on two. It also goes from monday - monday - tuesday whilte it should be monday and tuesday alternating. This doesn't seem like the expected behaviour.

I'm not very familiar with the source of When, i just use the source and find it very useful. So if somebody who knows the source well wants to have a look at this then great, else i'll have an attempt in solving this bug.

Week of month support?

Is there any way to generate dates based on the week of the month?

For instance every 2nd Thursday between April and June.

return human readable rrule

Id like to make a feature request. When providing a rrule, would it be possible to also make a human readable form available? Perhaps a toRruleAString method?

1st Monday in April every year for 2 years

20160404T090000Z
FREQ=YEARLY;BYDAY=MO;BYSETPOS=1;BYMONTH=4;COUNT=2

I get:

2016-04-04 09:00:00.000000
2016-04-11 09:00:00.000000

When I would expect

2016-04-04 09:00:00.000000
2017-04-03 09:00:00.000000

Any idea why?

Thank you!

FREQ=MONTHLY recur rule breaks

Doing a recur rule such as:

$r->recur(new DateTime('2011-09-15 10:00:00'))
  ->rrule('INTERVAL=1;FREQ=MONTHLY;UNTIL=2016-09-15T10:00:00+0100');

causes a recurrence for every single day until the end date, rather than on 2011-10-15, 2011-11-15, etc. until the end date.

Why is there an InvalidStartDate error?

It seems there is an unneeded error being thrown if the start day does not match one of the recurring days. For example if I create an event on Monday and say it is weekly recurring on Sunday and Monday, an error is thrown. This is a perfectly normal situation so I commented out the "throw new InvalidStartDate()" and it works correctly, but I am concerned that there may be a reason for the that I am unaware of. Is there a specific reason for this error?

Thanks, this is a very nice little library.

Error Message When including

I get this error when including the autoload.php from composer

Fatal error: Class 'When' not found in

I have done everything in the readme.md and added to composer.json file and installed.

Support for DTEND

Is there support for ( or planned for ) DTEND?

eg) 11th of the month - 13th every month for 4 months

Would be:

DTSTART:20160511T000000
DTEND:20160513T000000
RRULE:FREQ=MONTHLY;BYMONTHDAY=11;INTERVAL=1;COUNT=4

But I can't find where I can establish a DTEND in this class.

Any help is greatly appreciated, very handy class.

Best,
-M

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.