44

I am using jQuery FullCalendar as my calendar used in my website for availability agenda.

Is there any functions/methods/options in fullcalendar that handles my recurring events by Days? For example, Monday only to time 7:00AM to 9:00 AM, tuesdays - 4:00PM to 9:00PM, something like that?

4

9 Answers 9

102

Simple Repeating Events

To add a simple alternative to those listed here, Fullcalendar now (somewhat) supports weekly recurring events. So if you only need something like: [Every Monday and Thursday from 10:00am to 02:00pm], you can use the following:

events: [{
    title:"My repeating event",
    start: '10:00', // a start time (10am in this example)
    end: '14:00', // an end time (2pm in this example)
    dow: [ 1, 4 ] // Repeat monday and thursday
}],

JSFiddle

This is documented in Background events but it works for regular events as well.

Saving this to a database wouldn't be hard.

Add some restrictions

If you don't want them to repeat infinitely, you would need to add some start and end dates.

So, in the DB:

  • Let the event shown above represent the parent record
  • Have another table with start/end dates.
  • Joined table example:

eventId  timeStart  timeEnd   dow    dateStart      dateEnd
     1      10:00    12:00  [1,4]  2015/03/01   2015/04/01  // Month of March
     1      10:00    12:00  [1,4]  2015/05/01   2015/06/01  // Month of May
     1      10:00    12:00  [1,4]  2016/01/01   2017/01/01  // Year of 2017

Pass this to the client as JSON:

{ id:1, start:"10:00", end:"12:00", dow:[1,4],
  ranges[{start:"2015/03/01", end:"2015/04/01"},
         {start:"2015/05/01", end:"2015/06/01"},
         {start:"2016/01/01", end:"2017/01/01"},]
}

And client side, use fullcalendar's eventRender to only render events when there are within one of the time ranges. Something like this should work:

eventRender: function(event){
    return (event.ranges.filter(function(range){ // test event against all the ranges

        return (event.start.isBefore(range.end) &&
                event.end.isAfter(range.start));

    }).length)>0; //if it isn't in one of the ranges, don't render it (by returning false)
},

That's assuming your events are structured as:

var repeatingEvents = [{
    title:"My repeating event",
    id: 1,
    start: '10:00', 
    end: '14:00', 
    dow: [ 1, 4 ], 
    ranges: [{ //repeating events are only displayed if they are within at least one of the following ranges.
        start: moment().startOf('week'), //next two weeks
        end: moment().endOf('week').add(7,'d'),
    },{
        start: moment('2015-02-01','YYYY-MM-DD'), //all of february
        end: moment('2015-02-01','YYYY-MM-DD').endOf('month'),
    },/*...other ranges*/],
},/*...other repeating events*/];

JSFiddle


Overnight

In case you want overnight repeating events (like here), just go over 24:00 for the end time. For instance:

{
  start: '10:00', //starts at 10 on monday
  end:   '27:00', //24+3 is handled correctly.
  dow: [1]
}

JSFiddle

17
  • Thanks! This is helpful, but could spill more details on the ranges and eventRender? I could figure get it to render them within the time ranges.
    – Timber
    Commented Apr 14, 2015 at 19:22
  • Thank you so much for adding that! It worked. Big hugs. OP should mark this the answer :).
    – Timber
    Commented Apr 14, 2015 at 20:27
  • 1
    The answer works perfect... Thanks. I am trying to display the recurring event, but Need to remove it for a specific date, suppose I have a recurring event for all Sunday, but I need to remove the event for a specific Sunday only Commented Aug 1, 2015 at 6:45
  • 2
    @Deepanshu Just add another table called "excludedDates" so you have a list of exceptions for every range. Then in eventRender, check that the event you are rendering is not in that list. Let me know if that's clear.
    – DanielST
    Commented Aug 1, 2015 at 7:01
  • 1
    @KevinCohen sure, just add another condition to the eventRender function. This answer is a template to show how to conditionally render events, the actual conditions you use are flexible.
    – DanielST
    Commented Mar 30, 2017 at 19:18
14

Take a look at this site.

It offers alot of good insite on recurring events. FullCalendar does support recurring events in respect to the id. You can handle the events either server side or client side, but the preference would be server side. I will give you some ideas, but its not all inclusive. As I have learned recurring events are a pain to maintain.

If you wanted to handle them client side, you would have to loop through the frequency of the repeating event and the logic of which days. You would probably need to use the eventRender callback, then render each looped event using the options callback. The problem with this will be that you still have to save the recurring frequency and a logical operator for your frequency option in your database...

(column1:frequency=(int)8, column2:type=enum(a'b'c), a=daily, b=weekly, c=monthly etc).

...and then anytime you edited that event it would edit all of the events. If you needed delete just one event you would run into a series of issues within your logic and it could easily become a GIANT mess.

The second option was to do all this server side. Creating two tables, one with the parent event, and the second with all its recurrences. In the parent table you would store the general information, such as a unique id, color, background color, title, allDay, isRecurring, frequency, type etc. In the child table, you would use the unique id from the parent table to associate each recurrence (keep in mind if you want to delete/edit individual events the child table rows need to have their own unique id as well and a column that labels which table it is located). When you add a recurring event, you need to add a enum field that labels whether or not it is a recurring event or not AKA...

column:recurring=enum('0','1')---true/false

... and then you need to add each recurrence, into the child table with its specific information like start and end etc. When you query the event you could either query from the parent and then if the event is recurring get those events associated in a second query, or you could use an INNER JOIN on table1.id=table2.parentID in one single query.

As you can see, recurring event can get very detailed very fast, find out what logic you need and I hope this helps you or someone at least get started. Cheers.

4
  • 3
    For more information on implementing recurring events on the server side also check out this link, it looks like the author of the first link used this info to create his tables: martinfowler.com/apsupp/recurring.pdf
    – Ron E
    Commented May 30, 2013 at 1:59
  • Do you know a library or php code which could help me to save the recurrent events in the child table? Eg. From Thursday Jul, 17th, every Thursday from 10:00 to 11:00 until Thursday Nov. 20.... it would be about 19 records... thanks
    – Enrique
    Commented Jul 17, 2014 at 13:23
  • Honestly I don't know of any libraries, and the code I use for recurring events is quite complex and probably wouldn't do you any good. Check out the link I left and start from there. If you know how many occurrences there will be that just use an input field and then input the event that amount of times into your child table. Commented Jul 20, 2014 at 4:38
  • That link you posted at the start of your post is dead. Do you happen to have any updated info about resources for this package relating to recurring events? Commented Apr 4, 2019 at 9:24
7

No need to make parent and child relationship here is the code that provide simple solution for recurring events in jquery Full calender use these below functions in your php file that you use further to call all your events.

function render_fccalendar_events() {
        $_POST['start'] = strtotime('2013-05-01');
        $_POST['end'] = strtotime('2013-05-31');
        $start = date('Y-m-d',$_POST['start']);
        $end = date('Y-m-d', $_POST['end']);
        $readonly = (isset($_POST['readonly'])) ? true : false;    
        $events = fcdb_query_events($start, $end);       
        render_json(process_events($events, $start, $end, $readonly));
}

function process_events($events, $start, $end, $readonly) {
    if ($events) {
        $output = array();
        foreach ($events as $event) {
            $event->view_start = $start;
            $event->view_end = $end;
            $event = process_event($event, $readonly, true);
            if (is_array($event)) {
                foreach ($event as $repeat) {
                    array_push($output, $repeat);
                }
            } else {
                array_push($output, $event);
            }
        }
        return $output;
    }
}

function process_event($input, $readonly = false, $queue = false) {
    $output = array();
    if ($repeats = generate_repeating_event($input)) {
        foreach ($repeats as $repeat) {
            array_push($output, generate_event($repeat));
        }
    } else {
        array_push($output, generate_event($input));
    }

    if ($queue) {
        return $output;
    }
    render_json($output);
}


function generate_event($input) {
    $output = array(
        'id' => $input->id,
        'title' => $input->name,
        'start' => $input->start_date,
        'end' => $input->end_date,
        'allDay' => ($input->allDay) ? true : false,
        //'className' => "cat{$repeats}",
        'editable' => true,
        'repeat_i' => $input->repeat_int,
        'repeat_f' => $input->repeat_freq,
        'repeat_e' => $input->repeat_end
    );
    return $output;
}



function generate_repeating_event($event) {

    $repeat_desk = json_decode($event->repeat_desk);
    if ($event->repeat == "daily") {
        $event->repeat_int =0;
        $event->repeat_freq = $repeat_desk->every_day;
    }
    if ($event->repeat == "monthly") {
        $event->repeat_int =2;        
        $event->repeat_freq = $repeat_desk->every_month;
    }
    if ($event->repeat == "weekly") {
        $event->repeat_int =1;                
       $event->repeat_freq = $repeat_desk->every_weak;
    }
    if ($event->repeat == "year") {
        $event->repeat_int =3;                        
        $event->repeat_freq = $repeat_desk->every_year;
    }

    if ($event->occurrence == "after-no-of-occurrences") {
        if($event->repeat_int == 0){
            $ext = "days";
        }
        if($event->repeat_int == 1){
            $ext = "weeks";
        }
        if($event->repeat_int == 2){
            $ext = "months";
        }
        if($event->repeat_int == 3){
            $ext = "years";
        }
       $event->repeat_end =  date('Y-m-d',strtotime("+" . $event->repeat_int . " ".$ext));
    } else if ($event->occurrence == "no-end-date") {
        $event->repeat_end = "2023-04-13";
    } else if ($event->occurrence == "end-by-end-date") {
        $event->repeat_end = $event->end_date;
    }



    if ($event->repeat_freq) {

        $event_start = strtotime($event->start_date);
        $event_end = strtotime($event->end_date);
        $repeat_end = strtotime($event->repeat_end) + 86400;
        $view_start = strtotime($event->view_start);
        $view_end = strtotime($event->view_end);
        $repeats = array();

        while ($event_start < $repeat_end) {
            if ($event_start >= $view_start && $event_start <= $view_end) {
                $event = clone $event; // clone event details and override dates
                $event->start_date = date(AEC_DB_DATETIME_FORMAT, $event_start);
                $event->end_date = date(AEC_DB_DATETIME_FORMAT, $event_end);
                array_push($repeats, $event);
            }
            $event_start = get_next_date($event_start, $event->repeat_freq, $event->repeat_int);
            $event_end = get_next_date($event_end, $event->repeat_freq, $event->repeat_int);
        }
        return $repeats;
    }
    return false;
 }

function get_next_date($date, $freq, $int) {
    if ($int == 0)
        return strtotime("+" . $freq . " days", $date);
    if ($int == 1)
        return strtotime("+" . $freq . " weeks", $date);
    if ($int == 2)
        return get_next_month($date, $freq);
    if ($int == 3)
        return get_next_year($date, $freq);
}

function get_next_month($date, $n = 1) {
    $newDate = strtotime("+{$n} months", $date);
    // adjustment for events that repeat on the 29th, 30th and 31st of a month
    if (date('j', $date) !== (date('j', $newDate))) {
        $newDate = strtotime("+" . $n + 1 . " months", $date);
    }
    return $newDate;
}

function get_next_year($date, $n = 1) {
    $newDate = strtotime("+{$n} years", $date);
    // adjustment for events that repeat on february 29th
    if (date('j', $date) !== (date('j', $newDate))) {
        $newDate = strtotime("+" . $n + 3 . " years", $date);
    }
    return $newDate;
}

function render_json($output) {
    header("Content-Type: application/json");
    echo json_encode(cleanse_output($output));
    exit;
}


function cleanse_output($output) {
    if (is_array($output)) {
        array_walk_recursive($output, create_function('&$val', '$val = trim(stripslashes($val));'));
    } else {
        $output = stripslashes($output);
    }
    return $output;
}

function fcdb_query_events($start, $end) {
    global $wpdb;
    $limit = ($limit) ? " LIMIT {$limit}" : "";
    $result = $wpdb->get_results("SELECT id, name,start_date,end_date,repeat_desk,`repeat`,occurrence,occurrence_desk

                                        FROM " . 

$wpdb->prefix . "lgc_events
                                        WHERE (
                                        (start_date >= '{$start}' AND start_date < '{$end}')
                                        OR (end_date >= '{$start}' AND end_date < '{$end}')
                                        OR (start_date <= '{$start}' AND end_date >= '{$end}')
                                        OR (start_date < '{$end}' AND (`repeat`!= ''))


                            )
                                        ORDER BY start_date{$limit};");

    return return_result($result);
}


function return_result($result) {
    if ($result === false) {
        global $wpdb;
        $this->log($wpdb->print_error());
        return false;
    }
    return $result;
}

in the above code i used repeat_desk in which i store json code for repeat frequencyenter image description here

and jquery to call your file

 events:  {
                url: '<?php echo $lgc_plugindir; ?>includes/imagerotator.php',
                data: {
                    action: 'get_events'
                },
                type: 'POST'
            }

i used this for wordpress you can use this code as per your requirement

3

For people who have more complex recurring events than what FullCalendar can handle builtin (see slicedtoad's answer), you can use rSchedule.

For example, Monday only to time 7:00AM to 9:00 AM, tuesdays - 4:00PM to 9:00PM

import { Schedule } from '@rschedule/rschedule';
import { StandardDateAdapter } from '@rschedule/standard-date-adapter';

const mondayDate = new Date(2019, 6, 15);
const tuesdayDate = new Date(2019, 6, 16);

const schedule = new Schedule({
  // add specific dates
  dates: [
    new StandardDateAdapter(mondayDate, {duration: 1000 * 60 * 60 * 2})
  ],
  // add recurrence rules
  rrules: [{
    start: tuesdayDate,
    duration: 1000 * 60 * 60 * 5, // duration is expressed in milliseconds
    frequency: 'WEEKLY'
  }],
});

const firstFiveEvents = schedule
  .occurrences({ take: 5 })
  .toArray()
  .map(adapter => 
    ({title: 'My event title', start: adapter.date, end: adapter.end})
  );

// You can then pass `firstFiveEvents` to fullcalendar for rendering

rSchedule also supports moment / luxon, as well as timezones. For more information, you can check out the rSchedule docs.

4
  • how would this be integrated with fullCalendar, to display the events, then? It's not really clear from your answer.
    – ADyson
    Commented Jul 23, 2019 at 22:07
  • @ADyson I only have experience using the Angular FullCalendar component, but you simply map occurrences to the event format fullcalendar expects, and pass them to fullcalendar for rendering. A simple example: schedule.occurrences({ take: 5 }).toArray().map(adapter => ({title: 'My event title', start: adapter.date, end: adapter.end}))
    – John
    Commented Jul 24, 2019 at 2:10
  • If you make this part of the answer text itself, it's worth an upvote - thanks!
    – ADyson
    Commented Jul 24, 2019 at 6:42
  • I fleshed out the example 👍
    – John
    Commented Jul 24, 2019 at 10:26
2

At the moment I'm doing project where I have FullCalendar and I have to do Recurring events. So this is my why how it's can be done. Hope this code help someone:)

I have next table in database:

CREATE TABLE IF NOT EXISTS `app_ext_calendar_events` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `users_id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `description` text,
  `start_date` int(11) NOT NULL,
  `end_date` int(11) NOT NULL,
  `event_type` varchar(16) NOT NULL,
  `is_public` tinyint(1) DEFAULT NULL,
  `bg_color` varchar(16) DEFAULT NULL,
  `repeat_type` varchar(16) DEFAULT NULL,
  `repeat_interval` int(11) DEFAULT NULL,
  `repeat_days` varchar(16) DEFAULT NULL,
  `repeat_end` int(11) DEFAULT NULL,
  `repeat_limit` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_users_id` (`users_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=18 ;

and I developed next php class to get events with repeat events:

<?php

class calendar
{
  static public function get_events($date_from, $date_to,$calendar_type)
  {
    global $app_user;

    $list = array();

    $events_query = db_query("select * from app_ext_calendar_events where FROM_UNIXTIME(start_date,'%Y-%m-%d')>='" . $date_from . "' and  FROM_UNIXTIME(end_date,'%Y-%m-%d')<='" . $date_to . "' and event_type='" . $calendar_type . "' and users_id='" . db_input($app_user['id']) . "'");
    while($events = db_fetch_array($events_query))
    {
      $list[] = $events; 
    }

    if(count($repeat_events_list = calendar::get_repeat_events($date_to,$calendar_type)))
    {
      $list = array_merge($list,$repeat_events_list);
    }

    return $list;
  }

  public static function weeks_dif($start, $end)
  {
    $year_start = date('Y',$start);
    $year_end = date('Y',$end);

    $week_start = date('W',$start); 
    $week_end = date('W',$end);

    $dif_years = $year_end - $year_start;
    $dif_weeks = $week_end - $week_start;

    if($dif_years==0 and $dif_weeks==0)
    {
      return 0;
    }
    elseif($dif_years==0 and $dif_weeks>0)
    {
      return $dif_weeks;
    }
    elseif($dif_years==1)
    {
      return (42-$week_start)+$week_end;
    }
    elseif($dif_years>1)
    {
      return (42-$week_start)+$week_end+(($dif_years-2)*42);
    }


  }

  public static function months_dif($start, $end)
  {
    // Assume YYYY-mm-dd - as is common MYSQL format
    $splitStart = explode('-', date('Y-n',$start));
    $splitEnd = explode('-', date('Y-n',$end));

    if (is_array($splitStart) && is_array($splitEnd)) 
    {
        $startYear = $splitStart[0];
        $startMonth = $splitStart[1];
        $endYear = $splitEnd[0];
        $endMonth = $splitEnd[1];

        $difYears = $endYear - $startYear;
        $difMonth = $endMonth - $startMonth;

        if (0 == $difYears && 0 == $difMonth) 
        { // month and year are same
            return 0;
        }
        else if (0 == $difYears && $difMonth > 0) 
        { // same year, dif months
            return $difMonth;
        }
        else if (1 == $difYears) 
        {
            $startToEnd = 13 - $startMonth; // months remaining in start year(13 to include final month
            return ($startToEnd + $endMonth); // above + end month date
        }
        else if ($difYears > 1) 
        {
            $startToEnd = 13 - $startMonth; // months remaining in start year 
            $yearsRemaing = $difYears - 2;  // minus the years of the start and the end year
            $remainingMonths = 12 * $yearsRemaing; // tally up remaining months
            $totalMonths = $startToEnd + $remainingMonths + $endMonth; // Monthsleft + full years in between + months of last year
            return $totalMonths;
        }
    }
    else 
    {
      return false;
    }
  }

  public static function get_repeat_events($date_to,$calendar_type)
  {
    global $app_user;

    //convert date to timestamp
    $date_to_timestamp = get_date_timestamp($date_to);

    $list = array();

    //get all events that already started (start_date<=date_to)      
    $events_query = db_query("select * from app_ext_calendar_events where length(repeat_type)>0 and FROM_UNIXTIME(start_date,'%Y-%m-%d')<='" . $date_to . "' and  event_type='" . $calendar_type . "' and users_id='" . db_input($app_user['id']) . "'");
    while($events = db_fetch_array($events_query))
    {
      $start_date = $events['start_date'];

      //set repeat end      
      $repeat_end = false;
      if($events['repeat_end']>0)
      {
        $repeat_end = $events['repeat_end'];
      } 

      //get repeat events by type                       
      switch($events['repeat_type'])
      {
        case 'daily': 
            //check repeat events day bay day       
            for($date = $start_date; $date<=$date_to_timestamp; $date+=86400)            
            {
              if($date>$start_date)
              {
                $dif = round(abs($date-$start_date)/86400);

                if($dif>0)
                {  
                  $event_obj = $events;                                                
                  $event_obj['start_date'] = strtotime('+' . $dif . ' day',$event_obj['start_date']);                                                          
                  $event_obj['end_date'] = strtotime('+' . $dif . ' day',$event_obj['end_date']);

                  if(calendar::check_repeat_event_dif($dif,$event_obj,$repeat_end))
                  {                
                    $list[] = $event_obj;                
                  }
                }
              }
            }                                                          
          break;
        case 'weekly':  
            //check repeat events day bay day    
            for($date = $start_date; $date<=$date_to_timestamp; $date+=86400)            
            {
              if($date>$start_date)
              {
                //find days dif
                $dif = round(abs($date-$start_date)/86400);
                //find week dif
                $week_dif = calendar::weeks_dif($start_date, $date);

                if($dif>0 and (in_array(date('N',$date),explode(',',$events['repeat_days']))))
                {  
                  $event_obj = $events;                                                
                  $event_obj['start_date'] = strtotime('+' . $dif . ' day',$event_obj['start_date']);                                                          
                  $event_obj['end_date'] = strtotime('+' . $dif . ' day',$event_obj['end_date']);

                  if(calendar::check_repeat_event_dif($week_dif,$event_obj,$repeat_end))
                  {                
                    $list[] = $event_obj;                
                  }
                }
              }
            }                       

          break;            
        case 'monthly':                        
            /**
             *in calendar we display 3 month in one view
             *so we have to check difference for each month
             */

             //check 1                                      
            $date_to_timestamp2 = strtotime('-2 month',$date_to_timestamp);

            $dif =  calendar::months_dif($start_date, $date_to_timestamp2);

            if($dif>0)
            {  
              $event_obj = $events;                                                
              $event_obj['start_date'] = strtotime('+' . $dif . ' month',$event_obj['start_date']);                                                          
              $event_obj['end_date'] = strtotime('+' . $dif . ' month',$event_obj['end_date']);

              if(calendar::check_repeat_event_dif($dif,$event_obj,$repeat_end))
              {                
                $list[] = $event_obj;                
              }
            }

            //check 2
            $date_to_timestamp1 = strtotime('-1 month',$date_to_timestamp);

            $dif =  calendar::months_dif($start_date, $date_to_timestamp1);

            if($dif>0)
            {  
              $event_obj = $events;                                                
              $event_obj['start_date'] = strtotime('+' . $dif . ' month',$event_obj['start_date']);                                                          
              $event_obj['end_date'] = strtotime('+' . $dif . ' month',$event_obj['end_date']);

              if(calendar::check_repeat_event_dif($dif,$event_obj,$repeat_end))
              {                
                $list[] = $event_obj;                
              }
            }

            //check 3
            $dif =  calendar::months_dif($start_date, $date_to_timestamp);

            if($dif>0)
            {     
              $event_obj = $events;                                             
              $event_obj['start_date'] = strtotime('+' . $dif . ' month',$event_obj['start_date']);                                                          
              $event_obj['end_date'] = strtotime('+' . $dif . ' month',$event_obj['end_date']);

              if(calendar::check_repeat_event_dif($dif,$event_obj,$repeat_end))
              {                                
                $list[] = $event_obj;
              }
            }

          break;
        case 'yearly':                        
            $dif =  date('Y',$date_to_timestamp)-date('Y',$start_date);

            if($dif>0)
            {             
              $events['start_date'] = strtotime('+' . $dif . ' year',$events['start_date']);                                                          
              $events['end_date'] = strtotime('+' . $dif . ' year',$events['end_date']);

              if(calendar::check_repeat_event_dif($dif,$events,$repeat_end))
              {
                $list[] = $events;
              }
            }                                                                                               
          break;
      }            

    }

    return $list;

  }

  static public function check_repeat_event_dif($dif,$events,$repeat_end)
  {
    $check = true;

    if($dif>0)
    {
      //check interval
      if($dif/$events['repeat_interval']!=floor($dif/$events['repeat_interval'])) $check=false;            

      //check repeat limit
      if($events['repeat_limit']>0)            
      if(floor($dif/$events['repeat_interval'])>$events['repeat_limit']) $check=false;
    }
    else
    {
      $check=false; 
    } 

    //check repeat end date            
    if($repeat_end>0)
    {
      if($repeat_end<$events['start_date'])
      {
         $check=false;
      }
    }

    return $check;
  }  

}

function get_events get all events + recurring events in my way there are 4 type of recurring events: daily, weekly, monthly,yearly + there is repeat interval, repeat date end and repeat limit.

function get_repeat_events calculate difference for each type of event and include repeat event if exist.

note: function db_query() can be replace to myslq_query or something else

to prepare events to FullCalendar I'm using next code

$list = array();        

  foreach(calendar::get_events($_GET['start'],$_GET['end'],'personal') as $events)
  {
    $start = date('Y-m-d H:i',$events['start_date']);
    $end = date('Y-m-d H:i',$events['end_date']);


    $list[] = array('id' => $events['id'],
                  'title' => addslashes($events['name']),
                  'description' => str_replace(array("\n\r","\n","\r"),'<br>',$events['description']),
                  'start' => str_replace(' 00:00','',$start),
                  'end' => str_replace(' 00:00','',$end),
                  'color'=> $events['bg_color'],                      
                  'allDay'=>(strstr($start,'00:00') and strstr($end,'00:00')),
                  'url' => url_for('ext/calendar/personal_form','id=' . $events['id'])                      
                  );      
  }

  echo json_encode($list);
2

This seemed to work quite nicely within the eventRender: function(event, element){}

            EXAMPLE JSON:
            var json = [{title: "All Day Event",
              start: "2015-12-22T00:00",
              end: "2015-12-22T23:55",
              dow: [2,4],
              recurstart: moment("2015-12-22").startOf("week"),
              recurend: moment("2015-12-22").endOf("week").add(1,'w')},{
              title: "Long Event",
              start: "2015-12-21T00:00",
              end: "2015-12-24T23:55",
              recurstart: moment("2015-12-21").startOf("month"),
              recurend: moment("2015-12-24").endOf("month"),
            }];

            eventRender: function(event, element){
            var theDate = moment(event.start).format("YYYY-MM-DD");
            var startDate = event.recurstart;
            var endDate = event.recurend;

            if (startDate < theDate && theDate < endDate) {
                console.log(theDate);
                }
            else {
                return event.length>0;
            }
            }, /* End eventRender */

1) Set a Start/End date & time in the JSON.

2) Create two custom recur Start and recur Ends in the JSON.

3) Use moment.js to create the recur durations: http://momentjs.com/docs/#/durations/.

4) Recur Start uses the (start:) date to pinpoint the start of the week.

5) Recur End uses (end:) date to pinpoint the end of the week + adding 1 week.

6) Adding 1, 2, 3 weeks can create the recur limit.

7) Adding another part of the JSON called (recurlimit:"") could manage the recur limit.

8) Using variables within the eventRender - set the date my example uses (theDate) which is moment(event.start). It's important to format this correctly so that the start/ end/ recurstart etc all match formats i.e (YYYY-MM-DD) http://momentjs.com/docs/#/displaying/format/.

9) Variable for the custom recurstart

10) Variable for the custom recurend

11) Use an IF statement to see weather the (theDate) falls between (recurstart) & (recurend) - log result

12) Use ELSE statement to return the length>0 to hide other events that don't fall within that parameter.

13) Non recurring events must have moment("match start date").startOf("month") & moment("match start date").endOf("month") otherwise they won't be visible.

3
  • 1
    Answers are better if they include an explanation as well as code. Commented Dec 18, 2015 at 12:56
  • Apologies its my first stackoverflow post. I have added a little more info to hopefully make it a little clearer.
    – tohood87
    Commented Dec 18, 2015 at 13:31
  • recuring not working for me...plz help me and simple dow: [ 1, 4 ] also not working. Commented Jun 9, 2016 at 10:46
1

To handle complex recurring rules, we should use iCal's rrule implementation libraries. We have JS library (rrule). FullCalendar now also supports rrule plugin.

1

You can use below code to show one day, daily, weekly, every two weeks and monthly events with tooltip on mouse hover event.

/*This is js*/

document.addEventListener('DOMContentLoaded', function() {
  var calendarEl = document.getElementById('calendar');

  var calendar = new FullCalendar.Calendar(calendarEl, {
    
    //NOTE MUST HAVE REFERENCE rrule javascript and plugin 'rrule', not rrPlugin like docs
    //if using scripts, not import/build method
    defaultDate: "2021-08-25",
    plugins: ['interaction', 'dayGrid', 'timeGrid', 'rrule'],
   timeZone: 'UTC',
   defaultView: 'dayGridMonth',
   header: {
     left: 'prev,next today',
     center: 'title',
     right: 'dayGridMonth,timeGridWeek,timeGridDay'
   },
   editable: true,
    eventClick: function(arg) {
      console.log(arg);
      if (confirm('Are you sure you want to delete this event?')) {
        arg.event.remove()
      }
    },
    eventMouseEnter: function(info) {
            var tis=info.el;
            var popup=info.event.extendedProps.popup;
            var tooltip = '<div class="tooltipevent" style="top:'+($(tis).offset().top-5)+'px;left:'+($(tis).offset().left+($(tis).width())/2)+'px"><div>' + popup.title + '</div><div>' + popup.description + '</div></div>';
            var $tooltip = $(tooltip).appendTo('body');
        },
        eventMouseLeave: function(info) {           
            $(info.el).css('z-index', 8);
            $('.tooltipevent').remove();
        },
    events: [    
   { 
     title: 'One Day',
      popup: {
          title: 'One Day',
          description: 'This is the description',
      }, 
     backgroundColor: '#c1391c',
     rrule: {        
        dtstart: '2021-08-01T10:30:00',
        until: '2021-08-01T19:30:00',        
        },          
    },    
    {     
     title: 'Daily',
     popup: {
          title: 'Daily',
          description: 'This is Daily the description',
      },       
     backgroundColor: '#bcc11c',
     rrule: {        
        freq: 'daily',
        dtstart: '2021-08-02T10:30:00',
        until: '2021-08-05T19:30:00',        
        },          
    }, 
    {     
     title: 'Weekly Event',
     popup: {
          title: 'Daily',
          description: 'This is Daily the description',
      },             
     backgroundColor: '#1cc1ab',
     rrule: {        
        freq: 'weekly',
        dtstart: '2021-08-06T10:30:00',
        until: '2021-08-20T19:30:00',        
        },          
    },    
    {     
     title: 'Two Weekly Event',
     popup: {
          title: 'Daily',
          description: 'This is Daily the description',
      },             
     backgroundColor: '#1c60c1',
     rrule: {        
        interval: 2,
        freq: 'weekly',
        dtstart: '2021-08-07T10:30:00',
        until: '2021-08-30T19:30:00',        
        },          
    },
    {     
     title: 'Four Weekl Event',
     popup: {
          title: 'Daily',
          description: 'This is Daily the description',
      },             
     backgroundColor: '#c11cbc',
     rrule: {        
        interval: 4,
        freq: 'weekly',
        dtstart: '2021-08-01T10:30:00',
        until: '2021-12-30T19:30:00',        
        },          
    },       
    ]
   
  });
  
  
  calendar.render();
});
html, body {
  margin: 0;
  padding: 0;
  font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
  font-size: 14px;
}

#calendar {
  max-width: 900px;
  margin: 40px auto;
}
.tooltipevent{
    width:200px;/*
    height:100px;*/
    background:#ccc;
    position:absolute;
    z-index:10001;
    transform:translate3d(-50%,-100%,0);
    font-size: 0.8rem;
    box-shadow: 1px 1px 3px 0px #888888;
    line-height: 1rem;
}
.tooltipevent div{
    padding:10px;
}
.tooltipevent div:first-child{
    font-weight:bold;
    color:White;
    background-color:#888888;
    border:solid 1px black;
}
.tooltipevent div:last-child{
    background-color:whitesmoke;
    position:relative;
}
.tooltipevent div:last-child::after, .tooltipevent div:last-child::before{
    width:0;
    height:0;
    border:solid 5px transparent;/*
    box-shadow: 1px 1px 2px 0px #888888;*/
    border-bottom:0;
    border-top-color:whitesmoke;
    position: absolute;
    display: block;
    content: "";
    bottom:-4px;
    left:50%;
    transform:translateX(-50%);
}
.tooltipevent div:last-child::before{
    border-top-color:#888888;
    bottom:-5px;
}
<!-- Required js and css with url -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/es5/rrule.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@fullcalendar/[email protected]/main.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@fullcalendar/[email protected]/main.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@fullcalendar/[email protected]/main.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@fullcalendar/[email protected]/main.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@fullcalendar/[email protected]/main.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/tooltip.js/dist/umd/tooltip.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/popper.js/dist/umd/popper.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>

<link rel="stylesheet" type="text/css" href="https://unpkg.com/@fullcalendar/[email protected]/main.min.css">
<link rel="stylesheet" type="text/css" href="https://unpkg.com/@fullcalendar/[email protected]/main.min.css">
<link rel="stylesheet" type="text/css" href="https://unpkg.com/@fullcalendar/[email protected]/main.min.css">
<div id='calendar'></div>

2
  • Nitesh, I am keep getting the following error when trying to use your snippet: Uncaught TypeError: Cannot read property 'length' of undefined Any ideas? Do you have a codepen of this working by chance? This is definitely what I am looking to do and any advice you have would be most helpful. Commented Aug 16, 2021 at 2:25
  • 1
    Hello @Jordan_Walters , You can define length in event click function like below code: arg.length = '10'; I hope it will be helpful. Commented Aug 17, 2021 at 12:40
-2

There is now a plugin that will do what you want: http://momentjs.com/docs/#/plugins/recur/

1
  • it's not clear how this would be integrated with fullCalendar in order to do with the question asks. On its own it doesn't create a format whereby recurrence can be specified to the calendar, nor does it provide any code by which to manipulate the events feed in order to create recurrence. And just providing a link and no further info is a poor answer. Links can go stale.
    – ADyson
    Commented Sep 7, 2017 at 12:58

Not the answer you're looking for? Browse other questions tagged or ask your own question.