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
$ireland
is not defined by the methodfinale(...)
.The most likely cause is that the parameter was changed, but the annotation was not.