├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── src ├── Calculator │ ├── AstronomicalCalculator.php │ ├── NoaaCalculator.php │ └── SunTimesCalculator.php ├── Calendar │ ├── AstronomicalCalendar.php │ ├── ComplexZmanimCalendar.php │ └── ZmanimCalendar.php ├── Geo │ ├── GeoLocation.php │ └── GeoLocationUtils.php ├── HebrewCalendar │ ├── Daf.php │ ├── HebrewDateFormatter.php │ ├── JewishCalendar.php │ ├── JewishDate.php │ ├── Parsha.php │ ├── TefilaRules.php │ ├── YerushalmiYomiCalculator.php │ └── YomiCalculator.php └── Zmanim.php └── tests ├── Calculator ├── NoaaCalculatorTest.php └── SunTimesCalculatorTest.php ├── Calendar ├── AstronomicalCalendarTest.php └── ZmanimCalendarTest.php ├── Geo ├── GeoLocationTest.php └── GeoLocationUtilsTest.php └── HebrewCalendar ├── JewishCalendarTest.php └── JewishDateTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | /vendor 3 | .phpunit.result.cache 4 | phpunit.xml 5 | composer.lock -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PhpZmanim 2 | A PHP port of the [KosherJava Zmanim API](https://kosherjava.com) from Eliyahu Hershfeld (code at the [KosherJava Zmanim project](https://github.com/KosherJava/zmanim)). See Kosher Java documentation for comments for every class variable and method. See below for how to install and a more detailed list of what you can access and methods you can call. Once instantiated, you can ask for many Zmanim right out of the gate: 3 | 4 | ```php 5 | $zmanim = Zmanim::create(2019, 2, 22, 'Lakewood', 40.0721087, -74.2400243, 39.57, 'America/New_York'); 6 | $zmanim->tzais72->format('Y-m-d\TH:i:sP'); // 2019-02-22T18:52:38-05:00 7 | 8 | $jewishCalendar = Zmanim::jewishCalendar(Carbon::createFromDate(2023, 9, 30)); // This will give you a Jewish calendar date for the given date 9 | $jewishCalendar = Zmanim::jewishCalendar(5784, 7, 15); // This will give the same date, but with the Jewish date given as parameters 10 | $jewishCalendar->isRoshHashana(); // false 11 | $jewishCalendar->isSuccos(); // true 12 | 13 | $daf = $jewishCalendar->getDafYomiBavli(); 14 | $format = Zmanim::format(); 15 | $format->formatDafYomiBavli($daf); // Kiddushin 48 16 | 17 | $jewishCalendar = Zmanim::jewishCalendar(5784, 7, 26); 18 | $format->formatParsha($jewishCalendar); // Bereshis 19 | ``` 20 | 21 | ## Installation (with Composer) 22 | 23 | ``` 24 | $ composer require zachweix/php-zmanim 25 | ``` 26 | 27 | ```json 28 | { 29 | "require": { 30 | "zachweix/php-zmanim": "^2.0" 31 | } 32 | } 33 | ``` 34 | 35 | ## Setup 36 | 37 | ```php 38 | sunrise` or a method name you see that is called like `$zmanim->get("Sunrise")` can be called by concatenating `get` to the zman and making sure to capitalize the first letter, so you would get `$zmanim->getSunrise()`; the following three will return identical results, all of which are a Carbon object. 83 | 84 | ```php 85 | $sunrise = $zmanim->sunrise; 86 | $sunrise = $zmanim->get("Sunrise"); 87 | $sunrise = $zmanim->getSunrise(); 88 | ``` 89 | 90 | If you want to factor elevation when calculating, make sure to set the elevation when instantiating the object. If you set an elevation other than 0 and want to ignore elevation, you can call `$zmanim->setUseElevation(false)` and then when you want to use it again, simply call `$zmanim->setUseElevation(true)`. 91 | 92 | ### Available Methods 93 | 94 | ```php 95 | $zmanim->setCalculatorType($type); // 'SunTimes' and 'Noaa' are currently the only calculators 96 | $zmanim->setDate($year, $month, $day); // Change current date 97 | $zmanim->addDays($value); // Change current date, by adding requested number of days 98 | $zmanim->subDays($value); // Change current date, by subtracting requested number of days 99 | ``` 100 | 101 | ### List of Zmanim 102 | 103 | Here is a list of many possible Zmanim you can request, all of them will return a Carbon object in the timezone set when creating the `$zmanim` object. If you don't find what you are looking for, see below for how to get more custom Zmanim. 104 | 105 | #### Sunrise: 106 | ```php 107 | $zmanim->sunrise; // Get sunrise based on current elevation 108 | $zmanim->seaLevelSunrise; // Get sunrise at zero elevation 109 | $zmanim->beginCivilTwilight; // The point when sun's zenith is at 96 degrees 110 | $zmanim->beginNauticalTwilight; // The point when sun's zenith is at 102 degrees 111 | $zmanim->beginAstronomicalTwilight; // The point when sun's zenith is at 108 degrees 112 | ``` 113 | 114 | #### Sunset 115 | ```php 116 | $zmanim->sunset; // Sunset based on current elevation 117 | $zmanim->seaLevelSunset; // Sunset at zero elevation 118 | $zmanim->endCivilTwilight; // The point when sun's zenith is at 96 degrees 119 | $zmanim->endNauticalTwilight; // The point when sun's zenith is at 102 degrees 120 | $zmanim->endAstronomicalTwilight; // The point when sun's zenith is at 108 degrees 121 | ``` 122 | 123 | #### Length of Shaah Zmanim (in minutes) 124 | 125 | ```php 126 | $zmanim->shaahZmanis19Point8Degrees; 127 | $zmanim->shaahZmanis18Degrees; 128 | $zmanim->shaahZmanis26Degrees; 129 | $zmanim->shaahZmanis16Point1Degrees; 130 | $zmanim->shaahZmanis60Minutes; 131 | $zmanim->shaahZmanis72Minutes; 132 | $zmanim->shaahZmanis72MinutesZmanis; 133 | $zmanim->shaahZmanis90Minutes; 134 | $zmanim->shaahZmanis90MinutesZmanis; 135 | $zmanim->shaahZmanis96MinutesZmanis; 136 | $zmanim->shaahZmanisAteretTorah; // See note 1 below 137 | $zmanim->shaahZmanisAlos16Point1ToTzais3Point8; 138 | $zmanim->shaahZmanisAlos16Point1ToTzais3Point7; 139 | $zmanim->shaahZmanis96Minutes; 140 | $zmanim->shaahZmanis120Minutes; 141 | $zmanim->shaahZmanis120MinutesZmanis; 142 | $zmanim->shaahZmanisBaalHatanya; // See note 3 below 143 | $zmanim->shaahZmanisGra; 144 | $zmanim->shaahZmanisMGA; 145 | ``` 146 | 147 | #### Alos Hashachar 148 | ```php 149 | $zmanim->alosHashachar; // Sunrise offset by 16.1 degrees 150 | $zmanim->alos72; 151 | $zmanim->alos60; 152 | $zmanim->alos72Zmanis; 153 | $zmanim->alos96; 154 | $zmanim->alos90Zmanis; 155 | $zmanim->alos96Zmanis; 156 | $zmanim->alos90; 157 | $zmanim->alos120; 158 | $zmanim->alos120Zmanis; 159 | $zmanim->alos26Degrees; 160 | $zmanim->alos18Degrees; 161 | $zmanim->alos19Degrees; 162 | $zmanim->alos19Point8Degrees; 163 | $zmanim->alos16Point1Degrees; // Same as default 164 | $zmanim->alosBaalHatanya; // See note 3 below 165 | ``` 166 | 167 | #### Misheyakir 168 | ```php 169 | $zmanim->misheyakir11Point5Degrees; 170 | $zmanim->misheyakir11Degrees; 171 | $zmanim->misheyakir10Point2Degrees; 172 | $zmanim->misheyakir7Point65Degrees; 173 | $zmanim->misheyakir9Point5Degrees; 174 | ``` 175 | 176 | #### Sof Zman Shma 177 | ```php 178 | $zmanim->sofZmanShmaMGA; 179 | $zmanim->sofZmanShmaGra; 180 | $zmanim->sofZmanShmaMGA19Point8Degrees; 181 | $zmanim->sofZmanShmaMGA16Point1Degrees; 182 | $zmanim->sofZmanShmaMGA18Degrees; 183 | $zmanim->sofZmanShmaMGA72Minutes; 184 | $zmanim->sofZmanShmaMGA72MinutesZmanis; 185 | $zmanim->sofZmanShmaMGA90Minutes; 186 | $zmanim->sofZmanShmaMGA90MinutesZmanis; 187 | $zmanim->sofZmanShmaMGA96Minutes; 188 | $zmanim->sofZmanShmaMGA96MinutesZmanis; 189 | $zmanim->sofZmanShma3HoursBeforeChatzos; 190 | $zmanim->sofZmanShmaMGA120Minutes; 191 | $zmanim->sofZmanShmaAlos16Point1ToSunset; 192 | $zmanim->sofZmanShmaAlos16Point1ToTzaisGeonim7Point083Degrees; 193 | $zmanim->sofZmanShmaKolEliyahu; 194 | $zmanim->sofZmanShmaAteretTorah; // See note 1 below 195 | $zmanim->sofZmanShmaFixedLocal; // See note 2 below 196 | $zmanim->sofZmanShmaBaalHatanya; // See note 3 below 197 | $zmanim->sofZmanShmaMGA18DegreesToFixedLocalChatzos; 198 | $zmanim->sofZmanShmaMGA16Point1DegreesToFixedLocalChatzos; 199 | $zmanim->sofZmanShmaMGA90MinutesToFixedLocalChatzos; 200 | $zmanim->sofZmanShmaMGA72MinutesToFixedLocalChatzos; 201 | $zmanim->sofZmanShmaGRASunriseToFixedLocalChatzos; 202 | ``` 203 | 204 | #### Sof Zman Tfila 205 | ```php 206 | $zmanim->sofZmanTfilaMGA; 207 | $zmanim->sofZmanTfilaGra; 208 | $zmanim->sofZmanTfilaMGA19Point8Degrees; 209 | $zmanim->sofZmanTfilaMGA16Point1Degrees; 210 | $zmanim->sofZmanTfilaMGA18Degrees; 211 | $zmanim->sofZmanTfilaMGA72Minutes; 212 | $zmanim->sofZmanTfilaMGA72MinutesZmanis; 213 | $zmanim->sofZmanTfilaMGA90Minutes; 214 | $zmanim->sofZmanTfilaMGA90MinutesZmanis; 215 | $zmanim->sofZmanTfilaMGA96Minutes; 216 | $zmanim->sofZmanTfilaMGA96MinutesZmanis; 217 | $zmanim->sofZmanTfilaMGA120Minutes; 218 | $zmanim->sofZmanTfila2HoursBeforeChatzos; 219 | $zmanim->sofZmanTfilahAteretTorah; // See note 1 below 220 | $zmanim->sofZmanTfilaFixedLocal; // See note 2 below 221 | $zmanim->sofZmanTfilaBaalHatanya; // See note 3 below 222 | $zmanim->sofZmanTfilaGRASunriseToFixedLocalChatzos; 223 | ``` 224 | 225 | #### Erev Pesach 226 | ```php 227 | $zmanim->sofZmanAchilasChametzGRA; 228 | $zmanim->sofZmanAchilasChametzMGA72Minutes; 229 | $zmanim->sofZmanAchilasChametzMGA16Point1Degrees; 230 | $zmanim->sofZmanAchilasChametzBaalHatanya; // See note 3 below 231 | 232 | $zmanim->sofZmanBiurChametzGRA; 233 | $zmanim->sofZmanBiurChametzMGA72Minutes; 234 | $zmanim->sofZmanBiurChametzMGA16Point1Degrees; 235 | $zmanim->sofZmanBiurChametzBaalHatanya; // See note 3 below 236 | ``` 237 | 238 | #### Chatzos 239 | ```php 240 | $zmanim->chatzos; // This defaults to astronomical chatzos 241 | $zmanim->chatzosAsHalfDay; // This defaults to halfway between sunrise and sunset and falls back to chatzos if that is not possible (e.g. the North Pole during summer) 242 | $zmanim->fixedLocalChatzos; // See note 2 below 243 | ``` 244 | 245 | #### Mincha Gedola 246 | ```php 247 | $zmanim->minchaGedola; 248 | $zmanim->minchaGedola30Minutes; 249 | $zmanim->minchaGedola72Minutes; 250 | $zmanim->minchaGedola16Point1Degrees; 251 | $zmanim->minchaGedolaAhavatShalom; 252 | $zmanim->minchaGedolaGreaterThan30; // Fixed 30 minutes or degrees, whichever is later 253 | $zmanim->minchaGedolaAteretTorah; // See note 1 below 254 | $zmanim->minchaGedolaBaalHatanya; // See note 3 below 255 | $zmanim->minchaGedolaBaalHatanyaGreaterThan30; 256 | $zmanim->minchaGedolaGRAFixedLocalChatzos30Minutes; 257 | ``` 258 | 259 | #### Mincha Ketana 260 | ```php 261 | $zmanim->minchaKetana; 262 | $zmanim->minchaKetana16Point1Degrees; 263 | $zmanim->minchaKetanaAhavatShalom; 264 | $zmanim->minchaKetana72Minutes; 265 | $zmanim->minchaKetanaAteretTorah; // See note 1 below 266 | $zmanim->minchaKetanaBaalHatanya; // See note 3 below 267 | $zmanim->minchaKetanaGRAFixedLocalChatzosToSunset; 268 | 269 | $zmanim->samuchLeMinchaKetanaGRA; 270 | $zmanim->samuchLeMinchaKetana16Point1Degrees; 271 | $zmanim->samuchLeMinchaKetana72Minutes; 272 | ``` 273 | 274 | #### Plag Hamincha 275 | ```php 276 | $zmanim->plagHamincha; 277 | $zmanim->plagHamincha120MinutesZmanis; 278 | $zmanim->plagHamincha120Minutes; 279 | $zmanim->plagHamincha60Minutes; 280 | $zmanim->plagHamincha72Minutes; 281 | $zmanim->plagHamincha90Minutes; 282 | $zmanim->plagHamincha96Minutes; 283 | $zmanim->plagHamincha96MinutesZmanis; 284 | $zmanim->plagHamincha90MinutesZmanis; 285 | $zmanim->plagHamincha72MinutesZmanis; 286 | $zmanim->plagHamincha16Point1Degrees; 287 | $zmanim->plagHamincha19Point8Degrees; 288 | $zmanim->plagHamincha26Degrees; 289 | $zmanim->plagHamincha18Degrees; 290 | $zmanim->plagAlosToSunset; 291 | $zmanim->plagAlos16Point1ToTzaisGeonim7Point083Degrees; 292 | $zmanim->plagAhavatShalom; 293 | $zmanim->plagHaminchaAteretTorah; // See note 1 below 294 | $zmanim->plagHaminchaBaalHatanya; // See note 3 below 295 | $zmanim->plagHaminchaGRAFixedLocalChatzosToSunset; 296 | ``` 297 | 298 | #### Candle Lighting 299 | ```php 300 | $zmanim->candleLighting; // Get sea level sunset minus candle lighting offset. Default is 18 minutes 301 | $zmanim->setCandleLightingOffset($candleLightingOffset); 302 | ``` 303 | 304 | #### Start of Bain Hasmashos (According to Rabbeinu Tam) 305 | 306 | ```php 307 | $zmanim->bainHashmashosRT13Point24Degrees; 308 | $zmanim->bainHashmashosRT58Point5Minutes; 309 | $zmanim->bainHashmashosRT13Point5MinutesBefore7Point083Degrees; 310 | $zmanim->bainHashmashosRT2Stars; 311 | $zmanim->bainHashmashosYereim18Minutes; 312 | $zmanim->bainHashmashosYereim3Point05Degrees; 313 | $zmanim->bainHashmashosYereim16Point875Minutes; 314 | $zmanim->bainHashmashosYereim2Point8Degrees; 315 | $zmanim->bainHashmashosYereim13Point5Minutes; 316 | $zmanim->bainHashmashosYereim2Point1Degrees; 317 | ``` 318 | 319 | #### Tzais 320 | ```php 321 | $zmanim->tzais; // Sunset offset by 8.5 degrees 322 | $zmanim->tzais72;getTzais() 323 | $zmanim->tzaisGeonim3Point7Degrees; 324 | $zmanim->tzaisGeonim3Point8Degrees; 325 | $zmanim->tzaisGeonim5Point95Degrees; 326 | $zmanim->tzaisGeonim3Point65Degrees; 327 | $zmanim->tzaisGeonim3Point676Degrees; 328 | $zmanim->tzaisGeonim4Point61Degrees; 329 | $zmanim->tzaisGeonim4Point37Degrees; 330 | $zmanim->tzaisGeonim5Point88Degrees; 331 | $zmanim->tzaisGeonim4Point8Degrees; 332 | $zmanim->tzaisGeonim6Point45Degrees; 333 | $zmanim->tzaisGeonim7Point083Degrees; 334 | $zmanim->tzaisGeonim7Point67Degrees; 335 | $zmanim->tzaisGeonim8Point5Degrees; 336 | $zmanim->tzaisGeonim9Point3Degrees; 337 | $zmanim->tzaisGeonim9Point75Degrees; 338 | $zmanim->tzais60; 339 | $zmanim->tzaisAteretTorah; // See note 1 below 340 | $zmanim->tzais72Zmanis; 341 | $zmanim->tzais90Zmanis; 342 | $zmanim->tzais96Zmanis; 343 | $zmanim->tzais90; 344 | $zmanim->tzais120; 345 | $zmanim->tzais120Zmanis; 346 | $zmanim->tzais16Point1Degrees; 347 | $zmanim->tzais26Degrees; 348 | $zmanim->tzais18Degrees; 349 | $zmanim->tzais19Point8Degrees; 350 | $zmanim->tzais96; 351 | $zmanim->tzaisBaalHatanya; // See note 3 below 352 | $zmanim->tzais50; 353 | ``` 354 | 355 | #### Chatzos Halayla (Midnight) 356 | ```php 357 | $zmanim->solarMidnight; 358 | ``` 359 | 360 | #### Molad Zmanim 361 | ```php 362 | $zmanim->sofZmanKidushLevanaBetweenMoldos; 363 | $zmanim->sofZmanKidushLevana15Days; 364 | $zmanim->tchilasZmanKidushLevana3Days; 365 | $zmanim->zmanMolad; 366 | $zmanim->tchilasZmanKidushLevana7Days; 367 | ``` 368 | 369 | #### Notes 370 | 1. AteretTorah Zman is calculated based on a day being from Alos72Zmanis until 40 minutes after sunset. However, the exact time offset can be changed by calling `$zmanim->setAteretTorahSunsetOffset($ateretTorahSunsetOffset)`. 371 | 1. FixedLocalChatzos is based on a fixed time for Chatzos throughout the year, see KosherJava's documentation for more details. 372 | 1. Baal Hatanya calculates Zmanim based on a zenith of 1.583 degrees below the 90 degree geometric zenith. 373 | 374 | ### Alternative Zmanim 375 | 376 | If you are looking for any Zman which is an offset from sunrise or sunset that is not listed in the list above, you can call one of the following methods with your offset. 377 | 378 | ```php 379 | $zmanim->getSunriseOffsetByDegrees($offsetZenith); 380 | $zmanim->getSunriseSolarDipFromOffset($minutes); 381 | $zmanim->getSunsetOffsetByDegrees($offsetZenith); 382 | $zmanim->getSunsetSolarDipFromOffset($minutes); 383 | ``` 384 | 385 | You can use those times as parameters for custom calculations for the following functions: 386 | 387 | ```php 388 | $zmanim->getSofZmanShma($startOfDay, $endOfDay); 389 | $zmanim->getSofZmanTfila($startOfDay, $endOfDay); 390 | $zmanim->getMinchaGedola($startOfDay, $endOfDay); 391 | $zmanim->getMinchaKetana($startOfDay, $endOfDay); 392 | $zmanim->getPlagHamincha($startOfDay, $endOfDay); 393 | ``` 394 | 395 | ## Jewish Calendar Calculations 396 | 397 | For the usage syntax you can look at the KosherJava documentation. (TODO: Add the methods here as well). 398 | 399 | ## GeoLocation Mathematical Calculations 400 | 401 | There are a few functions that can be run for calculations on GeoLocation objects: 402 | 403 | ```php 404 | use PhpZmanim\Geo\GeoLocation; 405 | use PhpZmanim\Geo\GeoLocationUtils; 406 | 407 | $new_york = new GeoLocation("New York City", 40.850519, -73.929214, 200, "America/New_York"); 408 | $jerusalem = new GeoLocation('Jerusalem, Israel', 31.7781161, 35.233804, 740, 'Asia/Jerusalem'); 409 | 410 | GeoLocationUtils::getGeodesicInitialBearing($new_york, $jerusalem); 411 | GeoLocationUtils::getGeodesicFinalBearing($new_york, $jerusalem); 412 | GeoLocationUtils::getGeodesicDistance($new_york, $jerusalem); 413 | GeoLocationUtils::getRhumbLineBearing($new_york, $jerusalem); 414 | GeoLocationUtils::getRhumbLineDistance($new_york, $jerusalem); 415 | ``` 416 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zachweix/php-zmanim", 3 | "type": "library", 4 | "description": "Port of KosherJava to PHP", 5 | "license": "LGPL-2.1-only", 6 | "authors": [ 7 | { 8 | "name": "Zachary Weixelbaum", 9 | "email": "zachweix@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "nesbot/carbon": "^2.0 || ^3.0" 14 | }, 15 | "require-dev": { 16 | "phpunit/phpunit": "^8" 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "PhpZmanim\\": "src/" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Calculator/AstronomicalCalculator.php: -------------------------------------------------------------------------------- 1 | refraction = (double) 34 / 60.0; 49 | $this->solarRadius = (double) 16 / 60.0; 50 | $this->earthRadius = (double) 6356.9; 51 | } 52 | 53 | public static function create() { 54 | return new static(); 55 | } 56 | 57 | /* 58 | |-------------------------------------------------------------------------- 59 | | CALCULATOR 60 | |-------------------------------------------------------------------------- 61 | */ 62 | 63 | public static function getDefault() { 64 | return new NoaaCalculator(); 65 | } 66 | 67 | public function getCalculatorName() { 68 | return static::CALCULATOR_NAME; 69 | } 70 | 71 | /* 72 | |-------------------------------------------------------------------------- 73 | | EARTH RADIUS 74 | |-------------------------------------------------------------------------- 75 | */ 76 | 77 | public function getEarthRadius() { 78 | return $this->earthRadius; 79 | } 80 | 81 | public function setEarthRadius(float $earthRadius) { 82 | $this->earthRadius = $earthRadius; 83 | 84 | return $this; 85 | } 86 | 87 | /* 88 | |-------------------------------------------------------------------------- 89 | | ABSTRACT FUNCTIONS 90 | |-------------------------------------------------------------------------- 91 | */ 92 | 93 | abstract public function getUTCSunrise(Carbon $calendar, GeoLocation $geoLocation, $zenith, $adjustForElevation); 94 | abstract public function getUTCSunset(Carbon $calendar, GeoLocation $geoLocation, $zenith, $adjustForElevation); 95 | abstract public function getUTCNoon(Carbon $calendar, GeoLocation $geoLocation); 96 | 97 | /* 98 | |-------------------------------------------------------------------------- 99 | | FUNCTIONS 100 | |-------------------------------------------------------------------------- 101 | */ 102 | 103 | public function getElevationAdjustment($elevation) { 104 | $elevationAdjustment = rad2deg( acos($this->earthRadius / ($this->earthRadius + ($elevation / 1000.0)))); 105 | return $elevationAdjustment; 106 | } 107 | 108 | public function adjustZenith($zenith, $elevation) { 109 | $adjustedZenith = $zenith; 110 | if ($zenith == self::GEOMETRIC_ZENITH) { 111 | $adjustedZenith = $zenith + ($this->getSolarRadius() + $this->getRefraction() + $this->getElevationAdjustment($elevation)); 112 | } 113 | return $adjustedZenith; 114 | } 115 | 116 | /* 117 | |-------------------------------------------------------------------------- 118 | | GETTERS AND SETTERS 119 | |-------------------------------------------------------------------------- 120 | */ 121 | 122 | public function getRefraction() { 123 | return $this->refraction; 124 | } 125 | 126 | public function setRefraction(float $refraction) { 127 | $this->refraction = $refraction; 128 | 129 | return $this; 130 | } 131 | 132 | public function getSolarRadius() { 133 | return $this->solarRadius; 134 | } 135 | 136 | public function setSolarRadius(float $solarRadius) { 137 | $this->solarRadius = $solarRadius; 138 | 139 | return $this; 140 | } 141 | 142 | /* 143 | |-------------------------------------------------------------------------- 144 | | CLONEABLE 145 | |-------------------------------------------------------------------------- 146 | */ 147 | 148 | public function copy() { 149 | return clone $this; 150 | } 151 | } -------------------------------------------------------------------------------- /src/Calculator/NoaaCalculator.php: -------------------------------------------------------------------------------- 1 | getElevation() : 0; 47 | $adjustedZenith = $this->adjustZenith($zenith, $elevation); 48 | $sunrise = self::getSunriseUTC(self::getJulianDay($calendar), $geoLocation->getLatitude(), -$geoLocation->getLongitude(), $adjustedZenith); 49 | 50 | $sunrise = $sunrise / 60; 51 | 52 | while ($sunrise < 0.0) { 53 | $sunrise += 24.0; 54 | } 55 | while ($sunrise >= 24.0) { 56 | $sunrise -= 24.0; 57 | } 58 | 59 | return $sunrise; 60 | } 61 | 62 | public function getUTCSunset(Carbon $calendar, GeoLocation $geoLocation, $zenith, $adjustForElevation) { 63 | $elevation = $adjustForElevation ? $geoLocation->getElevation() : 0; 64 | $adjustedZenith = $this->adjustZenith($zenith, $elevation); 65 | $sunset = self::getSunsetUTC(self::getJulianDay($calendar), $geoLocation->getLatitude(), -$geoLocation->getLongitude(), $adjustedZenith); 66 | 67 | $sunset = $sunset / 60; 68 | 69 | while ($sunset < 0.0) { 70 | $sunset += 24.0; 71 | } 72 | while ($sunset >= 24.0) { 73 | $sunset -= 24.0; 74 | } 75 | 76 | return $sunset; 77 | } 78 | 79 | /* 80 | |-------------------------------------------------------------------------- 81 | | STATIC FUNCTIONS 82 | |-------------------------------------------------------------------------- 83 | */ 84 | 85 | private static function getJulianDay($calendar) { 86 | $year = $calendar->year; 87 | $month = $calendar->month; 88 | $day = $calendar->day; 89 | 90 | if ($month <= 2) { 91 | $year -= 1; 92 | $month += 12; 93 | } 94 | 95 | $a = (int) ($year / 100); 96 | $b = (int) (2 - $a + (int) ($a / 4)); 97 | 98 | return floor(365.25 * ($year + 4716)) + floor(30.6001 * ($month + 1)) + $day + $b - 1524.5; 99 | } 100 | 101 | private static function getJulianCenturiesFromJulianDay($julianDay) { 102 | return ($julianDay - self::JULIAN_DAY_JAN_1_2000) / self::JULIAN_DAYS_PER_CENTURY; 103 | } 104 | 105 | private static function getJulianDayFromJulianCenturies($julianCenturies) { 106 | return $julianCenturies * self::JULIAN_DAYS_PER_CENTURY + self::JULIAN_DAY_JAN_1_2000; 107 | } 108 | 109 | private static function getSunGeometricMeanLongitude($julianCenturies) { 110 | $longitude = 280.46646 + $julianCenturies * (36000.76983 + 0.0003032 * $julianCenturies); 111 | while ($longitude >= 360.0) { 112 | $longitude -= 360.0; 113 | } 114 | while ($longitude < 0.0) { 115 | $longitude += 360.0; 116 | } 117 | 118 | return $longitude; // in degrees 119 | } 120 | 121 | private static function getSunGeometricMeanAnomaly($julianCenturies) { 122 | return 357.52911 + $julianCenturies * (35999.05029 - 0.0001537 * $julianCenturies); // in degrees 123 | } 124 | 125 | private static function getEarthOrbitEccentricity($julianCenturies) { 126 | return 0.016708634 - $julianCenturies * (0.000042037 + 0.0000001267 * $julianCenturies); // unitless 127 | } 128 | 129 | private static function getSunEquationOfCenter($julianCenturies) { 130 | $m = self::getSunGeometricMeanAnomaly($julianCenturies); 131 | 132 | $mrad = deg2rad($m); 133 | $sinm = sin($mrad); 134 | $sin2m = sin($mrad + $mrad); 135 | $sin3m = sin($mrad + $mrad + $mrad); 136 | 137 | return $sinm * (1.914602 - $julianCenturies * (0.004817 + 0.000014 * $julianCenturies)) + $sin2m 138 | * (0.019993 - 0.000101 * $julianCenturies) + $sin3m * 0.000289;// in degrees 139 | } 140 | 141 | private static function getSunTrueLongitude($julianCenturies) { 142 | $sunLongitude = self::getSunGeometricMeanLongitude($julianCenturies); 143 | $center = self::getSunEquationOfCenter($julianCenturies); 144 | 145 | return $sunLongitude + $center; // in degrees 146 | } 147 | 148 | private static function getSunApparentLongitude($julianCenturies) { 149 | $sunTrueLongitude = self::getSunTrueLongitude($julianCenturies); 150 | 151 | $omega = 125.04 - 1934.136 * $julianCenturies; 152 | $lambda = $sunTrueLongitude - 0.00569 - 0.00478 * sin(deg2rad($omega)); 153 | return $lambda; // in degrees 154 | } 155 | 156 | private static function getMeanObliquityOfEcliptic($julianCenturies) { 157 | $seconds = 21.448 - $julianCenturies 158 | * (46.8150 + $julianCenturies * (0.00059 - $julianCenturies * (0.001813))); 159 | return 23.0 + (26.0 + ($seconds / 60.0)) / 60.0; // in degrees 160 | } 161 | 162 | private static function getObliquityCorrection($julianCenturies) { 163 | $obliquityOfEcliptic = self::getMeanObliquityOfEcliptic($julianCenturies); 164 | 165 | $omega = 125.04 - 1934.136 * $julianCenturies; 166 | return $obliquityOfEcliptic + 0.00256 * cos(deg2rad($omega)); // in degrees 167 | } 168 | 169 | private static function getSunDeclination($julianCenturies) { 170 | $obliquityCorrection = self::getObliquityCorrection($julianCenturies); 171 | $lambda = self::getSunApparentLongitude($julianCenturies); 172 | 173 | $sint = sin(deg2rad($obliquityCorrection)) * sin(deg2rad($lambda)); 174 | $theta = rad2deg(asin($sint)); 175 | return $theta; // in degrees 176 | } 177 | 178 | private static function getEquationOfTime($julianCenturies) { 179 | $epsilon = self::getObliquityCorrection($julianCenturies); 180 | $geomMeanLongSun = self::getSunGeometricMeanLongitude($julianCenturies); 181 | $eccentricityEarthOrbit = self::getEarthOrbitEccentricity($julianCenturies); 182 | $geomMeanAnomalySun = self::getSunGeometricMeanAnomaly($julianCenturies); 183 | 184 | $y = tan(deg2rad($epsilon) / 2.0); 185 | $y *= $y; 186 | 187 | $sin2l0 = sin(2.0 * deg2rad($geomMeanLongSun)); 188 | $sinm = sin(deg2rad($geomMeanAnomalySun)); 189 | $cos2l0 = cos(2.0 * deg2rad($geomMeanLongSun)); 190 | $sin4l0 = sin(4.0 * deg2rad($geomMeanLongSun)); 191 | $sin2m = sin(2.0 * deg2rad($geomMeanAnomalySun)); 192 | 193 | $equationOfTime = $y * $sin2l0 - 2.0 * $eccentricityEarthOrbit * $sinm + 4.0 * $eccentricityEarthOrbit * $y 194 | * $sinm * $cos2l0 - 0.5 * $y * $y * $sin4l0 - 1.25 * $eccentricityEarthOrbit * $eccentricityEarthOrbit * $sin2m; 195 | return rad2deg($equationOfTime) * 4.0; // in minutes of time 196 | } 197 | 198 | private static function getSunHourAngleAtSunrise($lat, $solarDec, $zenith) { 199 | $latRad = deg2rad($lat); 200 | $sdRad = deg2rad($solarDec); 201 | 202 | return (acos(cos(deg2rad($zenith)) / (cos($latRad) * cos($sdRad)) - tan($latRad) 203 | * tan($sdRad))); // in radians 204 | } 205 | 206 | private static function getSunHourAngleAtSunset($lat, $solarDec, $zenith) { 207 | $latRad = deg2rad($lat); 208 | $sdRad = deg2rad($solarDec); 209 | 210 | $hourAngle = (acos(cos(deg2rad($zenith)) / (cos($latRad) * cos($sdRad)) 211 | - tan($latRad) * tan($sdRad))); 212 | return -$hourAngle; // in radians 213 | } 214 | 215 | private static function getSolarElevation($calendar, $lat, $lon) { 216 | $julianDay = self::getJulianDay($calendar); 217 | $julianCenturies = self::getJulianCenturiesFromJulianDay($julianDay); 218 | 219 | $eot = self::getEquationOfTime($julianCenturies); 220 | 221 | $longitude = ($calendar->hour + 12.0) 222 | + ($calendar->minute + $eot + $calendar->second / 60.0) / 60.0; 223 | 224 | $longitude = -($longitude * 360.0 / 24.0) % 360.0; 225 | $hourAngle_rad = deg2rad($lon - $longitude); 226 | $declination = self::getSunDeclination($julianCenturies); 227 | $dec_rad = deg2rad($declination); 228 | $lat_rad = deg2rad($lat); 229 | 230 | return rad2deg(asin((sin($lat_rad) * sin($dec_rad)) 231 | + (cos($lat_rad) * cos($dec_rad) * cos($hourAngle_rad)))); 232 | } 233 | 234 | private static function getSolarAzimuth($calendar, $lat, $lon) { 235 | $julianDay = self::getJulianDay($calendar); 236 | $julianCenturies = self::getJulianCenturiesFromJulianDay($julianDay); 237 | 238 | $eot = self::getEquationOfTime($julianCenturies); 239 | 240 | $longitude = ($calendar->hour + 12.0) 241 | + ($calendar->minute + $eot + $calendar->second / 60.0) / 60.0; 242 | 243 | $longitude = -($longitude * 360.0 / 24.0) % 360.0; 244 | $hourAngle_rad = deg2rad($lon - $longitude); 245 | $declination = self::getSunDeclination($julianCenturies); 246 | $dec_rad = deg2rad($declination); 247 | $lat_rad = deg2rad($lat); 248 | 249 | return rad2deg(atan(sin($hourAngle_rad) 250 | / ((cos($hourAngle_rad) * sin($lat_rad)) - (tan($dec_rad) * cos($lat_rad)))))+180; 251 | } 252 | 253 | /* 254 | |-------------------------------------------------------------------------- 255 | | CALCULATIONS 256 | |-------------------------------------------------------------------------- 257 | */ 258 | 259 | private static function getSunriseUTC($julianDay, $latitude, $longitude, $zenith) { 260 | $julianCenturies = self::getJulianCenturiesFromJulianDay($julianDay); 261 | 262 | $noonmin = self::getSolarNoonUTC($julianCenturies, $longitude); 263 | $tnoon = self::getJulianCenturiesFromJulianDay($julianDay + $noonmin / 1440.0); 264 | 265 | $eqTime = self::getEquationOfTime($tnoon); 266 | $solarDec = self::getSunDeclination($tnoon); 267 | $hourAngle = self::getSunHourAngleAtSunrise($latitude, $solarDec, $zenith); 268 | 269 | 270 | $delta = $longitude - rad2deg($hourAngle); 271 | $timeDiff = 4.0 * $delta; 272 | $timeUTC = 720.0 + $timeDiff - $eqTime; 273 | 274 | $newt = self::getJulianCenturiesFromJulianDay(self::getJulianDayFromJulianCenturies($julianCenturies) + $timeUTC 275 | / 1440.0); 276 | $eqTime = self::getEquationOfTime($newt); 277 | $solarDec = self::getSunDeclination($newt); 278 | $hourAngle = self::getSunHourAngleAtSunrise($latitude, $solarDec, $zenith); 279 | 280 | $delta = $longitude - rad2deg($hourAngle); 281 | $timeDiff = 4.0 * $delta; 282 | $timeUTC = 720.0 + $timeDiff - $eqTime; // in minutes 283 | 284 | return $timeUTC; 285 | } 286 | 287 | public function getUTCNoon(Carbon $calendar, GeoLocation $geoLocation) { 288 | $julianDay = self::getJulianDay($calendar); 289 | $julianCenturies = self::getJulianCenturiesFromJulianDay($julianDay); 290 | 291 | $noon = self::getSolarNoonUTC($julianCenturies, -$geoLocation->getLongitude()); 292 | $noon = $noon / 60; 293 | 294 | // ensure that the time is >= 0 and < 24 295 | while ($noon < 0.0) { 296 | $noon += 24.0; 297 | } 298 | while ($noon >= 24.0) { 299 | $noon -= 24.0; 300 | } 301 | return $noon; 302 | } 303 | 304 | private static function getSolarNoonUTC($julianCenturies, $longitude) { 305 | // First pass uses approximate solar noon to calculate eqtime 306 | $tnoon = self::getJulianCenturiesFromJulianDay(self::getJulianDayFromJulianCenturies($julianCenturies) + $longitude 307 | / 360.0); 308 | $eqTime = self::getEquationOfTime($tnoon); 309 | $solNoonUTC = 720.0 + ($longitude * 4.0) - $eqTime; // min 310 | 311 | $newt = self::getJulianCenturiesFromJulianDay(self::getJulianDayFromJulianCenturies($julianCenturies) - 0.5 312 | + $solNoonUTC / 1440.0); 313 | 314 | $eqTime = self::getEquationOfTime($newt); 315 | return 720.0 + ($longitude * 4.0) - $eqTime; // min 316 | } 317 | 318 | private static function getSunsetUTC($julianDay, $latitude, $longitude, $zenith) { 319 | $julianCenturies = self::getJulianCenturiesFromJulianDay($julianDay); 320 | 321 | $noonmin = self::getSolarNoonUTC($julianCenturies, $longitude); 322 | $tnoon = self::getJulianCenturiesFromJulianDay($julianDay + $noonmin / 1440.0); 323 | 324 | $eqTime = self::getEquationOfTime($tnoon); 325 | $solarDec = self::getSunDeclination($tnoon); 326 | $hourAngle = self::getSunHourAngleAtSunset($latitude, $solarDec, $zenith); 327 | 328 | 329 | $delta = $longitude - rad2deg($hourAngle); 330 | $timeDiff = 4.0 * $delta; 331 | $timeUTC = 720.0 + $timeDiff - $eqTime; 332 | 333 | $newt = self::getJulianCenturiesFromJulianDay(self::getJulianDayFromJulianCenturies($julianCenturies) + $timeUTC 334 | / 1440.0); 335 | $eqTime = self::getEquationOfTime($newt); 336 | $solarDec = self::getSunDeclination($newt); 337 | $hourAngle = self::getSunHourAngleAtSunset($latitude, $solarDec, $zenith); 338 | 339 | $delta = $longitude - rad2deg($hourAngle); 340 | $timeDiff = 4.0 * $delta; 341 | $timeUTC = 720.0 + $timeDiff - $eqTime; // in minutes 342 | 343 | return $timeUTC; 344 | } 345 | } -------------------------------------------------------------------------------- /src/Calculator/SunTimesCalculator.php: -------------------------------------------------------------------------------- 1 | getElevation() : 0; 46 | $adjustedZenith = $this->adjustZenith($zenith, $elevation); 47 | $doubleTime = self::getTimeUTC($calendar, $geoLocation, $adjustedZenith, true); 48 | return $doubleTime; 49 | } 50 | 51 | public function getUTCSunset(Carbon $calendar, GeoLocation $geoLocation, $zenith, $adjustForElevation) { 52 | $elevation = $adjustForElevation ? $geoLocation->getElevation() : 0; 53 | $adjustedZenith = $this->adjustZenith($zenith, $elevation); 54 | $doubleTime = self::getTimeUTC($calendar, $geoLocation, $adjustedZenith, false); 55 | return $doubleTime; 56 | } 57 | 58 | /* 59 | |-------------------------------------------------------------------------- 60 | | STATIC FUNCTIONS 61 | |-------------------------------------------------------------------------- 62 | */ 63 | 64 | private static function sinDeg($deg) { 65 | return sin($deg * 2.0 * pi() / 360.0); 66 | } 67 | 68 | private static function acosDeg($x) { 69 | return acos($x) * 360.0 / (2 * pi()); 70 | } 71 | 72 | private static function asinDeg($x) { 73 | return asin($x) * 360.0 / (2 * pi()); 74 | } 75 | 76 | private static function tanDeg($deg) { 77 | return tan($deg * 2.0 * pi() / 360.0); 78 | } 79 | 80 | private static function cosDeg($deg) { 81 | return cos($deg * 2.0 * pi() / 360.0); 82 | } 83 | 84 | private static function getHoursFromMeridian($longitude) { 85 | return $longitude / self::DEG_PER_HOUR; 86 | } 87 | 88 | private static function getApproxTimeDays($dayOfYear, $hoursFromMeridian, $isSunrise) { 89 | if ($isSunrise) { 90 | return $dayOfYear + ((6.0 - $hoursFromMeridian) / 24); 91 | } else { 92 | return $dayOfYear + ((18.0 - $hoursFromMeridian) / 24); 93 | } 94 | } 95 | 96 | private static function getMeanAnomaly($dayOfYear, $longitude, $isSunrise) { 97 | $hoursFromMeridian = self::getHoursFromMeridian($longitude); 98 | $approxTimeDays = self::getApproxTimeDays($dayOfYear, $hoursFromMeridian, $isSunrise); 99 | return (0.9856 * $approxTimeDays) - 3.289; 100 | } 101 | 102 | private static function getSunTrueLongitude($sunMeanAnomaly) { 103 | $l = $sunMeanAnomaly + (1.916 * self::sinDeg($sunMeanAnomaly)) + (0.020 * self::sinDeg(2 * $sunMeanAnomaly)) + 282.634; 104 | 105 | if ($l >= 360.0) { 106 | $l -= 360.0; 107 | } 108 | if ($l < 0) { 109 | $l += 360.0; 110 | } 111 | 112 | return $l; 113 | } 114 | 115 | private static function getSunRightAscensionHours($sunTrueLongitude) { 116 | $a = 0.91764 * self::tanDeg($sunTrueLongitude); 117 | $ra = 360.0 / (2.0 * pi()) * atan($a); 118 | 119 | $lQuadrant = floor($sunTrueLongitude / 90.0) * 90.0; 120 | $raQuadrant = floor($ra / 90.0) * 90.0; 121 | $ra = $ra + ($lQuadrant - $raQuadrant); 122 | 123 | return $ra / self::DEG_PER_HOUR; 124 | } 125 | 126 | private static function getCosLocalHourAngle($sunTrueLongitude, $latitude, $zenith) { 127 | $sinDec = 0.39782 * self::sinDeg($sunTrueLongitude); 128 | $cosDec = self::cosDeg(self::asinDeg($sinDec)); 129 | 130 | $cosLocalHourAngle = (self::cosDeg($zenith) - ($sinDec * self::sinDeg($latitude))) / ($cosDec * self::cosDeg($latitude)); 131 | return $cosLocalHourAngle; 132 | } 133 | 134 | /* 135 | |-------------------------------------------------------------------------- 136 | | CALCULATIONS 137 | |-------------------------------------------------------------------------- 138 | */ 139 | 140 | private static function getLocalMeanTime($localHour, $sunRightAscensionHours, $approxTimeDays) { 141 | return $localHour + $sunRightAscensionHours - (0.06571 * $approxTimeDays) - 6.622; 142 | } 143 | 144 | private static function getTimeUTC($calendar, $geoLocation, $zenith, $isSunrise) { 145 | $dayOfYear = $calendar->format("z") + 1; 146 | $sunMeanAnomaly = self::getMeanAnomaly($dayOfYear, $geoLocation->getLongitude(), $isSunrise); 147 | $sunTrueLong = self::getSunTrueLongitude($sunMeanAnomaly); 148 | $sunRightAscensionHours = self::getSunRightAscensionHours($sunTrueLong); 149 | $cosLocalHourAngle = self::getCosLocalHourAngle($sunTrueLong, $geoLocation->getLatitude(), $zenith); 150 | 151 | $localHourAngle = 0; 152 | if ($isSunrise) { 153 | $localHourAngle = 360.0 - self::acosDeg($cosLocalHourAngle); 154 | } else { 155 | $localHourAngle = self::acosDeg($cosLocalHourAngle); 156 | } 157 | 158 | $localHour = $localHourAngle / self::DEG_PER_HOUR; 159 | 160 | $approxTimeDays = self::getApproxTimeDays($dayOfYear, self::getHoursFromMeridian($geoLocation->getLongitude()), $isSunrise); 161 | $localMeanTime = self::getLocalMeanTime($localHour, $sunRightAscensionHours, $approxTimeDays); 162 | $processedTime = $localMeanTime - self::getHoursFromMeridian($geoLocation->getLongitude()); 163 | 164 | while ($processedTime < 0.0) { 165 | $processedTime += 24.0; 166 | } 167 | while ($processedTime >= 24.0) { 168 | $processedTime -= 24.0; 169 | } 170 | 171 | return $processedTime; 172 | } 173 | 174 | public function getUTCNoon(Carbon $calendar, GeoLocation $geoLocation) { 175 | $sunrise = $this->getUTCSunrise($calendar, $geoLocation, 90, false); 176 | $sunset = $this->getUTCSunset($calendar, $geoLocation, 90, false); 177 | $noon = $sunrise + (($sunset - $sunrise) / 2); 178 | if ($noon < 0) { 179 | $noon += 12; 180 | } 181 | if ($noon < $sunrise) { 182 | $noon -= 12; 183 | } 184 | } 185 | } -------------------------------------------------------------------------------- /src/Calendar/AstronomicalCalendar.php: -------------------------------------------------------------------------------- 1 | getTimeZone()); 69 | } 70 | 71 | $this->setCalendar($calendar ?? Carbon::now("UTC")); 72 | $this->setGeoLocation($geoLocation); // duplicate call 73 | $this->setAstronomicalCalculator(AstronomicalCalculator::getDefault()); 74 | } 75 | 76 | /* 77 | |-------------------------------------------------------------------------- 78 | | SUNRISE 79 | |-------------------------------------------------------------------------- 80 | */ 81 | 82 | public function getSunrise() { 83 | $sunrise = $this->getUTCSunrise(self::GEOMETRIC_ZENITH); 84 | if (is_nan($sunrise)) { 85 | return null; 86 | } else { 87 | return $this->getDateFromTime($sunrise, true); 88 | } 89 | } 90 | 91 | public function getSeaLevelSunrise() { 92 | $sunrise = $this->getUTCSeaLevelSunrise(self::GEOMETRIC_ZENITH); 93 | if (is_nan($sunrise)) { 94 | return null; 95 | } else { 96 | return $this->getDateFromTime($sunrise, true); 97 | } 98 | } 99 | 100 | public function getBeginCivilTwilight() { 101 | return $this->getSunriseOffsetByDegrees(self::CIVIL_ZENITH); 102 | } 103 | 104 | public function getBeginNauticalTwilight() { 105 | return $this->getSunriseOffsetByDegrees(self::NAUTICAL_ZENITH); 106 | } 107 | 108 | public function getBeginAstronomicalTwilight() { 109 | return $this->getSunriseOffsetByDegrees(self::ASTRONOMICAL_ZENITH); 110 | } 111 | 112 | /* 113 | |-------------------------------------------------------------------------- 114 | | SUNSET 115 | |-------------------------------------------------------------------------- 116 | */ 117 | 118 | public function getSunset() { 119 | $sunset = $this->getUTCSunset(self::GEOMETRIC_ZENITH); 120 | if (is_nan($sunset)) { 121 | return null; 122 | } else { 123 | return $this->getDateFromTime($sunset, false); 124 | } 125 | } 126 | 127 | public function getSeaLevelSunset() { 128 | $sunset = $this->getUTCSeaLevelSunset(self::GEOMETRIC_ZENITH); 129 | if (is_nan($sunset)) { 130 | return null; 131 | } else { 132 | return $this->getDateFromTime($sunset, false); 133 | } 134 | } 135 | 136 | public function getEndCivilTwilight() { 137 | return $this->getSunsetOffsetByDegrees(self::CIVIL_ZENITH); 138 | } 139 | 140 | public function getEndNauticalTwilight() { 141 | return $this->getSunsetOffsetByDegrees(self::NAUTICAL_ZENITH); 142 | } 143 | 144 | public function getEndAstronomicalTwilight() { 145 | return $this->getSunsetOffsetByDegrees(self::ASTRONOMICAL_ZENITH); 146 | } 147 | 148 | /* 149 | |-------------------------------------------------------------------------- 150 | | UTILITIES 151 | |-------------------------------------------------------------------------- 152 | */ 153 | 154 | public function getTimeOffset($time, $offset) { 155 | if ($time == null || $offset == null) { 156 | return null; 157 | } 158 | 159 | return Carbon::createFromTimestamp($time->format("U.u") + ($offset / 1000), $time->getTimeZone()); 160 | } 161 | 162 | public function getSunriseOffsetByDegrees($offsetZenith) { 163 | $dawn = $this->getUTCSunrise($offsetZenith); 164 | if (is_nan($dawn)) { 165 | return null; 166 | } else { 167 | return $this->getDateFromTime($dawn, true); 168 | } 169 | } 170 | 171 | public function getSunsetOffsetByDegrees($offsetZenith) { 172 | $sunset = $this->getUTCSunset($offsetZenith); 173 | if (is_nan($sunset)) { 174 | return null; 175 | } else { 176 | return $this->getDateFromTime($sunset, false); 177 | } 178 | } 179 | 180 | public function getUTCSunrise($zenith) { 181 | return $this->getAstronomicalCalculator()->getUTCSunrise($this->getAdjustedCalendar(), $this->getGeoLocation(), $zenith, true); 182 | } 183 | 184 | public function getUTCSeaLevelSunrise($zenith) { 185 | return $this->getAstronomicalCalculator()->getUTCSunrise($this->getAdjustedCalendar(), $this->getGeoLocation(), $zenith, false); 186 | } 187 | 188 | public function getUTCSunset($zenith) { 189 | return $this->getAstronomicalCalculator()->getUTCSunset($this->getAdjustedCalendar(), $this->getGeoLocation(), $zenith, true); 190 | } 191 | 192 | public function getUTCSeaLevelSunset($zenith) { 193 | return $this->getAstronomicalCalculator()->getUTCSunset($this->getAdjustedCalendar(), $this->getGeoLocation(), $zenith, false); 194 | } 195 | 196 | public function getTemporalHour(Carbon $startOfDay = null, Carbon $endOfDay = null) { 197 | if ($startOfDay == null && $endOfDay == null) { 198 | $startOfDay = $this->getSeaLevelSunrise(); 199 | $endOfDay = $this->getSeaLevelSunset(); 200 | } 201 | 202 | if ($startOfDay == null || $endOfDay == null) { 203 | return null; 204 | } 205 | 206 | $startOfDayTotal = $startOfDay->getPreciseTimestamp(); 207 | $endOfDayTotal = $endOfDay->getPreciseTimestamp(); 208 | 209 | $dayTimeHours = $endOfDayTotal - $startOfDayTotal; 210 | return $dayTimeHours / 12000; 211 | } 212 | 213 | public function getSunTransit(Carbon $startOfDay = null, Carbon $endOfDay = null) { 214 | if ($startOfDay == null && $endOfDay == null) { 215 | $noon = $this->getAstronomicalCalculator()->getUTCNoon($this->getAdjustedCalendar(), $this->getGeoLocation()); 216 | 217 | return $this->getDateFromTime($noon, false); 218 | } 219 | 220 | $temporalHour = $this->getTemporalHour($startOfDay, $endOfDay); 221 | if (is_null($temporalHour)) { 222 | return null; 223 | } 224 | 225 | return $this->getTimeOffset($startOfDay, $temporalHour * 6); 226 | } 227 | 228 | public function getSolarMidnight() { 229 | $clonedCal = $this->copy(); 230 | $clonedCal->getCalendar()->addDay(); 231 | $chatzos_today = $this->getSunTransit(); 232 | $chatzos_tomorrow = $clonedCal->getSunTransit(); 233 | return $this->getTimeOffset($chatzos_today, ($chatzos_tomorrow->getPreciseTimestamp() - $chatzos_today->getPreciseTimestamp()) / 2000); 234 | } 235 | 236 | protected function getDateFromTime($time, $isSunrise) { 237 | if (is_nan($time)) { 238 | return null; 239 | } 240 | 241 | $calculatedTime = $time; 242 | 243 | $hours = (int) $calculatedTime; // retain only the hours 244 | $calculatedTime -= $hours; 245 | $minutes = (int) ($calculatedTime *= 60); // retain only the minutes 246 | $calculatedTime -= $minutes; 247 | $seconds = (int) ($calculatedTime *= 60); // retain only the seconds 248 | $calculatedTime -= $seconds; 249 | $microseconds = (int) ($calculatedTime * 1000000); 250 | 251 | // Check if a date transition has occurred, or is about to occur - this indicates the date of the event is 252 | // actually not the target date, but the day prior or after 253 | 254 | $adjustedCalendar = $this->getAdjustedCalendar(); 255 | $calendar = Carbon::create($adjustedCalendar->year, $adjustedCalendar->month, $adjustedCalendar->day, $hours, $minutes, $seconds, "UTC"); 256 | 257 | $localTimeHours = (int) $this->getGeoLocation()->getLongitude() / 15; 258 | if ($isSunrise && $localTimeHours + $hours > 18) { 259 | $calendar->subDay(); 260 | } else if (!$isSunrise && $localTimeHours + $hours < 6) { 261 | $calendar->addDay(); 262 | } 263 | 264 | $calendar->setTime($hours, $minutes, $seconds, $microseconds); 265 | $calendar->setTimeZone($this->getGeoLocation()->getTimeZone()); 266 | return $calendar; 267 | } 268 | 269 | public function getSunriseSolarDipFromOffset($minutes) { 270 | $offsetByDegrees = $this->getSeaLevelSunrise(); 271 | $offsetByTime = $this->getTimeOffset($this->getSeaLevelSunrise(), -($minutes * self::MINUTE_MILLIS)); 272 | 273 | $degrees = 0; 274 | $incrementor = 0.0001; 275 | while ( 276 | $offsetByDegrees == null || 277 | ($minutes < 0.0 && $offsetByDegrees->lt($offsetByTime)) || 278 | ($minutes > 0.0 && $offsetByDegrees->gt($offsetByTime)) 279 | ) { 280 | if ($minutes > 0.0) { 281 | $degrees = $degrees + $incrementor; 282 | } else { 283 | $degrees = $degrees - $incrementor; 284 | } 285 | $offsetByDegrees = $this->getSunriseOffsetByDegrees(self::GEOMETRIC_ZENITH + $degrees); 286 | } 287 | return $degrees; 288 | } 289 | 290 | public function getSunsetSolarDipFromOffset($minutes) { 291 | $offsetByDegrees = $this->getSeaLevelSunset(); 292 | $offsetByTime = $this->getTimeOffset($this->getSeaLevelSunset(), $minutes * GeoLocation::MINUTE_MILLIS); 293 | 294 | $degrees = 0; 295 | $incrementor = 0.0001; 296 | while ( 297 | $offsetByDegrees == null || 298 | ($minutes > 0.0 && $offsetByDegrees->lt($offsetByTime)) || 299 | ($minutes < 0.0 && $offsetByDegrees->gt($offsetByTime)) 300 | ) { 301 | if ($minutes > 0.0) { 302 | $degrees = $degrees + $incrementor; 303 | } else { 304 | $degrees = $degrees - $incrementor; 305 | } 306 | $offsetByDegrees = $this->getSunsetOffsetByDegrees(self::GEOMETRIC_ZENITH + $degrees); 307 | } 308 | return $degrees; 309 | } 310 | 311 | /* 312 | |-------------------------------------------------------------------------- 313 | | HELPERS 314 | |-------------------------------------------------------------------------- 315 | */ 316 | 317 | public function getLocalMeanTime($hours) { 318 | if($hours < 0 || $hours >= 24) { 319 | throw new \Exception("Hours must between 0 and 23.9999..."); 320 | } 321 | 322 | return $this->getTimeOffset($this->getDateFromTime($hours - $this->getGeoLocation()->getStandardTimeOffset() 323 | / AstronomicalCalendar::HOUR_MILLIS, true), -$this->getGeoLocation()->getLocalMeanTimeOffset()); 324 | } 325 | 326 | private function getAdjustedCalendar() { 327 | $offset = $this->getGeoLocation()->getAntimeridianAdjustment(); 328 | if ($offset == 0) { 329 | return $this->getCalendar(); 330 | } 331 | $adjustedCalendar = $this->getCalendar()->copy(); 332 | $adjustedCalendar->addDays($offset); 333 | return $adjustedCalendar; 334 | } 335 | 336 | public function equals($astronomicalCalendar) { 337 | if ($this == $astronomicalCalendar) { 338 | return true; 339 | } 340 | if (!($object instanceof AstronomicalCalendar)) { 341 | return false; 342 | } 343 | 344 | return $this->getCalendar()->eq($astronomicalCalendar->getCalendar()) && 345 | $this->getGeoLocation()->equals($astronomicalCalendar->getGeoLocation()) && 346 | $this->getAstronomicalCalculator()->getCalculatorName() == $astronomicalCalendar->getAstronomicalCalculator()->getCalculatorName(); 347 | } 348 | 349 | public function getGeoLocation() { 350 | return $this->geoLocation; 351 | } 352 | 353 | public function setGeoLocation(GeoLocation $geoLocation) { 354 | $this->geoLocation = $geoLocation; 355 | 356 | return $this; 357 | } 358 | 359 | public function getAstronomicalCalculator() { 360 | return $this->astronomicalCalculator; 361 | } 362 | 363 | public function setAstronomicalCalculator(AstronomicalCalculator $astronomicalCalculator) { 364 | $this->astronomicalCalculator = $astronomicalCalculator; 365 | 366 | return $this; 367 | } 368 | 369 | public function getCalendar() { 370 | return $this->calendar; 371 | } 372 | 373 | public function setCalendar(Carbon $calendar) { 374 | $calendar->startOfDay(); 375 | $this->calendar = $calendar; 376 | 377 | return $this; 378 | } 379 | 380 | public function setDate($year, $month, $day) { 381 | $this->getCalendar()->setDate($year, $month, $day); 382 | 383 | return $this; 384 | } 385 | 386 | public function addDays($value) { 387 | $this->getCalendar()->addDays($value); 388 | 389 | return $this; 390 | } 391 | 392 | public function subDays($value) { 393 | $this->getCalendar()->subDays($value); 394 | 395 | return $this; 396 | } 397 | 398 | public function copy() { 399 | $astronomicalCalculator = clone $this; 400 | $astronomicalCalculator->setGeoLocation($this->getGeoLocation()->copy()); 401 | $astronomicalCalculator->setCalendar($this->getCalendar()->copy()); 402 | $astronomicalCalculator->setAstronomicalCalculator($this->getAstronomicalCalculator()->copy()); 403 | 404 | return $astronomicalCalculator; 405 | } 406 | 407 | public function setCalculatorType($type) { 408 | switch ($type) { 409 | case 'SunTimes': 410 | $this->setAstronomicalCalculator(new SunTimesCalculator()); 411 | break; 412 | 413 | case 'Noaa': 414 | $this->setAstronomicalCalculator(new NoaaCalculator()); 415 | break; 416 | 417 | default: 418 | throw new \Exception("Only SunTimes and Noaa are implemented currently"); 419 | break; 420 | } 421 | 422 | return $this; 423 | } 424 | 425 | /* 426 | |-------------------------------------------------------------------------- 427 | | MAGIC GETTERS 428 | |-------------------------------------------------------------------------- 429 | */ 430 | 431 | public function get($zman, ...$args) { 432 | $method_name = "get" . ucfirst($zman); 433 | if (method_exists($this, $method_name)) { 434 | return $this->$method_name(...$args); 435 | } else { 436 | throw new Exception("Requested Zman does not exist"); 437 | } 438 | } 439 | 440 | public function __get($arg) { 441 | $response = null; 442 | 443 | try { 444 | $response = $this->get($arg); 445 | } catch (ArgumentCountError $e) { 446 | $response = null; 447 | } catch (Exception $e) { 448 | $response = null; 449 | } 450 | 451 | return $response; 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /src/Calendar/ZmanimCalendar.php: -------------------------------------------------------------------------------- 1 | useElevation; 54 | } 55 | 56 | public function setUseElevation($useElevation) { 57 | $this->useElevation = $useElevation; 58 | 59 | return $this; 60 | } 61 | 62 | public function isUseAstronomicalChatzos() { 63 | return $this->useAstronomicalChatzos; 64 | } 65 | 66 | public function setUseAstronomicalChatzos($useAstronomicalChatzos) { 67 | $this->useAstronomicalChatzos = $useAstronomicalChatzos; 68 | 69 | return $this; 70 | } 71 | 72 | public function isUseAstronomicalChatzosForOtherZmanim() { 73 | return $this->useAstronomicalChatzosForOtherZmanim; 74 | } 75 | 76 | public function setUseAstronomicalChatzosForOtherZmanim($useAstronomicalChatzosForOtherZmanim) { 77 | $this->useAstronomicalChatzosForOtherZmanim = $useAstronomicalChatzosForOtherZmanim; 78 | 79 | return $this; 80 | } 81 | 82 | protected function getElevationAdjustedSunrise() { 83 | if ($this->isUseElevation()) { 84 | return $this->getSunrise(); 85 | } else { 86 | return $this->getSeaLevelSunrise(); 87 | } 88 | } 89 | 90 | protected function getElevationAdjustedSunset() { 91 | if ($this->isUseElevation()) { 92 | return $this->getSunset(); 93 | } else { 94 | return $this->getSeaLevelSunset(); 95 | } 96 | } 97 | 98 | public function getTzais() { 99 | return $this->getSunsetOffsetByDegrees(self::ZENITH_8_POINT_5); 100 | } 101 | 102 | public function getAlosHashachar() { 103 | return $this->getSunriseOffsetByDegrees(self::ZENITH_16_POINT_1); 104 | } 105 | 106 | public function getAlos72() { 107 | return $this->getTimeOffset($this->getElevationAdjustedSunrise(), -72 * AstronomicalCalendar::MINUTE_MILLIS); 108 | } 109 | 110 | public function getChatzos() { 111 | if ($this->useAstronomicalChatzos) { 112 | return $this->getSunTransit(); 113 | } else { 114 | return $this->getChatzosAsHalfDay() ?? $this->getSunTransit(); 115 | } 116 | } 117 | 118 | public function getChatzosAsHalfDay() { 119 | return $this->getSunTransit($this->getSeaLevelSunrise(), $this->getSeaLevelSunset()); 120 | } 121 | 122 | public function getSofZmanShma($startOfDay, $endOfDay) { 123 | return $this->getShaahZmanisBasedZman($startOfDay, $endOfDay, 3); 124 | } 125 | 126 | public function getSofZmanShmaGRA() { 127 | return $this->getSofZmanShma($this->getElevationAdjustedSunrise(), $this->getElevationAdjustedSunset()); 128 | } 129 | 130 | public function getSofZmanShmaMGA() { 131 | return $this->getSofZmanShma($this->getAlos72(), $this->getTzais72()); 132 | } 133 | 134 | public function getTzais72() { 135 | return $this->getTimeOffset($this->getElevationAdjustedSunset(), 72 * AstronomicalCalendar::MINUTE_MILLIS); 136 | } 137 | 138 | public function getCandleLighting() { 139 | return $this->getTimeOffset($this->getSeaLevelSunset(), -$this->getCandleLightingOffset() * AstronomicalCalendar::MINUTE_MILLIS); 140 | } 141 | 142 | public function getSofZmanTfila($startOfDay, $endOfDay) { 143 | return $this->getShaahZmanisBasedZman($startOfDay, $endOfDay, 4); 144 | } 145 | 146 | public function getSofZmanTfilaGRA() { 147 | return $this->getSofZmanTfila($this->getElevationAdjustedSunrise(), $this->getElevationAdjustedSunset()); 148 | } 149 | 150 | public function getSofZmanTfilaMGA() { 151 | return $this->getSofZmanTfila($this->getAlos72(), $this->getTzais72()); 152 | } 153 | 154 | public function getMinchaGedola($startOfDay = null, $endOfDay = null) { 155 | if($this->isUseAstronomicalChatzosForOtherZmanim()) { 156 | $chatzos = $this->getSunTransit(); 157 | $sunset = $endOfDay ?? $this->getSunset(); 158 | 159 | return $this->getHalfDayBasedZman($chatzos, $sunset, 0.5); 160 | } 161 | 162 | if (is_null($startOfDay) && is_null($endOfDay)) { 163 | $startOfDay = $this->getElevationAdjustedSunrise(); 164 | $endOfDay = $this->getElevationAdjustedSunset(); 165 | } 166 | 167 | return $this->getShaahZmanisBasedZman($startOfDay, $endOfDay, 6.5); 168 | } 169 | 170 | public function getSamuchLeMinchaKetana($startOfDay, $endOfDay) { 171 | return $this->getShaahZmanisBasedZman($startOfDay, $endOfDay, 9); 172 | } 173 | 174 | public function getMinchaKetana($startOfDay = null, $endOfDay = null) { 175 | if (is_null($startOfDay) && is_null($endOfDay)) { 176 | $startOfDay = $this->getElevationAdjustedSunrise(); 177 | $endOfDay = $this->getElevationAdjustedSunset(); 178 | } 179 | 180 | return $this->getShaahZmanisBasedZman($startOfDay, $endOfDay, 9.5); 181 | } 182 | 183 | public function getPlagHamincha($startOfDay = null, $endOfDay = null) { 184 | if (is_null($startOfDay) && is_null($endOfDay)) { 185 | $startOfDay = $this->getElevationAdjustedSunrise(); 186 | $endOfDay = $this->getElevationAdjustedSunset(); 187 | } 188 | 189 | return $this->getShaahZmanisBasedZman($startOfDay, $endOfDay, 10.75); 190 | } 191 | 192 | public function getShaahZmanisGra() { 193 | return $this->getTemporalHour($this->getElevationAdjustedSunrise(), $this->getElevationAdjustedSunset()); 194 | } 195 | 196 | public function getShaahZmanisMGA() { 197 | return $this->getTemporalHour($this->getAlos72(), $this->getTzais72()); 198 | } 199 | 200 | public function getCandleLightingOffset() { 201 | return $this->candleLightingOffset; 202 | } 203 | 204 | public function setCandleLightingOffset($candleLightingOffset) { 205 | $this->candleLightingOffset = $candleLightingOffset; 206 | 207 | return $this; 208 | } 209 | 210 | public function isAssurBemlacha(Carbon $currentTime, Carbon $tzais, $inIsrael) { 211 | $jewishCalendar = new JewishCalendar(); 212 | $jewishCalendar->setGregorianDate($this->getCalendar()->year, $this->getCalendar()->month, $this->getCalendar()->day); 213 | $jewishCalendar->setInIsrael($inIsrael); 214 | 215 | if ($jewishCalendar->hasCandleLighting() && $currentTime->gt($this->getElevationAdjustedSunset())) { // erev shabbos, YT or YT sheni after shkiah 216 | return true; 217 | } 218 | 219 | if ($jewishCalendar->isAssurBemelacha() && $currentTime->lt($tzais)) { // is shabbos or YT and it is before tzais 220 | return true; 221 | } 222 | 223 | return false; 224 | } 225 | 226 | public function getShaahZmanisBasedZman($startOfDay, $endOfDay, $hours) { 227 | $shaahZmanis = $this->getTemporalHour($startOfDay, $endOfDay); 228 | return $this->getTimeOffset($startOfDay, $shaahZmanis * $hours); 229 | } 230 | 231 | public function getPercentOfShaahZmanisFromDegrees($degrees, $sunset) { 232 | $seaLevelSunrise = $this->getSeaLevelSunrise(); 233 | $seaLevelSunset = $this->getSeaLevelSunset(); 234 | $twilight = null; 235 | if($sunset) { 236 | $twilight = $this->getSunsetOffsetByDegrees(AstronomicalCalendar::GEOMETRIC_ZENITH + $degrees); 237 | } else { 238 | $twilight = $this->getSunriseOffsetByDegrees(AstronomicalCalendar::GEOMETRIC_ZENITH + $degrees); 239 | } 240 | if($seaLevelSunrise == null || $seaLevelSunset == null || $twilight == null) { 241 | return null; 242 | } 243 | $shaahZmanis = ($seaLevelSunset->getPreciseTimestamp() - $seaLevelSunrise->getPreciseTimestamp()) / 12000.0; 244 | $riseSetToTwilight; 245 | if($sunset) { 246 | $riseSetToTwilight = ($twilight->getPreciseTimestamp() - $seaLevelSunset->getPreciseTimestamp()) / 1000; 247 | } else { 248 | $riseSetToTwilight = ($seaLevelSunrise->getPreciseTimestamp() - $twilight->getPreciseTimestamp()) / 1000; 249 | } 250 | return $riseSetToTwilight / $shaahZmanis; 251 | } 252 | 253 | public function getHalfDayBasedZman($startOfHalfDay, $endOfHalfDay, $hours) { 254 | if ($startOfHalfDay == null || $endOfHalfDay == null) { 255 | return null; 256 | } 257 | $shaahZmanis = ($endOfHalfDay->getPreciseTimestamp() - $startOfHalfDay->getPreciseTimestamp()) / 6; 258 | 259 | return $this->getTimeOffset($startOfHalfDay, $shaahZmanis * $hours); 260 | } 261 | } -------------------------------------------------------------------------------- /src/Geo/GeoLocation.php: -------------------------------------------------------------------------------- 1 | setLocationName($locationName); 56 | $this->setLatitude($latitude); 57 | $this->setLongitude($longitude); 58 | $this->setElevation($elevation); 59 | $this->setTimeZone($timeZone); 60 | } 61 | 62 | /* 63 | |-------------------------------------------------------------------------- 64 | | ELEVATION 65 | |-------------------------------------------------------------------------- 66 | */ 67 | 68 | public function getElevation() { 69 | return $this->elevation; 70 | } 71 | 72 | public function setElevation($elevation) { 73 | if ($elevation < 0) { 74 | throw new \Exception("Elevation cannot be negative"); 75 | } 76 | $this->elevation = $elevation; 77 | 78 | return $this; 79 | } 80 | 81 | /* 82 | |-------------------------------------------------------------------------- 83 | | GETTERS AND SETTERS 84 | |-------------------------------------------------------------------------- 85 | */ 86 | 87 | public function setLatitude($latitude) { 88 | if ($latitude > 90.0 || $latitude < -90.0) { 89 | throw new \Exception("Latitude must be between -90 and 90"); 90 | } 91 | 92 | $this->latitude = $latitude; 93 | 94 | return $this; 95 | } 96 | 97 | public function setLatitudeFromDegrees($degrees, $minutes, $seconds, $direction) { 98 | $tempLat = $degrees + (($minutes + ($seconds / 60.0)) / 60.0); 99 | 100 | if ($tempLat > 90 || $tempLat < 0) { 101 | throw new \Exception("Latitude must be between 0 and 90. Use direction of S instead of negative."); 102 | } 103 | 104 | if ($direction == "S") { 105 | $tempLat *= -1; 106 | } else if ($direction != "N") { 107 | throw new IllegalArgumentException("Latitude direction must be N or S"); 108 | } 109 | 110 | $this->latitude = $tempLat; 111 | 112 | return $this; 113 | } 114 | 115 | public function getLatitude() { 116 | return $this->latitude; 117 | } 118 | 119 | public function setLongitude($longitude) { 120 | if ($longitude > 180.0 || $longitude < -180.0) { 121 | throw new \Exception("Longitude must be between -180 and 180"); 122 | } 123 | 124 | $this->longitude = $longitude; 125 | 126 | return $this; 127 | } 128 | 129 | public function setLongitudeFromDegrees($degrees, $minutes, $seconds, $direction) { 130 | $longTemp = $degrees + (($minutes + ($seconds / 60.0)) / 60.0); 131 | 132 | if ($longTemp > 180 || $longTemp < 0) { 133 | throw new \Exception("Latitude must be between 0 and 180. Use direction of W instead of negative."); 134 | } 135 | 136 | if ($direction == "W") { 137 | $longTemp *= -1; 138 | } else if ($direction != "E") { 139 | throw new IllegalArgumentException("Logitude direction must be E or W"); 140 | } 141 | 142 | $this->longitude = $longTemp; 143 | 144 | return $this; 145 | } 146 | 147 | public function getLongitude() { 148 | return $this->longitude; 149 | } 150 | 151 | public function getLocationName() { 152 | return $this->locationName; 153 | } 154 | 155 | public function setLocationName($locationName) { 156 | $this->locationName = $locationName; 157 | 158 | return $this; 159 | } 160 | 161 | public function getTimeZone() { 162 | return $this->timeZone; 163 | } 164 | 165 | public function setTimeZone($timeZone) { 166 | $this->timeZone = $timeZone; 167 | 168 | return $this; 169 | } 170 | 171 | /* 172 | |-------------------------------------------------------------------------- 173 | | FUNCTIONS 174 | |-------------------------------------------------------------------------- 175 | */ 176 | 177 | public function getLocalMeanTimeOffset() { 178 | $timeZone = new DateTimeZone($this->getTimeZone()); 179 | $utc = new DateTimeZone("UTC"); 180 | $utc_date = new DateTime("now", $utc); 181 | $offset = $timeZone->getOffset($utc_date) * 1000; 182 | return $this->getLongitude() * 4 * self::MINUTE_MILLIS - $offset; 183 | } 184 | 185 | public function getStandardTimeOffset() { 186 | $timeZone = new DateTimeZone($this->getTimeZone()); 187 | $utc = new DateTimeZone("UTC"); 188 | $utc_date = new DateTime("now", $utc); 189 | $offset = $timeZone->getOffset($utc_date) * 1000; 190 | return $offset; 191 | } 192 | 193 | public function getAntimeridianAdjustment() { 194 | $localHoursOffset = $this->getLocalMeanTimeOffset() / self::HOUR_MILLIS; 195 | 196 | if ($localHoursOffset >= 20){ 197 | return 1; 198 | } else if ($localHoursOffset <= -20) { 199 | return -1; 200 | } 201 | return 0; 202 | } 203 | 204 | /* 205 | |-------------------------------------------------------------------------- 206 | | GEODESIC FORMULAS 207 | |-------------------------------------------------------------------------- 208 | */ 209 | 210 | public function getGeodesicInitialBearing(GeoLocation $geoLocation) { 211 | return $this->vincentyFormula($geoLocation, self::INITIAL_BEARING); 212 | } 213 | 214 | public function getGeodesicFinalBearing(GeoLocation $geoLocation) { 215 | return $this->vincentyFormula($geoLocation, self::FINAL_BEARING); 216 | } 217 | 218 | public function getGeodesicDistance(GeoLocation $geoLocation) { 219 | return $this->vincentyFormula($geoLocation, self::DISTANCE); 220 | } 221 | 222 | private function vincentyFormula(GeoLocation $geoLocation, $formula) { 223 | $a = 6378137; // Equitorial Radius 224 | $b = 6356752.3142; // Polar Radius 225 | $f = 1 / 298.257223563; // WGS-84 ellipsiod 226 | $L = deg2rad($geoLocation->getLongitude() - $this->getLongitude()); 227 | $U1 = atan((1 - $f) * tan(deg2rad($this->getLatitude()))); 228 | $U2 = atan((1 - $f) * tan(deg2rad($geoLocation->getLatitude()))); 229 | $sinU1 = sin($U1); 230 | $cosU1 = cos($U1); 231 | $sinU2 = sin($U2); 232 | $cosU2 = cos($U2); 233 | 234 | $lambda = $L; 235 | $lambdaP = 2 * pi(); 236 | $iterLimit = 20; 237 | 238 | $sinLambda = 0; 239 | $cosLambda = 0; 240 | $sinSigma = 0; 241 | $cosSigma = 0; 242 | $sigma = 0; 243 | $sinAlpha = 0; 244 | $cosSqAlpha = 0; 245 | $cos2SigmaM = 0; 246 | 247 | $C; 248 | 249 | while (abs($lambda - $lambdaP) > 1e-12 && --$iterLimit > 0) { 250 | $sinLambda = sin($lambda); 251 | $cosLambda = cos($lambda); 252 | $sinSigma = sqrt(($cosU2 * $sinLambda) * ($cosU2 * $sinLambda) 253 | + ($cosU1 * $sinU2 - $sinU1 * $cosU2 * $cosLambda) * ($cosU1 * $sinU2 - $sinU1 * $cosU2 * $cosLambda)); 254 | 255 | if ($sinSigma == 0) { 256 | return 0; // co-incident points 257 | } 258 | 259 | $cosSigma = $sinU1 * $sinU2 + $cosU1 * $cosU2 * $cosLambda; 260 | $sigma = atan2($sinSigma, $cosSigma); 261 | $sinAlpha = $cosU1 * $cosU2 * $sinLambda / $sinSigma; 262 | $cosSqAlpha = 1 - $sinAlpha * $sinAlpha; 263 | $cos2SigmaM = $cosSigma - 2 * $sinU1 * $sinU2 / $cosSqAlpha; 264 | 265 | if (is_nan($cos2SigmaM)) { 266 | $cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (6) 267 | } 268 | 269 | $C = $f / 16 * $cosSqAlpha * (4 + $f * (4 - 3 * $cosSqAlpha)); 270 | $lambdaP = $lambda; 271 | $lambda = $L + (1 - $C) * $f * $sinAlpha 272 | * ($sigma + $C * $sinSigma * ($cos2SigmaM + $C * $cosSigma * (-1 + 2 * $cos2SigmaM * $cos2SigmaM))); 273 | } 274 | 275 | if ($iterLimit == 0) { 276 | return false; // formula failed to converge 277 | } 278 | 279 | $uSq = $cosSqAlpha * ($a * $a - $b * $b) / ($b * $b); 280 | $A = 1 + $uSq / 16384 * (4096 + $uSq * (-768 + $uSq * (320 - 175 * $uSq))); 281 | $B = $uSq / 1024 * (256 + $uSq * (-128 + $uSq * (74 - 47 * $uSq))); 282 | $deltaSigma = $B 283 | * $sinSigma 284 | * ($cos2SigmaM + $B 285 | / 4 286 | * ($cosSigma * (-1 + 2 * $cos2SigmaM * $cos2SigmaM) - $B / 6 * $cos2SigmaM 287 | * (-3 + 4 * $sinSigma * $sinSigma) * (-3 + 4 * $cos2SigmaM * $cos2SigmaM))); 288 | $distance = $b * $A * ($sigma - $deltaSigma); 289 | 290 | // initial bearing 291 | $fwdAz = rad2deg(atan2($cosU2 * $sinLambda, $cosU1 * $sinU2 - $sinU1 * $cosU2 * $cosLambda)); 292 | // final bearing 293 | $revAz = rad2deg(atan2($cosU1 * $sinLambda, -$sinU1 * $cosU2 + $cosU1 * $sinU2 * $cosLambda)); 294 | 295 | if ($formula == self::DISTANCE) { 296 | return $distance; 297 | } else if ($formula == self::INITIAL_BEARING) { 298 | return $fwdAz; 299 | } else if ($formula == self::FINAL_BEARING) { 300 | return $revAz; 301 | } else { // should never happpen 302 | return false; 303 | } 304 | } 305 | 306 | public function getRhumbLineBearing(GeoLocation $geoLocation) { 307 | $dLon = deg2rad($geoLocation->getLongitude() - $this->getLongitude()); 308 | $dPhi = log(tan(deg2rad($geoLocation->getLatitude()) / 2 + pi() / 4) 309 | / tan(deg2rad($this->getLatitude()) / 2 + pi() / 4)); 310 | if (abs($dLon) > pi()) { 311 | $dLon = $dLon > 0 ? -(2 * pi() - $dLon) : (2 * pi() + $dLon); 312 | } 313 | return rad2deg(atan2($dLon, $dPhi)); 314 | } 315 | 316 | public function getRhumbLineDistance(GeoLocation $geoLocation) { 317 | $earthRadius = 6378137; // Earth's radius in meters (WGS-84) 318 | $dLat = deg2rad($geoLocation->getLatitude()) - deg2rad($this->getLatitude()); 319 | $dLon = abs(deg2rad($geoLocation->getLongitude()) - deg2rad($this->getLongitude())); 320 | $dPhi = log(tan(deg2rad($geoLocation->getLatitude()) / 2 + pi() / 4) 321 | / tan(deg2rad($this->getLatitude()) / 2 + pi() / 4)); 322 | $q = $dLat / $dPhi; 323 | 324 | if (!is_finite($q)) { 325 | $q = cos(deg2rad($this->getLatitude())); 326 | } 327 | // if $dLon over 180° take shorter rhumb across 180° meridian: 328 | if ($dLon > pi()) { 329 | $dLon = 2 * pi() - $dLon; 330 | } 331 | $d = sqrt($dLat * $dLat + $q * $q * $dLon * $dLon); 332 | return $d * $earthRadius; 333 | } 334 | 335 | /* 336 | |-------------------------------------------------------------------------- 337 | | HELPER METHODS 338 | |-------------------------------------------------------------------------- 339 | */ 340 | 341 | public function equals($geoLocation) { 342 | if ($this == $geoLocation) { 343 | return true; 344 | } 345 | if (!($object instanceof GeoLocation)) { 346 | return false; 347 | } 348 | 349 | return $this->latitude == $geoLocation->latitude 350 | && $this->longitude == $geoLocation->longitude 351 | && $this->elevation == $geoLocation->elevation 352 | && $this->locationName == $geoLocation->locationName 353 | && $this->timeZone == $geoLocation->timeZone; 354 | } 355 | 356 | /* 357 | |-------------------------------------------------------------------------- 358 | | CLONEABLE 359 | |-------------------------------------------------------------------------- 360 | */ 361 | 362 | public function copy() { 363 | return clone $this; 364 | } 365 | } -------------------------------------------------------------------------------- /src/Geo/GeoLocationUtils.php: -------------------------------------------------------------------------------- 1 | getGeodesicInitialBearing($destination); 34 | } 35 | 36 | public static function getGeodesicFinalBearing(GeoLocation $geoLocation, GeoLocation $destination) { 37 | return $geoLocation->getGeodesicFinalBearing($destination); 38 | } 39 | 40 | public static function getGeodesicDistance(GeoLocation $geoLocation, GeoLocation $destination) { 41 | return $geoLocation->getGeodesicDistance($destination); 42 | } 43 | 44 | public static function getRhumbLineBearing(GeoLocation $geoLocation, GeoLocation $destination) { 45 | return $geoLocation->getRhumbLineBearing($destination); 46 | } 47 | 48 | public static function getRhumbLineDistance(GeoLocation $geoLocation, GeoLocation $destination) { 49 | return $geoLocation->getRhumbLineDistance($destination); 50 | } 51 | } -------------------------------------------------------------------------------- /src/HebrewCalendar/Daf.php: -------------------------------------------------------------------------------- 1 | masechtaNumber = $masechtaNumber; 88 | $this->daf = $daf; 89 | } 90 | 91 | /* 92 | |-------------------------------------------------------------------------- 93 | | GETTERS AND SETTERS 94 | |-------------------------------------------------------------------------- 95 | */ 96 | 97 | public function getMasechtaNumber() { 98 | return $this->masechtaNumber; 99 | } 100 | 101 | public function setMasechtaNumber($masechtaNumber) { 102 | $this->masechtaNumber = $masechtaNumber; 103 | 104 | return $this; 105 | } 106 | 107 | public function getDaf() { 108 | return $this->daf; 109 | } 110 | 111 | public function setDaf($daf) { 112 | $this->daf = $daf; 113 | 114 | return $this; 115 | } 116 | 117 | /* 118 | |-------------------------------------------------------------------------- 119 | | BAVLI MASECHTA INFORMATION 120 | |-------------------------------------------------------------------------- 121 | */ 122 | 123 | public function getMasechtaTransliterated() { 124 | return self::$masechtosBavliTransliterated[$this->masechtaNumber]; 125 | } 126 | 127 | public function setMasechtaTransliterated($masechtosBavliTransliterated) { 128 | self::$masechtosBavliTransliterated = $masechtosBavliTransliterated; 129 | } 130 | 131 | public function getMasechta() { 132 | return self::$masechtosBavli[$this->masechtaNumber]; 133 | } 134 | 135 | /* 136 | |-------------------------------------------------------------------------- 137 | | YERUSHALMI MASECHTA INFORMATION 138 | |-------------------------------------------------------------------------- 139 | */ 140 | 141 | public function getYerushalmiMasechtaTransliterated() { 142 | return self::$masechtosYerushalmiTransliterated[$this->masechtaNumber]; 143 | } 144 | 145 | public function setYerushalmiMasechtaTransliterated($masechtosYerushalmiTransliterated) { 146 | self::$masechtosYerushalmiTransliterated = $masechtosYerushalmiTransliterated; 147 | } 148 | 149 | public static function getYerushalmiMasechtosTransliterated() { 150 | return self::$masechtosYerushalmiTransliterated; 151 | } 152 | 153 | public static function getYerushalmiMasechtos() { 154 | return self::$masechtosYerushalmi; 155 | } 156 | 157 | public function getYerushalmiMasechta() { 158 | return self::$masechtosYerushalmi[$this->masechtaNumber]; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/HebrewCalendar/Parsha.php: -------------------------------------------------------------------------------- 1 | getYomTovIndex(); 48 | $day = $jewishCalendar->getJewishDayOfMonth(); 49 | $month = $jewishCalendar->getJewishMonth(); 50 | 51 | if ($jewishCalendar->getDayOfWeek() == 7 52 | || (!self::$tachanunRecitedSundays && $jewishCalendar->getDayOfWeek() == 1) 53 | || (!self::$tachanunRecitedFridays && $jewishCalendar->getDayOfWeek() == 6) 54 | || $month == JewishDate::NISSAN 55 | || ($month == JewishDate::TISHREI && ((!self::$tachanunRecitedEndOfTishrei && $day > 8) 56 | || (self::$tachanunRecitedEndOfTishrei && ($day > 8 && $day < 22)))) 57 | || ($month == JewishDate::SIVAN && (self::$tachanunRecitedWeekAfterShavuos && $day < 7 58 | || !self::$tachanunRecitedWeekAfterShavuos && $day < (!$jewishCalendar->getInIsrael() 59 | && !self::$tachanunRecited13SivanOutOfIsrael ? 14: 13))) 60 | || ($jewishCalendar->isYomTov($holidayIndex) && (! $jewishCalendar->isTaanis($holidayIndex) 61 | || (!self::$tachanunRecitedPesachSheni && $holidayIndex == JewishCalendar::PESACH_SHENI))) // Erev YT is included in isYomTov() 62 | || (!$jewishCalendar->getInIsrael() && !self::$tachanunRecitedPesachSheni && !self::$tachanunRecited15IyarOutOfIsrael 63 | && $jewishCalendar->getJewishMonth() == JewishDate::IYAR && $day == 15) 64 | || $holidayIndex == JewishCalendar::TISHA_BEAV || $jewishCalendar->isIsruChag($holidayIndex) 65 | || $jewishCalendar->isRoshChodesh() 66 | || (!self::$tachanunRecitedShivasYemeiHamiluim && 67 | ((!$jewishCalendar->isJewishLeapYear() && $month == JewishDate::ADAR) 68 | || ($jewishCalendar->isJewishLeapYear() && $month == JewishDate::ADAR_II)) && $day > 22) 69 | || (!self::$tachanunRecitedWeekOfPurim && 70 | ((!$jewishCalendar->isJewishLeapYear() && $month == JewishDate::ADAR) 71 | || ($jewishCalendar->isJewishLeapYear() && $month == JewishDate::ADAR_II)) && $day > 10 && $day < 18) 72 | || ($jewishCalendar->isUseModernHolidays() 73 | && ($holidayIndex == JewishCalendar::YOM_HAATZMAUT || $holidayIndex == JewishCalendar::YOM_YERUSHALAYIM)) 74 | || (!self::$tachanunRecitedWeekOfHod && $month == JewishDate::IYAR && $day > 13 && $day < 21)) { 75 | return false; 76 | } 77 | return true; 78 | } 79 | 80 | public static function isTachanunRecitedMincha(JewishCalendar $jewishCalendar) { 81 | $tomorrow = $jewishCalendar->copy()->addDays(1); 82 | $tomorrowHolidayIndex = $jewishCalendar->getYomTovIndex(); 83 | 84 | if (!self::$tachanunRecitedMinchaAllYear 85 | || $jewishCalendar->getDayOfWeek() == 6 86 | || ! self::isTachanunRecitedShacharis($jewishCalendar) 87 | || (! self::isTachanunRecitedShacharis($tomorrow) && 88 | !($tomorrowHolidayIndex == JewishCalendar::EREV_ROSH_HASHANA) && 89 | !($tomorrowHolidayIndex == JewishCalendar::EREV_YOM_KIPPUR) && 90 | !($tomorrowHolidayIndex == JewishCalendar::PESACH_SHENI)) 91 | || ! self::$tachanunRecitedMinchaErevLagBaomer && $tomorrowHolidayIndex == JewishCalendar::LAG_BAOMER) { 92 | return false; 93 | } 94 | return true; 95 | } 96 | 97 | /* 98 | |-------------------------------------------------------------------------- 99 | | VESEIN TAL UMATAR/BRACHA 100 | |-------------------------------------------------------------------------- 101 | */ 102 | 103 | public static function isVeseinTalUmatarStartDate(JewishCalendar $jewishCalendar) { 104 | if ($jewishCalendar->getInIsrael()) { 105 | // The 7th Cheshvan can't occur on Shabbos, so always return true for 7 Cheshvan 106 | if ($jewishCalendar->getJewishMonth() == JewishDate::CHESHVAN && $jewishCalendar->getJewishDayOfMonth() == 7) { 107 | return true; 108 | } 109 | } else { 110 | if ($jewishCalendar->getDayOfWeek() == 7) { //Not recited on Friday night 111 | return false; 112 | } 113 | if($jewishCalendar->getDayOfWeek() == 1) { // When starting on Sunday, it can be the start date or delayed from Shabbos 114 | return $jewishCalendar->getTekufasTishreiElapsedDays() == 48 || $jewishCalendar->getTekufasTishreiElapsedDays() == 47; 115 | } else { 116 | return $jewishCalendar->getTekufasTishreiElapsedDays() == 47; 117 | } 118 | } 119 | return false; // keep the compiler happy 120 | } 121 | 122 | public static function isVeseinTalUmatarStartingTonight(JewishCalendar $jewishCalendar) { 123 | if ($jewishCalendar->getInIsrael()) { 124 | // The 7th Cheshvan can't occur on Shabbos, so always return true for 6 Cheshvan 125 | if ($jewishCalendar->getJewishMonth() == JewishDate::CHESHVAN && $jewishCalendar->getJewishDayOfMonth() == 6) { 126 | return true; 127 | } 128 | } else { 129 | if ($jewishCalendar->getDayOfWeek() == 6) { //Not recited on Friday night 130 | return false; 131 | } 132 | if($jewishCalendar->getDayOfWeek() == 7) { // When starting on motzai Shabbos, it can be the start date or delayed from Friday night 133 | return $jewishCalendar->getTekufasTishreiElapsedDays() == 47 || $jewishCalendar->getTekufasTishreiElapsedDays() == 46; 134 | } else { 135 | return $jewishCalendar->getTekufasTishreiElapsedDays() == 46; 136 | } 137 | } 138 | return false; 139 | } 140 | 141 | public static function isVeseinTalUmatarRecited(JewishCalendar $jewishCalendar) { 142 | if ($jewishCalendar->getJewishMonth() == JewishDate::NISSAN && $jewishCalendar->getJewishDayOfMonth() < 15) { 143 | return true; 144 | } 145 | if ($jewishCalendar->getJewishMonth() < JewishDate::CHESHVAN) { 146 | return false; 147 | } 148 | if ($jewishCalendar->getInIsrael()) { 149 | return $jewishCalendar->getJewishMonth() != JewishDate::CHESHVAN || $jewishCalendar->getJewishDayOfMonth() >= 7; 150 | } else { 151 | return $jewishCalendar->getTekufasTishreiElapsedDays() >= 47; 152 | } 153 | } 154 | 155 | public static function isVeseinBerachaRecited(JewishCalendar $jewishCalendar) { 156 | return !self::isVeseinTalUmatarRecited($jewishCalendar); 157 | } 158 | 159 | /* 160 | |-------------------------------------------------------------------------- 161 | | MASHIV HARUACH/MORID HATAL 162 | |-------------------------------------------------------------------------- 163 | */ 164 | 165 | public static function isMashivHaruachStartDate(JewishCalendar $jewishCalendar) { 166 | return $jewishCalendar->getJewishMonth() == JewishDate::TISHREI && $jewishCalendar->getJewishDayOfMonth() == 22; 167 | } 168 | 169 | public static function isMashivHaruachEndDate(JewishCalendar $jewishCalendar) { 170 | return $jewishCalendar->getJewishMonth() == JewishDate::NISSAN && $jewishCalendar->getJewishDayOfMonth() == 15; 171 | } 172 | 173 | public static function isMashivHaruachRecited(JewishCalendar $jewishCalendar) { 174 | $startDate = new JewishDate($jewishCalendar->getJewishYear(), JewishDate::TISHREI, 22); 175 | $endDate = new JewishDate($jewishCalendar->getJewishYear(), JewishDate::NISSAN, 15); 176 | return $jewishCalendar->compareTo($startDate) > 0 && $jewishCalendar->compareTo($endDate) < 0; 177 | } 178 | 179 | public static function isMoridHatalRecited(JewishCalendar $jewishCalendar) { 180 | return !self::isMashivHaruachRecited($jewishCalendar) || self::isMashivHaruachStartDate($jewishCalendar) || self::isMashivHaruachEndDate($jewishCalendar); 181 | } 182 | 183 | /* 184 | |-------------------------------------------------------------------------- 185 | | HALLEL 186 | |-------------------------------------------------------------------------- 187 | */ 188 | 189 | public static function isHallelRecited(JewishCalendar $jewishCalendar) { 190 | $day = $jewishCalendar->getJewishDayOfMonth(); 191 | $month = $jewishCalendar->getJewishMonth(); 192 | $holidayIndex = $jewishCalendar->getYomTovIndex(); 193 | $inIsrael = $jewishCalendar->getInIsrael(); 194 | 195 | if($jewishCalendar->isRoshChodesh()) { //RH returns false for RC 196 | return true; 197 | } 198 | if($jewishCalendar->isChanukah($holidayIndex)) { 199 | return true; 200 | } 201 | switch ($month) { 202 | case JewishDate::NISSAN: 203 | if($day >= 15 && (($inIsrael && day <= 21) || (!$inIsrael && $day <= 22))){ 204 | return true; 205 | } 206 | break; 207 | case JewishDate::IYAR: // modern holidays 208 | if($jewishCalendar->isUseModernHolidays() && ($holidayIndex == JewishCalendar::YOM_HAATZMAUT 209 | || $holidayIndex == JewishCalendar::YOM_YERUSHALAYIM)){ 210 | return true; 211 | } 212 | break; 213 | case JewishDate::SIVAN: 214 | if ($day == 6 || (!$inIsrael && ($day == 7))){ 215 | return true; 216 | } 217 | break; 218 | case JewishDate::TISHREI: 219 | if ($day >= 15 && ($day <= 22 || (!$inIsrael && ($day <= 23)))){ 220 | return true; 221 | } 222 | } 223 | return false; 224 | } 225 | 226 | public static function isHallelShalemRecited(JewishCalendar $jewishCalendar) { 227 | $day = $jewishCalendar->getJewishDayOfMonth(); 228 | $month = $jewishCalendar->getJewishMonth(); 229 | $inIsrael = $jewishCalendar->getInIsrael(); 230 | if(self::isHallelRecited($jewishCalendar)) { 231 | if(($jewishCalendar->isRoshChodesh() && ! $jewishCalendar->isChanukah()) 232 | || ($month == JewishDate::NISSAN && (($inIsrael && $day > 15) || (!$inIsrael && $day > 16)))) { 233 | return false; 234 | } else { 235 | return true; 236 | } 237 | } 238 | return false; 239 | } 240 | 241 | /* 242 | |-------------------------------------------------------------------------- 243 | | AL HANISIM 244 | |-------------------------------------------------------------------------- 245 | */ 246 | 247 | public static function isAlHanissimRecited(JewishCalendar $jewishCalendar) { 248 | return $jewishCalendar->isPurim() || $jewishCalendar->isChanukah(); 249 | } 250 | 251 | /* 252 | |-------------------------------------------------------------------------- 253 | | YAALEH VEYAVO 254 | |-------------------------------------------------------------------------- 255 | */ 256 | 257 | public static function isYaalehVeyavoRecited(JewishCalendar $jewishCalendar) { 258 | $holidayIndex = $holidayIndex ?? $jewishCalendar->getYomTovIndex(); 259 | return $jewishCalendar->isPesach($holidayIndex) || $jewishCalendar->isShavuos($holidayIndex) ||$jewishCalendar->isRoshHashana($holidayIndex) || $jewishCalendar->isYomKippur($holidayIndex) 260 | || $jewishCalendar->isSuccos($holidayIndex) || $jewishCalendar->isShminiAtzeres($holidayIndex) || $jewishCalendar->isSimchasTorah($holidayIndex) 261 | || $jewishCalendar->isRoshChodesh(); 262 | } 263 | 264 | /* 265 | |-------------------------------------------------------------------------- 266 | | EXTRA STUFF 267 | |-------------------------------------------------------------------------- 268 | */ 269 | 270 | public static function isMizmorLesodaRecited(JewishCalendar $jewishCalendar) { 271 | if($jewishCalendar->isAssurBemelacha()) { 272 | return false; 273 | } 274 | 275 | $holidayIndex = $jewishCalendar->getYomTovIndex(); 276 | if(!self::isMizmorLesodaRecitedErevYomKippurAndPesach() 277 | && ($holidayIndex == JewishCalendar::EREV_YOM_KIPPUR 278 | || $holidayIndex == JewishCalendar::EREV_PESACH 279 | || $jewishCalendar->isCholHamoedPesach())) { 280 | return false; 281 | } 282 | return true; 283 | } 284 | 285 | /* 286 | |-------------------------------------------------------------------------- 287 | | SETTERS AND GETTERS 288 | |-------------------------------------------------------------------------- 289 | */ 290 | 291 | public static function isTachanunRecitedWeekOfPurim() { 292 | return self::$tachanunRecitedWeekOfPurim; 293 | } 294 | 295 | public static function setTachanunRecitedWeekOfPurim($tachanunRecitedWeekOfPurim) { 296 | self::$tachanunRecitedWeekOfPurim = $tachanunRecitedWeekOfPurim; 297 | } 298 | 299 | public static function isTachanunRecitedWeekOfHod() { 300 | return self::$tachanunRecitedWeekOfHod; 301 | } 302 | 303 | public static function setTachanunRecitedWeekOfHod($tachanunRecitedWeekOfHod) { 304 | self::$tachanunRecitedWeekOfHod = $tachanunRecitedWeekOfHod; 305 | } 306 | 307 | public static function isTachanunRecitedEndOfTishrei() { 308 | return self::$tachanunRecitedEndOfTishrei; 309 | } 310 | 311 | public static function setTachanunRecitedEndOfTishrei($tachanunRecitedEndOfTishrei) { 312 | self::$tachanunRecitedEndOfTishrei = $tachanunRecitedEndOfTishrei; 313 | } 314 | 315 | public static function isTachanunRecitedWeekAfterShavuos() { 316 | return self::$tachanunRecitedWeekAfterShavuos; 317 | } 318 | 319 | public static function setTachanunRecitedWeekAfterShavuos($tachanunRecitedWeekAfterShavuos) { 320 | self::$tachanunRecitedWeekAfterShavuos = $tachanunRecitedWeekAfterShavuos; 321 | } 322 | 323 | public static function isTachanunRecited13SivanOutOfIsrael() { 324 | return self::$tachanunRecited13SivanOutOfIsrael; 325 | } 326 | 327 | public static function setTachanunRecited13SivanOutOfIsrael($tachanunRecitedThirteenSivanOutOfIsrael) { 328 | self::$tachanunRecited13SivanOutOfIsrael = $tachanunRecitedThirteenSivanOutOfIsrael; 329 | } 330 | 331 | public static function isTachanunRecitedPesachSheni() { 332 | return self::$tachanunRecitedPesachSheni; 333 | } 334 | 335 | public static function setTachanunRecitedPesachSheni($tachanunRecitedPesachSheni) { 336 | self::$tachanunRecitedPesachSheni = $tachanunRecitedPesachSheni; 337 | } 338 | 339 | public static function isTachanunRecited15IyarOutOfIsrael() { 340 | return self::$tachanunRecited15IyarOutOfIsrael; 341 | } 342 | 343 | public static function setTachanunRecited15IyarOutOfIsrael($tachanunRecited15IyarOutOfIsrael) { 344 | self::$tachanunRecited15IyarOutOfIsrael = $tachanunRecited15IyarOutOfIsrael; 345 | } 346 | 347 | public static function isTachanunRecitedMinchaErevLagBaomer() { 348 | return self::$tachanunRecitedMinchaErevLagBaomer; 349 | } 350 | 351 | public static function setTachanunRecitedMinchaErevLagBaomer($tachanunRecitedMinchaErevLagBaomer) { 352 | self::$tachanunRecitedMinchaErevLagBaomer = $tachanunRecitedMinchaErevLagBaomer; 353 | } 354 | 355 | public static function isTachanunRecitedShivasYemeiHamiluim() { 356 | return self::$tachanunRecitedShivasYemeiHamiluim; 357 | } 358 | 359 | public static function setTachanunRecitedShivasYemeiHamiluim($tachanunRecitedShivasYemeiHamiluim) { 360 | self::$tachanunRecitedShivasYemeiHamiluim = $tachanunRecitedShivasYemeiHamiluim; 361 | } 362 | 363 | public static function isTachanunRecitedFridays() { 364 | return self::$tachanunRecitedFridays; 365 | } 366 | 367 | public static function setTachanunRecitedFridays($tachanunRecitedFridays) { 368 | self::$tachanunRecitedFridays = $tachanunRecitedFridays; 369 | } 370 | 371 | public static function isTachanunRecitedSundays() { 372 | return self::$tachanunRecitedSundays; 373 | } 374 | 375 | public static function setTachanunRecitedSundays($tachanunRecitedSundays) { 376 | self::$tachanunRecitedSundays = $tachanunRecitedSundays; 377 | } 378 | 379 | public static function isTachanunRecitedMinchaAllYear() { 380 | return self::$tachanunRecitedMinchaAllYear; 381 | } 382 | 383 | public static function setTachanunRecitedMinchaAllYear($tachanunRecitedMinchaAllYear) { 384 | self::$tachanunRecitedMinchaAllYear = $tachanunRecitedMinchaAllYear; 385 | } 386 | 387 | public static function setMizmorLesodaRecitedErevYomKippurAndPesach($mizmorLesodaRecitedErevYomKippurAndPesach) { 388 | self::$mizmorLesodaRecitedErevYomKippurAndPesach = $mizmorLesodaRecitedErevYomKippurAndPesach; 389 | } 390 | 391 | public static function isMizmorLesodaRecitedErevYomKippurAndPesach() { 392 | return self::$mizmorLesodaRecitedErevYomKippurAndPesach; 393 | } 394 | } 395 | -------------------------------------------------------------------------------- /src/HebrewCalendar/YerushalmiYomiCalculator.php: -------------------------------------------------------------------------------- 1 | getGregorianCalendar()->utc(); 36 | 37 | if ($calendar->getYomTovIndex() == JewishCalendar::YOM_KIPPUR || 38 | $calendar->getYomTovIndex() == JewishCalendar::TISHA_BEAV ) { 39 | return null; 40 | } 41 | 42 | if ($requested->toDateString() < self::DAF_YOMI_START_DAY) { 43 | throw new \Exception($requested->toDateString() . " is prior to organized Daf Yomi Yerushalmi cycles that started on February 2, 1980"); 44 | } 45 | 46 | $nextCycle = Carbon::createMidnightDate(1980, 2, 2, "UTC"); 47 | $prevCycle = $nextCycle->clone(); 48 | 49 | while ($requested->gt($nextCycle)) { 50 | $prevCycle->setDate($nextCycle->year, $nextCycle->month, $nextCycle->day); 51 | 52 | $nextCycle->addDays(self::WHOLE_SHAS_DAFS); 53 | $nextCycle->addDays(self::getNumOfSpecialDays($prevCycle, $nextCycle)); 54 | } 55 | 56 | // Get the number of days from cycle start until request. 57 | $dafNo = (int) $requested->diffInDays($prevCycle, true); 58 | 59 | $specialDays = self::getNumOfSpecialDays($prevCycle, $requested); 60 | 61 | $total = $dafNo - $specialDays; 62 | 63 | $masechta = 0; 64 | for ($j = 0; $j < count(self::BLATT_PER_MASECHTA); $j++) { 65 | if ($total < self::BLATT_PER_MASECHTA[$j]) { 66 | $dafYomi = new Daf($masechta, $total + 1); 67 | break; 68 | } 69 | $total -= self::BLATT_PER_MASECHTA[$j]; 70 | $masechta++; 71 | } 72 | 73 | return $dafYomi ?? null; 74 | } 75 | 76 | private static function getNumOfSpecialDays($start, $end) { 77 | $startYear = JewishDate::create($start)->getJewishYear(); 78 | $endYear = JewishDate::create($end)->getJewishYear(); 79 | 80 | $specialDays = 0; 81 | 82 | $yom_kippur = new JewishCalendar(5770, 7, 10); 83 | $tisha_beav = new JewishCalendar(5770, 5, 9); 84 | 85 | for ($i = $startYear; $i <= $endYear; $i++) { 86 | $yom_kippur->setJewishYear($i); 87 | $tisha_beav->setJewishYear($i); 88 | 89 | if (self::isBetween($start, $yom_kippur->getGregorianCalendar(), $end)) { 90 | $specialDays++; 91 | } 92 | if (self::isBetween($start, $tisha_beav->getGregorianCalendar(), $end)) { 93 | $specialDays++; 94 | } 95 | } 96 | 97 | return $specialDays; 98 | } 99 | 100 | private static function isBetween($start, $date, $end) { 101 | return $start->lt($date) && $end->gt($date); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/HebrewCalendar/YomiCalculator.php: -------------------------------------------------------------------------------- 1 | getGregorianYear(); 33 | $month = $calendar->getGregorianMonth(); 34 | $day = $calendar->getGregorianDayOfMonth(); 35 | 36 | $blattPerMasechta = [64, 157, 105, 121, 22, 88, 56, 40, 35, 31, 32, 29, 27, 122, 112, 91, 66, 49, 90, 82, 37 | 119, 119, 176, 113, 24, 49, 76, 14, 120, 110, 142, 61, 34, 34, 28, 22, 4, 9, 5, 73]; 38 | 39 | $julianDay = self::getJulianDay($year, $month, $day); 40 | 41 | if ($julianDay < self::DAF_YOMI_JULIAN_START_DAY) { 42 | throw new \Exception($year . '-' . $month . '-' . $day . " is prior to organized Daf Yomi Bavli cycles that started on September 11, 1923"); 43 | } 44 | 45 | if ($julianDay >= self::SHEKALIM_JULIAN_CHANGE_DAY) { 46 | $cycleNo = 8 + (int) (($julianDay - self::SHEKALIM_JULIAN_CHANGE_DAY) / 2711); 47 | $dafNo = (($julianDay - self::SHEKALIM_JULIAN_CHANGE_DAY) % 2711); 48 | } else { 49 | $cycleNo = 1 + (int) (($julianDay - self::DAF_YOMI_JULIAN_START_DAY) / 2702); 50 | $dafNo = (($julianDay - self::DAF_YOMI_JULIAN_START_DAY) % 2702); 51 | } 52 | 53 | $total = 0; 54 | $masechta = -1; 55 | $blatt = 0; 56 | 57 | if ($cycleNo <= 7) { 58 | $blattPerMasechta[4] = 13; 59 | } else { 60 | $blattPerMasechta[4] = 22; 61 | } 62 | 63 | for ($j = 0; $j < count($blattPerMasechta); $j++) { 64 | $masechta++; 65 | $total = $total + $blattPerMasechta[$j] - 1; 66 | if ($dafNo < $total) { 67 | $blatt = 1 + $blattPerMasechta[$j] - ($total - $dafNo); 68 | // Fiddle with the weird ones near the end. 69 | if ($masechta == 36) { 70 | $blatt += 21; 71 | } else if ($masechta == 37) { 72 | $blatt += 24; 73 | } else if ($masechta == 38) { 74 | $blatt += 32; 75 | } 76 | $dafYomi = new Daf($masechta, $blatt); 77 | break; 78 | } 79 | } 80 | 81 | return $dafYomi ?? null; 82 | } 83 | 84 | private static function getJulianDay($year, $month, $day) { 85 | if ($month <= 2) { 86 | $year -= 1; 87 | $month += 12; 88 | } 89 | $a = (int) ($year / 100); 90 | $b = 2 - $a + (int) ($a / 4); 91 | return (int) (floor(365.25 * ($year + 4716)) + floor(30.6001 * ($month + 1)) + $day + $b - 1524.5); 92 | } 93 | } -------------------------------------------------------------------------------- /src/Zmanim.php: -------------------------------------------------------------------------------- 1 | assertEquals($noaaCalculator->getCalculatorName(), "US National Oceanic and Atmospheric Administration Algorithm"); 37 | } 38 | 39 | /** 40 | * @test 41 | */ 42 | public function testGetUTCSunrise() { 43 | $noaaCalculator = new NoaaCalculator(); 44 | 45 | $tests = [ 46 | ['2017-10-17', 41.1181036, -74.0840691, 167, 11.13543634], 47 | ['2017-10-17', 34.0201613, -118.6919095, 71, 14.00708152], 48 | ['1955-02-26', 31.7962994, 35.1053185, 754, 4.11885084], 49 | ['2017-06-21', 70.1498248, 9.1456867, 0, false], 50 | ]; 51 | 52 | foreach ($tests as $test) { 53 | $calendar = Carbon::parse($test[0]); 54 | $geo = new GeoLocation("", $test[1], $test[2], $test[3], "UTC"); 55 | 56 | $sunrise = $noaaCalculator->getUTCSunrise($calendar, $geo, AstronomicalCalculator::GEOMETRIC_ZENITH, true); 57 | if (is_nan($sunrise)) { 58 | $sunrise = false; 59 | } else { 60 | $sunrise = round($sunrise, 8); 61 | } 62 | 63 | $this->assertEquals($sunrise, $test[4]); 64 | } 65 | } 66 | 67 | /** 68 | * @test 69 | */ 70 | public function testGetUTCSunset() { 71 | $noaaCalculator = new NoaaCalculator(); 72 | 73 | $tests = [ 74 | ['2017-10-17', 41.1181036, -74.0840691, 167, 22.24078702], 75 | ['1955-02-26', 31.7962994, 35.1053185, 754, 15.64531391], 76 | ['2017-06-21', 70.1498248, 9.1456867, 0, false], 77 | ]; 78 | 79 | foreach ($tests as $test) { 80 | $calendar = Carbon::parse($test[0]); 81 | $geo = new GeoLocation("", $test[1], $test[2], $test[3], "UTC"); 82 | 83 | $sunset = $noaaCalculator->getUTCSunset($calendar, $geo, AstronomicalCalculator::GEOMETRIC_ZENITH, true); 84 | if (is_nan($sunset)) { 85 | $sunset = false; 86 | } else { 87 | $sunset = round($sunset, 8); 88 | } 89 | 90 | $this->assertEquals($sunset, $test[4]); 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /tests/Calculator/SunTimesCalculatorTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($sunTimesCalculator->getCalculatorName(), "US Naval Almanac Algorithm"); 37 | } 38 | 39 | /** 40 | * @test 41 | */ 42 | public function testGetUTCSunrise() { 43 | $sunTimesCalculator = new SunTimesCalculator(); 44 | 45 | $tests = [ 46 | ['2017-10-17', 41.1181036, -74.0840691, 0.167, 11.16276401], 47 | ['1955-02-26', 31.7962994, 35.1053185, 0.754, 4.17848602], 48 | ['2017-06-21', 70.1498248, 9.1456867, 0, false], 49 | ]; 50 | 51 | foreach ($tests as $test) { 52 | $calendar = Carbon::parse($test[0]); 53 | $geo = new GeoLocation("", $test[1], $test[2], $test[3], "America/New_York"); 54 | 55 | $sunrise = $sunTimesCalculator->getUTCSunrise($calendar, $geo, AstronomicalCalculator::GEOMETRIC_ZENITH, true); 56 | if (is_nan($sunrise)) { 57 | $sunrise = false; 58 | } else { 59 | $sunrise = round($sunrise, 8); 60 | } 61 | 62 | $this->assertEquals($sunrise, $test[4]); 63 | } 64 | } 65 | 66 | /** 67 | * @test 68 | */ 69 | public function testGetUTCSunset() { 70 | $sunTimesCalculator = new SunTimesCalculator(); 71 | 72 | $tests = [ 73 | ['2017-10-17', 41.1181036, -74.0840691, 0.167, 22.21747591], 74 | ['1955-02-26', 31.7962994, 35.1053185, 0.754, 15.58295081], 75 | ['2017-06-21', 70.1498248, 9.1456867, 0, false], 76 | ]; 77 | 78 | foreach ($tests as $test) { 79 | $calendar = Carbon::parse($test[0]); 80 | $geo = new GeoLocation("", $test[1], $test[2], $test[3], "America/New_York"); 81 | 82 | $sunset = $sunTimesCalculator->getUTCSunset($calendar, $geo, AstronomicalCalculator::GEOMETRIC_ZENITH, true); 83 | if (is_nan($sunset)) { 84 | $sunset = false; 85 | } else { 86 | $sunset = round($sunset, 8); 87 | } 88 | 89 | $this->assertEquals($sunset, $test[4]); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /tests/Calendar/AstronomicalCalendarTest.php: -------------------------------------------------------------------------------- 1 | locations = [ 40 | ['Lakewood, NJ', 40.0721087, -74.2400243, 15, 'America/New_York'], 41 | ['Jerusalem, Israel', 31.7781161, 35.233804, 740, 'Asia/Jerusalem'], 42 | ['Los Angeles, CA', 34.0201613, -118.6919095, 71, 'America/Los_Angeles'], 43 | ['Tokyo, Japan', 35.6733227, 139.6403486, 40, 'Asia/Tokyo'], 44 | ['Fort Conger, NU Canada', 81.7449398, -64.7945858, 127, 'America/Toronto'], 45 | ['Apia, Samoa', -13.8599098, -171.8031745, 1858, 'Pacific/Apia'], 46 | ]; 47 | } 48 | 49 | /** 50 | * @test 51 | */ 52 | public function changeCalculator() { 53 | $astronomicalCalendar = new AstronomicalCalendar(); 54 | 55 | $noaa_sunset = $astronomicalCalendar->getSunset(); 56 | $astronomicalCalendar->setCalculatorType('SunTimes'); 57 | $suntimes_sunset = $astronomicalCalendar->getSunset(); 58 | $astronomicalCalendar->setCalculatorType('Noaa'); 59 | $this->assertEquals($astronomicalCalendar->getSunset(), $noaa_sunset); 60 | 61 | $astronomicalCalendar->setCalculatorType('SunTimes'); 62 | $this->assertEquals($astronomicalCalendar->getSunset(), $suntimes_sunset); 63 | } 64 | 65 | /** 66 | * @test 67 | */ 68 | public function testSunrise() { 69 | $expected_dates = [ 70 | "2017-10-17T07:09:11-04:00", 71 | "2017-10-17T06:39:32+03:00", 72 | "2017-10-17T07:00:25-07:00", 73 | "2017-10-17T05:48:20+09:00", 74 | null, 75 | "2017-10-17T06:54:18+14:00", 76 | ]; 77 | 78 | foreach ($this->locations as $index => $location) { 79 | $geo = new GeoLocation($location[0], $location[1], $location[2], $location[3], $location[4]); 80 | 81 | $astronomicalCalendar = new AstronomicalCalendar($geo, 2017, 10, 17); 82 | 83 | $sunrise = $astronomicalCalendar->getSunrise(); 84 | if (!is_null($sunrise)) { 85 | $sunrise = $sunrise->format('Y-m-d\TH:i:sP'); 86 | } 87 | 88 | $this->assertEquals($sunrise, $expected_dates[ $index ]); 89 | } 90 | } 91 | 92 | /** 93 | * @test 94 | */ 95 | public function testSeaLevelSunrise() { 96 | $expected_dates = [ 97 | "2017-10-17T07:09:51-04:00", 98 | "2017-10-17T06:43:43+03:00", 99 | "2017-10-17T07:01:45-07:00", 100 | "2017-10-17T05:49:21+09:00", 101 | null, 102 | "2017-10-17T07:00:05+14:00", 103 | ]; 104 | 105 | foreach ($this->locations as $index => $location) { 106 | $geo = new GeoLocation($location[0], $location[1], $location[2], $location[3], $location[4]); 107 | 108 | $astronomicalCalendar = new AstronomicalCalendar($geo, 2017, 10, 17); 109 | 110 | $sunrise = $astronomicalCalendar->getSeaLevelSunrise(); 111 | if (!is_null($sunrise)) { 112 | $sunrise = $sunrise->format('Y-m-d\TH:i:sP'); 113 | } 114 | 115 | $this->assertEquals($sunrise, $expected_dates[ $index ]); 116 | } 117 | } 118 | 119 | /** 120 | * @test 121 | */ 122 | public function testUTCSunrise() { 123 | $expected_dates = [ 124 | 11.15327065, 125 | 3.65893934, 126 | 14.00708152, 127 | 20.8057012, 128 | null, 129 | 16.90510688, 130 | ]; 131 | 132 | foreach ($this->locations as $index => $location) { 133 | $geo = new GeoLocation($location[0], $location[1], $location[2], $location[3], $location[4]); 134 | 135 | $astronomicalCalendar = new AstronomicalCalendar($geo, 2017, 10, 17); 136 | 137 | $sunrise = $astronomicalCalendar->getUTCSunrise(90); 138 | if (is_nan($sunrise)) { 139 | $sunrise = null; 140 | } else { 141 | $sunrise = round($sunrise, 8); 142 | } 143 | 144 | $this->assertEquals($sunrise, $expected_dates[ $index ]); 145 | } 146 | } 147 | 148 | /** 149 | * @test 150 | */ 151 | public function testUTCSeaLevelSunrise() { 152 | $expected_dates = [ 153 | 11.16434723, 154 | 3.72862262, 155 | 14.02926518, 156 | 20.82268461, 157 | null, 158 | 17.00158411, 159 | ]; 160 | 161 | foreach ($this->locations as $index => $location) { 162 | $geo = new GeoLocation($location[0], $location[1], $location[2], $location[3], $location[4]); 163 | 164 | $astronomicalCalendar = new AstronomicalCalendar($geo, 2017, 10, 17); 165 | 166 | $sunrise = $astronomicalCalendar->getUTCSeaLevelSunrise(90); 167 | if (is_nan($sunrise)) { 168 | $sunrise = null; 169 | } else { 170 | $sunrise = round($sunrise, 8); 171 | } 172 | 173 | $this->assertEquals($sunrise, $expected_dates[ $index ]); 174 | } 175 | } 176 | 177 | /** 178 | * @test 179 | */ 180 | public function testSunriseOffsetByDegreesForBasicLocations() { 181 | $expected_dates = [ 182 | "2017-10-17T06:10:57-04:00", 183 | "2017-10-17T05:50:43+03:00", 184 | "2017-10-17T06:07:22-07:00", 185 | "2017-10-17T04:53:55+09:00", 186 | "2017-10-17T04:47:28-04:00", 187 | "2017-10-17T06:13:13+14:00", 188 | ]; 189 | 190 | foreach ($this->locations as $index => $location) { 191 | $geo = new GeoLocation($location[0], $location[1], $location[2], $location[3], $location[4]); 192 | 193 | $astronomicalCalendar = new AstronomicalCalendar($geo, 2017, 10, 17); 194 | 195 | $sunrise = $astronomicalCalendar->getSunriseOffsetByDegrees(102); 196 | if (!is_null($sunrise)) { 197 | $sunrise = $sunrise->format('Y-m-d\TH:i:sP'); 198 | } 199 | 200 | $this->assertEquals($sunrise, $expected_dates[ $index ]); 201 | } 202 | } 203 | 204 | /** 205 | * @test 206 | */ 207 | public function testSunriseOffsetByDegreesForArcticTimeZoneExtremities() { 208 | $geo = new GeoLocation('Daneborg, Greenland', 74.2999996, -20.2420877, 0, 'America/Godthab'); 209 | 210 | $astronomicalCalendar = new AstronomicalCalendar($geo, 2017, 4, 20); 211 | 212 | $sunrise = $astronomicalCalendar->getSunriseOffsetByDegrees(94); 213 | $sunrise = $sunrise->format('Y-m-d\TH:i:sP'); 214 | 215 | $this->assertEquals($sunrise, '2017-04-19T23:54:23-02:00'); 216 | } 217 | 218 | /** 219 | * @test 220 | */ 221 | public function testSunset() { 222 | $expected_dates = [ 223 | "2017-10-17T18:14:38-04:00", 224 | "2017-10-17T18:08:46+03:00", 225 | "2017-10-17T18:19:05-07:00", 226 | "2017-10-17T17:04:46+09:00", 227 | null, 228 | "2017-10-17T19:31:07+14:00", 229 | ]; 230 | 231 | foreach ($this->locations as $index => $location) { 232 | $geo = new GeoLocation($location[0], $location[1], $location[2], $location[3], $location[4]); 233 | 234 | $astronomicalCalendar = new AstronomicalCalendar($geo, 2017, 10, 17); 235 | 236 | $sunset = $astronomicalCalendar->getSunset(); 237 | if (!is_null($sunset)) { 238 | $sunset = $sunset->format('Y-m-d\TH:i:sP'); 239 | } 240 | 241 | $this->assertEquals($sunset, $expected_dates[ $index ]); 242 | } 243 | } 244 | 245 | /** 246 | * @test 247 | */ 248 | public function testSeaLevelSunset() { 249 | $expected_dates = [ 250 | "2017-10-17T18:13:58-04:00", 251 | "2017-10-17T18:04:36+03:00", 252 | "2017-10-17T18:17:45-07:00", 253 | "2017-10-17T17:03:45+09:00", 254 | null, 255 | "2017-10-17T19:25:19+14:00", 256 | ]; 257 | 258 | foreach ($this->locations as $index => $location) { 259 | $geo = new GeoLocation($location[0], $location[1], $location[2], $location[3], $location[4]); 260 | 261 | $astronomicalCalendar = new AstronomicalCalendar($geo, 2017, 10, 17); 262 | 263 | $sunset = $astronomicalCalendar->getSeaLevelSunset(); 264 | if (!is_null($sunset)) { 265 | $sunset = $sunset->format('Y-m-d\TH:i:sP'); 266 | } 267 | 268 | $this->assertEquals($sunset, $expected_dates[ $index ]); 269 | } 270 | } 271 | 272 | /** 273 | * @test 274 | */ 275 | public function testUTCSunset() { 276 | $expected_dates = [ 277 | 22.24410903, 278 | 15.14635336, 279 | 1.31819979, 280 | 8.07962871, 281 | null, 282 | 5.51873532, 283 | ]; 284 | 285 | foreach ($this->locations as $index => $location) { 286 | $geo = new GeoLocation($location[0], $location[1], $location[2], $location[3], $location[4]); 287 | 288 | $astronomicalCalendar = new AstronomicalCalendar($geo, 2017, 10, 17); 289 | 290 | $sunset = $astronomicalCalendar->getUTCSunset(90); 291 | if (is_nan($sunset)) { 292 | $sunset = null; 293 | } else { 294 | $sunset = round($sunset, 8); 295 | } 296 | 297 | $this->assertEquals($sunset, $expected_dates[ $index ]); 298 | } 299 | } 300 | 301 | /** 302 | * @test 303 | */ 304 | public function testUTCSeaLevelSunset() { 305 | $expected_dates = [ 306 | 22.23304301, 307 | 15.07671429, 308 | 1.29603174, 309 | 8.06265871, 310 | null, 311 | 5.42214918, 312 | ]; 313 | 314 | foreach ($this->locations as $index => $location) { 315 | $geo = new GeoLocation($location[0], $location[1], $location[2], $location[3], $location[4]); 316 | 317 | $astronomicalCalendar = new AstronomicalCalendar($geo, 2017, 10, 17); 318 | 319 | $sunset = $astronomicalCalendar->getUTCSeaLevelSunset(90); 320 | if (is_nan($sunset)) { 321 | $sunset = null; 322 | } else { 323 | $sunset = round($sunset, 8); 324 | } 325 | 326 | $this->assertEquals($sunset, $expected_dates[ $index ]); 327 | } 328 | } 329 | 330 | /** 331 | * @test 332 | */ 333 | public function testSunsetOffsetByDegreesForBasicLocations() { 334 | $expected_dates = [ 335 | "2017-10-17T19:12:49-04:00", 336 | "2017-10-17T18:57:33+03:00", 337 | "2017-10-17T19:12:05-07:00", 338 | "2017-10-17T17:59:08+09:00", 339 | "2017-10-17T19:15:04-04:00", 340 | "2017-10-17T20:12:15+14:00", 341 | ]; 342 | 343 | foreach ($this->locations as $index => $location) { 344 | $geo = new GeoLocation($location[0], $location[1], $location[2], $location[3], $location[4]); 345 | 346 | $astronomicalCalendar = new AstronomicalCalendar($geo, 2017, 10, 17); 347 | 348 | $sunset = $astronomicalCalendar->getSunsetOffsetByDegrees(102); 349 | if (!is_null($sunset)) { 350 | $sunset = $sunset->format('Y-m-d\TH:i:sP'); 351 | } 352 | 353 | $this->assertEquals($sunset, $expected_dates[ $index ]); 354 | } 355 | } 356 | 357 | /** 358 | * @test 359 | */ 360 | public function testSunsetOffsetByDegreesForArcticTimeZoneExtremities() { 361 | $geo = new GeoLocation('Hooper Bay, Alaska', 61.520182, -166.1740437, 8, 'America/Anchorage'); 362 | 363 | $astronomicalCalendar = new AstronomicalCalendar($geo, 2017, 6, 21); 364 | 365 | $sunset = $astronomicalCalendar->getSunsetOffsetByDegrees(94); 366 | $sunset = $sunset->format('Y-m-d\TH:i:sP'); 367 | 368 | $this->assertEquals($sunset, '2017-06-22T02:00:16-08:00'); 369 | } 370 | 371 | /** 372 | * @test 373 | */ 374 | public function testTemporalHour() { 375 | $expected_dates = [ 376 | 0.92239132, 377 | 0.94567431, 378 | 0.93889721, 379 | 0.93666451, 380 | null, 381 | 1.03504709, 382 | ]; 383 | 384 | foreach ($this->locations as $index => $location) { 385 | $geo = new GeoLocation($location[0], $location[1], $location[2], $location[3], $location[4]); 386 | 387 | $astronomicalCalendar = new AstronomicalCalendar($geo, 2017, 10, 17); 388 | $sunset = $astronomicalCalendar->getTemporalHour(); 389 | if (!is_null($sunset)) { 390 | $sunset = $sunset / AstronomicalCalendar::HOUR_MILLIS; 391 | $sunset = round($sunset, 8); 392 | } 393 | 394 | $this->assertEquals($sunset, $expected_dates[ $index ]); 395 | } 396 | } 397 | 398 | /** 399 | * @test 400 | */ 401 | public function testSunTransit() { 402 | $expected_dates = [ 403 | "2017-10-17T12:41:55-04:00", 404 | "2017-10-17T12:24:09+03:00", 405 | "2017-10-17T12:39:45-07:00", 406 | "2017-10-17T11:26:33+09:00", 407 | "2017-10-17T12:04:32-04:00", // This would return null if we didn't use astronimical noon 408 | "2017-10-17T13:12:42+14:00", 409 | ]; 410 | 411 | foreach ($this->locations as $index => $location) { 412 | $geo = new GeoLocation($location[0], $location[1], $location[2], $location[3], $location[4]); 413 | 414 | $astronomicalCalendar = new AstronomicalCalendar($geo, 2017, 10, 17); 415 | 416 | $startOfDay = $astronomicalCalendar->getSeaLevelSunrise(); 417 | $endOfDay = $astronomicalCalendar->getSeaLevelSunset(); 418 | $sunTransit = $astronomicalCalendar->getSunTransit($startOfDay, $endOfDay); 419 | if (!is_null($sunTransit)) { 420 | $sunTransit = $sunTransit->format('Y-m-d\TH:i:sP'); 421 | } 422 | 423 | $this->assertEquals($sunTransit, $expected_dates[ $index ]); 424 | } 425 | } 426 | 427 | /** 428 | * @test 429 | */ 430 | public function testDefaultData() { 431 | $geo = new GeoLocation(); 432 | 433 | $astronomicalCalendar1 = new AstronomicalCalendar(); 434 | $astronomicalCalendar2 = new AstronomicalCalendar(null, 1); 435 | $astronomicalCalendar3 = new AstronomicalCalendar($geo, 1, 1); 436 | 437 | $this->assertEquals($astronomicalCalendar1->getGeoLocation(), $geo); 438 | $this->assertEquals($astronomicalCalendar2, $astronomicalCalendar3); 439 | } 440 | } -------------------------------------------------------------------------------- /tests/Calendar/ZmanimCalendarTest.php: -------------------------------------------------------------------------------- 1 | geo = new GeoLocation($lakewood[0], $lakewood[1], $lakewood[2], $lakewood[3], $lakewood[4]); 52 | } 53 | 54 | /** 55 | * @test 56 | */ 57 | public function testTzais() { 58 | $zmanimCalendar = new ZmanimCalendar($this->geo, 2017, 10, 17); 59 | 60 | $tzais = $zmanimCalendar->getTzais(); 61 | $this->assertEquals($tzais->format('Y-m-d\TH:i:sP'), "2017-10-17T18:54:29-04:00"); 62 | } 63 | 64 | /** 65 | * @test 66 | */ 67 | public function testTzaisWithCustomDegreeOffset() { 68 | $czc = new ComplexZmanimCalendar($this->geo, 2017, 10, 17); 69 | 70 | $tzais = $czc->getTzais19Point8Degrees(); 71 | $this->assertEquals($tzais->format('Y-m-d\TH:i:sP'), "2017-10-17T19:53:34-04:00"); 72 | } 73 | 74 | /** 75 | * @test 76 | */ 77 | public function useWrapperClass() { 78 | $zmanim = Zmanim::create(2017, 10, 17, 'Lakewood, NJ', 40.0721087, -74.2400243, 15, 'America/New_York'); 79 | 80 | $tzais = $zmanim->getTzais19Point8Degrees(); 81 | $this->assertEquals($tzais->format('Y-m-d\TH:i:sP'), "2017-10-17T19:53:34-04:00"); 82 | } 83 | 84 | /** 85 | * @test 86 | */ 87 | public function testBasicTimes() { 88 | $zmanim = Zmanim::create(2019, 2, 22, 'Lakewood, NJ', 40.0721087, -74.2400243, 39.57, 'America/New_York'); 89 | 90 | $zmanim->setUseElevation(false); 91 | 92 | $this->assertEquals($zmanim->getAlos96()->format('Y-m-d\TH:i:sP'), "2019-02-22T05:04:50-05:00"); 93 | $this->assertEquals($zmanim->getAlos72()->format('Y-m-d\TH:i:sP'), "2019-02-22T05:28:50-05:00"); 94 | $this->assertEquals($zmanim->getCandleLighting()->format('Y-m-d\TH:i:sP'), "2019-02-22T17:22:38-05:00"); 95 | $this->assertEquals($zmanim->getTzais72()->format('Y-m-d\TH:i:sP'), "2019-02-22T18:52:38-05:00"); 96 | } 97 | 98 | /** 99 | * @test 100 | */ 101 | public function testBaalHatanya() { 102 | $zmanim = Zmanim::create(2019, 2, 18, 'Lakewood, NJ', 40.0721087, -74.2400243, 39.57, 'America/New_York'); 103 | 104 | $this->assertEquals($zmanim->getMinchaGedolaBaalHatanya()->format('Y-m-d\TH:i:sP'), "2019-02-18T12:38:34-05:00"); 105 | $this->assertEquals($zmanim->getPlagHaminchaBaalHatanya()->format('Y-m-d\TH:i:sP'), "2019-02-18T16:31:32-05:00"); 106 | $this->assertEquals($zmanim->getTzaisBaalHatanya()->format('Y-m-d\TH:i:sP'), "2019-02-18T18:03:40-05:00"); 107 | } 108 | 109 | /** 110 | * @test 111 | */ 112 | public function testChangingDate() { 113 | $zmanim = Zmanim::create(2019, 2, 18, 'Lakewood, NJ', 40.0721087, -74.2400243, 39.57, 'America/New_York'); 114 | 115 | $this->assertEquals($zmanim->getAlos72()->format('Y-m-d\TH:i:sP'), "2019-02-18T05:33:13-05:00"); 116 | 117 | $zmanim->addDays(3); 118 | $this->assertEquals($zmanim->getAlos72()->format('Y-m-d\TH:i:sP'), "2019-02-21T05:29:09-05:00"); 119 | 120 | $zmanim->subDays(3); 121 | $this->assertEquals($zmanim->getAlos72()->format('Y-m-d\TH:i:sP'), "2019-02-18T05:33:13-05:00"); 122 | 123 | $zmanim->setDate(2017, 10, 17); 124 | $this->assertEquals($zmanim->getTzais19Point8Degrees()->format('Y-m-d\TH:i:sP'), "2017-10-17T19:53:34-04:00"); 125 | } 126 | 127 | /** 128 | * @test 129 | */ 130 | public function testGetZmanHelper() { 131 | $zmanim = Zmanim::create(2019, 2, 18, 'Lakewood, NJ', 40.0721087, -74.2400243, 39.57, 'America/New_York'); 132 | $startOfDay = $zmanim->get("SunriseOffsetByDegrees", 90); 133 | $endOfDay = $zmanim->get("SunsetOffsetByDegrees", 90); 134 | 135 | $this->assertEquals($zmanim->get("Alos72")->format('Y-m-d\TH:i:sP'), "2019-02-18T05:33:13-05:00"); 136 | $this->assertEquals($zmanim->alos72->format('Y-m-d\TH:i:sP'), "2019-02-18T05:33:13-05:00"); 137 | $this->assertEquals($zmanim->get("SofZmanShma", $startOfDay, $endOfDay)->format('Y-m-d\TH:i:sP'), "2019-02-18T09:28:11-05:00"); 138 | // $this->assertEquals($zmanim->invalidName, null); 139 | 140 | $czc = new ComplexZmanimCalendar($this->geo, 2017, 10, 17); 141 | $this->assertEquals($czc->tzais19Point8Degrees->format('Y-m-d\TH:i:sP'), "2017-10-17T19:53:34-04:00"); 142 | 143 | $this->expectException(\Exception::class); 144 | $zmanim->get("InvalidName"); 145 | } 146 | 147 | /** 148 | * @test 149 | */ 150 | public function testAll() { 151 | // Note that some of these may be wrong, I assume they're working, but please test them against another library to confirm 152 | 153 | $zmanim = Zmanim::create(2023, 9, 29, 'Lakewood, NJ', 40.0721087, -74.2400243, 39.57, 'America/New_York'); 154 | 155 | $this->assertEquals($zmanim->sunrise->format('Y-m-d\TH:i:sP'), "2023-09-29T06:50:03-04:00"); 156 | $this->assertEquals($zmanim->seaLevelSunrise->format('Y-m-d\TH:i:sP'), "2023-09-29T06:51:07-04:00"); 157 | $this->assertEquals($zmanim->beginCivilTwilight->format('Y-m-d\TH:i:sP'), "2023-09-29T06:24:05-04:00"); 158 | $this->assertEquals($zmanim->beginNauticalTwilight->format('Y-m-d\TH:i:sP'), "2023-09-29T05:52:35-04:00"); 159 | $this->assertEquals($zmanim->beginAstronomicalTwilight->format('Y-m-d\TH:i:sP'), "2023-09-29T05:20:44-04:00"); 160 | $this->assertEquals($zmanim->tzais->format('Y-m-d\TH:i:sP'), "2023-09-29T19:22:54-04:00"); 161 | $this->assertEquals($zmanim->alosHashachar->format('Y-m-d\TH:i:sP'), "2023-09-29T05:30:52-04:00"); 162 | $this->assertEquals($zmanim->alos72->format('Y-m-d\TH:i:sP'), "2023-09-29T05:38:03-04:00"); 163 | $this->assertEquals($zmanim->chatzos->format('Y-m-d\TH:i:sP'), "2023-09-29T12:47:28-04:00"); 164 | $this->assertEquals($zmanim->chatzosAsHalfDay->format('Y-m-d\TH:i:sP'), "2023-09-29T12:46:59-04:00"); 165 | $this->assertEquals($zmanim->sofZmanShmaGRA->format('Y-m-d\TH:i:sP'), "2023-09-29T09:48:31-04:00"); 166 | $this->assertEquals($zmanim->sofZmanShmaMGA->format('Y-m-d\TH:i:sP'), "2023-09-29T09:12:31-04:00"); 167 | $this->assertEquals($zmanim->tzais72->format('Y-m-d\TH:i:sP'), "2023-09-29T19:55:54-04:00"); 168 | $this->assertEquals($zmanim->candleLighting->format('Y-m-d\TH:i:sP'), "2023-09-29T18:24:51-04:00"); 169 | $this->assertEquals($zmanim->sofZmanTfilaGRA->format('Y-m-d\TH:i:sP'), "2023-09-29T10:48:00-04:00"); 170 | $this->assertEquals($zmanim->sofZmanTfilaMGA->format('Y-m-d\TH:i:sP'), "2023-09-29T10:24:00-04:00"); 171 | $this->assertEquals($zmanim->minchaGedola->format('Y-m-d\TH:i:sP'), "2023-09-29T13:16:43-04:00"); 172 | $this->assertEquals($zmanim->minchaKetana->format('Y-m-d\TH:i:sP'), "2023-09-29T16:15:11-04:00"); 173 | $this->assertEquals($zmanim->plagHamincha->format('Y-m-d\TH:i:sP'), "2023-09-29T17:29:33-04:00"); 174 | $this->assertEquals(round($zmanim->shaahZmanis19Point8Degrees, 7), 4558102.2461667); 175 | $this->assertEquals(round($zmanim->shaahZmanis18Degrees, 7), 4461488.6310833); 176 | $this->assertEquals(round($zmanim->shaahZmanis26Degrees, 7), 4897305.4895833); 177 | $this->assertEquals(round($zmanim->shaahZmanis16Point1Degrees, 7), 4360175.11775); 178 | $this->assertEquals(round($zmanim->shaahZmanis60Minutes, 7), 4169282.8666667); 179 | $this->assertEquals(round($zmanim->shaahZmanis72Minutes, 7), 4289282.8666667); 180 | $this->assertEquals(round($zmanim->shaahZmanis72MinutesZmanis, 7), 4283139.44); 181 | $this->assertEquals(round($zmanim->shaahZmanis90Minutes, 7), 4469282.8666667); 182 | $this->assertEquals(round($zmanim->shaahZmanis90MinutesZmanis, 7), 4461603.5833333); 183 | $this->assertEquals(round($zmanim->shaahZmanis96MinutesZmanis, 7), 4521091.6311667); 184 | $this->assertEquals(round($zmanim->shaahZmanisAteretTorah, 7), 4126211.1533333); 185 | $this->assertEquals(round($zmanim->shaahZmanisAlos16Point1ToTzais3Point8, 7), 4037361.2094167); 186 | $this->assertEquals(round($zmanim->shaahZmanisAlos16Point1ToTzais3Point7, 7), 4034750.58175); 187 | $this->assertEquals(round($zmanim->shaahZmanis96Minutes, 7), 4529282.8666667); 188 | $this->assertEquals(round($zmanim->shaahZmanis120Minutes, 7), 4769282.8666667); 189 | $this->assertEquals(round($zmanim->shaahZmanis120MinutesZmanis, 7), 4759043.82225); 190 | $this->assertEquals($zmanim->plagHamincha120MinutesZmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T19:03:44-04:00"); 191 | $this->assertEquals($zmanim->plagHamincha120Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T19:04:33-04:00"); 192 | $this->assertEquals($zmanim->alos60->format('Y-m-d\TH:i:sP'), "2023-09-29T05:50:03-04:00"); 193 | $this->assertEquals($zmanim->alos72Zmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T05:38:40-04:00"); 194 | $this->assertEquals($zmanim->alos96->format('Y-m-d\TH:i:sP'), "2023-09-29T05:14:03-04:00"); 195 | $this->assertEquals($zmanim->alos90Zmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T05:20:49-04:00"); 196 | $this->assertEquals($zmanim->alos96Zmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T05:14:52-04:00"); 197 | $this->assertEquals($zmanim->alos90->format('Y-m-d\TH:i:sP'), "2023-09-29T05:20:03-04:00"); 198 | $this->assertEquals($zmanim->alos120->format('Y-m-d\TH:i:sP'), "2023-09-29T04:50:03-04:00"); 199 | $this->assertEquals($zmanim->alos120Zmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T04:51:04-04:00"); 200 | $this->assertEquals($zmanim->alos26Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T04:37:04-04:00"); 201 | $this->assertEquals($zmanim->alos18Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T05:20:44-04:00"); 202 | $this->assertEquals($zmanim->alos19Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T05:15:22-04:00"); 203 | $this->assertEquals($zmanim->alos19Point8Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T05:11:03-04:00"); 204 | $this->assertEquals($zmanim->alos16Point1Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T05:30:52-04:00"); 205 | $this->assertEquals($zmanim->misheyakir11Point5Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T05:55:13-04:00"); 206 | $this->assertEquals($zmanim->misheyakir11Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T05:57:51-04:00"); 207 | $this->assertEquals($zmanim->misheyakir10Point2Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T06:02:04-04:00"); 208 | $this->assertEquals($zmanim->misheyakir7Point65Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T06:15:26-04:00"); 209 | $this->assertEquals($zmanim->misheyakir9Point5Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T06:05:44-04:00"); 210 | $this->assertEquals($zmanim->sofZmanShmaMGA19Point8Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T08:58:57-04:00"); 211 | $this->assertEquals($zmanim->sofZmanShmaMGA16Point1Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T09:08:53-04:00"); 212 | $this->assertEquals($zmanim->sofZmanShmaMGA18Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T09:03:48-04:00"); 213 | $this->assertEquals($zmanim->sofZmanShmaMGA72Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T09:12:31-04:00"); 214 | $this->assertEquals($zmanim->sofZmanShmaMGA72MinutesZmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T09:12:49-04:00"); 215 | $this->assertEquals($zmanim->sofZmanShmaMGA90Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T09:03:31-04:00"); 216 | $this->assertEquals($zmanim->sofZmanShmaMGA90MinutesZmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T09:03:54-04:00"); 217 | $this->assertEquals($zmanim->sofZmanShmaMGA96Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T09:00:31-04:00"); 218 | $this->assertEquals($zmanim->sofZmanShmaMGA96MinutesZmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T09:00:55-04:00"); 219 | $this->assertEquals($zmanim->sofZmanShma3HoursBeforeChatzos->format('Y-m-d\TH:i:sP'), "2023-09-29T09:47:28-04:00"); 220 | $this->assertEquals($zmanim->sofZmanShmaMGA120Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T08:48:31-04:00"); 221 | $this->assertEquals($zmanim->sofZmanShmaAlos16Point1ToSunset->format('Y-m-d\TH:i:sP'), "2023-09-29T08:49:08-04:00"); 222 | $this->assertEquals($zmanim->sofZmanShmaAlos16Point1ToTzaisGeonim7Point083Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T08:57:02-04:00"); 223 | $this->assertEquals($zmanim->sofZmanShmaKolEliyahu->format('Y-m-d\TH:i:sP'), "2023-09-29T09:53:30-04:00"); 224 | $this->assertEquals($zmanim->sofZmanTfilaMGA19Point8Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T10:14:55-04:00"); 225 | $this->assertEquals($zmanim->sofZmanTfilaMGA16Point1Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T10:21:33-04:00"); 226 | $this->assertEquals($zmanim->sofZmanTfilaMGA18Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T10:18:10-04:00"); 227 | $this->assertEquals($zmanim->sofZmanTfilaMGA72Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T10:24:00-04:00"); 228 | $this->assertEquals($zmanim->sofZmanTfilaMGA72MinutesZmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T10:24:12-04:00"); 229 | $this->assertEquals($zmanim->sofZmanTfilaMGA90Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T10:18:00-04:00"); 230 | $this->assertEquals($zmanim->sofZmanTfilaMGA90MinutesZmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T10:18:16-04:00"); 231 | $this->assertEquals($zmanim->sofZmanTfilaMGA96Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T10:16:00-04:00"); 232 | $this->assertEquals($zmanim->sofZmanTfilaMGA96MinutesZmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T10:16:17-04:00"); 233 | $this->assertEquals($zmanim->sofZmanTfilaMGA120Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T10:08:00-04:00"); 234 | $this->assertEquals($zmanim->sofZmanTfila2HoursBeforeChatzos->format('Y-m-d\TH:i:sP'), "2023-09-29T10:47:28-04:00"); 235 | $this->assertEquals($zmanim->minchaGedola30Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T13:17:28-04:00"); 236 | $this->assertEquals($zmanim->minchaGedola72Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T13:22:43-04:00"); 237 | $this->assertEquals($zmanim->minchaGedola16Point1Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T13:23:14-04:00"); 238 | $this->assertEquals($zmanim->minchaGedolaAhavatShalom->format('Y-m-d\TH:i:sP'), "2023-09-29T13:21:06-04:00"); 239 | $this->assertEquals($zmanim->minchaGedolaGreaterThan30->format('Y-m-d\TH:i:sP'), "2023-09-29T13:17:28-04:00"); 240 | $this->assertEquals($zmanim->minchaKetana16Point1Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T17:01:14-04:00"); 241 | $this->assertEquals($zmanim->minchaKetanaAhavatShalom->format('Y-m-d\TH:i:sP'), "2023-09-29T16:10:07-04:00"); 242 | $this->assertEquals($zmanim->minchaKetana72Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T16:57:11-04:00"); 243 | $this->assertEquals($zmanim->plagHamincha60Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T18:17:03-04:00"); 244 | $this->assertEquals($zmanim->plagHamincha72Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T18:26:33-04:00"); 245 | $this->assertEquals($zmanim->plagHamincha90Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T18:40:48-04:00"); 246 | $this->assertEquals($zmanim->plagHamincha96Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T18:45:33-04:00"); 247 | $this->assertEquals($zmanim->plagHamincha96MinutesZmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T18:44:54-04:00"); 248 | $this->assertEquals($zmanim->plagHamincha90MinutesZmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T18:40:11-04:00"); 249 | $this->assertEquals($zmanim->plagHamincha72MinutesZmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T18:26:04-04:00"); 250 | $this->assertEquals($zmanim->plagHamincha16Point1Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T18:32:04-04:00"); 251 | $this->assertEquals($zmanim->plagHamincha19Point8Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T18:47:43-04:00"); 252 | $this->assertEquals($zmanim->plagHamincha26Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T19:14:30-04:00"); 253 | $this->assertEquals($zmanim->plagHamincha18Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T18:40:05-04:00"); 254 | $this->assertEquals($zmanim->plagAlosToSunset->format('Y-m-d\TH:i:sP'), "2023-09-29T17:21:18-04:00"); 255 | $this->assertEquals($zmanim->plagAlos16Point1ToTzaisGeonim7Point083Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T17:49:36-04:00"); 256 | $this->assertEquals($zmanim->plagAhavatShalom->format('Y-m-d\TH:i:sP'), "2023-09-29T17:34:14-04:00"); 257 | $this->assertEquals($zmanim->bainHashmashosRT13Point24Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T19:47:47-04:00"); 258 | $this->assertEquals($zmanim->bainHashmashosRT58Point5Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T19:42:24-04:00"); 259 | $this->assertEquals($zmanim->bainHashmashosRT13Point5MinutesBefore7Point083Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T19:02:00-04:00"); 260 | $this->assertEquals($zmanim->bainHashmashosRT2Stars->format('Y-m-d\TH:i:sP'), "2023-09-29T19:11:24-04:00"); 261 | $this->assertEquals($zmanim->bainHashmashosYereim18Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T18:25:54-04:00"); 262 | $this->assertEquals($zmanim->bainHashmashosYereim3Point05Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T18:22:31-04:00"); 263 | $this->assertEquals($zmanim->bainHashmashosYereim16Point875Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T18:27:02-04:00"); 264 | $this->assertEquals($zmanim->bainHashmashosYereim2Point8Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T18:23:50-04:00"); 265 | $this->assertEquals($zmanim->bainHashmashosYereim13Point5Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T18:30:24-04:00"); 266 | $this->assertEquals($zmanim->bainHashmashosYereim2Point1Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T18:27:30-04:00"); 267 | $this->assertEquals($zmanim->tzaisGeonim3Point7Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T18:57:49-04:00"); 268 | $this->assertEquals($zmanim->tzaisGeonim3Point8Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T18:58:21-04:00"); 269 | $this->assertEquals($zmanim->tzaisGeonim5Point95Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T19:09:34-04:00"); 270 | $this->assertEquals($zmanim->tzaisGeonim3Point65Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T18:57:34-04:00"); 271 | $this->assertEquals($zmanim->tzaisGeonim3Point676Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T18:57:42-04:00"); 272 | $this->assertEquals($zmanim->tzaisGeonim4Point61Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T19:02:35-04:00"); 273 | $this->assertEquals($zmanim->tzaisGeonim4Point37Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T19:01:19-04:00"); 274 | $this->assertEquals($zmanim->tzaisGeonim5Point88Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T19:09:12-04:00"); 275 | $this->assertEquals($zmanim->tzaisGeonim4Point8Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T19:03:34-04:00"); 276 | $this->assertEquals($zmanim->tzaisGeonim6Point45Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T19:12:11-04:00"); 277 | $this->assertEquals($zmanim->tzaisGeonim7Point083Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T19:15:30-04:00"); 278 | $this->assertEquals($zmanim->tzaisGeonim7Point67Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T19:18:34-04:00"); 279 | $this->assertEquals($zmanim->tzaisGeonim8Point5Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T19:22:54-04:00"); 280 | $this->assertEquals($zmanim->tzaisGeonim9Point3Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T19:27:05-04:00"); 281 | $this->assertEquals($zmanim->tzaisGeonim9Point75Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T19:29:27-04:00"); 282 | $this->assertEquals($zmanim->tzais60->format('Y-m-d\TH:i:sP'), "2023-09-29T19:43:54-04:00"); 283 | $this->assertEquals($zmanim->tzaisAteretTorah->format('Y-m-d\TH:i:sP'), "2023-09-29T19:23:54-04:00"); 284 | $this->assertEquals($zmanim->sofZmanShmaAteretTorah->format('Y-m-d\TH:i:sP'), "2023-09-29T09:04:59-04:00"); 285 | $this->assertEquals($zmanim->sofZmanTfilahAteretTorah->format('Y-m-d\TH:i:sP'), "2023-09-29T10:13:45-04:00"); 286 | $this->assertEquals($zmanim->minchaGedolaAteretTorah->format('Y-m-d\TH:i:sP'), "2023-09-29T13:05:40-04:00"); 287 | $this->assertEquals($zmanim->minchaKetanaAteretTorah->format('Y-m-d\TH:i:sP'), "2023-09-29T16:31:59-04:00"); 288 | $this->assertEquals($zmanim->plagHaminchaAteretTorah->format('Y-m-d\TH:i:sP'), "2023-09-29T17:57:57-04:00"); 289 | $this->assertEquals($zmanim->tzais72Zmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T19:55:18-04:00"); 290 | $this->assertEquals($zmanim->tzais90Zmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T20:13:08-04:00"); 291 | $this->assertEquals($zmanim->tzais96Zmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T20:19:05-04:00"); 292 | $this->assertEquals($zmanim->tzais90->format('Y-m-d\TH:i:sP'), "2023-09-29T20:13:54-04:00"); 293 | $this->assertEquals($zmanim->tzais120->format('Y-m-d\TH:i:sP'), "2023-09-29T20:43:54-04:00"); 294 | $this->assertEquals($zmanim->tzais120Zmanis->format('Y-m-d\TH:i:sP'), "2023-09-29T20:42:53-04:00"); 295 | $this->assertEquals($zmanim->tzais16Point1Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T20:02:55-04:00"); 296 | $this->assertEquals($zmanim->tzais26Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T20:56:31-04:00"); 297 | $this->assertEquals($zmanim->tzais18Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T20:13:02-04:00"); 298 | $this->assertEquals($zmanim->tzais19Point8Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T20:22:40-04:00"); 299 | $this->assertEquals($zmanim->tzais96->format('Y-m-d\TH:i:sP'), "2023-09-29T20:19:54-04:00"); 300 | $this->assertEquals($zmanim->fixedLocalChatzos->format('Y-m-d\TH:i:sP'), "2023-09-29T12:56:57-04:00"); 301 | $this->assertEquals($zmanim->sofZmanShmaFixedLocal->format('Y-m-d\TH:i:sP'), "2023-09-29T09:56:57-04:00"); 302 | $this->assertEquals($zmanim->sofZmanTfilaFixedLocal->format('Y-m-d\TH:i:sP'), "2023-09-29T10:56:57-04:00"); 303 | $this->assertEquals($zmanim->sofZmanKidushLevanaBetweenMoldos->format('Y-m-d\TH:i:sP'), "2023-09-29T23:50:05+02:00"); 304 | $this->assertEquals($zmanim->sofZmanKidushLevana15Days->format('Y-m-d\TH:i:sP'), "2023-09-30T05:28:03+02:00"); 305 | 306 | $zmanim = Zmanim::create(2023, 9, 17, 'Lakewood, NJ', 40.0721087, -74.2400243, 39.57, 'America/New_York'); 307 | $this->assertEquals($zmanim->tchilasZmanKidushLevana3Days->format('Y-m-d\TH:i:sP'), "2023-09-18T05:28:03+02:00"); 308 | 309 | $zmanim = Zmanim::create(2023, 9, 14, 'Lakewood, NJ', 40.0721087, -74.2400243, 39.57, 'America/New_York'); 310 | $this->assertEquals($zmanim->zmanMolad->format('Y-m-d\TH:i:sP'), "2023-09-14T23:28:03-04:00"); 311 | 312 | $zmanim = Zmanim::create(2023, 9, 21, 'Lakewood, NJ', 40.0721087, -74.2400243, 39.57, 'America/New_York'); 313 | $this->assertEquals($zmanim->tchilasZmanKidushLevana7Days->format('Y-m-d\TH:i:sP'), "2023-09-22T05:28:03+02:00"); 314 | 315 | $zmanim = Zmanim::create(2023, 9, 29, 'Lakewood, NJ', 40.0721087, -74.2400243, 39.57, 'America/New_York'); 316 | $this->assertEquals($zmanim->sofZmanAchilasChametzGRA->format('Y-m-d\TH:i:sP'), "2023-09-29T10:48:00-04:00"); 317 | $this->assertEquals($zmanim->sofZmanAchilasChametzMGA72Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T10:24:00-04:00"); 318 | $this->assertEquals($zmanim->sofZmanAchilasChametzMGA16Point1Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T10:21:33-04:00"); 319 | $this->assertEquals($zmanim->sofZmanBiurChametzGRA->format('Y-m-d\TH:i:sP'), "2023-09-29T11:47:29-04:00"); 320 | $this->assertEquals($zmanim->sofZmanBiurChametzMGA72Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T11:35:29-04:00"); 321 | $this->assertEquals($zmanim->sofZmanBiurChametzMGA16Point1Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T11:34:13-04:00"); 322 | $this->assertEquals($zmanim->shaahZmanisBaalHatanya, 3597915.74075); 323 | $this->assertEquals($zmanim->alosBaalHatanya->format('Y-m-d\TH:i:sP'), "2023-09-29T05:26:37-04:00"); 324 | $this->assertEquals($zmanim->sofZmanShmaBaalHatanya->format('Y-m-d\TH:i:sP'), "2023-09-29T09:47:05-04:00"); 325 | $this->assertEquals($zmanim->sofZmanTfilaBaalHatanya->format('Y-m-d\TH:i:sP'), "2023-09-29T10:47:03-04:00"); 326 | $this->assertEquals($zmanim->sofZmanAchilasChametzBaalHatanya->format('Y-m-d\TH:i:sP'), "2023-09-29T10:47:03-04:00"); 327 | $this->assertEquals($zmanim->sofZmanBiurChametzBaalHatanya->format('Y-m-d\TH:i:sP'), "2023-09-29T11:47:01-04:00"); 328 | $this->assertEquals($zmanim->minchaGedolaBaalHatanya->format('Y-m-d\TH:i:sP'), "2023-09-29T13:16:58-04:00"); 329 | $this->assertEquals($zmanim->minchaGedolaBaalHatanyaGreaterThan30->format('Y-m-d\TH:i:sP'), "2023-09-29T13:17:28-04:00"); 330 | $this->assertEquals($zmanim->minchaKetanaBaalHatanya->format('Y-m-d\TH:i:sP'), "2023-09-29T16:16:51-04:00"); 331 | $this->assertEquals($zmanim->plagHaminchaBaalHatanya->format('Y-m-d\TH:i:sP'), "2023-09-29T17:31:49-04:00"); 332 | $this->assertEquals($zmanim->tzaisBaalHatanya->format('Y-m-d\TH:i:sP'), "2023-09-29T19:09:50-04:00"); 333 | $this->assertEquals($zmanim->sofZmanShmaMGA18DegreesToFixedLocalChatzos->format('Y-m-d\TH:i:sP'), "2024-03-05T14:12:51-05:00"); 334 | $this->assertEquals($zmanim->sofZmanShmaMGA16Point1DegreesToFixedLocalChatzos->format('Y-m-d\TH:i:sP'), "2024-03-02T01:49:32-05:00"); 335 | $this->assertEquals($zmanim->sofZmanShmaMGA90MinutesToFixedLocalChatzos->format('Y-m-d\TH:i:sP'), "2024-03-05T19:50:26-05:00"); 336 | $this->assertEquals($zmanim->sofZmanShmaMGA72MinutesToFixedLocalChatzos->format('Y-m-d\TH:i:sP'), "2024-02-28T14:08:26-05:00"); 337 | $this->assertEquals($zmanim->sofZmanShmaGRASunriseToFixedLocalChatzos->format('Y-m-d\TH:i:sP'), "2024-02-03T15:20:26-05:00"); 338 | $this->assertEquals($zmanim->sofZmanTfilaGRASunriseToFixedLocalChatzos->format('Y-m-d\TH:i:sP'), "2024-03-17T03:30:33-04:00"); 339 | $this->assertEquals($zmanim->minchaGedolaGRAFixedLocalChatzos30Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T13:26:57-04:00"); 340 | $this->assertEquals($zmanim->minchaKetanaGRAFixedLocalChatzosToSunset->format('Y-m-d\TH:i:sP'), "2024-02-17T01:07:51-05:00"); 341 | $this->assertEquals($zmanim->plagHaminchaGRAFixedLocalChatzosToSunset->format('Y-m-d\TH:i:sP'), "2024-04-07T06:50:18-04:00"); 342 | $this->assertEquals($zmanim->tzais50->format('Y-m-d\TH:i:sP'), "2023-09-29T19:33:54-04:00"); 343 | $this->assertEquals($zmanim->samuchLeMinchaKetanaGRA->format('Y-m-d\TH:i:sP'), "2023-09-29T15:45:27-04:00"); 344 | $this->assertEquals($zmanim->samuchLeMinchaKetana16Point1Degrees->format('Y-m-d\TH:i:sP'), "2023-09-29T16:24:54-04:00"); 345 | $this->assertEquals($zmanim->samuchLeMinchaKetana72Minutes->format('Y-m-d\TH:i:sP'), "2023-09-29T16:21:27-04:00"); 346 | } 347 | } -------------------------------------------------------------------------------- /tests/Geo/GeoLocationTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($geoLocation->getLocationName(), null); 33 | $this->assertEquals($geoLocation->getLatitude(), 51.4772); 34 | $this->assertEquals($geoLocation->getLongitude(), 0.0); 35 | $this->assertEquals($geoLocation->getElevation(), 0.0); 36 | $this->assertEquals($geoLocation->getTimeZone(), "GMT"); 37 | } 38 | 39 | /** @test */ 40 | public function createGeoLocationWithData() { 41 | $geoLocation = new GeoLocation("Lakewood, NJ", 40.0828, -74.2094, 20, "America/New_York"); 42 | 43 | $this->assertEquals($geoLocation->getLocationName(), "Lakewood, NJ"); 44 | $this->assertEquals($geoLocation->getLatitude(), 40.0828); 45 | $this->assertEquals($geoLocation->getLongitude(), -74.2094); 46 | $this->assertEquals($geoLocation->getElevation(), 20.0); 47 | $this->assertEquals($geoLocation->getTimeZone(), "America/New_York"); 48 | 49 | return [ 50 | 'geo' => $geoLocation, 51 | ]; 52 | } 53 | 54 | /** 55 | * @test 56 | */ 57 | public function updateLatitudeAndLongitude() { 58 | $geoLocation = new GeoLocation("Lakewood, NJ", 40.0828, -74.2094, 20, "America/New_York"); 59 | 60 | $geoLocation->setLatitudeFromDegrees(41, 7, 5.17296, 'N'); 61 | $this->assertEquals($geoLocation->getLatitude(), 41.1181036); 62 | 63 | $geoLocation->setLatitudeFromDegrees(41, 7, 5.17296, 'S'); 64 | $this->assertEquals($geoLocation->getLatitude(), -41.1181036); 65 | 66 | $geoLocation->setLongitudeFromDegrees(41, 7, 5.17296, 'E'); 67 | $this->assertEquals($geoLocation->getLongitude(), 41.1181036); 68 | 69 | $geoLocation->setLongitudeFromDegrees(41, 7, 5.17296, 'W'); 70 | $this->assertEquals($geoLocation->getLongitude(), -41.1181036); 71 | } 72 | 73 | /** 74 | * @test 75 | */ 76 | public function testAntimeridianAdjustmentGmt() { 77 | $geoLocation = new GeoLocation(); 78 | 79 | $this->assertEquals($geoLocation->getAntimeridianAdjustment(), 0); 80 | } 81 | 82 | /** 83 | * @test 84 | */ 85 | public function testAntimeridianAdjustmentNy() { 86 | $geoLocation = new GeoLocation("Lakewood, NJ", 40.0828, -74.2094, 20, "America/New_York"); 87 | 88 | $this->assertEquals($geoLocation->getAntimeridianAdjustment(), 0); 89 | } 90 | 91 | /** 92 | * @test 93 | */ 94 | public function testAntimeridianAdjustmentEast() { 95 | $geoLocation = new GeoLocation("Apia, Samoa", -13.8599098, -171.8031745, 1858, "Pacific/Apia"); 96 | 97 | $this->assertEquals($geoLocation->getAntimeridianAdjustment(), -1); 98 | } 99 | 100 | /** 101 | * @test 102 | */ 103 | public function testAntimeridianAdjustmentWest() { 104 | $geoLocation = new GeoLocation("GMT +12", -13.8599098, 179, 0, "Etc/GMT+12"); 105 | 106 | $this->assertEquals($geoLocation->getAntimeridianAdjustment(), 1); 107 | } 108 | 109 | /** 110 | * @test 111 | */ 112 | public function testVincentyFormulae() { 113 | $pointA = new GeoLocation("", 0 , 0, 0, "Etc/GMT+12"); 114 | $pointA->setLatitudeFromDegrees(50, 3, 58.76, "N"); 115 | $pointA->setLongitudeFromDegrees(5, 42, 53.1, "W"); 116 | 117 | $pointB = new GeoLocation("", 0 , 0, 0, "Etc/GMT+12"); 118 | $pointB->setLatitudeFromDegrees(58, 38, 38.48, "N"); 119 | $pointB->setLongitudeFromDegrees(3, 4, 12.34, "W"); 120 | 121 | $initialBearing = $pointA->getGeodesicInitialBearing($pointB); 122 | $finalBearing = $pointA->getGeodesicFinalBearing($pointB); 123 | $distance = $pointA->getGeodesicDistance($pointB); 124 | 125 | $this->assertEquals(round($initialBearing, 8), 9.14186191); 126 | $this->assertEquals(round($finalBearing, 8), 11.29720127); 127 | $this->assertEquals(round($distance, 8), 969954.11445043); 128 | } 129 | 130 | /** 131 | * @test 132 | */ 133 | public function testRhumbLineBearing() { 134 | $pointA = new GeoLocation("", 0 , 0, 0, "Etc/GMT+12"); 135 | $pointA->setLatitudeFromDegrees(50, 3, 59, "N"); 136 | $pointA->setLongitudeFromDegrees(5, 42, 53, "W"); 137 | 138 | $pointB = new GeoLocation("", 0 , 0, 0, "Etc/GMT+12"); 139 | $pointB->setLatitudeFromDegrees(58, 38, 38, "N"); 140 | $pointB->setLongitudeFromDegrees(3, 4, 12, "W"); 141 | 142 | $rhumbLineBearing = $pointA->getRhumbLineBearing($pointB); 143 | 144 | $this->assertEquals(round($rhumbLineBearing, 8), 10.14069288); 145 | } 146 | 147 | /** 148 | * @test 149 | */ 150 | public function testRhumbLineDistance() { 151 | $pointA = new GeoLocation("", 0 , 0, 0, "Etc/GMT+12"); 152 | $pointA->setLatitudeFromDegrees(50, 3, 59, "N"); 153 | $pointA->setLongitudeFromDegrees(5, 42, 53, "W"); 154 | 155 | $pointB = new GeoLocation("", 0 , 0, 0, "Etc/GMT+12"); 156 | $pointB->setLatitudeFromDegrees(58, 38, 38, "N"); 157 | $pointB->setLongitudeFromDegrees(3, 4, 12, "W"); 158 | 159 | $rhumbLineDistance = $pointA->getRhumbLineDistance($pointB); 160 | 161 | $this->assertEquals(round($rhumbLineDistance, 8), 969995.8368008); 162 | } 163 | } -------------------------------------------------------------------------------- /tests/Geo/GeoLocationUtilsTest.php: -------------------------------------------------------------------------------- 1 | setLatitudeFromDegrees(50, 3, 58.76, "N"); 35 | $pointA->setLongitudeFromDegrees(5, 42, 53.1, "W"); 36 | 37 | $pointB = new GeoLocation("", 0 , 0, 0, "Etc/GMT+12"); 38 | $pointB->setLatitudeFromDegrees(58, 38, 38.48, "N"); 39 | $pointB->setLongitudeFromDegrees(3, 4, 12.34, "W"); 40 | 41 | $this->assertEquals(GeoLocationUtils::getGeodesicInitialBearing($pointA, $pointB), 9.141861908318441); 42 | $this->assertEquals(GeoLocationUtils::getGeodesicFinalBearing($pointA, $pointB), 11.297201271086411); 43 | $this->assertEquals(GeoLocationUtils::getGeodesicDistance($pointA, $pointB), 969954.114450429); 44 | } 45 | 46 | /** 47 | * @test 48 | */ 49 | public function testRhumbLineBearing() { 50 | $pointA = new GeoLocation("", 0 , 0, 0, "Etc/GMT+12"); 51 | $pointA->setLatitudeFromDegrees(50, 3, 59, "N"); 52 | $pointA->setLongitudeFromDegrees(5, 42, 53, "W"); 53 | 54 | $pointB = new GeoLocation("", 0 , 0, 0, "Etc/GMT+12"); 55 | $pointB->setLatitudeFromDegrees(58, 38, 38, "N"); 56 | $pointB->setLongitudeFromDegrees(3, 4, 12, "W"); 57 | 58 | $this->assertEquals(GeoLocationUtils::getRhumbLineBearing($pointA, $pointB), 10.14069287904878); 59 | } 60 | 61 | /** 62 | * @test 63 | */ 64 | public function testRhumbLineDistance() { 65 | $pointA = new GeoLocation("", 0 , 0, 0, "Etc/GMT+12"); 66 | $pointA->setLatitudeFromDegrees(50, 3, 59, "N"); 67 | $pointA->setLongitudeFromDegrees(5, 42, 53, "W"); 68 | 69 | $pointB = new GeoLocation("", 0 , 0, 0, "Etc/GMT+12"); 70 | $pointB->setLatitudeFromDegrees(58, 38, 38, "N"); 71 | $pointB->setLongitudeFromDegrees(3, 4, 12, "W"); 72 | 73 | $this->assertEquals(GeoLocationUtils::getRhumbLineDistance($pointA, $pointB), 969995.8368007989); 74 | } 75 | } -------------------------------------------------------------------------------- /tests/HebrewCalendar/JewishCalendarTest.php: -------------------------------------------------------------------------------- 1 | setDate($cal); 39 | $this->assertTrue($date->isRoshHashana()); 40 | 41 | $date->addDays(1); 42 | $this->assertTrue($date->isRoshHashana()); 43 | 44 | $date->addDays(1); 45 | $this->assertFalse($date->isRoshHashana()); 46 | $this->assertTrue($date->isTaanis()); 47 | } 48 | 49 | /** @test */ 50 | public function testDafYomi() { 51 | $date = new JewishCalendar(Carbon::createMidnightDate(2023, 9, 16)); 52 | 53 | $daf = $date->getDafYomiBavli(); 54 | $formatter = HebrewDateFormatter::create(); 55 | 56 | $this->assertEquals("Kiddushin 34", $formatter->formatDafYomiBavli($daf)); 57 | $this->assertEquals("קידושין ל״ד", $formatter->setHebrewFormat(true)->formatDafYomiBavli($daf)); 58 | 59 | $daf = $date->getDafYomiYerushalmi(); 60 | $formatter = Zmanim::format(); 61 | 62 | $this->assertEquals("Ma'aser Sheni 7", $formatter->formatDafYomiYerushalmi($daf)); 63 | $this->assertEquals("מעשר שני ז׳", $formatter->setHebrewFormat(true)->formatDafYomiYerushalmi($daf)); 64 | } 65 | 66 | /** @test */ 67 | public function testParsha() { 68 | $date = new JewishCalendar(Carbon::createMidnightDate(2023, 10, 14)); 69 | 70 | $this->assertEquals("Bereshis", HebrewDateFormatter::create()->formatParsha($date)); 71 | $this->assertEquals("בראשית", HebrewDateFormatter::create()->setHebrewFormat(true)->formatParsha($date)); 72 | 73 | $date = Zmanim::jewishCalendar(Carbon::createMidnightDate(2024, 2, 17)); 74 | 75 | $this->assertEquals("Terumah", HebrewDateFormatter::create()->formatParsha($date)); 76 | $this->assertEquals("תרומה", HebrewDateFormatter::create()->setHebrewFormat(true)->formatParsha($date)); 77 | } 78 | 79 | /** @test */ 80 | public function testPurim() { 81 | $date = new JewishCalendar(); 82 | 83 | $date->setJewishDate(5783, JewishDate::ADAR, 14); 84 | $this->assertTrue($date->isPurim()); 85 | $date->setIsMukafChoma(true); 86 | $this->assertFalse($date->isPurim()); 87 | 88 | $date->addDays(1); 89 | $this->assertTrue($date->isPurim()); 90 | $date->setIsMukafChoma(false); 91 | $this->assertFalse($date->isPurim()); 92 | 93 | $date->setJewishDate(5784, JewishDate::ADAR, 14); 94 | $this->assertFalse($date->isPurim()); 95 | $date->setIsMukafChoma(true); 96 | $this->assertFalse($date->isPurim()); 97 | 98 | $date->setJewishDate(5784, JewishDate::ADAR_II, 14); 99 | $this->assertFalse($date->isPurim()); 100 | $date->setIsMukafChoma(true); 101 | $this->assertFalse($date->isPurim()); 102 | } 103 | 104 | /** @test */ 105 | public function testTefilaRules() { 106 | $date = new JewishCalendar(Carbon::createMidnightDate(2023, 8, 21)); 107 | 108 | $this->assertTrue(TefilaRules::isTachanunRecitedShacharis($date)); 109 | $this->assertTrue(TefilaRules::isTachanunRecitedMincha($date)); 110 | $this->assertFalse(TefilaRules::isVeseinTalUmatarStartDate($date)); 111 | $this->assertFalse(TefilaRules::isVeseinTalUmatarStartingTonight($date)); 112 | $this->assertFalse(TefilaRules::isVeseinTalUmatarRecited($date)); 113 | $this->assertTrue(TefilaRules::isVeseinBerachaRecited($date)); 114 | $this->assertFalse(TefilaRules::isMashivHaruachStartDate($date)); 115 | $this->assertFalse(TefilaRules::isMashivHaruachEndDate($date)); 116 | $this->assertFalse(TefilaRules::isMashivHaruachRecited($date)); 117 | $this->assertTrue(TefilaRules::isMoridHatalRecited($date)); 118 | $this->assertFalse(TefilaRules::isHallelRecited($date)); 119 | $this->assertFalse(TefilaRules::isHallelShalemRecited($date)); 120 | $this->assertFalse(TefilaRules::isAlHanissimRecited($date)); 121 | $this->assertFalse(TefilaRules::isYaalehVeyavoRecited($date)); 122 | $this->assertTrue(TefilaRules::isMizmorLesodaRecited($date)); 123 | 124 | 125 | $date = new JewishCalendar(Carbon::createMidnightDate(2023, 10, 7)); 126 | 127 | $this->assertFalse(TefilaRules::isTachanunRecitedShacharis($date)); 128 | $this->assertFalse(TefilaRules::isTachanunRecitedMincha($date)); 129 | $this->assertFalse(TefilaRules::isVeseinTalUmatarStartDate($date)); 130 | $this->assertFalse(TefilaRules::isVeseinTalUmatarStartingTonight($date)); 131 | $this->assertFalse(TefilaRules::isVeseinTalUmatarRecited($date)); 132 | $this->assertTrue(TefilaRules::isVeseinBerachaRecited($date)); 133 | $this->assertTrue(TefilaRules::isMashivHaruachStartDate($date)); 134 | $this->assertFalse(TefilaRules::isMashivHaruachEndDate($date)); 135 | $this->assertFalse(TefilaRules::isMashivHaruachRecited($date)); 136 | $this->assertTrue(TefilaRules::isMoridHatalRecited($date)); 137 | $this->assertTrue(TefilaRules::isHallelRecited($date)); 138 | $this->assertTrue(TefilaRules::isHallelShalemRecited($date)); 139 | $this->assertFalse(TefilaRules::isAlHanissimRecited($date)); 140 | $this->assertTrue(TefilaRules::isYaalehVeyavoRecited($date)); 141 | $this->assertFalse(TefilaRules::isMizmorLesodaRecited($date)); 142 | } 143 | } -------------------------------------------------------------------------------- /tests/HebrewCalendar/JewishDateTest.php: -------------------------------------------------------------------------------- 1 | assertDaysInMonth(false, $hebrewDate); 35 | } 36 | 37 | /** @test */ 38 | public function testDaysInMonthLeapYear() { 39 | $hebrewDate = new JewishDate(); 40 | 41 | $cal = Carbon::createMidnightDate(2012, 1, 1); 42 | $hebrewDate->setDate($cal); 43 | 44 | $this->assertDaysInMonth(true, $hebrewDate); 45 | } 46 | 47 | /** @test */ 48 | public function testDaysInMonth100Year() { 49 | $hebrewDate = new JewishDate(); 50 | 51 | $cal = Carbon::createMidnightDate(2100, 1, 1); 52 | $hebrewDate->setDate($cal); 53 | 54 | $this->assertDaysInMonth(false, $hebrewDate); 55 | } 56 | 57 | /** @test */ 58 | public function testDaysInMonth400Year() { 59 | $hebrewDate = new JewishDate(); 60 | 61 | $cal = Carbon::createMidnightDate(2000, 1, 1); 62 | $hebrewDate->setDate($cal); 63 | 64 | $this->assertDaysInMonth(true, $hebrewDate); 65 | } 66 | 67 | /** @test */ 68 | public function daysInMonthsInHaserYear() { 69 | $this->assertHaser(5773); 70 | $this->assertHaser(5777); 71 | $this->assertHaser(5781); 72 | 73 | $this->assertHaserLeap(5784); 74 | $this->assertHaserLeap(5790); 75 | $this->assertHaserLeap(5793); 76 | } 77 | 78 | /** @test */ 79 | public function daysInMonthsInQesidrahYear() { 80 | $this->assertQesidrah(5769); 81 | $this->assertQesidrah(5772); 82 | $this->assertQesidrah(5778); 83 | $this->assertQesidrah(5786); 84 | $this->assertQesidrah(5789); 85 | $this->assertQesidrah(5792); 86 | 87 | $this->assertQesidrahLeap(5782); 88 | } 89 | 90 | /** @test */ 91 | public function daysInMonthsInShalemYear() { 92 | $this->assertShalem(5770); 93 | $this->assertShalem(5780); 94 | $this->assertShalem(5783); 95 | $this->assertShalem(5785); 96 | $this->assertShalem(5788); 97 | $this->assertShalem(5791); 98 | $this->assertShalem(5794); 99 | 100 | $this->assertShalemLeap(5771); 101 | $this->assertShalemLeap(5774); 102 | $this->assertShalemLeap(5776); 103 | $this->assertShalemLeap(5779); 104 | $this->assertShalemLeap(5787); 105 | $this->assertShalemLeap(5795); 106 | } 107 | 108 | /** @test */ 109 | public function gregorianForwardMonthToMonth() { 110 | $cal = Carbon::createMidnightDate(2011, 1, 31); 111 | 112 | $hebrewDate = new JewishDate($cal); 113 | $this->assertEquals(5771, $hebrewDate->getJewishYear()); 114 | $this->assertEquals(11, $hebrewDate->getJewishMonth()); 115 | $this->assertEquals(26, $hebrewDate->getJewishDayOfMonth()); 116 | 117 | $hebrewDate->addDays(1); 118 | $this->assertEquals(2, $hebrewDate->getGregorianMonth()); 119 | $this->assertEquals(1, $hebrewDate->getGregorianDayOfMonth()); 120 | $this->assertEquals(11, $hebrewDate->getJewishMonth()); 121 | $this->assertEquals(27, $hebrewDate->getJewishDayOfMonth()); 122 | 123 | $cal->setDate(2011, 2, 28); 124 | $hebrewDate->setDate($cal); 125 | $this->assertEquals(2, $hebrewDate->getGregorianMonth()); 126 | $this->assertEquals(28, $hebrewDate->getGregorianDayOfMonth()); 127 | $this->assertEquals(12, $hebrewDate->getJewishMonth()); 128 | $this->assertEquals(24, $hebrewDate->getJewishDayOfMonth()); 129 | 130 | $hebrewDate->addDays(1); 131 | $this->assertEquals(3, $hebrewDate->getGregorianMonth()); 132 | $this->assertEquals(1, $hebrewDate->getGregorianDayOfMonth()); 133 | $this->assertEquals(12, $hebrewDate->getJewishMonth()); 134 | $this->assertEquals(25, $hebrewDate->getJewishDayOfMonth()); 135 | 136 | $cal->setDate(2011, 3, 31); 137 | $hebrewDate->setDate($cal); 138 | $hebrewDate->addDays(1); 139 | $this->assertEquals(4, $hebrewDate->getGregorianMonth()); 140 | $this->assertEquals(1, $hebrewDate->getGregorianDayOfMonth()); 141 | $this->assertEquals(13, $hebrewDate->getJewishMonth()); 142 | $this->assertEquals(26, $hebrewDate->getJewishDayOfMonth()); 143 | 144 | $hebrewDate->addMonthsGregorian(1); 145 | $this->assertEquals(5, $hebrewDate->getGregorianMonth()); 146 | $this->assertEquals(1, $hebrewDate->getGregorianDayOfMonth()); 147 | $this->assertEquals(1, $hebrewDate->getJewishMonth()); 148 | $this->assertEquals(27, $hebrewDate->getJewishDayOfMonth()); 149 | 150 | $cal->setDate(2011, 5, 31); 151 | $hebrewDate->setDate($cal); 152 | $hebrewDate->addDays(1); 153 | $this->assertEquals(6, $hebrewDate->getGregorianMonth()); 154 | $this->assertEquals(1, $hebrewDate->getGregorianDayOfMonth()); 155 | $this->assertEquals(2, $hebrewDate->getJewishMonth()); 156 | $this->assertEquals(28, $hebrewDate->getJewishDayOfMonth()); 157 | 158 | $cal->setDate(2011, 6, 30); 159 | $hebrewDate->setDate($cal); 160 | $hebrewDate->addDays(1); 161 | $this->assertEquals(7, $hebrewDate->getGregorianMonth()); 162 | $this->assertEquals(1, $hebrewDate->getGregorianDayOfMonth()); 163 | $this->assertEquals(3, $hebrewDate->getJewishMonth()); 164 | $this->assertEquals(29, $hebrewDate->getJewishDayOfMonth()); 165 | 166 | $cal->setDate(2011, 7, 31); 167 | $hebrewDate->setDate($cal); 168 | $hebrewDate->addDays(1); 169 | $this->assertEquals(8, $hebrewDate->getGregorianMonth()); 170 | $this->assertEquals(1, $hebrewDate->getGregorianDayOfMonth()); 171 | $this->assertEquals(5, $hebrewDate->getJewishMonth()); 172 | $this->assertEquals(1, $hebrewDate->getJewishDayOfMonth()); 173 | 174 | $cal->setDate(2011, 8, 31); 175 | $hebrewDate->setDate($cal); 176 | $hebrewDate->addDays(1); 177 | $this->assertEquals(9, $hebrewDate->getGregorianMonth()); 178 | $this->assertEquals(1, $hebrewDate->getGregorianDayOfMonth()); 179 | $this->assertEquals(6, $hebrewDate->getJewishMonth()); 180 | $this->assertEquals(2, $hebrewDate->getJewishDayOfMonth()); 181 | 182 | $cal->setDate(2011, 9, 30); 183 | $hebrewDate->setDate($cal); 184 | $hebrewDate->addDays(1); 185 | $this->assertEquals(10, $hebrewDate->getGregorianMonth()); 186 | $this->assertEquals(1, $hebrewDate->getGregorianDayOfMonth()); 187 | $this->assertEquals(7, $hebrewDate->getJewishMonth()); 188 | $this->assertEquals(3, $hebrewDate->getJewishDayOfMonth()); 189 | 190 | $cal->setDate(2011, 10, 31); 191 | $hebrewDate->setDate($cal); 192 | $hebrewDate->addDays(1); 193 | $this->assertEquals(11, $hebrewDate->getGregorianMonth()); 194 | $this->assertEquals(1, $hebrewDate->getGregorianDayOfMonth()); 195 | $this->assertEquals(5772, $hebrewDate->getJewishYear()); 196 | $this->assertEquals(8, $hebrewDate->getJewishMonth()); 197 | $this->assertEquals(4, $hebrewDate->getJewishDayOfMonth()); 198 | 199 | $cal->setDate(2011, 11, 30); 200 | $hebrewDate->setDate($cal); 201 | $hebrewDate->addDays(1); 202 | $this->assertEquals(12, $hebrewDate->getGregorianMonth()); 203 | $this->assertEquals(1, $hebrewDate->getGregorianDayOfMonth()); 204 | $this->assertEquals(9, $hebrewDate->getJewishMonth()); 205 | $this->assertEquals(5, $hebrewDate->getJewishDayOfMonth()); 206 | 207 | $hebrewDate->addMonthsGregorian(1); 208 | $this->assertEquals(2012, $hebrewDate->getGregorianYear()); 209 | $this->assertEquals(1, $hebrewDate->getGregorianMonth()); 210 | $this->assertEquals(1, $hebrewDate->getGregorianDayOfMonth()); 211 | $this->assertEquals(10, $hebrewDate->getJewishMonth()); 212 | $this->assertEquals(6, $hebrewDate->getJewishDayOfMonth()); 213 | 214 | $hebrewDate->addDays(650); 215 | $this->assertEquals(2013, $hebrewDate->getGregorianYear()); 216 | $this->assertEquals(10, $hebrewDate->getGregorianMonth()); 217 | $this->assertEquals(12, $hebrewDate->getGregorianDayOfMonth()); 218 | $this->assertEquals(5774, $hebrewDate->getJewishYear()); 219 | $this->assertEquals(8, $hebrewDate->getJewishMonth()); 220 | $this->assertEquals(8, $hebrewDate->getJewishDayOfMonth()); 221 | 222 | $hebrewDate->addDays(218); 223 | $this->assertEquals(2014, $hebrewDate->getGregorianYear()); 224 | $this->assertEquals(5, $hebrewDate->getGregorianMonth()); 225 | $this->assertEquals(18, $hebrewDate->getGregorianDayOfMonth()); 226 | $this->assertEquals(5774, $hebrewDate->getJewishYear()); 227 | $this->assertEquals(2, $hebrewDate->getJewishMonth()); 228 | $this->assertEquals(18, $hebrewDate->getJewishDayOfMonth()); 229 | 230 | $hebrewDate->addMonthsJewish(1); 231 | $this->assertEquals(2014, $hebrewDate->getGregorianYear()); 232 | $this->assertEquals(6, $hebrewDate->getGregorianMonth()); 233 | $this->assertEquals(16, $hebrewDate->getGregorianDayOfMonth()); 234 | $this->assertEquals(5774, $hebrewDate->getJewishYear()); 235 | $this->assertEquals(3, $hebrewDate->getJewishMonth()); 236 | $this->assertEquals(18, $hebrewDate->getJewishDayOfMonth()); 237 | 238 | $hebrewDate->addMonthsJewish(5); 239 | $this->assertEquals(2014, $hebrewDate->getGregorianYear()); 240 | $this->assertEquals(11, $hebrewDate->getGregorianMonth()); 241 | $this->assertEquals(11, $hebrewDate->getGregorianDayOfMonth()); 242 | $this->assertEquals(5775, $hebrewDate->getJewishYear()); 243 | $this->assertEquals(8, $hebrewDate->getJewishMonth()); 244 | $this->assertEquals(18, $hebrewDate->getJewishDayOfMonth()); 245 | 246 | $hebrewDate->subMonthsJewish(5); 247 | $this->assertEquals(2014, $hebrewDate->getGregorianYear()); 248 | $this->assertEquals(6, $hebrewDate->getGregorianMonth()); 249 | $this->assertEquals(16, $hebrewDate->getGregorianDayOfMonth()); 250 | $this->assertEquals(5774, $hebrewDate->getJewishYear()); 251 | $this->assertEquals(3, $hebrewDate->getJewishMonth()); 252 | $this->assertEquals(18, $hebrewDate->getJewishDayOfMonth()); 253 | 254 | $hebrewDate->subMonthsJewish(1); 255 | $this->assertEquals(2014, $hebrewDate->getGregorianYear()); 256 | $this->assertEquals(5, $hebrewDate->getGregorianMonth()); 257 | $this->assertEquals(18, $hebrewDate->getGregorianDayOfMonth()); 258 | $this->assertEquals(5774, $hebrewDate->getJewishYear()); 259 | $this->assertEquals(2, $hebrewDate->getJewishMonth()); 260 | $this->assertEquals(18, $hebrewDate->getJewishDayOfMonth()); 261 | } 262 | 263 | 264 | /** @test */ 265 | public function gregorianBackwardMonthToMonth() { 266 | $cal = Carbon::createMidnightDate(2011, 1, 1); 267 | 268 | $hebrewDate = new JewishDate($cal); 269 | $hebrewDate->subDays(1); 270 | $this->assertEquals(2010, $hebrewDate->getGregorianYear()); 271 | $this->assertEquals(12, $hebrewDate->getGregorianMonth()); 272 | $this->assertEquals(31, $hebrewDate->getGregorianDayOfMonth()); 273 | $this->assertEquals(10, $hebrewDate->getJewishMonth()); 274 | $this->assertEquals(24, $hebrewDate->getJewishDayOfMonth()); 275 | 276 | $cal->setDate(2010, 12, 1); 277 | $hebrewDate->setDate($cal); 278 | $hebrewDate->subDays(1); 279 | $this->assertEquals(11, $hebrewDate->getGregorianMonth()); 280 | $this->assertEquals(30, $hebrewDate->getGregorianDayOfMonth()); 281 | $this->assertEquals(9, $hebrewDate->getJewishMonth()); 282 | $this->assertEquals(23, $hebrewDate->getJewishDayOfMonth()); 283 | 284 | $cal->setDate(2010, 11, 1); 285 | $hebrewDate->setDate($cal); 286 | $hebrewDate->subDays(1); 287 | $this->assertEquals(10, $hebrewDate->getGregorianMonth()); 288 | $this->assertEquals(31, $hebrewDate->getGregorianDayOfMonth()); 289 | $this->assertEquals(8, $hebrewDate->getJewishMonth()); 290 | $this->assertEquals(23, $hebrewDate->getJewishDayOfMonth()); 291 | 292 | $cal->setDate(2010, 10, 1); 293 | $hebrewDate->setDate($cal); 294 | $hebrewDate->subDays(1); 295 | $this->assertEquals(9, $hebrewDate->getGregorianMonth()); 296 | $this->assertEquals(30, $hebrewDate->getGregorianDayOfMonth()); 297 | $this->assertEquals(7, $hebrewDate->getJewishMonth()); 298 | $this->assertEquals(22, $hebrewDate->getJewishDayOfMonth()); 299 | 300 | $cal->setDate(2010, 9, 1); 301 | $hebrewDate->setDate($cal); 302 | $hebrewDate->subDays(1); 303 | $this->assertEquals(8, $hebrewDate->getGregorianMonth()); 304 | $this->assertEquals(31, $hebrewDate->getGregorianDayOfMonth()); 305 | $this->assertEquals(5770, $hebrewDate->getJewishYear()); 306 | $this->assertEquals(6, $hebrewDate->getJewishMonth()); 307 | $this->assertEquals(21, $hebrewDate->getJewishDayOfMonth()); 308 | 309 | $cal->setDate(2010, 8, 1); 310 | $hebrewDate->setDate($cal); 311 | $hebrewDate->subDays(1); 312 | $this->assertEquals(7, $hebrewDate->getGregorianMonth()); 313 | $this->assertEquals(31, $hebrewDate->getGregorianDayOfMonth()); 314 | $this->assertEquals(5, $hebrewDate->getJewishMonth()); 315 | $this->assertEquals(20, $hebrewDate->getJewishDayOfMonth()); 316 | 317 | $cal->setDate(2010, 7, 1); 318 | $hebrewDate->setDate($cal); 319 | $hebrewDate->subDays(1); 320 | $this->assertEquals(6, $hebrewDate->getGregorianMonth()); 321 | $this->assertEquals(30, $hebrewDate->getGregorianDayOfMonth()); 322 | $this->assertEquals(4, $hebrewDate->getJewishMonth()); 323 | $this->assertEquals(18, $hebrewDate->getJewishDayOfMonth()); 324 | 325 | $cal->setDate(2010, 6, 1); 326 | $hebrewDate->setDate($cal); 327 | $hebrewDate->subDays(1); 328 | $this->assertEquals(5, $hebrewDate->getGregorianMonth()); 329 | $this->assertEquals(31, $hebrewDate->getGregorianDayOfMonth()); 330 | $this->assertEquals(3, $hebrewDate->getJewishMonth()); 331 | $this->assertEquals(18, $hebrewDate->getJewishDayOfMonth()); 332 | 333 | $cal->setDate(2010, 5, 1); 334 | $hebrewDate->setDate($cal); 335 | $hebrewDate->subDays(1); 336 | $this->assertEquals(4, $hebrewDate->getGregorianMonth()); 337 | $this->assertEquals(30, $hebrewDate->getGregorianDayOfMonth()); 338 | $this->assertEquals(2, $hebrewDate->getJewishMonth()); 339 | $this->assertEquals(16, $hebrewDate->getJewishDayOfMonth()); 340 | 341 | $cal->setDate(2010, 4, 1); 342 | $hebrewDate->setDate($cal); 343 | $hebrewDate->subDays(1); 344 | $this->assertEquals(3, $hebrewDate->getGregorianMonth()); 345 | $this->assertEquals(31, $hebrewDate->getGregorianDayOfMonth()); 346 | $this->assertEquals(1, $hebrewDate->getJewishMonth()); 347 | $this->assertEquals(16, $hebrewDate->getJewishDayOfMonth()); 348 | 349 | $cal->setDate(2010, 3, 1); 350 | $hebrewDate->setDate($cal); 351 | $hebrewDate->subDays(1); 352 | $this->assertEquals(2, $hebrewDate->getGregorianMonth()); 353 | $this->assertEquals(28, $hebrewDate->getGregorianDayOfMonth()); 354 | $this->assertEquals(12, $hebrewDate->getJewishMonth()); 355 | $this->assertEquals(14, $hebrewDate->getJewishDayOfMonth()); 356 | 357 | $cal->setDate(2010, 2, 1); 358 | $hebrewDate->setDate($cal); 359 | $hebrewDate->subDays(1); 360 | $this->assertEquals(1, $hebrewDate->getGregorianMonth()); 361 | $this->assertEquals(31, $hebrewDate->getGregorianDayOfMonth()); 362 | $this->assertEquals(11, $hebrewDate->getJewishMonth()); 363 | $this->assertEquals(16, $hebrewDate->getJewishDayOfMonth()); 364 | 365 | ///////// 366 | $cal->setDate(2014, 5, 18); 367 | $hebrewDate->setDate($cal); 368 | $hebrewDate->subDays(218); 369 | $this->assertEquals(2013, $hebrewDate->getGregorianYear()); 370 | $this->assertEquals(10, $hebrewDate->getGregorianMonth()); 371 | $this->assertEquals(12, $hebrewDate->getGregorianDayOfMonth()); 372 | $this->assertEquals(5774, $hebrewDate->getJewishYear()); 373 | $this->assertEquals(8, $hebrewDate->getJewishMonth()); 374 | $this->assertEquals(8, $hebrewDate->getJewishDayOfMonth()); 375 | 376 | $hebrewDate->subDays(653); 377 | $this->assertEquals(2011, $hebrewDate->getGregorianYear()); 378 | $this->assertEquals(12, $hebrewDate->getGregorianMonth()); 379 | $this->assertEquals(29, $hebrewDate->getGregorianDayOfMonth()); 380 | $this->assertEquals(5772, $hebrewDate->getJewishYear()); 381 | $this->assertEquals(10, $hebrewDate->getJewishMonth()); 382 | $this->assertEquals(3, $hebrewDate->getJewishDayOfMonth()); 383 | } 384 | 385 | /** @test */ 386 | public function testFormat() { 387 | $hebrewDate = JewishDate::create(Carbon::createMidnightDate(2011, 1, 1)); 388 | $format = Zmanim::format(); 389 | $format->setHebrewFormat(true); 390 | 391 | $this->assertEquals("כ״ה טבת תשע״א", $format->format($hebrewDate)); 392 | } 393 | 394 | private function assertDaysInMonth($febIsLeap, $hebrewDate) { 395 | $this->assertEquals(31, JewishDate::getLastDayOfGregorianMonth(1, $hebrewDate->getGregorianYear())); 396 | $this->assertEquals($febIsLeap ? 29 : 28, JewishDate::getLastDayOfGregorianMonth(2, $hebrewDate->getGregorianYear())); 397 | $this->assertEquals(31, JewishDate::getLastDayOfGregorianMonth(3, $hebrewDate->getGregorianYear())); 398 | $this->assertEquals(30, JewishDate::getLastDayOfGregorianMonth(4, $hebrewDate->getGregorianYear())); 399 | $this->assertEquals(31, JewishDate::getLastDayOfGregorianMonth(5, $hebrewDate->getGregorianYear())); 400 | $this->assertEquals(30, JewishDate::getLastDayOfGregorianMonth(6, $hebrewDate->getGregorianYear())); 401 | $this->assertEquals(31, JewishDate::getLastDayOfGregorianMonth(7, $hebrewDate->getGregorianYear())); 402 | $this->assertEquals(31, JewishDate::getLastDayOfGregorianMonth(8, $hebrewDate->getGregorianYear())); 403 | $this->assertEquals(30, JewishDate::getLastDayOfGregorianMonth(9, $hebrewDate->getGregorianYear())); 404 | $this->assertEquals(31, JewishDate::getLastDayOfGregorianMonth(10, $hebrewDate->getGregorianYear())); 405 | $this->assertEquals(30, JewishDate::getLastDayOfGregorianMonth(11, $hebrewDate->getGregorianYear())); 406 | $this->assertEquals(31, JewishDate::getLastDayOfGregorianMonth(12, $hebrewDate->getGregorianYear())); 407 | } 408 | 409 | private function assertHaser($year) { 410 | $jewishDate = new JewishDate(); 411 | $jewishDate->setJewishMonth(1); 412 | $jewishDate->setJewishYear($year); 413 | 414 | $this->assertFalse(JewishDate::isCheshvanLong($jewishDate->getJewishYear())); 415 | $this->assertTrue(JewishDate::isKislevShort($jewishDate->getJewishYear())); 416 | } 417 | 418 | 419 | private function assertHaserLeap($year) { 420 | $jewishDate = new JewishDate(); 421 | $jewishDate->setJewishMonth(1); 422 | $jewishDate->setJewishYear($year); 423 | 424 | $this->assertHaser($year); 425 | $this->assertTrue(JewishDate::isJewishLeapYear($jewishDate->getJewishYear())); 426 | } 427 | 428 | 429 | private function assertQesidrah($year) { 430 | $jewishDate = new JewishDate(); 431 | $jewishDate->setJewishMonth(1); 432 | $jewishDate->setJewishYear($year); 433 | 434 | $this->assertFalse(JewishDate::isCheshvanLong($jewishDate->getJewishYear())); 435 | $this->assertFalse(JewishDate::isKislevShort($jewishDate->getJewishYear())); 436 | } 437 | 438 | 439 | private function assertQesidrahLeap($year) { 440 | $jewishDate = new JewishDate(); 441 | $jewishDate->setJewishMonth(1); 442 | $jewishDate->setJewishYear($year); 443 | 444 | $this->assertQesidrah($year); 445 | $this->assertTrue(JewishDate::isJewishLeapYear($jewishDate->getJewishYear())); 446 | } 447 | 448 | 449 | private function assertShalem($year) { 450 | $jewishDate = new JewishDate(); 451 | $jewishDate->setJewishMonth(1); 452 | $jewishDate->setJewishYear($year); 453 | 454 | $this->assertTrue(JewishDate::isCheshvanLong($jewishDate->getJewishYear())); 455 | $this->assertFalse(JewishDate::isKislevShort($jewishDate->getJewishYear())); 456 | } 457 | 458 | 459 | private function assertShalemLeap($year) { 460 | $jewishDate = new JewishDate(); 461 | $jewishDate->setJewishMonth(1); 462 | $jewishDate->setJewishYear($year); 463 | 464 | $this->assertShalem($year); 465 | $this->assertTrue(JewishDate::isJewishLeapYear($jewishDate->getJewishYear())); 466 | } 467 | } --------------------------------------------------------------------------------