|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace SMW; |
|
4
|
|
|
|
|
5
|
|
|
use SMWDITime as DITime; |
|
6
|
|
|
use RuntimeException; |
|
7
|
|
|
|
|
8
|
|
|
/** |
|
9
|
|
|
* Julian dates (abbreviated JD) are a continuous count of days and fractions |
|
10
|
|
|
* since noon Universal Time on January 1, 4713 BCE (on the Julian calendar). |
|
11
|
|
|
* |
|
12
|
|
|
* It is assumed that the changeover from the Julian calendar to the Gregorian |
|
13
|
|
|
* calendar occurred in October of 1582. |
|
14
|
|
|
* |
|
15
|
|
|
* For dates on or before 4 October 1582, the Julian calendar is used; for dates |
|
16
|
|
|
* on or after 15 October 1582, the Gregorian calendar is used. |
|
17
|
|
|
* |
|
18
|
|
|
* @license GNU GPL v2+ |
|
19
|
|
|
* @since 2.4 |
|
20
|
|
|
* |
|
21
|
|
|
* @author Markus Krötzsch |
|
22
|
|
|
*/ |
|
23
|
|
|
class JulianDay { |
|
24
|
|
|
|
|
25
|
|
|
/** |
|
26
|
|
|
* Moment of switchover to Gregorian calendar. |
|
27
|
|
|
*/ |
|
28
|
|
|
const J1582 = 2299160.5; |
|
29
|
|
|
|
|
30
|
|
|
/** |
|
31
|
|
|
* Offset of Julian Days for Modified JD inputs. |
|
32
|
|
|
*/ |
|
33
|
|
|
const MJD = 2400000.5; |
|
34
|
|
|
|
|
35
|
|
|
/** |
|
36
|
|
|
* Create a new time dataItem from a specified Julian Day number, |
|
37
|
|
|
* calendar model, presicion. |
|
38
|
|
|
* |
|
39
|
|
|
* @param double $jdValue |
|
40
|
|
|
* @param integer|null $calendarmodel |
|
|
|
|
|
|
41
|
|
|
* @param integer|null $precision |
|
42
|
|
|
* |
|
43
|
|
|
* @return DITime object |
|
44
|
|
|
*/ |
|
45
|
|
|
public static function newDiFromJD( $jdValue, $calendarModel = null, $precision = null ) { |
|
46
|
|
|
|
|
47
|
|
|
if ( $calendarModel === null ) { // 1582/10/15 |
|
|
|
|
|
|
48
|
|
|
$calendarModel = $jdValue < self::J1582 ? DITime::CM_JULIAN : DITime::CM_GREGORIAN; |
|
49
|
|
|
} |
|
50
|
|
|
|
|
51
|
|
|
if ( $precision === null ) { |
|
52
|
|
|
$precision = strpos( strval( $jdValue ), '.5' ) !== false ? DITime::PREC_YMD : DITime::PREC_YMDT; |
|
53
|
|
|
} |
|
54
|
|
|
|
|
55
|
|
|
list( $year, $month, $day ) = self::JD2Date( $jdValue, $calendarModel ); |
|
56
|
|
|
|
|
57
|
|
|
if ( $precision <= DITime::PREC_YM ) { |
|
58
|
|
|
$day = false; |
|
59
|
|
|
if ( $precision === DITime::PREC_Y ) { |
|
60
|
|
|
$month = false; |
|
61
|
|
|
} |
|
62
|
|
|
} |
|
63
|
|
|
|
|
64
|
|
|
if ( $precision === DITime::PREC_YMDT ) { |
|
65
|
|
|
list( $hour, $minute, $second ) = self::JD2Time( $jdValue ); |
|
66
|
|
|
} else { |
|
67
|
|
|
$hour = $minute = $second = false; |
|
68
|
|
|
} |
|
69
|
|
|
|
|
70
|
|
|
return new DITime( $calendarModel, $year, $month, $day, $hour, $minute, $second ); |
|
|
|
|
|
|
71
|
|
|
} |
|
72
|
|
|
|
|
73
|
|
|
/** |
|
74
|
|
|
* @since 2.4 |
|
75
|
|
|
* |
|
76
|
|
|
* @param DITime $dataItem |
|
77
|
|
|
* |
|
78
|
|
|
* @return float |
|
79
|
|
|
*/ |
|
80
|
|
|
public static function get( DITime $dataItem ) { |
|
81
|
|
|
|
|
82
|
|
|
$jdValue = self::date2JD( |
|
83
|
|
|
$dataItem->getYear(), |
|
84
|
|
|
$dataItem->getMonth(), |
|
85
|
|
|
$dataItem->getDay(), |
|
86
|
|
|
$dataItem->getCalendarModel() |
|
87
|
|
|
) + self::time2JDoffset( |
|
88
|
|
|
$dataItem->getHour(), |
|
89
|
|
|
$dataItem->getMinute(), |
|
90
|
|
|
$dataItem->getSecond() |
|
91
|
|
|
); |
|
92
|
|
|
|
|
93
|
|
|
// Keep microseconds to a certain degree distinguishable |
|
94
|
|
|
return floatval( number_format( $jdValue, 7, '.', '' ) ); |
|
95
|
|
|
} |
|
96
|
|
|
|
|
97
|
|
|
/** |
|
98
|
|
|
* The MJD has a starting point of midnight on November 17, 1858 and is |
|
99
|
|
|
* computed by MJD = JD - 2400000.5 |
|
100
|
|
|
* |
|
101
|
|
|
* @since 2.4 |
|
102
|
|
|
* |
|
103
|
|
|
* @param DITime $dataItem |
|
104
|
|
|
* |
|
105
|
|
|
* @return float |
|
106
|
|
|
*/ |
|
107
|
|
|
public static function getModifiedJulianDate( DITime $dataItem ) { |
|
108
|
|
|
return self::get( $dataItem ) - self::MJD; |
|
109
|
|
|
} |
|
110
|
|
|
|
|
111
|
|
|
/** |
|
112
|
|
|
* Compute the Julian Day number from a given date in the specified |
|
113
|
|
|
* calendar model. This calculation assumes that neither calendar |
|
114
|
|
|
* has a year 0. |
|
115
|
|
|
* |
|
116
|
|
|
* @param $year integer representing the year |
|
117
|
|
|
* @param $month integer representing the month |
|
118
|
|
|
* @param $day integer representing the day |
|
119
|
|
|
* @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN |
|
120
|
|
|
* |
|
121
|
|
|
* @return float Julian Day number |
|
122
|
|
|
* @throws RuntimeException |
|
123
|
|
|
*/ |
|
124
|
|
|
protected static function date2JD( $year, $month, $day, $calendarmodel ) { |
|
125
|
|
|
$astroyear = ( $year < 1 ) ? ( $year + 1 ) : $year; |
|
126
|
|
|
|
|
127
|
|
|
if ( $calendarmodel === DITime::CM_GREGORIAN ) { |
|
128
|
|
|
$a = intval( ( 14 - $month ) / 12 ); |
|
129
|
|
|
$y = $astroyear + 4800 - $a; |
|
130
|
|
|
$m = $month + 12 * $a - 3; |
|
131
|
|
|
return $day + floor( ( 153 * $m + 2 ) / 5 ) + 365 * $y + floor( $y / 4 ) - floor( $y / 100 ) + floor( $y / 400 ) - 32045.5; |
|
132
|
|
|
} elseif ( $calendarmodel === DITime::CM_JULIAN ) { |
|
133
|
|
|
$y2 = ( $month <= 2 ) ? ( $astroyear - 1 ) : $astroyear; |
|
134
|
|
|
$m2 = ( $month <= 2 ) ? ( $month + 12 ) : $month; |
|
135
|
|
|
return floor( ( 365.25 * ( $y2 + 4716 ) ) ) + floor( ( 30.6001 * ( $m2 + 1 ) ) ) + $day - 1524.5; |
|
136
|
|
|
} |
|
137
|
|
|
|
|
138
|
|
|
throw new RuntimeException( "Unsupported calendar model ($calendarmodel)" ); |
|
139
|
|
|
} |
|
140
|
|
|
|
|
141
|
|
|
/** |
|
142
|
|
|
* Compute the offset for the Julian Day number from a given time. |
|
143
|
|
|
* This computation is the same for all calendar models. |
|
144
|
|
|
* |
|
145
|
|
|
* @param $hours integer representing the hour |
|
146
|
|
|
* @param $minutes integer representing the minutes |
|
147
|
|
|
* @param $seconds integer representing the seconds |
|
148
|
|
|
* |
|
149
|
|
|
* @return float offset for a Julian Day number to get this time |
|
150
|
|
|
*/ |
|
151
|
|
|
protected static function time2JDoffset( $hours, $minutes, $seconds ) { |
|
152
|
|
|
return ( $hours / 24 ) + ( $minutes / ( 60 * 24 ) ) + ( $seconds / ( 3600 * 24 ) ); |
|
153
|
|
|
} |
|
154
|
|
|
|
|
155
|
|
|
/** |
|
156
|
|
|
* Convert a Julian Day number to a date in the given calendar model. |
|
157
|
|
|
* This calculation assumes that neither calendar has a year 0. |
|
158
|
|
|
* @note The algorithm may fail for some cases, in particular since the |
|
159
|
|
|
* conversion to Gregorian needs positive JD. If this happens, wrong |
|
160
|
|
|
* values will be returned. Avoid date conversions before 10000 BCE. |
|
161
|
|
|
* |
|
162
|
|
|
* @param $jdvalue float number of Julian Days |
|
163
|
|
|
* @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN |
|
164
|
|
|
* |
|
165
|
|
|
* @return array( yearnumber, monthnumber, daynumber ) |
|
|
|
|
|
|
166
|
|
|
* @throws RuntimeException |
|
167
|
|
|
*/ |
|
168
|
|
|
protected static function JD2Date( $jdvalue, $calendarmodel ) { |
|
169
|
|
|
|
|
170
|
|
|
if ( $calendarmodel === DITime::CM_GREGORIAN ) { |
|
171
|
|
|
$jdvalue += 2921940; // add the days of 8000 years (this algorithm only works for positive JD) |
|
172
|
|
|
$j = floor( $jdvalue + 0.5 ) + 32044; |
|
173
|
|
|
$g = floor( $j / 146097 ); |
|
174
|
|
|
$dg = $j % 146097; |
|
175
|
|
|
$c = floor( ( ( floor( $dg / 36524 ) + 1 ) * 3 ) / 4 ); |
|
176
|
|
|
$dc = $dg - $c * 36524; |
|
177
|
|
|
$b = floor( $dc / 1461 ); |
|
178
|
|
|
$db = $dc % 1461; |
|
179
|
|
|
$a = floor( ( ( floor( $db / 365 ) + 1 ) * 3 ) / 4 ); |
|
180
|
|
|
$da = $db - ( $a * 365 ); |
|
181
|
|
|
$y = $g * 400 + $c * 100 + $b * 4 + $a; |
|
182
|
|
|
$m = floor( ( $da * 5 + 308 ) / 153 ) - 2; |
|
183
|
|
|
$d = $da - floor( ( ( $m + 4 ) * 153 ) / 5 ) + 122; |
|
184
|
|
|
|
|
185
|
|
|
$year = $y - 4800 + floor( ( $m + 2 ) / 12 ) - 8000; |
|
186
|
|
|
$month = ( ( $m + 2 ) % 12 + 1 ); |
|
187
|
|
|
$day = $d + 1; |
|
188
|
|
|
} elseif ( $calendarmodel === DITime::CM_JULIAN ) { |
|
189
|
|
|
$b = floor( $jdvalue + 0.5 ) + 1524; |
|
190
|
|
|
$c = floor( ( $b - 122.1 ) / 365.25 ); |
|
191
|
|
|
$d = floor( 365.25 * $c ); |
|
192
|
|
|
$e = floor( ( $b - $d ) / 30.6001 ); |
|
193
|
|
|
|
|
194
|
|
|
$month = floor( ( $e < 14 ) ? ( $e - 1 ) : ( $e - 13 ) ); |
|
195
|
|
|
$year = floor( ( $month > 2 ) ? ( $c - 4716 ) : ( $c - 4715 ) ); |
|
196
|
|
|
$day = ( $b - $d - floor( 30.6001 * $e ) ); |
|
197
|
|
|
} else { |
|
198
|
|
|
throw new RuntimeException( "Unsupported calendar model ($calendarmodel)" ); |
|
199
|
|
|
} |
|
200
|
|
|
|
|
201
|
|
|
$year = ( $year < 1 ) ? ( $year - 1 ) : $year; // correct "year 0" to -1 (= 1 BC(E)) |
|
202
|
|
|
|
|
203
|
|
|
return array( $year, $month, $day ); |
|
204
|
|
|
} |
|
205
|
|
|
|
|
206
|
|
|
/** |
|
207
|
|
|
* Extract the time from a Julian Day number and return it as a string. |
|
208
|
|
|
* This conversion is the same for all calendar models. |
|
209
|
|
|
* |
|
210
|
|
|
* @param $jdvalue float number of Julian Days |
|
211
|
|
|
* @return array( hours, minutes, seconds ) |
|
|
|
|
|
|
212
|
|
|
*/ |
|
213
|
|
|
protected static function JD2Time( $jdvalue ) { |
|
214
|
|
|
$wjd = $jdvalue + 0.5; |
|
215
|
|
|
$fraction = $wjd - floor( $wjd ); |
|
216
|
|
|
$time = round( $fraction * 3600 * 24 ); |
|
217
|
|
|
$hours = floor( $time / 3600 ); |
|
218
|
|
|
$time = $time - $hours * 3600; |
|
219
|
|
|
$minutes = floor( $time / 60 ); |
|
220
|
|
|
$seconds = floor( $time - $minutes * 60 ); |
|
221
|
|
|
return array( $hours, $minutes, $seconds ); |
|
222
|
|
|
} |
|
223
|
|
|
|
|
224
|
|
|
} |
|
225
|
|
|
|
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.
Consider the following example. The parameter
$irelandis not defined by the methodfinale(...).The most likely cause is that the parameter was changed, but the annotation was not.