/php/PasoonDate.php

https://gitlab.com/ataiemajid_63/pasoon-date · PHP · 1275 lines · 983 code · 184 blank · 108 comment · 87 complexity · 38c895710983eb81d31362a5cc27fd11 MD5 · raw file

  1. <?php
  2. namespace Pasoon;
  3. class PasoonDate
  4. {
  5. private $J1970 = 2440587.5; // Julian date at Unix epoch: 1970-01-01
  6. private $jalaliDate;
  7. private $gregorianDate;
  8. private $islamicDate;
  9. private $shiaDate;
  10. private $julianDayNumber;
  11. private $offset;
  12. private $hours;
  13. private $minute;
  14. private $second;
  15. /**
  16. * PasoonDate constructor.
  17. * @param int|string $timestamp
  18. * @param int $offset
  19. */
  20. public function __construct($timestamp = 'now', $offset = null)
  21. {
  22. $time = time();
  23. if (is_numeric($timestamp)) {
  24. $time = (integer) $timestamp;
  25. }
  26. $this->setOffset($offset ?: date('Z'));
  27. $this->setTimestamp($time);
  28. }
  29. private function julianTimeToTime($julianNumber)
  30. {
  31. $julianNumber += 0.5; /* Astronomical to civil */
  32. $ij = (($julianNumber - floor($julianNumber)) * 86400.0) + 0.5;
  33. return [
  34. floor($ij / 3600),
  35. floor(($ij / 60) % 60),
  36. floor($ij % 60)
  37. ];
  38. }
  39. public function setTimestamp($timestamp)
  40. {
  41. $date = floor($timestamp / (60 * 60 * 24));
  42. $time = $timestamp - ($date * 60 * 60 * 24);
  43. $hour = floor($time / (60*60));
  44. $minute = floor(($time / 60) - ($hour * 60));
  45. $second = $time - ((($hour * 60) + $minute) * 60);
  46. $this->setJulianDayNumber(($this->J1970 + $date));
  47. $this->setHours($hour);
  48. $this->setMinute($minute);
  49. $this->setSecond($second);
  50. }
  51. public function getTimestamp()
  52. {
  53. $timestamp = ($this->getJulianDayNumber() - $this->J1970) * (60 * 60 * 24);
  54. return (($this->getHours() * 60 * 60) + ($this->getMinute() * 60) + $this->getSecond()) + $timestamp;
  55. }
  56. public function setJulianDayNumber($number)
  57. {
  58. $this->julianDayNumber = $number;
  59. }
  60. public function getJulianDayNumber()
  61. {
  62. return $this->julianDayNumber;
  63. }
  64. public function setOffset($offset)
  65. {
  66. $this->offset = $offset;
  67. }
  68. public function getOffset()
  69. {
  70. return $this->offset;
  71. }
  72. /**
  73. * @return mixed
  74. */
  75. public function getHours()
  76. {
  77. return $this->hours;
  78. }
  79. /**
  80. * @param mixed $hours
  81. */
  82. public function setHours($hours)
  83. {
  84. $this->hours = (int)$hours;
  85. }
  86. /**
  87. * @return mixed
  88. */
  89. public function getMinute()
  90. {
  91. return $this->minute;
  92. }
  93. /**
  94. * @param mixed $minute
  95. */
  96. public function setMinute($minute)
  97. {
  98. $this->minute = (int)$minute;
  99. }
  100. /**
  101. * @return mixed
  102. */
  103. public function getSecond()
  104. {
  105. return $this->second;
  106. }
  107. /**
  108. * @param mixed $second
  109. */
  110. public function setSecond($second)
  111. {
  112. $this->second = (int)$second;
  113. }
  114. /**
  115. * @param bool|int $year
  116. * @param bool|int $month
  117. * @param bool|int $day
  118. * @param bool|int $hours
  119. * @param bool|int $minute
  120. * @param bool|int $second
  121. * @return JalaliDate
  122. */
  123. public function jalali($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  124. {
  125. if (empty($this->jalaliDate) or $year)
  126. $this->jalaliDate = new JalaliDate($this, $year, $month, $day, $hours, $minute, $second);
  127. return $this->jalaliDate;
  128. }
  129. /**
  130. * @param bool|int $year
  131. * @param bool|int $month
  132. * @param bool|int $day
  133. * @param bool|int $hours
  134. * @param bool|int $minute
  135. * @param bool|int $second
  136. * @return GregorianDate
  137. */
  138. public function gregorian($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  139. {
  140. if (empty($this->gregorianDate) or $year) {
  141. $this->gregorianDate = new GregorianDate($this, $year, $month, $day, $hours, $minute, $second);
  142. }
  143. return $this->gregorianDate;
  144. }
  145. /**
  146. * @param bool|int $year
  147. * @param bool|int $month
  148. * @param bool|int $day
  149. * @param bool|int $hours
  150. * @param bool|int $minute
  151. * @param bool|int $second
  152. * @return IslamicDate
  153. */
  154. public function islamic($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  155. {
  156. if (empty($this->islamicDate) or $year)
  157. $this->islamicDate = new IslamicDate($this, $year, $month, $day, $hours, $minute, $second);
  158. return $this->islamicDate;
  159. }
  160. /**
  161. * @param bool|int $year
  162. * @param bool|int $month
  163. * @param bool|int $day
  164. * @param bool|int $hours
  165. * @param bool|int $minute
  166. * @param bool|int $second
  167. * @return ShiaDate
  168. */
  169. public function shia($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  170. {
  171. if (empty($this->shiaDate) or $year)
  172. $this->shiaDate = new ShiaDate($this, $year, $month, $day, $hours, $minute, $second);
  173. return $this->shiaDate;
  174. }
  175. }
  176. class AbstractPasoonDate
  177. {
  178. protected function mod($a, $b)
  179. {
  180. return $a - ($b * floor($a / $b));
  181. }
  182. protected function dayOfWeek($julianDayNumber)
  183. {
  184. return $this->mod(floor(($julianDayNumber + 1.5)), 7);
  185. }
  186. }
  187. class GregorianDate extends AbstractPasoonDate
  188. {
  189. private $GREGORIAN_EPOCH = 1721425.5;
  190. private $pasoonDate;
  191. /**
  192. * @param PasoonDate $pasoonDate
  193. * @param $year
  194. * @param $month
  195. * @param $day
  196. * @param $hours
  197. * @param $minute
  198. * @param $second
  199. */
  200. public function __construct($pasoonDate, $year, $month, $day, $hours, $minute, $second)
  201. {
  202. $this->pasoonDate = $pasoonDate;
  203. if ($year and $month and $day) {
  204. $pasoonDate->setHours(isset($hours) ? $hours : date('H'));
  205. $pasoonDate->setMinute(isset($minute) ? $minute : date('i'));
  206. $pasoonDate->setSecond(isset($second) ? $second : date('s'));
  207. $pasoonDate->setJulianDayNumber($this->toJulianDayNumber($year, $month, $day));
  208. $pasoonDate->setTimestamp($pasoonDate->getTimestamp() - $pasoonDate->getOffset());
  209. }
  210. }
  211. private function toJulianDayNumber($year, $month, $day)
  212. {
  213. return ($this->GREGORIAN_EPOCH - 1) +
  214. (365 * ($year - 1)) +
  215. floor(($year - 1) / 4) +
  216. (-floor(($year - 1) / 100)) +
  217. floor(($year - 1) / 400) +
  218. floor((((367 * $month) - 362) / 12) + (($month <= 2) ? 0 : ($this->isLeap($year) ? -1 : -2)) + $day);
  219. }
  220. private function toGregorian()
  221. {
  222. $jd = $this->pasoonDate->getJulianDayNumber();
  223. $wjd = floor($jd - 0.5) + 0.5;
  224. $depoch = $wjd - $this->GREGORIAN_EPOCH;
  225. $quadricent = floor($depoch / 146097);
  226. $dqc = $this->mod($depoch, 146097);
  227. $cent = floor($dqc / 36524);
  228. $dcent = $this->mod($dqc, 36524);
  229. $quad = floor($dcent / 1461);
  230. $dquad = $this->mod($dcent, 1461);
  231. $yindex = floor($dquad / 365);
  232. $year = ($quadricent * 400) + ($cent * 100) + ($quad * 4) + $yindex;
  233. if (!(($cent == 4) || ($yindex == 4))) {
  234. $year++;
  235. }
  236. $yearday = $wjd - $this->toJulianDayNumber($year, 1, 1);
  237. $leapadj = (($wjd < $this->toJulianDayNumber($year, 3, 1)) ? 0 : ($this->isLeap($year) ? 1 : 2));
  238. $month = floor(((($yearday + $leapadj) * 12) + 373) / 367);
  239. $day = ($wjd - $this->toJulianDayNumber($year, $month, 1)) + 1;
  240. return [$year, $month, $day];
  241. }
  242. private function isLeap($year)
  243. {
  244. return (($year % 4) == 0) && (!((($year % 100) == 0) && (($year % 400) != 0)));
  245. }
  246. public function daysOfMonth($year, $month)
  247. {
  248. $countDays = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
  249. if ($month < 1 || $month > 12) {
  250. throw new Exception("$month Out Of Range Exception");
  251. }
  252. if ($year && $this->isLeap($year) && $month == 2) {
  253. return 29;
  254. }
  255. return $countDays[$month - 1];
  256. }
  257. public function startDayOfMonth($year, $month)
  258. {
  259. return $this->dayOfWeek($this->toJulianDayNumber($year, $month, 1));
  260. }
  261. public function lastDayOfMonth($year, $month)
  262. {
  263. return $this->dayOfWeek($this->toJulianDayNumber($year, $month, $this->daysOfMonth($year, $month)));
  264. }
  265. public function monthName($month, $short = false)
  266. {
  267. $names = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
  268. if (empty($month)) {
  269. return $names;
  270. }
  271. if ($month < 1 || $month > 12) {
  272. throw new Exception("$month Out Of Range Exception");
  273. }
  274. if ($short) {
  275. return substr($names[$month - 1], 0, 3);
  276. }
  277. return $names[$month - 1];
  278. }
  279. public function nameOfDay($dayOfWeek, $short = false)
  280. {
  281. $names = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
  282. if ($dayOfWeek < 0 || $dayOfWeek > 6) {
  283. throw new Exception("$dayOfWeek Out Of Range Exception");
  284. }
  285. if ($short) {
  286. return substr($names[$dayOfWeek], 0, 3);
  287. }
  288. return $names[$dayOfWeek];
  289. }
  290. public function get()
  291. {
  292. $date = $this->toGregorian();
  293. $datetime = new PasoonDateTime();
  294. $utcTimestamp = $this->pasoonDate->getTimestamp();
  295. $utcDate = $this->toGregorian();
  296. $localTimestamp = $utcTimestamp + $this->pasoonDate->getOffset();
  297. $localDate;
  298. $datetime->setTimestamp($utcTimestamp);
  299. $datetime->setJulianDay($this->pasoonDate->getJulianDayNumber());
  300. $datetime->setUTCYear($utcDate[0]);
  301. $datetime->setUTCMonth($utcDate[1]);
  302. $datetime->setUTCDay($utcDate[2]);
  303. $datetime->setUTCHour($this->pasoonDate->getHours());
  304. $datetime->setUTCMinute($this->pasoonDate->getMinute());
  305. $datetime->setUTCSecond($this->pasoonDate->getSecond());
  306. $datetime->setUTCDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), false);
  307. $datetime->setUTCDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), true);
  308. $datetime->setUTCMonthName($this->monthName($utcDate[1]), false);
  309. $datetime->setUTCMonthName($this->monthName($utcDate[1], true), true);
  310. $this->pasoonDate->setTimestamp($localTimestamp);
  311. $localDate = $this->toGregorian();
  312. $datetime->setTimestamp($localTimestamp);
  313. $datetime->setJulianDay($this->pasoonDate->getJulianDayNumber());
  314. $datetime->setYear($localDate[0]);
  315. $datetime->setMonth($localDate[1]);
  316. $datetime->setDay($localDate[2]);
  317. $datetime->setHour($this->pasoonDate->getHours());
  318. $datetime->setMinute($this->pasoonDate->getMinute());
  319. $datetime->setSecond($this->pasoonDate->getSecond());
  320. $datetime->setDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), false);
  321. $datetime->setDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), true);
  322. $datetime->setMonthName($this->monthName($localDate[1]), false);
  323. $datetime->setMonthName($this->monthName($localDate[1], true), true);
  324. $this->pasoonDate->setTimestamp($utcTimestamp);
  325. return $datetime;
  326. }
  327. public function islamic($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  328. {
  329. return $this->pasoonDate->islamic($year, $month, $day, $hours, $minute, $second);
  330. }
  331. public function jalali($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  332. {
  333. return $this->pasoonDate->jalali($year, $month, $day, $hours, $minute, $second);
  334. }
  335. public function shia($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  336. {
  337. return $this->pasoonDate->shia($year, $month, $day, $hours, $minute, $second);
  338. }
  339. }
  340. class IslamicDate extends AbstractPasoonDate
  341. {
  342. private $ISLAMIC_EPOCH = 1948439.5;
  343. private $pasoonDate;
  344. /**
  345. * @param PasoonDate $pasoonDate
  346. * @param $year
  347. * @param $month
  348. * @param $day
  349. * @param $hours
  350. * @param $minute
  351. * @param $second
  352. */
  353. public function __construct($pasoonDate, $year, $month, $day, $hours, $minute, $second)
  354. {
  355. $this->pasoonDate = $pasoonDate;
  356. if ($year and $month and $day) {
  357. $pasoonDate->setHours(isset($hours) ? $hours : date('H'));
  358. $pasoonDate->setMinute(isset($minute) ? $minute : date('i'));
  359. $pasoonDate->setSecond(isset($second) ? $second : date('s'));
  360. $pasoonDate->setJulianDayNumber($this->toJulianDayNumber($year, $month, $day));
  361. $pasoonDate->setTimestamp($pasoonDate->getTimestamp() - $pasoonDate->getOffset());
  362. }
  363. }
  364. private function toJulianDayNumber($year, $month, $day)
  365. {
  366. return ($day +
  367. ceil(29.5 * ($month - 1)) +
  368. ($year - 1) * 354 +
  369. floor((3 + (11 * $year)) / 30) +
  370. $this->ISLAMIC_EPOCH) - 1;
  371. }
  372. private function toIslamic()
  373. {
  374. $jd = $this->pasoonDate->getJulianDayNumber();
  375. $jd = floor($jd) + 0.5;
  376. $year = floor(((30 * ($jd - $this->ISLAMIC_EPOCH)) + 10646) / 10631);
  377. $month = min(12, ceil(($jd - (29 + $this->toJulianDayNumber($year, 1, 1))) / 29.5) + 1);
  378. $day = $jd - $this->toJulianDayNumber($year, $month, 1) + 1;
  379. return [$year, $month, $day];
  380. }
  381. private function isLeap($year)
  382. {
  383. return ((($year * 11) + 14) % 30) < 11;
  384. }
  385. public function daysOfMonth($year, $month)
  386. {
  387. $countDays = array(30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29); //30
  388. if ($month < 1 || $month > 12) {
  389. throw new Exception("$month Out Of Range Exception");
  390. }
  391. if ($year && $this->isLeap($year) && $month == 12) {
  392. return 30;
  393. }
  394. return $countDays[$month - 1];
  395. }
  396. public function startDayOfMonth($year, $month)
  397. {
  398. return $this->dayOfWeek($this->toJulianDayNumber($year, $month, 1));
  399. }
  400. public function lastDayOfMonth($year, $month)
  401. {
  402. return $this->dayOfWeek($this->toJulianDayNumber($year, $month, $this->daysOfMonth($year, $month)));
  403. }
  404. public function monthName($month)
  405. {
  406. $names = ["محرم", "صفر", "ربیع الاول", "ربیع الثانی", "جمادی الاول", "جمادی الثانی", "رجب", "شعبان", "رمضان", "شوال", "ذی‌القعده", "ذی‌الحجه"];
  407. if (empty($month)) {
  408. return $names;
  409. }
  410. if ($month < 1 || $month > 12) {
  411. throw new Exception("$month Out Of Range Exception");
  412. }
  413. return $names[$month - 1];
  414. }
  415. public function nameOfDay($dayOfWeek, $short = false)
  416. {
  417. $names = ["الأحد", "الاثنین", "الثلاثاء", "الأربعاء", "الخمیس", "الجمعة", "السبت"];
  418. $shortNames = ["ح", "ن", "ث", "ر", "خ", "ج", "س"];
  419. if ($dayOfWeek < 0 || $dayOfWeek > 6) {
  420. throw new Exception("$dayOfWeek Out Of Range Exception");
  421. }
  422. if ($short) {
  423. return $shortNames[$dayOfWeek];
  424. }
  425. return $names[$dayOfWeek];
  426. }
  427. /**
  428. * @return PasoonDate
  429. */
  430. public function get()
  431. {
  432. $date = $this->toIslamic();
  433. $datetime = new PasoonDateTime();
  434. $utcTimestamp = $this->pasoonDate->getTimestamp();
  435. $utcDate = $this->toIslamic();
  436. $localTimestamp = $utcTimestamp + $this->pasoonDate->getOffset();
  437. $localDate;
  438. $datetime->setTimestamp($utcTimestamp);
  439. $datetime->setJulianDay($this->pasoonDate->getJulianDayNumber());
  440. $datetime->setUTCYear($utcDate[0]);
  441. $datetime->setUTCMonth($utcDate[1]);
  442. $datetime->setUTCDay($utcDate[2]);
  443. $datetime->setUTCHour($this->pasoonDate->getHours());
  444. $datetime->setUTCMinute($this->pasoonDate->getMinute());
  445. $datetime->setUTCSecond($this->pasoonDate->getSecond());
  446. $datetime->setUTCDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), false);
  447. $datetime->setUTCDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), true);
  448. $datetime->setUTCMonthName($this->monthName($utcDate[1]), false);
  449. $datetime->setUTCMonthName($this->monthName($utcDate[1], true), true);
  450. $this->pasoonDate->setTimestamp($localTimestamp);
  451. $localDate = $this->toIslamic();
  452. $datetime->setTimestamp($localTimestamp);
  453. $datetime->setJulianDay($this->pasoonDate->getJulianDayNumber());
  454. $datetime->setYear($localDate[0]);
  455. $datetime->setMonth($localDate[1]);
  456. $datetime->setDay($localDate[2]);
  457. $datetime->setHour($this->pasoonDate->getHours());
  458. $datetime->setMinute($this->pasoonDate->getMinute());
  459. $datetime->setSecond($this->pasoonDate->getSecond());
  460. $datetime->setDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), false);
  461. $datetime->setDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), true);
  462. $datetime->setMonthName($this->monthName($localDate[1]), false);
  463. $datetime->setMonthName($this->monthName($localDate[1], true), true);
  464. $this->pasoonDate->setTimestamp($utcTimestamp);
  465. return $datetime;
  466. }
  467. public function gregorian($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  468. {
  469. return $this->pasoonDate->gregorian($year, $month, $day, $hours, $minute, $second);
  470. }
  471. public function jalali($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  472. {
  473. return $this->pasoonDate->jalali($year, $month, $day, $hours, $minute, $second);
  474. }
  475. public function shia($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  476. {
  477. return $this->pasoonDate->shia($year, $month, $day, $hours, $minute, $second);
  478. }
  479. }
  480. class ShiaDate extends AbstractPasoonDate
  481. {
  482. private $ISLAMIC_EPOCH = 1948439.5;
  483. private $pasoonDate;
  484. /**
  485. * @param PasoonDate $pasoonDate
  486. * @param $year
  487. * @param $month
  488. * @param $day
  489. * @param $hours
  490. * @param $minute
  491. * @param $second
  492. */
  493. public function __construct($pasoonDate, $year, $month, $day, $hours, $minute, $second)
  494. {
  495. $this->pasoonDate = $pasoonDate;
  496. if ($year and $month and $day) {
  497. $pasoonDate->setHours(isset($hours) ? $hours : date('H'));
  498. $pasoonDate->setMinute(isset($minute) ? $minute : date('i'));
  499. $pasoonDate->setSecond(isset($second) ? $second : date('s'));
  500. $pasoonDate->setJulianDayNumber($this->toJulianDayNumber($year, $month, $day));
  501. $pasoonDate->setTimestamp($pasoonDate->getTimestamp() - $pasoonDate->getOffset());
  502. }
  503. }
  504. private function toJulianDayNumber($year, $month, $day)
  505. {
  506. $days = 0;
  507. for ($i = 0; $i < ($month - 1); $i++) {
  508. $days += $this->daysOfMonth($this, $i + 1);
  509. }
  510. return ($day +
  511. $days +
  512. ($year - 1) * 354 +
  513. floor((3 + (11 * $year)) / 30) +
  514. $this->ISLAMIC_EPOCH) - 1;
  515. }
  516. private function toShia()
  517. {
  518. $jd = $this->pasoonDate->getJulianDayNumber();
  519. $jd = floor($jd) + 0.5;
  520. $year = floor(((30 * ($jd - $this->ISLAMIC_EPOCH)) + 10646) / 10631);
  521. $month = min(12, ceil(($jd - (29 + $this->toJulianDayNumber($year, 1, 1))) / 29.5) + 1);
  522. $dayOfYear = $jd - $this->toJulianDayNumber($year, 1, 1);
  523. $days = 0;
  524. for ($i = 0; $i < 12; $i++) {
  525. $days += $this->daysOfMonth($year, $i + 1);
  526. if ($dayOfYear < $days) {
  527. $month = $i + 1;
  528. break;
  529. }
  530. }
  531. $day = ($jd - (($days - $this->daysOfMonth($year, $month)) + ($year - 1) * 354 + floor((3 + (11 * $year)) / 30) + $this->ISLAMIC_EPOCH) + 1);
  532. return [$year, $month, $day];
  533. }
  534. private function isLeap($year)
  535. {
  536. return ((($year * 11) + 14) % 30) < 11;
  537. }
  538. public function daysOfMonth($year, $month)
  539. {
  540. $islamicCountDays = array(30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29); //30
  541. $countDays = array(
  542. 1435 => array(29, 30, 29, 30, 29, 30, 29, 30, 30, 30, 29, 30),
  543. 1436 => array(29, 30, 29, 29, 30, 29, 30, 29, 30, 29, 30, 30),
  544. 1437 => array(29, 30, 30, 29, 30, 29, 29, 30, 29, 29, 30, 30),
  545. 1438 => array(29, 30, 30, 30, 29, 30, 29, 29, 30, 29, 29, 30),
  546. 1439 => array(29, 30, 30, 30, 30, 29, 30, 29, 29, 30, 29, 29),
  547. 1440 => array(30, 29, 30, 30, 30, 29, 29, 30, 29, 30, 29, 29),
  548. );
  549. if ($month < 1 || $month > 12) {
  550. throw new \Exception("$month Out Of Range Exception");
  551. }
  552. if (empty($countDays[$year])) {
  553. if ($year && $this->isLeap($year) && $month == 12) {
  554. return 30;
  555. }
  556. return $islamicCountDays[$month - 1];
  557. }
  558. return $countDays[$year][$month - 1];
  559. }
  560. public function startDayOfMonth($year, $month)
  561. {
  562. return $this->dayOfWeek($this->toJulianDayNumber($year, $month, 1));
  563. }
  564. public function lastDayOfMonth($year, $month)
  565. {
  566. return $this->dayOfWeek($this->toJulianDayNumber($year, $month, $this->daysOfMonth($year, $month)));
  567. }
  568. public function monthName($month)
  569. {
  570. $names = ["محرم", "صفر", "ربیع الاول", "ربیع الثانی", "جمادی الاول", "جمادی الثانی", "رجب", "شعبان", "رمضان", "شوال", "ذی‌القعده", "ذی‌الحجه"];
  571. if (empty($month)) {
  572. return $names;
  573. }
  574. if ($month < 1 || $month > 12) {
  575. throw new Exception("$month Out Of Range Exception");
  576. }
  577. return $names[$month - 1];
  578. }
  579. public function nameOfDay($dayOfWeek, $short = false)
  580. {
  581. $names = ["الأحد", "الاثنین", "الثلاثاء", "الأربعاء", "الخمیس", "الجمعة", "السبت"];
  582. $shortNames = ["ح", "ن", "ث", "ر", "خ", "ج", "س"];
  583. if ($dayOfWeek < 0 || $dayOfWeek > 6) {
  584. throw new Exception("$dayOfWeek Out Of Range Exception");
  585. }
  586. if ($short) {
  587. return $shortNames[$dayOfWeek];
  588. }
  589. return $names[$dayOfWeek];
  590. }
  591. public function get()
  592. {
  593. $date = $this->toShia();
  594. $datetime = new PasoonDateTime();
  595. $utcTimestamp = $this->pasoonDate->getTimestamp();
  596. $utcDate = $this->toShia();
  597. $localTimestamp = $utcTimestamp + $this->pasoonDate->getOffset();
  598. $localDate;
  599. $datetime->setTimestamp($utcTimestamp);
  600. $datetime->setJulianDay($this->pasoonDate->getJulianDayNumber());
  601. $datetime->setUTCYear($utcDate[0]);
  602. $datetime->setUTCMonth($utcDate[1]);
  603. $datetime->setUTCDay($utcDate[2]);
  604. $datetime->setUTCHour($this->pasoonDate->getHours());
  605. $datetime->setUTCMinute($this->pasoonDate->getMinute());
  606. $datetime->setUTCSecond($this->pasoonDate->getSecond());
  607. $datetime->setUTCDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), false);
  608. $datetime->setUTCDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), true);
  609. $datetime->setUTCMonthName($this->monthName($utcDate[1]), false);
  610. $datetime->setUTCMonthName($this->monthName($utcDate[1], true), true);
  611. $this->pasoonDate->setTimestamp($localTimestamp);
  612. $localDate = $this->toShia();
  613. $datetime->setTimestamp($localTimestamp);
  614. $datetime->setJulianDay($this->pasoonDate->getJulianDayNumber());
  615. $datetime->setYear($localDate[0]);
  616. $datetime->setMonth($localDate[1]);
  617. $datetime->setDay($localDate[2]);
  618. $datetime->setHour($this->pasoonDate->getHours());
  619. $datetime->setMinute($this->pasoonDate->getMinute());
  620. $datetime->setSecond($this->pasoonDate->getSecond());
  621. $datetime->setDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), false);
  622. $datetime->setDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), true);
  623. $datetime->setMonthName($this->monthName($localDate[1]), false);
  624. $datetime->setMonthName($this->monthName($localDate[1], true), true);
  625. $this->pasoonDate->setTimestamp($utcTimestamp);
  626. return $datetime;
  627. }
  628. public function gregorian($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  629. {
  630. return $this->pasoonDate->gregorian($year, $month, $day, $hours, $minute, $second);
  631. }
  632. public function jalali($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  633. {
  634. return $this->pasoonDate->jalali($year, $month, $day, $hours, $minute, $second);
  635. }
  636. public function islamic($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  637. {
  638. return $this->pasoonDate->islamic($year, $month, $day, $hours, $minute, $second);
  639. }
  640. }
  641. class JalaliDate extends AbstractPasoonDate
  642. {
  643. private $PERSIAN_EPOCH = 1948320.5;
  644. private $pasoonDate;
  645. /**
  646. * @param PasoonDate $pasoonDate
  647. * @param $year
  648. * @param $month
  649. * @param $day
  650. * @param $hours
  651. * @param $minute
  652. * @param $second
  653. */
  654. public function __construct($pasoonDate, $year, $month, $day, $hours, $minute, $second)
  655. {
  656. $this->pasoonDate = $pasoonDate;
  657. if ($year and $month and $day) {
  658. $pasoonDate->setHours(isset($hours) ? $hours : date('H'));
  659. $pasoonDate->setMinute(isset($minute) ? $minute : date('i'));
  660. $pasoonDate->setSecond(isset($second) ? $second : date('s'));
  661. $pasoonDate->setJulianDayNumber($this->toJulianDayNumber($year, $month, $day));
  662. $pasoonDate->setTimestamp($pasoonDate->getTimestamp() - $pasoonDate->getOffset());
  663. }
  664. }
  665. private function toJulianDayNumber($year, $month, $day)
  666. {
  667. $epbase = $year - (($year >= 0) ? 474 : 473);
  668. $epyear = 474 + $this->mod($epbase, 2820);
  669. return $day +
  670. (($month <= 7) ? (($month - 1) * 31) : ((($month - 1) * 30) + 6)) +
  671. floor((($epyear * 682) - 110) / 2816) +
  672. ($epyear - 1) * 365 +
  673. floor($epbase / 2820) * 1029983 +
  674. ($this->PERSIAN_EPOCH - 1);
  675. }
  676. private function toJalali()
  677. {
  678. $jd = $this->pasoonDate->getJulianDayNumber();
  679. $year = $month = $day = $depoch = $cycle = $cyear = $ycycle = $aux1 = $aux2 = $yday = 0;
  680. $jd = floor($jd) + 0.5;
  681. $depoch = $jd - $this->toJulianDayNumber(475, 1, 1);
  682. $cycle = floor($depoch / 1029983);
  683. $cyear = $this->mod($depoch, 1029983);
  684. if ($cyear == 1029982) {
  685. $ycycle = 2820;
  686. } else {
  687. $aux1 = floor($cyear / 366);
  688. $aux2 = $this->mod($cyear, 366);
  689. $ycycle = floor(((2134 * $aux1) + (2816 * $aux2) + 2815) / 1028522) +
  690. $aux1 + 1;
  691. }
  692. $year = $ycycle + (2820 * $cycle) + 474;
  693. if ($year <= 0) {
  694. $year--;
  695. }
  696. $yday = ($jd - $this->toJulianDayNumber($year, 1, 1)) + 1;
  697. $month = ($yday <= 186) ? ceil($yday / 31) : ceil(($yday - 6) / 30);
  698. $day = ($jd - $this->toJulianDayNumber($year, $month, 1)) + 1;
  699. return [$year, $month, $day];
  700. }
  701. private function isLeap($year)
  702. {
  703. return (((((($year - (($year > 0) ? 474 : 473)) % 2820) + 474) + 38) * 682) % 2816) < 682;
  704. }
  705. public function daysOfMonth($year, $month)
  706. {
  707. $countDays = array(31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29);
  708. if ($month < 1 || $month > 12) {
  709. throw new Exception("$month Out Of Range Exception");
  710. }
  711. if ($year && $this->isLeap($year) && $month == 12) {
  712. return 30;
  713. }
  714. return $countDays[$month - 1];
  715. }
  716. public function startDayOfMonth($year, $month)
  717. {
  718. return $this->dayOfWeek($this->toJulianDayNumber($year, $month, 1));
  719. }
  720. public function lastDayOfMonth($year, $month)
  721. {
  722. return $this->dayOfWeek($this->toJulianDayNumber($year, $month, $this->daysOfMonth($year, $month)));
  723. }
  724. public function monthName($month)
  725. {
  726. $names = ['فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند'];
  727. if (empty($month)) {
  728. return $names;
  729. }
  730. if ($month < 1 || $month > 12) {
  731. throw new Exception("$month Out Of Range Exception");
  732. }
  733. return $names[$month - 1];
  734. }
  735. public function nameOfDay($dayOfWeek, $short = false)
  736. {
  737. $names = ["یک شنبه", "دوشنبه", "سه شنبه", "چهارشنبه", "پنج شنبه", "جمعه", "شنبه"];
  738. if ($dayOfWeek < 0 || $dayOfWeek > 6) {
  739. throw new Exception("$dayOfWeek Out Of Range Exception");
  740. }
  741. if ($short) {
  742. return substr($names[$dayOfWeek], 0, 2);
  743. }
  744. return $names[$dayOfWeek];
  745. }
  746. public function get()
  747. {
  748. $date = $this->toJalali();
  749. $datetime = new PasoonDateTime();
  750. $utcTimestamp = $this->pasoonDate->getTimestamp();
  751. $utcDate = $this->toJalali();
  752. $localTimestamp = $utcTimestamp + $this->pasoonDate->getOffset();
  753. $localDate;
  754. $datetime->setTimestamp($utcTimestamp);
  755. $datetime->setJulianDay($this->pasoonDate->getJulianDayNumber());
  756. $datetime->setUTCYear($utcDate[0]);
  757. $datetime->setUTCMonth($utcDate[1]);
  758. $datetime->setUTCDay($utcDate[2]);
  759. $datetime->setUTCHour($this->pasoonDate->getHours());
  760. $datetime->setUTCMinute($this->pasoonDate->getMinute());
  761. $datetime->setUTCSecond($this->pasoonDate->getSecond());
  762. $datetime->setUTCDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), false);
  763. $datetime->setUTCDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), true);
  764. $datetime->setUTCMonthName($this->monthName($utcDate[1]), false);
  765. $datetime->setUTCMonthName($this->monthName($utcDate[1], true), true);
  766. $this->pasoonDate->setTimestamp($localTimestamp);
  767. $localDate = $this->toJalali();
  768. $datetime->setTimestamp($localTimestamp);
  769. $datetime->setJulianDay($this->pasoonDate->getJulianDayNumber());
  770. $datetime->setYear($localDate[0]);
  771. $datetime->setMonth($localDate[1]);
  772. $datetime->setDay($localDate[2]);
  773. $datetime->setHour($this->pasoonDate->getHours());
  774. $datetime->setMinute($this->pasoonDate->getMinute());
  775. $datetime->setSecond($this->pasoonDate->getSecond());
  776. $datetime->setDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), false);
  777. $datetime->setDayName($this->nameOfDay($this->dayOfWeek($this->pasoonDate->getJulianDayNumber())), true);
  778. $datetime->setMonthName($this->monthName($localDate[1]), false);
  779. $datetime->setMonthName($this->monthName($localDate[1], true), true);
  780. $this->pasoonDate->setTimestamp($utcTimestamp);
  781. return $datetime;
  782. }
  783. public function gregorian($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  784. {
  785. return $this->pasoonDate->gregorian($year, $month, $day, $hours, $minute, $second);
  786. }
  787. public function islamic($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  788. {
  789. return $this->pasoonDate->islamic($year, $month, $day, $hours, $minute, $second);
  790. }
  791. public function shia($year = false, $month = false, $day = false, $hours = false, $minute = false, $second = false)
  792. {
  793. return $this->pasoonDate->shia($year, $month, $day, $hours, $minute, $second);
  794. }
  795. }
  796. class PasoonDateTime
  797. {
  798. private $timestamp;
  799. private $offset;
  800. private $julianDay;
  801. private $year;
  802. private $month;
  803. private $day;
  804. private $hour;
  805. private $minute;
  806. private $second;
  807. private $utcYear;
  808. private $utcMonth;
  809. private $utcDay;
  810. private $utcHour;
  811. private $utcMinute;
  812. private $utcSecond;
  813. private $dayName;
  814. private $shortDayName;
  815. private $monthName;
  816. private $shortMonthName;
  817. private $utcDayName;
  818. private $utcShortDayName;
  819. private $utcMonthName;
  820. private $utcShortMonthName;
  821. public function __construct()
  822. {
  823. }
  824. public function setTimestamp($value)
  825. {
  826. $this->timestamp = (integer)$value;
  827. }
  828. public function getTimestamp()
  829. {
  830. return $this->timestamp;
  831. }
  832. public function setOffset($value)
  833. {
  834. $this->offset = (integer)$value;
  835. }
  836. public function getOffset()
  837. {
  838. return $this->offset;
  839. }
  840. public function setJulianDay($value)
  841. {
  842. $this->julianDay = $value;
  843. }
  844. public function getJulianDay()
  845. {
  846. return $this->julianDay;
  847. }
  848. public function setYear($value)
  849. {
  850. $this->year = (integer)$value;
  851. }
  852. public function getYear()
  853. {
  854. return $this->year;
  855. }
  856. public function setMonth($value)
  857. {
  858. $this->month = (integer)$value;
  859. }
  860. public function getMonth()
  861. {
  862. return $this->month;
  863. }
  864. public function setDay($value)
  865. {
  866. $this->day = (integer)$value;
  867. }
  868. public function getDay()
  869. {
  870. return $this->day;
  871. }
  872. public function setHour($value)
  873. {
  874. $this->hour = (integer)$value;
  875. }
  876. public function getHour()
  877. {
  878. return $this->hour;
  879. }
  880. public function setMinute($value)
  881. {
  882. $this->minute = (integer)$value;
  883. }
  884. public function getMinute()
  885. {
  886. return $this->minute;
  887. }
  888. public function setSecond($value)
  889. {
  890. $this->second = (integer)$value;
  891. }
  892. public function getSecond()
  893. {
  894. return $this->second;
  895. }
  896. public function setDayName($value, $short = false)
  897. {
  898. if($short) {
  899. $this->shortDayName = $value;
  900. }
  901. else {
  902. $this->dayName = $value;
  903. }
  904. }
  905. public function getDayName($short = false)
  906. {
  907. if ($short)
  908. return $this->shortDayName;
  909. return $this->dayName;
  910. }
  911. public function setMonthName($value, $short = false)
  912. {
  913. if($short) {
  914. $this->shortMonthName = $value;
  915. }
  916. else {
  917. $this->monthName = $value;
  918. }
  919. }
  920. public function getMonthName($short = false)
  921. {
  922. if ($short)
  923. return $this->shortMonthName;
  924. return $this->monthName;
  925. }
  926. public function setUTCYear($value)
  927. {
  928. $this->utcYear = (integer)$value;
  929. }
  930. public function getUTCYear()
  931. {
  932. return $this->utcYear;
  933. }
  934. public function setUTCMonth($value)
  935. {
  936. $this->utcMonth = (integer)$value;
  937. }
  938. public function getUTCMonth()
  939. {
  940. return $this->utcMonth;
  941. }
  942. public function setUTCDay($value)
  943. {
  944. $this->utcDay = (integer)$value;
  945. }
  946. public function getUTCDay()
  947. {
  948. return $this->utcDay;
  949. }
  950. public function setUTCHour($value)
  951. {
  952. $this->utcHour = (integer)$value;
  953. }
  954. public function getUTCHour()
  955. {
  956. return $this->utcHour;
  957. }
  958. public function setUTCMinute($value)
  959. {
  960. $this->utcMinute = (integer)$value;
  961. }
  962. public function getUTCMinute()
  963. {
  964. return $this->utcMinute;
  965. }
  966. public function setUTCSecond($value)
  967. {
  968. $this->utcSecond = (integer)$value;
  969. }
  970. public function getUTCSecond()
  971. {
  972. return $this->utcSecond;
  973. }
  974. public function setUTCDayName($value, $short = false)
  975. {
  976. if($short) {
  977. $this->utcShortDayName = $value;
  978. }
  979. else {
  980. $this->utcDayName = $value;
  981. }
  982. }
  983. public function getUTCDayName($short = false)
  984. {
  985. if ($short)
  986. return $this->utcShortDayName;
  987. return $this->utcDayName;
  988. }
  989. public function setUTCMonthName($value, $short = false)
  990. {
  991. if($short) {
  992. $this->utcShortMonthName = $value;
  993. }
  994. else {
  995. $this->utcMonthName = $value;
  996. }
  997. }
  998. public function getUTCMonthName($short = false)
  999. {
  1000. if ($short)
  1001. return $this->utcShortMonthName;
  1002. return $this->utcMonthName;
  1003. }
  1004. /**
  1005. * @param $pattern
  1006. * @return string
  1007. */
  1008. public function format($pattern)
  1009. {
  1010. $temp = $pattern;
  1011. $temp = str_replace('%Day%', $this->leadingZeros($this->day), $temp);
  1012. $temp = str_replace('%day%', $this->day, $temp);
  1013. $temp = str_replace(array('%day:name:short%', '%Day:name:short%'), $this->shortDayName, $temp);
  1014. $temp = str_replace(array('%day:name%', '%Day:name%'), $this->dayName, $temp);
  1015. $temp = str_replace(array('%month:name%', '%Month:name%'), $this->monthName, $temp);
  1016. $temp = str_replace(array('%month:name:short%', '%Month:name:short%'), $this->shortMonthName, $temp);
  1017. $temp = str_replace('%Month%', $this->leadingZeros($this->month), $temp);
  1018. $temp = str_replace('%month%', $this->month, $temp);
  1019. $temp = str_replace('%Year%', $this->year, $temp);
  1020. $temp = str_replace('%year%', substr($this->year, 2), $temp);
  1021. $temp = str_replace('meridiem', ($this->hour > 12 ? 'pm' : 'am'), $temp);
  1022. $temp = str_replace('Meridiem', ($this->hour > 12 ? 'PM' : 'pm'), $temp);
  1023. $temp = str_replace('%hour:12%', ($this->hour > 12 ? $this->hour - 12 : $this->hour), $temp);
  1024. $temp = str_replace('%hour%', $this->hour, $temp);
  1025. $temp = str_replace('%Hour:12%', $this->leadingZeros(($this->hour > 12 ? $this->hour - 12 : $this->hour)), $temp);
  1026. $temp = str_replace('%Hour%', $this->leadingZeros($this->hour), $temp);
  1027. $temp = str_replace('%minute%', $this->minute, $temp);
  1028. $temp = str_replace('%Minute%', $this->leadingZeros($this->minute), $temp);
  1029. $temp = str_replace('%second%', $this->second, $temp);
  1030. $temp = str_replace('%Second%', $this->leadingZeros($this->second), $temp);
  1031. $temp = str_replace('%UDay%', $this->leadingZeros($this->utcDay), $temp);
  1032. $temp = str_replace('%uday%', $this->utcDay, $temp);
  1033. $temp = str_replace(array('%uday:name:short%', '%UDay:name:short%'), $this->utcShortDayName, $temp);
  1034. $temp = str_replace(array('%uday:name%', '%UDay:name%'), $this->utcDayName, $temp);
  1035. $temp = str_replace(array('%umonth:name%', '%UMonth:name%'), $this->utcMonthName, $temp);
  1036. $temp = str_replace(array('%umonth:name:short%', '%UMonth:name:short%'), $this->utcShortMonthName, $temp);
  1037. $temp = str_replace('%UMonth%', $this->leadingZeros($this->utcMonth), $temp);
  1038. $temp = str_replace('%umonth%', $this->utcMonth, $temp);
  1039. $temp = str_replace('%UYear%', $this->utcYear, $temp);
  1040. $temp = str_replace('%uyear%', substr($this->utcYear, 2), $temp);
  1041. $temp = str_replace('umeridiem', ($this->utcHour > 12 ? 'pm' : 'am'), $temp);
  1042. $temp = str_replace('UMeridiem', ($this->utcHour > 12 ? 'PM' : 'pm'), $temp);
  1043. $temp = str_replace('%uhour:12%', ($this->utcHour > 12 ? $this->utcHour - 12 : $this->utcHour), $temp);
  1044. $temp = str_replace('%uhour%', $this->utcHour, $temp);
  1045. $temp = str_replace('%UHour:12%', $this->leadingZeros(($this->utcHour > 12 ? $this->utcHour - 12 : $this->utcHour)), $temp);
  1046. $temp = str_replace('%UHour%', $this->leadingZeros($this->utcHour), $temp);
  1047. $temp = str_replace('%uminute%', $this->utcMinute, $temp);
  1048. $temp = str_replace('%UMinute%', $this->leadingZeros($this->utcMinute), $temp);
  1049. $temp = str_replace('%usecond%', $this->utcSecond, $temp);
  1050. $temp = str_replace('%USecond%', $this->leadingZeros($this->utcSecond), $temp);
  1051. return $temp;
  1052. }
  1053. /**
  1054. * @return array
  1055. */
  1056. public function getArray()
  1057. {
  1058. return array(
  1059. 'year' => $this->getYear(),
  1060. 'month' => $this->getMonth(),
  1061. 'day' => $this->getDay(),
  1062. 'hour' => $this->getHour(),
  1063. 'minute' => $this->getMinute(),
  1064. 'second' => $this->getSecond(),
  1065. 'day name' => $this->getDayName(),
  1066. 'month name' => $this->getMonthName(),
  1067. 'utc year' => $this->getUTCYear(),
  1068. 'utc month' => $this->getUTCMonth(),
  1069. 'utc day' => $this->getUTCDay(),
  1070. 'utc hour' => $this->getUTCHour(),
  1071. 'utc minute' => $this->getUTCMinute(),
  1072. 'utc second' => $this->getUTCSecond(),
  1073. 'utc day name' => $this->getUTCDayName(),
  1074. 'utc month name' => $this->getUTCMonthName(),
  1075. 'timestamp' => $this->getTimestamp(),
  1076. 'offset' => $this->getOffset(),
  1077. 'julian day' => $this->getJulianDay()
  1078. );
  1079. }
  1080. /**
  1081. * @return string
  1082. */
  1083. public function __toString()
  1084. {
  1085. return "{$this->getYear()}-{$this->getMonth()}-{$this->getDay()} {$this->getHour()}:{$this->getMinute()}:{$this->getSecond()}";
  1086. }
  1087. private function leadingZeros($number)
  1088. {
  1089. return sprintf('%02d', $number);
  1090. }
  1091. }