(01.02.2014, 16:11)Raphael schrieb: So Leute... nachdem es mich auch gestört hat, das speziell bei Events / Terminen, die als sich wiederholend angegeben wurden, das Plugin bisher nicht die Ausgabe entsprechend optimal gehandhabt hat... habe ich einige Stunden mit Timestamp-Mathematik zugebracht und die Funktion für die Ausgabe der Termine überarbeitet.
Der Code ist nicht zu 100% optimiert, ich habe aber auf eine möglichst gute Performance geachtet.
Bestimmte Bugs, die mir in dem Zusammenhang mit dem MyBB Kalender aufgefallen sind, habe ich -nicht- dubliziert.
Bug-Beispiel in MyBB Core Calendar:
Event Beginnend: 01.02.2014
Event Endet: 01.02.2016
Wiederholung: Monatlich
am letzten Dienstag jedes 2 Monats
--> Eintrag erscheint u.a für den 1.Juli 2014
Hier die überarbeitete overview_next_events() Funktion + eine Helper-Funktion für spätere Sortierung.
PHP-Code:
// Next events function overview_next_events() { global $mybb, $settings, $db, $templates, $theme, $lang, $trow;
// TODO: Instead of substracting 24 hours, align to the users timezone boundary. $today = TIME_NOW - 60*60*24;
// Decide whether we can include private events or not. if(intval($settings['overview_cache']) > 0) { $private = "e.private='0'"; }
else { $private = "(e.private='0' OR e.uid='".intval($mybb->user['uid'])."')"; }
// Fetch data; Raphael: we need more from the db for repeated events $query = $db->query("SELECT e.eid, e.name, e.starttime, e.endtime, e.uid, e.repeats, u.username, u.usergroup, u.displaygroup FROM ".TABLE_PREFIX."events e LEFT JOIN ".TABLE_PREFIX."users u ON (e.uid=u.uid) WHERE e.visible = '1' AND {$private} AND (e.starttime > '{$today}' OR e.endtime > '{$today}') {$cids} ORDER BY starttime ASC LIMIT 0,{$settings['overview_max']};");
/* NEW DATE CHECKING MECHANISM by Raphael */ $event_dates = array(); while( $events = $db->fetch_array($query) ){ $repeat_data = unserialize($events['repeats']);
//Check, if repeated event if( isset($repeat_data) ){ //Create timestamps of this events for the next starts
//Get the starttime for the loop, set to today, if in the past if( $events['starttime'] < TIME_NOW ) $starttime = mktime(0,0,0,date('m'),date('d'),date('Y')); else $starttime = strtotime( date("Y-m-d", $events['starttime']) ); //Truncate -> without time
//Optimized date-generation strategy based on repeat-type $elements_added = 0; //Control Variable for performance optimized breakpoint
//"Truncate" starttime; get only d.m.Y without time for easy checking $truncated_starttime = strtotime( date("Y-m-d", $events['starttime']) ); $time_of_starttime = $events['starttime'] - $truncated_starttime;
//Daily repeated event on x days case "1": if( !isset($repeat_data['days']) ) $days = 1; else $days = $repeat_data['days'];
//Divider for Rule $divider = $days * (60*60*24);
for( $i = $starttime; $i <= $events['endtime']; $i = $i + (60*60*24) ){ //Check, if current timestamp meet requirements of repeatment rule //Rule = day_difference is multiple of $days if( ( $i - $truncated_starttime ) % ($divider) == 0){
//Add Event to list with time for exact sorting $event_date_time = $i + $time_of_starttime; array_push($event_dates, array( $event_date_time => $events ) ); $elements_added++;
//Weekly repeated event on x weeks per y days case "3": $days = $repeat_data['days']; //Array date("w") $weeks = $repeat_data['weeks'];
//Multipler for Rule $multipler = (60*60*24*7); $kw_startevent = date("W", $truncated_starttime);
//If event_startime is not in $days, but in future, add it if( !in_array( date("w", $truncated_starttime), $days) && $truncated_starttime >= $starttime ){ //Add Event to list with time for exact sorting array_push($event_dates, array( $events['starttime'] => $events ) ); $elements_added++; }
//Second loop: Days for( $i = $weekbeginning; $i <= $weekend; $i = $i + (60*60*24) ){ //Check, if current timestamp meet requirements of repeatment rule //Rule = day is in $days if( in_array( date("w", $i) , $days) ){
//Add Event to list with time for exact sorting $event_date_time = $i + $time_of_starttime; array_push($event_dates, array( $event_date_time => $events ) ); $elements_added++;
if( $elements_added == $settings['overview_max']){ break;//break day loop, if enough dates } }
}
if( $elements_added == $settings['overview_max']){ break;//break weekloop, if enough dates }
//Option 1 - Repeat on day x of month y if( isset($repeat_data['day']) ){ //Go through monthly for( $i = $starttime; $i <= $events['endtime']; $i = mktime(0,0,0,date("m",$i)+$month,1,date("Y",$i)) ){ //Check, if current timestamp meet requirements of repeatment rule //Rule = each day x of month y $event_date_time = mktime(0,0,0,date("m",$i) - $correction, $day, date("Y",$i), false) + $time_of_starttime;
//Correction for bigger starttime in month than first date-day of beginning if( $truncated_starttime > $event_date_time ) $correction = $month - 1;
if( $event_date_time >= $starttime && $event_date_time < $events['endtime'] ){ //Add Event to list with time for exact sorting array_push($event_dates, array( $event_date_time => $events ) ); $elements_added++;
if( $elements_added == $settings['overview_max']){ break;//break loop, if enough dates } } } } else { //Option 2 - Repeat on x-th y-day of z months //Go through monthly for( $i = $starttime; $i <= $events['endtime']; $i = mktime(0,0,0,date("m",$i)+$month,1,date("Y",$i)) ){ //Check, if current timestamp meet requirements of repeatment rule //Rule = each day x of month y
//Calc day of month if( $occurance != "last" ){ $first_weekday_of_month = date("N", mktime(0,0,0,date("m",$i) - $correction,1,date("Y",$i)) ); $day_part = 7 - $first_weekday_of_month + $weekday; if( $day_part >= 7 ) $day_part -= 7; $day = $day_part + (($occurance - 1) *7) + 1; } else { //MyBB "last of month" Core Calendar has bugs. (E.G. last of month = first of next month); We do it the right way. $temp_occurance = 5; $first_weekday_of_month = date("N", mktime(0,0,0,date("m",$i) - $correction,1,date("Y",$i)) ); $last_weekday_of_month_date = date("d", mktime(0,0,0,date("m",$i)+1 - $correction,0,date("Y",$i)) );
//Yearly repeated event on x date of y years case "5": $year = $repeat_data['years']; $month = $repeat_data['month']; $day = $repeat_data['day']; $occurance = $repeat_data['occurance']; $weekday = $repeat_data['weekday']; $correction = 0;
//Go through yearly //Option 1 repeat on day x of month y all z years if( !isset($repeat_data['occurance']) ){ for( $i = $starttime; $i <= $events['endtime']; $i = mktime(0,0,0,date("m",$i),1,date("Y",$i)+$year) ){ //Check, if current timestamp meet requirements of repeatment rule //Rule = each day x of month y in year z $event_date_time = mktime(0,0,0,$month, $day, date("Y",$i) - $correction, false) + $time_of_starttime;
//Correction for bigger starttime in month than first date-day of beginning if( $truncated_starttime > $event_date_time ) $correction = $year - 1;
if( $event_date_time >= $starttime && $event_date_time < $events['endtime'] ){ //Add Event to list with time for exact sorting array_push($event_dates, array( $event_date_time => $events ) ); $elements_added++;
if( $elements_added == $settings['overview_max']){ break;//break loop, if enough dates } } } } else { //Option 2: repeat on a-th occurance of b-day each c-month every d years //Go through yearly for( $i = $starttime; $i <= $events['endtime']; $i = mktime(0,0,0,$month,1,date("Y",$i)+$year) ){ //Check, if current timestamp meet requirements of repeatment rule //Rule
//Calc day of month if( $occurance != "last" ){ $first_weekday_of_month = date("N", mktime(0,0,0,$month,1,date("Y",$i)- $correction) ); $day_part = 7 - $first_weekday_of_month + $weekday; if( $day_part >= 7 ) $day_part -= 7; $day = $day_part + (($occurance - 1) *7) + 1; } else { //MyBB "last of month" Core Calendar has bugs. (E.G. last of month = first of next month); We do it the right way. $temp_occurance = 5; $first_weekday_of_month = date("N", mktime(0,0,0,$month,1,date("Y",$i) - $correction) ); $last_weekday_of_month_date = date("d", mktime(0,0,0,$month+1,0,date("Y",$i) - $correction) );