Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 111 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
CargoRecurringEvent | |
0.00% |
0 / 111 |
|
0.00% |
0 / 2 |
1892 | |
0.00% |
0 / 1 |
getJD | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
run | |
0.00% |
0 / 110 |
|
0.00% |
0 / 1 |
1806 |
1 | <?php |
2 | /** |
3 | * Class for the #recurring_event function. |
4 | * |
5 | * @author Yaron Koren |
6 | * @ingroup Cargo |
7 | */ |
8 | |
9 | class CargoRecurringEvent { |
10 | |
11 | /** |
12 | * Gets the "Julian calendar days" of the specified date. |
13 | * |
14 | * @param string[] $date |
15 | * @return int |
16 | */ |
17 | public static function getJD( $date ) { |
18 | return gregorianToJD( $date['month'], $date['day'], $date['year'] ); |
19 | } |
20 | |
21 | /** |
22 | * Handles the #recurring_event parser function - prints out a |
23 | * string that is a delimited list of recurring events. |
24 | * |
25 | * @param Parser $parser Unused |
26 | * @return string |
27 | */ |
28 | public static function run( $parser ) { |
29 | global $wgCargoRecurringEventMaxInstances; |
30 | |
31 | // Code based in large part on the code for Semantic |
32 | // MediaWiki's #set_recurring_event function. |
33 | $params = func_get_args(); |
34 | array_shift( $params ); // we already know the $parser... |
35 | |
36 | $allDateStrings = []; |
37 | $startDate = $endDate = $unit = $period = $weekNum = null; |
38 | $timeString = null; |
39 | $delimiter = '; '; // default |
40 | $includedDates = []; |
41 | $excludedDatesJD = []; |
42 | |
43 | foreach ( $params as $param ) { |
44 | $parts = explode( '=', $param, 2 ); |
45 | |
46 | if ( count( $parts ) != 2 ) { |
47 | continue; |
48 | } |
49 | $key = trim( $parts[0] ); |
50 | $value = trim( $parts[1] ); |
51 | |
52 | if ( $key == 'start' ) { |
53 | $startDate = date_parse( $value ); |
54 | // We can assume that the time of day for |
55 | // all automatically-generated dates will |
56 | // be the same as that of the start date (if |
57 | // it was set at all). |
58 | $curHour = $startDate['hour']; |
59 | $curMinute = $startDate['minute']; |
60 | if ( $curHour !== false && $curMinute !== false ) { |
61 | $timeString = ' ' . str_pad( $curHour, 2, '0', STR_PAD_LEFT ) . ':' |
62 | . str_pad( $curMinute, 2, '0', STR_PAD_LEFT ); |
63 | } |
64 | } elseif ( $key == 'end' ) { |
65 | $endDate = date_parse( $value ); |
66 | } elseif ( $key == 'unit' ) { |
67 | $unit = $value; |
68 | } elseif ( $key == 'period' ) { |
69 | $period = $value; |
70 | } elseif ( $key == 'week number' ) { |
71 | $weekNum = $value; |
72 | } elseif ( $key == 'include' ) { |
73 | $includedDates = explode( ';', $value ); |
74 | } elseif ( $key == 'exclude' ) { |
75 | $excludedDates = explode( ';', $value ); |
76 | foreach ( $excludedDates as $dateStr ) { |
77 | $excludedDatesJD[] = self::getJD( date_parse( $dateStr ) ); |
78 | } |
79 | } elseif ( $key == 'delimiter' ) { |
80 | $delimiter = $value; |
81 | } |
82 | } |
83 | |
84 | if ( $startDate === null ) { |
85 | return CargoUtils::formatError( 'Start date must be specified.' ); |
86 | } |
87 | if ( $unit === null ) { |
88 | return CargoUtils::formatError( 'Unit must be specified.' ); |
89 | } |
90 | |
91 | // If the period is null, or outside of normal bounds, |
92 | // set it to 1. |
93 | if ( $period === null ) { |
94 | $period = 1; |
95 | } |
96 | |
97 | // Handle 'week number', but only if it's of unit 'month'. |
98 | if ( $unit == 'month' && $weekNum !== null ) { |
99 | $unit = 'dayofweekinmonth'; |
100 | if ( $weekNum < -4 || $weekNum > 5 || $weekNum == 0 ) { |
101 | $weekNum = null; |
102 | } |
103 | } |
104 | |
105 | if ( $unit == 'dayofweekinmonth' && $weekNum === null ) { |
106 | $weekNum = ceil( $startDate['day'] / 7 ); |
107 | } |
108 | |
109 | // Get the Julian day value for both the start and |
110 | // end date. |
111 | $endDateJD = self::getJD( $endDate ); |
112 | $curDate = $startDate; |
113 | $curDateJD = self::getJD( $curDate ); |
114 | |
115 | $instanceNum = 0; |
116 | do { |
117 | $instanceNum++; |
118 | $excludeDate = ( in_array( $curDateJD, $excludedDatesJD ) ); |
119 | if ( !$excludeDate ) { |
120 | $dateStr = $curDate['year'] . '-' . $curDate['month'] . '-' . $curDate['day'] . $timeString; |
121 | $allDateStrings[] = $dateStr; |
122 | } |
123 | |
124 | // Now get the next date. |
125 | // Handling is different depending on whether it's |
126 | // month/year or week/day since the latter is a set |
127 | // number of days while the former isn't. |
128 | if ( $unit === 'year' || $unit == 'month' ) { |
129 | $curYear = $curDate['year']; |
130 | $curMonth = $curDate['month']; |
131 | $curDay = $startDate['day']; |
132 | |
133 | if ( $unit == 'year' ) { |
134 | $curYear += $period; |
135 | $displayMonth = $curMonth; |
136 | } else { // $unit === 'month' |
137 | $curMonth += $period; |
138 | $curYear += (int)( ( $curMonth - 1 ) / 12 ); |
139 | $curMonth %= 12; |
140 | $displayMonth = ( $curMonth == 0 ) ? 12 : $curMonth; |
141 | } |
142 | |
143 | // If the date is February 29, and this isn't |
144 | // a leap year, change it to February 28. |
145 | if ( $curMonth == 2 && $curDay == 29 ) { |
146 | if ( !date( 'L', strtotime( "$curYear-1-1" ) ) ) { |
147 | $curDay = 28; |
148 | } |
149 | } |
150 | |
151 | $dateStr = "$curYear-$displayMonth-$curDay" . $timeString; |
152 | $curDate = date_parse( $dateStr ); |
153 | $allDateStrings = array_merge( $allDateStrings, $includedDates ); |
154 | $curDateJD = self::getJD( $curDate ); |
155 | } elseif ( $unit == 'dayofweekinmonth' ) { |
156 | // e.g., "3rd Monday of every month" |
157 | $prevMonth = $curDate['month']; |
158 | $prevYear = $curDate['year']; |
159 | |
160 | $newMonth = ( $prevMonth + $period ) % 12; |
161 | if ( $newMonth == 0 ) { |
162 | $newMonth = 12; |
163 | } |
164 | |
165 | $newYear = $prevYear + floor( ( $prevMonth + $period - 1 ) / 12 ); |
166 | $curDateJD += ( 28 * $period ) - 7; |
167 | |
168 | // We're sometime before the actual date now - |
169 | // keep incrementing by a week, until we get there. |
170 | do { |
171 | $curDateJD += 7; |
172 | $curDate = date_parse( JDToGregorian( $curDateJD ) ); |
173 | $rightMonth = ( $curDate['month'] == $newMonth ); |
174 | |
175 | if ( $weekNum < 0 ) { |
176 | $nextWeekJD = $curDateJD; |
177 | |
178 | do { |
179 | $nextWeekJD += 7; |
180 | // FIXME: This method doesn't exist! |
181 | $nextWeekDate = self::getJulianDayTimeValue( $nextWeekJD ); |
182 | $rightWeek = ( $nextWeekDate['month'] != $newMonth ) || |
183 | ( $nextWeekDate['year'] != $newYear ); |
184 | } while ( !$rightWeek ); |
185 | |
186 | $curDateJD = $nextWeekJD + ( 7 * $weekNum ); |
187 | // FIXME: This method doesn't exist! |
188 | $curDate = self::getJulianDayTimeValue( $curDateJD ); |
189 | } else { |
190 | $curWeekNum = ceil( $curDate['day'] / 7 ); |
191 | $rightWeek = ( $curWeekNum == $weekNum ); |
192 | |
193 | if ( $weekNum == 5 && ( $curDate['month'] % 12 == ( $newMonth + 1 ) % 12 ) ) { |
194 | $curDateJD -= 7; |
195 | // FIXME: This method doesn't exist! |
196 | $curDate = self::getJulianDayTimeValue( $curDateJD ); |
197 | $rightMonth = $rightWeek = true; |
198 | } |
199 | } |
200 | } while ( !$rightMonth || !$rightWeek ); |
201 | } else { // $unit == 'day' or 'week' |
202 | // Assume 'day' if it's none of the above. |
203 | $curDateJD += ( $unit === 'week' ) ? 7 * $period : $period; |
204 | $curDate = date_parse( JDToGregorian( $curDateJD ) ); |
205 | } |
206 | |
207 | // Should we stop? |
208 | $reachedEndDate = ( $instanceNum > $wgCargoRecurringEventMaxInstances || |
209 | ( $endDate !== null && ( $curDateJD > $endDateJD ) ) ); |
210 | } while ( !$reachedEndDate ); |
211 | |
212 | // Add in the 'include' dates as well. |
213 | $allDateStrings = array_filter( array_merge( $allDateStrings, $includedDates ) ); |
214 | |
215 | return implode( $delimiter, $allDateStrings ); |
216 | } |
217 | |
218 | } |