Completed
Pull Request — master (#10)
by Jonathan
01:51
created

Shim::jdToUnix()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 2
nop 1
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace Fisharebest\ExtCalendar;
3
4
use InvalidArgumentException;
5
6
/**
7
 * class Shim - PHP implementations of functions from the PHP calendar extension.
8
 *
9
 * @link      http://php.net/manual/en/book.calendar.php
10
 *
11
 * @author    Greg Roach <[email protected]>
12
 * @copyright (c) 2014-2017 Greg Roach
13
 * @license   This program is free software: you can redistribute it and/or modify
14
 *            it under the terms of the GNU General Public License as published by
15
 *            the Free Software Foundation, either version 3 of the License, or
16
 *            (at your option) any later version.
17
 *
18
 *            This program is distributed in the hope that it will be useful,
19
 *            but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 *            MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 *            GNU General Public License for more details.
22
 *
23
 *            You should have received a copy of the GNU General Public License
24
 *            along with this program.  If not, see <http://www.gnu.org/licenses/>.
25
 */
26
class Shim {
0 ignored issues
show
Coding Style introduced by
Shim does not seem to conform to the naming convention (Utils?$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
27
	/** @var FrenchCalendar */
28
	private static $french_calendar;
29
30
	/** @var GregorianCalendar */
31
	private static $gregorian_calendar;
32
33
	/** @var JewishCalendar */
34
	private static $jewish_calendar;
35
36
	/** @var JulianCalendar */
37
	private static $julian_calendar;
38
39
	/**
40
	 * English names for the days of the week.
41
	 *
42
	 * @var string[]
43
	 */
44
	private static $DAY_NAMES = array(
45
		'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday',
46
	);
47
48
	/**
49
	 * Abbreviated English names for the days of the week.
50
	 *
51
	 * @var string[]
52
	 */
53
	private static $DAY_NAMES_SHORT = array(
54
		'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat',
55
	);
56
57
	/** @var string[] Names of the months of the Gregorian/Julian calendars */
58
	private static $MONTH_NAMES = array(
59
		'', 'January', 'February', 'March', 'April', 'May', 'June',
60
		'July', 'August', 'September', 'October', 'November', 'December',
61
	);
62
63
	/** @var string[] Abbreviated names of the months of the Gregorian/Julian calendars */
64
	private static $MONTH_NAMES_SHORT = array(
65
		'', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
66
	);
67
68
	/** @var string[] Name of the months of the French calendar */
69
	private static $MONTH_NAMES_FRENCH = array(
70
		'', 'Vendemiaire', 'Brumaire', 'Frimaire', 'Nivose', 'Pluviose', 'Ventose',
71
		'Germinal', 'Floreal', 'Prairial', 'Messidor', 'Thermidor', 'Fructidor', 'Extra'
72
	);
73
74
	/** @var string[] Names of the months of the Jewish calendar in a non-leap year */
75
	private static $MONTH_NAMES_JEWISH = array(
76
		'', 'Tishri', 'Heshvan', 'Kislev', 'Tevet', 'Shevat', 'Adar',
77
		'Adar', 'Nisan', 'Iyyar', 'Sivan', 'Tammuz', 'Av', 'Elul',
78
	);
79
80
	/** @var string[] Names of the months of the Jewish calendar in a leap year */
81
	private static $MONTH_NAMES_JEWISH_LEAP_YEAR = array(
82
		'', 'Tishri', 'Heshvan', 'Kislev', 'Tevet', 'Shevat', 'Adar I',
83
		'Adar II', 'Nisan', 'Iyyar', 'Sivan', 'Tammuz', 'Av', 'Elul',
84
	);
85
86
	/** @var string[] Names of the months of the Jewish calendar (before PHP bug 54254 was fixed) */
87
	private static $MONTH_NAMES_JEWISH_54254 = array(
88
		'', 'Tishri', 'Heshvan', 'Kislev', 'Tevet', 'Shevat', 'AdarI',
89
		'AdarII', 'Nisan', 'Iyyar', 'Sivan', 'Tammuz', 'Av', 'Elul',
90
	);
91
92
	/**
93
	 * Create the necessary shims to emulate the ext/calendar package.
94
	 *
95
	 * @return void
96
	 */
97
	public static function create() {
98
		self::$french_calendar    = new FrenchCalendar;
99
		self::$gregorian_calendar = new GregorianCalendar;
100
		self::$jewish_calendar    = new JewishCalendar(array(
101
			JewishCalendar::EMULATE_BUG_54254 => self::shouldEmulateBug54254(),
102
		));
103
		self::$julian_calendar    = new JulianCalendar;
104
	}
105
106
	/**
107
	 * Do we need to emulate PHP bug #54254?
108
	 *
109
	 * This bug relates to the names used for months 6 and 7 in the Jewish calendar.
110
	 *
111
	 * It was fixed in PHP 5.5.0
112
	 *
113
	 * @link https://bugs.php.net/bug.php?id=54254
114
	 *
115
	 * @return boolean
116
	 */
117
	public static function shouldEmulateBug54254() {
118
		return version_compare(PHP_VERSION, '5.5.0', '<');
119
	}
120
121
	/**
122
	 * Do we need to emulate PHP bug #67960?
123
	 *
124
	 * This bug relates to the constants CAL_DOW_SHORT and CAL_DOW_LONG.
125
	 *
126
	 * It was fixed in PHP 5.6.5 and 5.5.21
127
	 *
128
	 * @link https://bugs.php.net/bug.php?id=67960
129
	 * @link https://github.com/php/php-src/pull/806
130
	 *
131
	 * @return boolean
132
	 */
133
	public static function shouldEmulateBug67960() {
134
		return version_compare(PHP_VERSION, '5.5.21', '<') || version_compare(PHP_VERSION, '5.6.0', '>=') && version_compare(PHP_VERSION, '5.6.5', '<') ;
135
	}
136
137
	/**
138
	 * Do we need to emulate PHP bug #67976?
139
	 *
140
	 * This bug relates to the number of days in the month 13 of year 14 in
141
	 * the French calendar.
142
	 *
143
	 * @link https://bugs.php.net/bug.php?id=67976
144
	 *
145
	 * @return boolean
146
	 */
147
	public static function shouldEmulateBug67976() {
148
		return true;
149
	}
150
151
	/**
152
	 * Return the number of days in a month for a given year and calendar.
153
	 *
154
	 * Shim implementation of cal_days_in_month()
155
	 *
156
	 * @link https://php.net/cal_days_in_month
157
	 * @link https://bugs.php.net/bug.php?id=67976
158
	 *
159
	 * @param integer $calendar_id
160
	 * @param integer $month
161
	 * @param integer $year
162
	 *
163
	 * @return integer|boolean The number of days in the specified month, or false on error
164
	 */
165
	public static function calDaysInMonth($calendar_id, $month, $year) {
166
		switch ($calendar_id) {
167
		case CAL_FRENCH:
168
			return self::calDaysInMonthFrench($year, $month);
169
170
		case CAL_GREGORIAN:
171
			return self::calDaysInMonthCalendar(self::$gregorian_calendar, $year, $month);
172
173
		case CAL_JEWISH:
174
			return self::calDaysInMonthCalendar(self::$jewish_calendar, $year, $month);
175
176
		case CAL_JULIAN:
177
			return self::calDaysInMonthCalendar(self::$julian_calendar, $year, $month);
178
179
		default:
180
			return trigger_error('invalid calendar ID ' . $calendar_id, E_USER_WARNING);
181
		}
182
	}
183
184
	/**
185
	 * Calculate the number of days in a month in a specified (Gregorian or Julian) calendar.
186
	 *
187
	 * @param CalendarInterface $calendar
188
	 * @param integer           $year
189
	 * @param integer           $month
190
	 *
191
	 * @return integer|boolean
192
	 */
193
	private static function calDaysInMonthCalendar(CalendarInterface $calendar, $year, $month) {
194
		try {
195
			return $calendar->daysInMonth($year, $month);
196
		} catch (InvalidArgumentException $ex) {
197
			return trigger_error('invalid date.', E_USER_WARNING);
198
		}
199
	}
200
201
	/**
202
	 * Calculate the number of days in a month in the French calendar.
203
	 *
204
	 * Mimic PHP’s validation of the parameters
205
	 *
206
	 * @param integer $year
207
	 * @param integer $month
208
	 *
209
	 * @return integer|boolean
210
	 */
211
	private static function calDaysInMonthFrench($year, $month) {
212
		if ($month == 13 && $year == 14 && self::shouldEmulateBug67976()) {
213
			return -2380948;
214
		} elseif ($year > 14) {
215
			return trigger_error('invalid date.', E_USER_WARNING);
216
		} else {
217
			return self::calDaysInMonthCalendar(self::$french_calendar, $year, $month);
218
		}
219
	}
220
221
	/**
222
	 * Converts from Julian Day Count to a supported calendar.
223
	 *
224
	 * Shim implementation of cal_from_jd()
225
	 *
226
	 * @link https://php.net/cal_from_jd
227
	 *
228
	 * @param integer $julian_day  Julian Day number
229
	 * @param integer $calendar_id Calendar constant
230
	 *
231
	 * @return array|boolean
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string|integer>|boolean.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
232
	 */
233
	public static function calFromJd($julian_day, $calendar_id) {
234
		switch ($calendar_id) {
235
		case CAL_FRENCH:
236
			return self::calFromJdCalendar($julian_day, self::jdToFrench($julian_day), self::$MONTH_NAMES_FRENCH, self::$MONTH_NAMES_FRENCH);
237
238
		case CAL_GREGORIAN:
239
			return self::calFromJdCalendar($julian_day, self::jdToGregorian($julian_day), self::$MONTH_NAMES, self::$MONTH_NAMES_SHORT);
240
241
		case CAL_JEWISH:
242
			$months = self::jdMonthNameJewishMonths($julian_day);
243
244
			return self::calFromJdCalendar($julian_day, self::jdToCalendar(self::$jewish_calendar, $julian_day, 347998, 324542846), $months, $months);
245
246
		case CAL_JULIAN:
247
			return self::calFromJdCalendar($julian_day, self::jdToJulian($julian_day), self::$MONTH_NAMES, self::$MONTH_NAMES_SHORT);
248
249
		default:
250
			return trigger_error('invalid calendar ID ' . $calendar_id, E_USER_WARNING);
251
		}
252
	}
253
254
	/**
255
	 * Convert a Julian day number to a calendar and provide details.
256
	 *
257
	 * @param integer  $julian_day
258
	 * @param string   $mdy
259
	 * @param string[] $months
260
	 * @param string[] $months_short
261
	 *
262
	 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string|integer>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
263
	 */
264
	private static function calFromJdCalendar($julian_day, $mdy, $months, $months_short) {
265
		list($month, $day, $year) = explode('/', $mdy);
266
267
		return array(
268
			'date'          => $month . '/' . $day . '/' . $year,
269
			'month'         => (int) $month,
270
			'day'           => (int) $day,
271
			'year'          => (int) $year,
272
			'dow'           => self::jdDayOfWeek($julian_day, 0),
273
			'abbrevdayname' => self::jdDayOfWeek($julian_day, 2),
274
			'dayname'       => self::jdDayOfWeek($julian_day, 1),
275
			'abbrevmonth'   => $months_short[$month],
276
			'monthname'     => $months[$month],
277
		);
278
	}
279
280
	/**
281
	 * Returns information about a particular calendar.
282
	 *
283
	 * Shim implementation of cal_info()
284
	 *
285
	 * @link https://php.net/cal_info
286
	 *
287
	 * @param integer $calendar_id
288
	 *
289
	 * @return array|boolean
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string[]|in...olean>|boolean>|boolean.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
290
	 */
291
	public static function calInfo($calendar_id) {
292
		switch ($calendar_id) {
293
		case CAL_FRENCH:
294
			return self::calInfoCalendar(self::$MONTH_NAMES_FRENCH, self::$MONTH_NAMES_FRENCH, 30, 'French', 'CAL_FRENCH');
295
296
		case CAL_GREGORIAN:
297
			return self::calInfoCalendar(self::$MONTH_NAMES, self::$MONTH_NAMES_SHORT, 31, 'Gregorian', 'CAL_GREGORIAN');
298
299
		case CAL_JEWISH:
300
			$months = self::shouldEmulateBug54254() ? self::$MONTH_NAMES_JEWISH_54254 : self::$MONTH_NAMES_JEWISH_LEAP_YEAR;
301
302
			return self::calInfoCalendar($months, $months, 30, 'Jewish', 'CAL_JEWISH');
303
304
		case CAL_JULIAN:
305
			return self::calInfoCalendar(self::$MONTH_NAMES, self::$MONTH_NAMES_SHORT, 31, 'Julian', 'CAL_JULIAN');
306
307
		case -1:
308
			return array(
309
				CAL_GREGORIAN => self::calInfo(CAL_GREGORIAN),
310
				CAL_JULIAN    => self::calInfo(CAL_JULIAN),
311
				CAL_JEWISH    => self::calInfo(CAL_JEWISH),
312
				CAL_FRENCH    => self::calInfo(CAL_FRENCH),
313
			);
314
315
		default:
316
			return trigger_error('invalid calendar ID ' . $calendar_id, E_USER_WARNING);
317
		}
318
	}
319
320
	/**
321
	 * Returns information about the French calendar.
322
	 *
323
	 * @param string[] $month_names
324
	 * @param string[] $month_names_short
325
	 * @param integer  $max_days_in_month
326
	 * @param string   $calendar_name
327
	 * @param string   $calendar_symbol
328
	 *
329
	 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string[]|integer|string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
330
	 */
331
	private static function calInfoCalendar($month_names, $month_names_short, $max_days_in_month, $calendar_name, $calendar_symbol) {
332
		return array(
333
			'months'         => array_slice($month_names, 1, null, true),
334
			'abbrevmonths'   => array_slice($month_names_short, 1, null, true),
335
			'maxdaysinmonth' => $max_days_in_month,
336
			'calname'        => $calendar_name,
337
			'calsymbol'      => $calendar_symbol,
338
		);
339
	}
340
341
	/**
342
	 *  Converts from a supported calendar to Julian Day Count
343
	 *
344
	 * Shim implementation of cal_to_jd()
345
	 *
346
	 * @link https://php.net/cal_to_jd
347
	 *
348
	 * @param integer $calendar_id
349
	 * @param integer $month
350
	 * @param integer $day
351
	 * @param integer $year
352
	 *
353
	 * @return integer|boolean
354
	 */
355
	public static function calToJd($calendar_id, $month, $day, $year) {
356
		switch ($calendar_id) {
357
		case CAL_FRENCH:
358
			return self::frenchToJd($month, $day, $year);
359
360
		case CAL_GREGORIAN:
361
			return self::gregorianToJd($month, $day, $year);
362
363
		case CAL_JEWISH:
364
			return self::jewishToJd($month, $day, $year);
365
366
		case CAL_JULIAN:
367
			return self::julianToJd($month, $day, $year);
368
369
		default:
370
			return trigger_error('invalid calendar ID ' . $calendar_id . '.', E_USER_WARNING);
371
		}
372
	}
373
374
	/**
375
	 * Get Unix timestamp for midnight on Easter of a given year.
376
	 *
377
	 * Shim implementation of easter_date()
378
	 *
379
	 * @link https://php.net/easter_date
380
	 *
381
	 * @param integer $year
382
	 *
383
	 * @return integer|boolean
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|integer|double?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
384
	 */
385
	public static function easterDate($year) {
386
		if ($year < 1970 || $year > 2037) {
387
			return trigger_error('This function is only valid for years between 1970 and 2037 inclusive', E_USER_WARNING);
388
		}
389
390
		$days = self::$gregorian_calendar->easterDays($year);
391
392
		// Calculate time-zone offset
393
		$date_time      = new \DateTime('now', new \DateTimeZone(date_default_timezone_get()));
394
		$offset_seconds = (int) $date_time->format('Z');
395
396
		if ($days < 11) {
397
			return self::jdtounix(self::$gregorian_calendar->ymdToJd($year, 3, $days + 21)) - $offset_seconds;
398
		} else {
399
			return self::jdtounix(self::$gregorian_calendar->ymdToJd($year, 4, $days - 10)) - $offset_seconds;
400
		}
401
	}
402
403
	/**
404
	 * Get number of days after March 21 on which Easter falls for a given year.
405
	 *
406
	 * Shim implementation of easter_days()
407
	 *
408
	 * @link https://php.net/easter_days
409
	 *
410
	 * @param integer $year
411
	 * @param integer $method Use the Julian or Gregorian calendar
412
	 *
413
	 * @return integer
414
	 */
415
	public static function easterDays($year, $method) {
416
		if (
417
			$method == CAL_EASTER_ALWAYS_JULIAN ||
418
			$method == CAL_EASTER_ROMAN && $year <= 1582 ||
419
			$year <= 1752 && $method != CAL_EASTER_ROMAN && $method != CAL_EASTER_ALWAYS_GREGORIAN
420
		) {
421
			return self::$julian_calendar->easterDays($year);
422
		} else {
423
			return self::$gregorian_calendar->easterDays($year);
424
		}
425
	}
426
427
	/**
428
	 * Converts a date from the French Republican Calendar to a Julian Day Count.
429
	 *
430
	 * Shim implementation of FrenchToJD()
431
	 *
432
	 * @link https://php.net/FrenchToJD
433
	 *
434
	 * @param integer $month
435
	 * @param integer $day
436
	 * @param integer $year
437
	 *
438
	 * @return integer
439
	 */
440
	public static function frenchToJd($month, $day, $year) {
441
		if ($year <= 0) {
442
			return 0;
443
		} else {
444
			return self::$french_calendar->ymdToJd($year, $month, $day);
445
		}
446
	}
447
448
	/**
449
	 * Converts a Gregorian date to Julian Day Count.
450
	 *
451
	 * Shim implementation of GregorianToJD()
452
	 *
453
	 * @link https://php.net/GregorianToJD
454
	 *
455
	 * @param integer $month
456
	 * @param integer $day
457
	 * @param integer $year
458
	 *
459
	 * @return integer
460
	 */
461
	public static function gregorianToJd($month, $day, $year) {
462
		if ($year == 0) {
463
			return 0;
464
		} else {
465
			return self::$gregorian_calendar->ymdToJd($year, $month, $day);
466
		}
467
	}
468
469
	/**
470
	 * Returns the day of the week.
471
	 *
472
	 * Shim implementation of JDDayOfWeek()
473
	 *
474
	 * @link https://php.net/JDDayOfWeek
475
	 * @link https://bugs.php.net/bug.php?id=67960
476
	 *
477
	 * @param integer $julian_day
478
	 * @param integer $mode
479
	 *
480
	 * @return integer|string
481
	 */
482
	public static function jdDayOfWeek($julian_day, $mode) {
483
		$day_of_week = ($julian_day + 1) % 7;
484
		if ($day_of_week < 0) {
485
			$day_of_week += 7;
486
		}
487
488
		switch ($mode) {
489
		case 1: // 1, not CAL_DOW_LONG - see bug 67960
490
			return self::$DAY_NAMES[$day_of_week];
491
492
		case 2: // 2, not CAL_DOW_SHORT - see bug 67960
493
			return self::$DAY_NAMES_SHORT[$day_of_week];
494
495
		default: // CAL_DOW_DAYNO or anything else
496
			return $day_of_week;
497
		}
498
	}
499
500
	/**
501
	 * Returns a month name.
502
	 *
503
	 * Shim implementation of JDMonthName()
504
	 *
505
	 * @link https://php.net/JDMonthName
506
	 *
507
	 * @param integer $julian_day
508
	 * @param integer $mode
509
	 *
510
	 * @return string
511
	 */
512
	public static function jdMonthName($julian_day, $mode) {
513
		switch ($mode) {
514
		case CAL_MONTH_GREGORIAN_LONG:
515
			return self::jdMonthNameCalendar(self::$gregorian_calendar, $julian_day, self::$MONTH_NAMES);
516
517
		case CAL_MONTH_JULIAN_LONG:
518
			return self::jdMonthNameCalendar(self::$julian_calendar, $julian_day, self::$MONTH_NAMES);
519
520
		case CAL_MONTH_JULIAN_SHORT:
521
			return self::jdMonthNameCalendar(self::$julian_calendar, $julian_day, self::$MONTH_NAMES_SHORT);
522
523
		case CAL_MONTH_JEWISH:
524
			return self::jdMonthNameCalendar(self::$jewish_calendar, $julian_day, self::jdMonthNameJewishMonths($julian_day));
525
526
		case CAL_MONTH_FRENCH:
527
			return self::jdMonthNameCalendar(self::$french_calendar, $julian_day, self::$MONTH_NAMES_FRENCH);
528
529
		case CAL_MONTH_GREGORIAN_SHORT:
530
		default:
531
			return self::jdMonthNameCalendar(self::$gregorian_calendar, $julian_day, self::$MONTH_NAMES_SHORT);
532
		}
533
	}
534
535
	/**
536
	 * Calculate the month-name for a given julian day, in a given calendar,
537
	 * with given set of month names.
538
	 *
539
	 * @param CalendarInterface $calendar
540
	 * @param integer           $julian_day
541
	 * @param string[]          $months
542
	 *
543
	 * @return string
544
	 */
545
	private static function jdMonthNameCalendar(CalendarInterface $calendar, $julian_day, $months) {
546
		list(, $month) = $calendar->jdToYmd($julian_day);
547
548
		return $months[$month];
549
	}
550
551
	/**
552
	 * Determine which month names to use for the Jewish calendar.
553
	 *
554
	 * @param integer $julian_day
555
	 *
556
	 * @return string[]
557
	 */
558
	private static function jdMonthNameJewishMonths($julian_day) {
559
		list(, , $year) = explode('/', self::jdToCalendar(self::$jewish_calendar, $julian_day, 347998, 324542846));
560
561
		if (self::$jewish_calendar->isLeapYear($year)) {
562
			return self::shouldEmulateBug54254() ? self::$MONTH_NAMES_JEWISH_54254 : self::$MONTH_NAMES_JEWISH_LEAP_YEAR;
563
		} else {
564
			return self::shouldEmulateBug54254() ? self::$MONTH_NAMES_JEWISH_54254 : self::$MONTH_NAMES_JEWISH;
565
		}
566
	}
567
568
	/**
569
	 * Convert a Julian day in a specific calendar to a day/month/year.
570
	 *
571
	 * Julian days outside the specified range are returned as “0/0/0”.
572
	 *
573
	 * @param CalendarInterface $calendar
574
	 * @param integer           $julian_day
575
	 * @param integer           $min_jd
576
	 * @param integer           $max_jd
577
	 *
578
	 * @return string
579
	 */
580
	private static function jdToCalendar(CalendarInterface $calendar, $julian_day, $min_jd, $max_jd) {
581
		if ($julian_day >= $min_jd && $julian_day <= $max_jd) {
582
			list($year, $month, $day) = $calendar->jdToYmd($julian_day);
583
584
			return $month . '/' . $day . '/' . $year;
585
		} else {
586
			return '0/0/0';
587
		}
588
	}
589
590
	/**
591
	 * Converts a Julian Day Count to the French Republican Calendar.
592
	 *
593
	 * Shim implementation of JDToFrench()
594
	 *
595
	 * @link https://php.net/JDToFrench
596
	 *
597
	 * @param integer $julian_day A Julian Day number
598
	 *
599
	 * @return string A string of the form "month/day/year"
600
	 */
601
	public static function jdToFrench($julian_day) {
602
		// JDToFrench() converts years 1 to 14 inclusive, even though the calendar
603
		// officially ended on 10 Nivôse 14 (JD 2380687, 31st December 1805 Gregorian).
604
		return self::jdToCalendar(self::$french_calendar, $julian_day, 2375840, 2380952);
605
	}
606
607
	/**
608
	 * Converts Julian Day Count to Gregorian date.
609
	 *
610
	 * Shim implementation of JDToGregorian()
611
	 *
612
	 * @link https://php.net/JDToGregorian
613
	 *
614
	 * @param integer $julian_day A Julian Day number
615
	 *
616
	 * @return string A string of the form "month/day/year"
617
	 */
618
	public static function jdToGregorian($julian_day) {
619
		// PHP has different limits on 32 and 64 bit systems.
620
		$MAX_JD = PHP_INT_SIZE == 4 ? 536838866 : 2305843009213661906;
621
622
		return self::jdToCalendar(self::$gregorian_calendar, $julian_day, 1, $MAX_JD);
623
	}
624
625
	/**
626
	 * Converts a Julian day count to a Jewish calendar date.
627
	 *
628
	 * Shim implementation of JdtoJjewish()
629
	 *
630
	 * @link https://php.net/JdtoJewish
631
	 *
632
	 * @param integer $julian_day A Julian Day number
633
	 * @param boolean $hebrew     If true, the date is returned in Hebrew text
634
	 * @param integer $fl         If $hebrew is true, then add alafim and gereshayim to the text
635
	 *
636
	 * @return string|boolean A string of the form "month/day/year", or false on error
637
	 */
638
	public static function jdToJewish($julian_day, $hebrew, $fl) {
639
		if ($hebrew) {
640
			if ($julian_day < 347998 || $julian_day > 4000075) {
641
				return trigger_error('Year out of range (0-9999).', E_USER_WARNING);
642
			}
643
644
			return self::$jewish_calendar->jdToHebrew(
645
				$julian_day,
646
				(bool)($fl & CAL_JEWISH_ADD_ALAFIM_GERESH),
647
				(bool)($fl & CAL_JEWISH_ADD_ALAFIM),
648
				(bool)($fl & CAL_JEWISH_ADD_GERESHAYIM)
649
			);
650
		} else {
651
			// The upper limit is hard-coded into PHP to prevent numeric overflow on 32 bit systems.
652
			return self::jdToCalendar(self::$jewish_calendar, $julian_day, 347998, 324542846);
653
		}
654
	}
655
656
	/**
657
	 * Converts a Julian Day Count to a Julian Calendar Date.
658
	 *
659
	 * Shim implementation of JDToJulian()
660
	 *
661
	 * @link https://php.net/JDToJulian
662
	 *
663
	 * @param integer $julian_day A Julian Day number
664
	 *
665
	 * @return string A string of the form "month/day/year"
666
	 */
667
	public static function jdToJulian($julian_day) {
668
		// PHP has different limits on 32 and 64 bit systems.
669
		$MAX_JD = PHP_INT_SIZE == 4 ? 536838829 : 784368370349;
670
671
		return self::jdToCalendar(self::$julian_calendar, $julian_day, 1, $MAX_JD);
672
	}
673
674
	/**
675
	 * Convert Julian Day to Unix timestamp.
676
	 *
677
	 * Shim implementation of jdtounix()
678
	 *
679
	 * @link https://php.net/jdtounix
680
	 *
681
	 * @param integer $julian_day
682
	 *
683
	 * @return integer|false
684
	 */
685
	public static function jdToUnix($julian_day) {
686
		if ($julian_day >= 2440588 && $julian_day <= 2465343) {
687
			return (int) ($julian_day - 2440588) * 86400;
688
		} else {
689
			return false;
690
		}
691
	}
692
693
	/**
694
	 * Converts a date in the Jewish Calendar to Julian Day Count.
695
	 *
696
	 * Shim implementation of JewishToJD()
697
	 *
698
	 * @link https://php.net/JewishToJD
699
	 *
700
	 * @param integer $month
701
	 * @param integer $day
702
	 * @param integer $year
703
	 *
704
	 * @return integer
705
	 */
706
	public static function jewishToJd($month, $day, $year) {
707
		if ($year <= 0) {
708
			return 0;
709
		} else {
710
			return self::$jewish_calendar->ymdToJd($year, $month, $day);
711
		}
712
	}
713
714
	/**
715
	 * Converts a Julian Calendar date to Julian Day Count.
716
	 *
717
	 * Shim implementation of JdToJulian()
718
	 *
719
	 * @link https://php.net/JdToJulian
720
	 *
721
	 * @param integer $month
722
	 * @param integer $day
723
	 * @param integer $year
724
	 *
725
	 * @return integer
726
	 */
727
	public static function julianToJd($month, $day, $year) {
728
		if ($year == 0) {
729
			return 0;
730
		} else {
731
			return self::$julian_calendar->ymdToJd($year, $month, $day);
732
		}
733
	}
734
735
	/**
736
	 * Convert Unix timestamp to Julian Day.
737
	 *
738
	 * Shim implementation of unixtojd()
739
	 *
740
	 * @link https://php.net/unixtojd
741
	 *
742
	 * @param integer $timestamp
743
	 *
744
	 * @return false|integer
745
	 */
746
	public static function unixToJd($timestamp) {
747
		if ($timestamp < 0) {
748
			return false;
749
		} else {
750
			// Convert timestamp based on local timezone
751
			return self::GregorianToJd(gmdate('n', $timestamp), gmdate('j', $timestamp), gmdate('Y', $timestamp));
752
		}
753
	}
754
	
755
	/**
756
	 * By default, the result of A % B will be of the sign of A.
757
	 * This is not necessarily the agreed version of the mod operator by other languages.
758
	 * This mod function returns always the positive result of the modulo operation.
759
	 * 
760
	 * For example: 
761
	 *    (Standard PHP)   -3 % 4 = -3
762
	 *    (Shim)           Shim::mod(-3, 4) = 1
763
	 * 
764
	 * @param number $dividend
765
	 * @param number $divisor
766
	 * @return number
767
	 */
768
	public static function mod($dividend, $divisor) {
769
		if ($divisor === 0) return 0;
770
771
		$modulus = $dividend % $divisor;
772
		if($modulus < 0) {
773
			$modulus += $divisor;
774
		}
775
776
		return $modulus;
777
	}
778
	
779
}
780