/src/Jalalian.php

https://github.com/morilog/jalali · PHP · 612 lines · 438 code · 113 blank · 61 comment · 25 complexity · c840c26c73d7c7f4e2ccffad71b09eb1 MD5 · raw file

  1. <?php
  2. namespace Morilog\Jalali;
  3. use Assert\Assertion;
  4. use Carbon\Carbon;
  5. class Jalalian
  6. {
  7. /**
  8. * @var int
  9. */
  10. private $year;
  11. /**
  12. * @var int
  13. */
  14. private $month;
  15. /**
  16. * @var int
  17. */
  18. private $day;
  19. /**
  20. * @var int
  21. */
  22. private $hour;
  23. /**
  24. * @var int
  25. */
  26. private $minute;
  27. /**
  28. * @var int
  29. */
  30. private $second;
  31. /**
  32. * @var \DateTimeZone
  33. */
  34. private $timezone;
  35. public function __construct(
  36. int $year,
  37. int $month,
  38. int $day,
  39. int $hour = 0,
  40. int $minute = 0,
  41. int $second = 0,
  42. \DateTimeZone $timezone = null
  43. ) {
  44. Assertion::between($year, 1000, 3000);
  45. Assertion::between($month, 1, 12);
  46. Assertion::between($day, 1, 31);
  47. if ($month > 6) {
  48. Assertion::between($day, 1, 30);
  49. }
  50. if (!CalendarUtils::isLeapJalaliYear($year) && $month === 12) {
  51. Assertion::between($day, 1, 29);
  52. }
  53. Assertion::between($hour, 0, 24);
  54. Assertion::between($minute, 0, 59);
  55. Assertion::between($second, 0, 59);
  56. $this->year = $year;
  57. $this->month = $month;
  58. $this->day = $day;
  59. $this->hour = $hour;
  60. $this->minute = $minute;
  61. $this->second = $second;
  62. $this->timezone = $timezone;
  63. }
  64. public static function now(\DateTimeZone $timeZone = null): Jalalian
  65. {
  66. return static::fromCarbon(Carbon::now($timeZone));
  67. }
  68. /**
  69. * @param Carbon $carbon
  70. * @return Jalalian
  71. */
  72. public static function fromCarbon(Carbon $carbon): Jalalian
  73. {
  74. $jDate = CalendarUtils::toJalali($carbon->year, $carbon->month, $carbon->day);
  75. return new static($jDate[0], $jDate[1], $jDate[2], $carbon->hour, $carbon->minute, $carbon->second,
  76. $carbon->getTimezone());
  77. }
  78. public static function fromFormat(string $format, string $timestamp, \DateTimeZone $timeZone = null): Jalalian
  79. {
  80. return static::fromCarbon(CalendarUtils::createCarbonFromFormat($format, $timestamp, $timeZone));
  81. }
  82. public static function forge($timestamp, \DateTimeZone $timeZone = null): Jalalian
  83. {
  84. return static::fromDateTime($timestamp, $timeZone);
  85. }
  86. /**
  87. * @param \DateTimeInterface| string $dateTime
  88. * @param \DateTimeZone|null $timeZone
  89. * @return Jalalian
  90. */
  91. public static function fromDateTime($dateTime, \DateTimeZone $timeZone = null): Jalalian
  92. {
  93. if (is_numeric($dateTime)) {
  94. return static::fromCarbon(Carbon::createFromTimestamp($dateTime, $timeZone));
  95. }
  96. return static::fromCarbon(new Carbon($dateTime, $timeZone));
  97. }
  98. public function getMonthDays()
  99. {
  100. if ($this->getMonth() <= 6) {
  101. return 31;
  102. }
  103. if ($this->getMonth() < 12 || $this->isLeapYear()) {
  104. return 30;
  105. }
  106. return 29;
  107. }
  108. /**
  109. * @return int
  110. */
  111. public function getMonth(): int
  112. {
  113. return $this->month;
  114. }
  115. public function isLeapYear(): bool
  116. {
  117. return CalendarUtils::isLeapJalaliYear($this->getYear());
  118. }
  119. /**
  120. * @return int
  121. */
  122. public function getYear()
  123. {
  124. return $this->year;
  125. }
  126. public function subMonths(int $months = 1): Jalalian
  127. {
  128. Assertion::greaterOrEqualThan($months, 1);
  129. $diff = ($this->getMonth() - $months);
  130. if ($diff >= 1) {
  131. $day = $this->getDay();
  132. $targetMonthDays = $this->getDaysOf($diff);
  133. $targetDay = $day <= $targetMonthDays ? $day : $targetMonthDays;
  134. return new static(
  135. $this->getYear(),
  136. $diff,
  137. $targetDay,
  138. $this->getHour(),
  139. $this->getMinute(),
  140. $this->getSecond(),
  141. $this->getTimezone()
  142. );
  143. }
  144. $years = abs((int)($diff / 12));
  145. $date = $years > 0 ? $this->subYears($years) : clone $this;
  146. $diff = 12 - abs($diff % 12) - $date->getMonth();
  147. return $diff > 0 ? $date->subYears(1)->addMonths($diff) : $date->subYears(1);
  148. }
  149. /**
  150. * @return int
  151. */
  152. public function getDay(): int
  153. {
  154. return $this->day;
  155. }
  156. public function getDaysOf(int $monthNumber = 1): int
  157. {
  158. Assertion::between($monthNumber, 1, 12);
  159. $months = [
  160. 1 => 31,
  161. 2 => 31,
  162. 3 => 31,
  163. 4 => 31,
  164. 5 => 31,
  165. 6 => 31,
  166. 7 => 30,
  167. 8 => 30,
  168. 9 => 30,
  169. 10 => 30,
  170. 11 => 30,
  171. 12 => $this->isLeapYear() ? 30 : 29,
  172. ];
  173. return $months[$monthNumber];
  174. }
  175. /**
  176. * @return int
  177. */
  178. public function getHour(): int
  179. {
  180. return $this->hour;
  181. }
  182. /**
  183. * @return int
  184. */
  185. public function getMinute(): int
  186. {
  187. return $this->minute;
  188. }
  189. /**
  190. * @return int
  191. */
  192. public function getSecond(): int
  193. {
  194. return $this->second;
  195. }
  196. /**
  197. * @return \DateTimeZone|null
  198. */
  199. public function getTimezone()
  200. {
  201. return $this->timezone;
  202. }
  203. public function subYears(int $years = 1): Jalalian
  204. {
  205. Assertion::greaterOrEqualThan($years, 1);
  206. return new static(
  207. $this->getYear() - $years,
  208. $this->getMonth(),
  209. $this->getDay(),
  210. $this->getHour(),
  211. $this->getMinute(),
  212. $this->getSecond(),
  213. $this->getTimezone()
  214. );
  215. }
  216. public function addMonths(int $months = 1): Jalalian
  217. {
  218. Assertion::greaterOrEqualThan($months, 1);
  219. $years = (int)($months / 12);
  220. $months = (int)($months % 12);
  221. $date = $years > 0 ? $this->addYears($years) : clone $this;
  222. while ($months > 0) {
  223. $nextMonth = ($date->getMonth() + 1) % 12;
  224. $nextMonthDays = $date->getDaysOf($nextMonth === 0 ? 12 : $nextMonth);
  225. $nextMonthDay = $date->getDay() <= $nextMonthDays ? $date->getDay() : $nextMonthDays;
  226. $days = ($date->getMonthDays() - $date->getDay()) + $nextMonthDay;
  227. $date = $date->addDays($days);
  228. $months--;
  229. }
  230. return $date;
  231. }
  232. public function addYears(int $years = 1): Jalalian
  233. {
  234. Assertion::greaterOrEqualThan($years, 1);
  235. $year = $this->getYear() + $years;
  236. if (false === CalendarUtils::isLeapJalaliYear($year) && $this->getMonth() === 12 && $this->getDay() === $this->getDaysOf(12)) {
  237. $day = 29;
  238. } else {
  239. $day = $this->getDay();
  240. }
  241. return new static(
  242. $year,
  243. $this->getMonth(),
  244. $day,
  245. $this->getHour(),
  246. $this->getMinute(),
  247. $this->getSecond(),
  248. $this->getTimezone()
  249. );
  250. }
  251. public function subDays(int $days = 1): Jalalian
  252. {
  253. return static::fromCarbon($this->toCarbon()->subDays($days));
  254. }
  255. /**
  256. * @return Carbon
  257. */
  258. public function toCarbon(): Carbon
  259. {
  260. $gDate = CalendarUtils::toGregorian($this->getYear(), $this->getMonth(), $this->getDay());
  261. $carbon = Carbon::createFromDate($gDate[0], $gDate[1], $gDate[2], $this->getTimezone());
  262. $carbon->setTime($this->getHour(), $this->getMinute(), $this->getSecond());
  263. return $carbon;
  264. }
  265. public function addHours(int $hours = 1): Jalalian
  266. {
  267. return static::fromCarbon($this->toCarbon()->addHours($hours));
  268. }
  269. public function subHours(int $hours = 1): Jalalian
  270. {
  271. return static::fromCarbon($this->toCarbon()->subHours($hours));
  272. }
  273. public function addMinutes(int $minutes = 1): Jalalian
  274. {
  275. return static::fromCarbon($this->toCarbon()->addMinutes($minutes));
  276. }
  277. public function subMinutes(int $minutes = 1): Jalalian
  278. {
  279. return static::fromCarbon($this->toCarbon()->subMinutes($minutes));
  280. }
  281. public function addSeconds(int $secs = 1): Jalalian
  282. {
  283. return static::fromCarbon($this->toCarbon()->addSeconds($secs));
  284. }
  285. public function subSeconds(int $secs = 1): Jalalian
  286. {
  287. return static::fromCarbon($this->toCarbon()->subSeconds($secs));
  288. }
  289. public function equalsTo(Jalalian $other): bool
  290. {
  291. return $this->equalsToCarbon($other->toCarbon());
  292. }
  293. public function equalsToCarbon(Carbon $carbon): bool
  294. {
  295. return $this->toCarbon()->equalTo($carbon);
  296. }
  297. public function greaterThan(Jalalian $other): bool
  298. {
  299. return $this->greaterThanCarbon($other->toCarbon());
  300. }
  301. public function greaterThanCarbon(Carbon $carbon): bool
  302. {
  303. return $this->toCarbon()->greaterThan($carbon);
  304. }
  305. public function lessThan(Jalalian $other): bool
  306. {
  307. return $this->lessThanCarbon($other->toCarbon());
  308. }
  309. public function lessThanCarbon(Carbon $carbon): bool
  310. {
  311. return $this->toCarbon()->lessThan($carbon);
  312. }
  313. public function greaterThanOrEqualsTo(Jalalian $other): bool
  314. {
  315. return $this->greaterThanOrEqualsToCarbon($other->toCarbon());
  316. }
  317. public function greaterThanOrEqualsToCarbon(Carbon $carbon): bool
  318. {
  319. return $this->toCarbon()->greaterThanOrEqualTo($carbon);
  320. }
  321. public function lessThanOrEqualsTo(Jalalian $other): bool
  322. {
  323. return $this->lessThanOrEqualsToCarbon($other->toCarbon());
  324. }
  325. public function lessThanOrEqualsToCarbon(Carbon $carbon): bool
  326. {
  327. return $this->toCarbon()->lessThanOrEqualTo($carbon);
  328. }
  329. public function isStartOfWeek(): bool
  330. {
  331. return $this->isSaturday();
  332. }
  333. public function isSaturday(): bool
  334. {
  335. return $this->isDayOfWeek(Carbon::SATURDAY);
  336. }
  337. public function isDayOfWeek(int $day): bool
  338. {
  339. Assertion::between($day, 0, 6);
  340. return $this->toCarbon()->isDayOfWeek($day);
  341. }
  342. public function isEndOfWeek(): bool
  343. {
  344. return $this->isFriday();
  345. }
  346. public function isFriday(): bool
  347. {
  348. return $this->isDayOfWeek(Carbon::FRIDAY);
  349. }
  350. public function isToday(): bool
  351. {
  352. return $this->toCarbon()->isToday();
  353. }
  354. public function isTomorrow(): bool
  355. {
  356. return $this->toCarbon()->isTomorrow();
  357. }
  358. public function isYesterday(): bool
  359. {
  360. return $this->toCarbon()->isYesterday();
  361. }
  362. public function isFuture(): bool
  363. {
  364. return $this->toCarbon()->isFuture();
  365. }
  366. public function isPast(): bool
  367. {
  368. return $this->toCarbon()->isPast();
  369. }
  370. public function toArray(): array
  371. {
  372. return [
  373. 'year' => $this->year,
  374. 'month' => $this->month,
  375. 'day' => $this->day,
  376. 'dayOfWeek' => $this->getDayOfWeek(),
  377. 'dayOfYear' => $this->getDayOfYear(),
  378. 'hour' => $this->hour,
  379. 'minute' => $this->minute,
  380. 'second' => $this->second,
  381. 'micro' => $this->toCarbon()->micro,
  382. 'timestamp' => $this->toCarbon()->timestamp,
  383. 'formatted' => $this->toString(),
  384. 'timezone' => $this->timezone,
  385. ];
  386. }
  387. public function getDayOfWeek(): int
  388. {
  389. if ($this->isSaturday()) {
  390. return 0;
  391. }
  392. if ($this->isSunday()) {
  393. return 1;
  394. }
  395. if ($this->isMonday()) {
  396. return 2;
  397. }
  398. if ($this->isTuesday()) {
  399. return 3;
  400. }
  401. if ($this->isWednesday()) {
  402. return 4;
  403. }
  404. if ($this->isThursday()) {
  405. return 5;
  406. }
  407. return 6;
  408. }
  409. public function isSunday(): bool
  410. {
  411. return $this->isDayOfWeek(Carbon::SUNDAY);
  412. }
  413. public function isMonday(): bool
  414. {
  415. return $this->isDayOfWeek(Carbon::MONDAY);
  416. }
  417. public function isTuesday(): bool
  418. {
  419. return $this->isDayOfWeek(Carbon::TUESDAY);
  420. }
  421. public function isWednesday(): bool
  422. {
  423. return $this->isDayOfWeek(Carbon::WEDNESDAY);
  424. }
  425. public function isThursday(): bool
  426. {
  427. return $this->isDayOfWeek(Carbon::THURSDAY);
  428. }
  429. public function getDayOfYear(): int
  430. {
  431. $dayOfYear = 0;
  432. for ($m = 1; $m < $this->getMonth(); $m++) {
  433. if ($m <= 6) {
  434. $dayOfYear += 31;
  435. continue;
  436. }
  437. if ($m < 12) {
  438. $dayOfYear += 30;
  439. continue;
  440. }
  441. }
  442. return $dayOfYear + $this->getDay();
  443. }
  444. public function toString(): string
  445. {
  446. return $this->format('Y-m-d H:i:s');
  447. }
  448. public function format(string $format): string
  449. {
  450. return CalendarUtils::strftime($format, $this->toCarbon());
  451. }
  452. public function __toString(): string
  453. {
  454. return $this->toString();
  455. }
  456. public function ago(): string
  457. {
  458. $now = time();
  459. $time = $this->getTimestamp();
  460. // catch error
  461. if (!$time) {
  462. return false;
  463. }
  464. // build period and length arrays
  465. $periods = ['ثانیه', 'دقیقه', 'ساعت', 'روز', 'هفته', 'ماه', 'سال', 'قرن'];
  466. $lengths = [60, 60, 24, 7, 4.35, 12, 10];
  467. // get difference
  468. $difference = $now - $time;
  469. // set descriptor
  470. if ($difference < 0) {
  471. $difference = abs($difference); // absolute value
  472. $negative = true;
  473. }
  474. // do math
  475. for ($j = 0; $difference >= $lengths[$j] and $j < count($lengths) - 1; $j++) {
  476. $difference /= $lengths[$j];
  477. }
  478. // round difference
  479. $difference = intval(round($difference));
  480. // return
  481. return number_format($difference) . ' ' . $periods[$j] . ' ' . (isset($negative) ? '' : 'پیش');
  482. }
  483. public function getTimestamp(): int
  484. {
  485. return $this->toCarbon()->getTimestamp();
  486. }
  487. public function getNextWeek(): Jalalian
  488. {
  489. return $this->addDays(7);
  490. }
  491. public function addDays(int $days = 1): Jalalian
  492. {
  493. return static::fromCarbon($this->toCarbon()->addDays($days));
  494. }
  495. public function getNextMonth(): Jalalian
  496. {
  497. return $this->addMonths(1);
  498. }
  499. }