├── .github
├── FUNDING.yml
└── workflows
│ └── tests.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml
├── src
├── CalendarUtils.php
├── Converter.php
├── Jalalian.php
└── helpers.php
└── tests
├── CalendarUtilsTest.php
├── HelperTest.php
└── JalalianTest.php
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 |
2 | issuehunt: morilog
3 |
4 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: tests
2 |
3 | on:
4 | push:
5 | pull_request:
6 |
7 | jobs:
8 | tests:
9 | runs-on: ubuntu-22.04
10 |
11 | strategy:
12 | fail-fast: true
13 | matrix:
14 | php: [ 7.1, 7.2, 7.3, 7.4, 8.0, 8.1, 8.2, 8.3 ]
15 |
16 | name: PHP ${{ matrix.php }}
17 |
18 | steps:
19 | - name: Checkout code
20 | uses: actions/checkout@v4
21 |
22 | - name: Setup PHP
23 | uses: shivammathur/setup-php@v2
24 | with:
25 | php-version: ${{ matrix.php }}
26 | ini-values: error_reporting=E_ALL
27 | tools: composer:v2
28 | coverage: none
29 |
30 | - name: Install dependencies
31 | run: |
32 | composer update --prefer-dist --no-interaction --no-progress
33 |
34 | - name: Execute tests
35 | run: vendor/bin/phpunit
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.phar
3 | composer.lock
4 | .DS_Store
5 | .idea
6 | .phpunit.*
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 7.0
5 | - 7.1
6 | - 7.2
7 | - 7.3
8 | - 7.4
9 | before_script:
10 | - curl -s http://getcomposer.org/installer | php
11 | - php composer.phar install --dev
12 |
13 | script: ./vendor/bin/phpunit
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Morilog
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/morilog/jalali)
2 | morilog/jalali
3 | ======
4 | - Jalali calendar is a solar calendar that was used in Persia, variants of which today are still in use in Iran as well as Afghanistan. [Read more on Wikipedia](http://en.wikipedia.org/wiki/Jalali_calendar) or see [Calendar Converter](http://www.fourmilab.ch/documents/calendar/).
5 |
6 | - Calendar conversion is based on the [algorithm provided by Kazimierz M. Borkowski](http://www.astro.uni.torun.pl/~kb/Papers/EMP/PersianC-EMP.htm) and has a very good performance.
7 |
8 | - CalendarUtils class was ported from [jalaali/jalaali-js](https://github.com/jalaali/jalaali-js)
9 |
10 | ## Version 3 features
11 | - High human readable API
12 | - DateTime manipulating API
13 | - DateTime comparing API
14 | - Immutable
15 |
16 | ## Installation Version 3.*
17 | > If you are using version <= 2.*, please read [old docs](https://github.com/morilog/jalali/blob/v2.3.0/README.md)
18 | #### Requirements:
19 | - `php >= 7.0`
20 |
21 | Run the Composer update command
22 |
23 | $ composer require morilog/jalali:3.*
24 |
25 |
26 | ## Basic Usage
27 | In the current version, I introduced `Jalalian` class for manipulating Jalali date time
28 | ### Jalalian
29 | In version >= 1.1, you can use `jdate()` instead of `Jalalian::forge()`;
30 | #### `now([$timestamp = null])`
31 | ``` php
32 | // the default timestamp is Now
33 | $date = \Morilog\Jalali\Jalalian::now()
34 | // OR
35 | $date = jdate();
36 |
37 | // pass timestamps
38 | $date = Jalalian::forge(1333857600);
39 | // OR
40 | $date = jdate(1333857600);
41 |
42 | // pass human readable strings to make timestamps
43 | $date = Jalalian::forge('last sunday');
44 |
45 | // get the timestamp
46 | $date = Jalalian::forge('last sunday')->getTimestamp(); // 1333857600
47 |
48 | // format the timestamp
49 | $date = Jalalian::forge('last sunday')->format('%B %d، %Y'); // دی 02، 1391
50 | $date = Jalalian::forge('today')->format('%A, %d %B %y'); // جمعه، 23 اسفند 97
51 |
52 | // get a predefined format
53 | $date = Jalalian::forge('last sunday')->format('datetime'); // 1391-10-02 00:00:00
54 | $date = Jalalian::forge('last sunday')->format('date'); // 1391-10-02
55 | $date = Jalalian::forge('last sunday')->format('time'); // 00:00:00
56 |
57 | // get relative 'ago' format
58 | $date = Jalalian::forge('now - 10 minutes')->ago() // 10 دقیقه پیش
59 | ```
60 |
61 | #### Methods api
62 | ---
63 |
64 |
65 | ```php
66 | public static function now(?\DateTimeZone $timeZone = null): Jalalian
67 |
68 | $jDate = Jalalian::now();
69 | ```
70 |
71 | ---
72 | ```php
73 | public static function fromCarbon(Carbon $carbon): Jalalian
74 |
75 | $jDate = Jalalian::fromCarbon(Carbon::now());
76 | ```
77 |
78 | ---
79 | ```php
80 | public static function fromFormat(string $format, string $timestamp, ?\DateTimeZone$timeZone = null): Jalalian
81 |
82 | $jDate = Jalalian::fromFormat('Y-m-d H:i:s', '1397-01-18 12:00:40');
83 | ```
84 |
85 |
86 | ---
87 | ```php
88 | public static function forge($timestamp, ?\DateTimeZone $timeZone = null): Jalalian
89 |
90 | // Alias fo fromDatetime
91 | ```
92 |
93 | ---
94 | ```php
95 | public static function fromDateTime($dateTime, ?\DateTimeZone $timeZone = null): Jalalian
96 |
97 | $jDate = Jalalian::fromDateTime(Carbon::now())
98 | // OR
99 | $jDate = Jalalian::fromDateTime(new \DateTime());
100 | // OR
101 | $jDate = Jalalian::fromDateTime('yesterday');
102 |
103 | ```
104 |
105 |
106 | ---
107 | ```php
108 | public function getMonthDays(): int
109 |
110 | $date = (new Jalalian(1397, 1, 18))->getMonthDays()
111 | // output: 31
112 | ```
113 |
114 | ---
115 | ```php
116 | public function getMonth(): int
117 |
118 | $date = (new Jalalian(1397, 1, 18))->getMonth()
119 | // output: 1
120 | ```
121 |
122 | ---
123 | ```php
124 | public function isLeapYear(): bool
125 |
126 | $date = (new Jalalian(1397, 1, 18))->isLeapYear()
127 | // output: false
128 |
129 | ```
130 |
131 | ---
132 | ```php
133 | public function getYear(): int
134 |
135 | $date = (new Jalalian(1397, 1, 18))->getYear()
136 | // output: 1397
137 | ```
138 |
139 | ---
140 | ```php
141 | public function subMonths(int $months = 1): Jalalian
142 |
143 | $date = (new Jalalian(1397, 1, 18))->subMonths(1)->toString()
144 | // output: 1396-12-18 00:00:00
145 |
146 | ```
147 |
148 | ---
149 | ```php
150 | public function subYears(int $years = 1): Jalalian
151 |
152 | $date = (new Jalalian(1397, 1, 18))->subYears(1)->toString()
153 | // output: 1396-01-18 00:00:00
154 | ```
155 |
156 | ---
157 | ```php
158 | public function getDay(): int
159 |
160 | $date = (new Jalalian(1397, 1, 18))->getDay()
161 | // output: 18
162 |
163 | ```
164 |
165 | ---
166 | ```php
167 | public function getHour(): int
168 |
169 | $date = (new Jalalian(1397, 1, 18, 12, 0, 0))->getHour()
170 | // output: 12
171 |
172 |
173 | ```
174 |
175 | ---
176 | ```php
177 | public function getMinute(): int
178 |
179 | $date = (new Jalalian(1397, 1, 18, 12, 10, 0))->getMinute()
180 | // output: 10
181 |
182 | ```
183 |
184 | ---
185 | ```php
186 | public function getSecond(): int
187 |
188 | $date = (new Jalalian(1397, 1, 18, 12, 10, 45))->getSecond()
189 | // output: 45
190 | ```
191 |
192 | ---
193 | ```php
194 | public function getTimezone(): \DateTimeZone
195 |
196 | // Get current timezone
197 | ```
198 |
199 | ---
200 | ```php
201 | public function addMonths(int $months = 1): Jalalian
202 |
203 | $date = (new Jalalian(1397, 1, 18, 12, 10, 0))->addMonths(1)->format('m')
204 | // output: 02
205 |
206 | ```
207 |
208 | ---
209 | ```php
210 | public function addYears(int $years = 1): Jalalian
211 |
212 | $date = (new Jalalian(1397, 1, 18, 12, 10, 0))->addYears(1)->format('Y')
213 | // output: 1398
214 |
215 | ```
216 |
217 | ---
218 | ```php
219 | public function getDaysOf(int $monthNumber = 1): int
220 |
221 | $date = (new Jalalian(1397, 1, 18, 12, 10, 0))->getDaysOf(1)
222 | // output: 31
223 | ```
224 |
225 | ---
226 | ```php
227 | public function addDays(int $days = 1): Jalalian
228 |
229 | $date = (new Jalalian(1397, 1, 18, 12, 10, 0))->addDays(1)->format('d')
230 | // output: 18
231 |
232 | ```
233 |
234 | ---
235 | ```php
236 | public function toCarbon(): Carbon
237 |
238 | $date = (new Jalalian(1397, 1, 18, 12, 10, 0))->toCarbon()->toDateTimeString()
239 | // output: 2018-04-07 12:10:00
240 | ```
241 |
242 | ---
243 | ```php
244 | public function subDays(int $days = 1): Jalalian
245 |
246 | $date = (new Jalalian(1397, 1, 18, 12, 10, 0))->subDays(10)->format('d')
247 | // output: 08
248 | ```
249 |
250 | ---
251 | ```php
252 | public function addHours(int $hours = 1): Jalalian
253 |
254 | $date = (new Jalalian(1397, 1, 18, 12, 10, 0))->addHours(1)->format('H')
255 | // output: 13
256 |
257 | ```
258 |
259 | ---
260 | ```php
261 | public function subHours(int $hours = 1): Jalalian
262 |
263 | $date = (new Jalalian(1397, 1, 18, 12, 10, 0))->subHours(1)->format('H')
264 | // output: 11
265 |
266 | ```
267 |
268 | ---
269 | ```php
270 | public function addMinutes(int $minutes = 1): Jalalian
271 |
272 | $date = (new Jalalian(1397, 1, 18, 12, 10, 0))->addMinutes(10)->format('i')
273 | // output: 22
274 |
275 | ```
276 |
277 | ---
278 | ```php
279 | public function subMinutes(int $minutes = 1): Jalalian
280 |
281 | $date = (new Jalalian(1397, 1, 18, 12, 10, 0))->subMinutes(10)->format('i')
282 | // output: 02
283 |
284 | ```
285 |
286 | ---
287 | ```php
288 | public function addSeconds(int $secs = 1): Jalalian
289 |
290 | $date = (new Jalalian(1397, 1, 18, 12, 10, 0))->addSeconds(10)->format('s')
291 | // output: 10
292 |
293 | ```
294 |
295 | ---
296 | ```php
297 | public function subSeconds(int $secs = 1): Jalalian
298 |
299 | $date = (new Jalalian(1397, 1, 18, 12, 10, 0))->subSeconds(10)->format('i:s')
300 | // output: 11:40
301 |
302 |
303 | ```
304 |
305 | ---
306 | ```php
307 | public function equalsTo(Jalalian $other): bool
308 |
309 | $date = (new Jalalian(1397, 1, 18, 12, 10, 0))->equalsTo(Jalalian::now())
310 | // output: false
311 |
312 | $date = Jalalian::now()->equalsTo(Jalalian::now())
313 | // output: true
314 |
315 | ```
316 |
317 | ---
318 | ```php
319 | public function equalsToCarbon(Carbon $carbon): bool
320 |
321 | $date = Jalalian::now()->equalsToCarbon(Carbon::now())
322 | // output: true
323 | ```
324 |
325 | ---
326 | ```php
327 | public function greaterThan(Jalalian $other): bool
328 |
329 | $date = Jalalian::now()->greaterThan(Jalalian::now()->subDays(1)))
330 | // output: true
331 | ```
332 |
333 | ---
334 | ```php
335 | public function greaterThanCarbon(Carbon $carbon): bool
336 |
337 | $date = Jalalian::now()->greaterThanCarbon(Carbon::now()->subDays(1)))
338 | // output: true
339 |
340 | ```
341 |
342 | ---
343 | ```php
344 | public function lessThan(Jalalian $other): bool
345 |
346 | $date = Jalalian::now()->lessThan(Jalalian::now()->addDays(1)))
347 | // output: true
348 |
349 | ```
350 |
351 | ---
352 | ```php
353 | public function lessThanCarbon(Carbon $carbon): bool
354 |
355 | $date = Jalalian::now()->lessThanCarbon(Carbon::now()->addDays(1)))
356 | // output: true
357 |
358 | ```
359 |
360 | ---
361 | ```php
362 | public function greaterThanOrEqualsTo(Jalalian $other): bool
363 |
364 | $date = Jalalian::now()->greaterThan(Jalalian::now()->subDays(1)))
365 | // output: true
366 |
367 | ```
368 |
369 | ---
370 | ```php
371 | public function greaterThanOrEqualsToCarbon(Carbon $carbon): bool
372 |
373 | $date = Jalalian::now()->greaterThanOrEqualsToCarbon(Carbon::now()))
374 | // output: true
375 |
376 | ```
377 |
378 | ---
379 | ```php
380 | public function lessThanOrEqualsTo(Jalalian $other): bool
381 |
382 | $date = Jalalian::now()->lessThanOrEqualsTo(Jalalian::now()))
383 | // output: true
384 |
385 | ```
386 |
387 | ---
388 | ```php
389 | public function lessThanOrEqualsToCarbon(Carbon $carbon): bool
390 |
391 | $date = Jalalian::now()->lessThanOrEqualsToCarbon(Carbon::now()))
392 | // output: true
393 |
394 | ```
395 |
396 | ---
397 | ```php
398 | public function isStartOfWeek(): bool
399 |
400 | $date = (new Jalalian(1397, 6, 24))->isStartOfWeek()
401 | // output: true
402 |
403 | ```
404 | ---
405 | ```php
406 | public function getEndDayOfYear(): bool
407 |
408 | $date = (new Jalalian(1397, 6, 24))->getEndDayOfYear()
409 | // output: 1397, 12, 29
410 |
411 | ```
412 | ---
413 | ```php
414 | public function getFirstDayOfMonth(): bool
415 |
416 | $date = (new Jalalian(1397, 6, 24))->getFirstDayOfMonth()
417 | // output: 1397, 6, 1
418 |
419 | ```
420 | ---
421 | ```php
422 | public function getEndDayOfMonth(): bool
423 |
424 | $date = (new Jalalian(1397, 6, 24))->getEndDayOfMonth()
425 | // output: 1397, 6, 30
426 |
427 | ```
428 | ---
429 | ```php
430 | public function isSaturday(): bool
431 |
432 | $date = (new Jalalian(1397, 6, 24))->isSaturday()
433 | // output: true
434 |
435 | ```
436 |
437 | ---
438 | ```php
439 | public function isDayOfWeek(int $day): bool
440 |
441 | $date = (new Jalalian(1397, 6, 24))->isDayOfWeek(0)
442 | // output: true
443 |
444 | ```
445 |
446 | ---
447 | ```php
448 | public function isEndOfWeek(): bool
449 |
450 | $date = (new Jalalian(1397, 6, 24))->isEndOfWeek()
451 | // output: false
452 |
453 | ```
454 |
455 | ---
456 | ```php
457 | public function isFriday(): bool
458 |
459 | $date = (new Jalalian(1397, 6, 24))->isFriday()
460 | // output: false
461 |
462 | ```
463 |
464 | ---
465 | ```php
466 | public function isToday(): bool
467 |
468 | $date = (new Jalalian(1397, 6, 24))->isToday()
469 | // output: (!maybe) true
470 |
471 | ```
472 |
473 | ---
474 | ```php
475 | public function isTomorrow(): bool
476 |
477 | $date = (new Jalalian(1397, 6, 25))->isTomorrow()
478 | // output: true
479 |
480 | ```
481 |
482 | ---
483 | ```php
484 | public function isYesterday(): bool
485 |
486 | $date = (new Jalalian(1397, 6, 23))->isYesterday()
487 | // output: true
488 |
489 | ```
490 |
491 | ---
492 | ```php
493 | public function isFuture(): bool
494 |
495 | $date = (new Jalalian(1397, 6, 26))->isFuture()
496 | // output: true
497 |
498 | ```
499 |
500 | ---
501 | ```php
502 | public function isPast(): bool
503 |
504 | $date = (new Jalalian(1397, 5, 24))->isPast()
505 | // output: true
506 |
507 | ```
508 |
509 | ---
510 | ```php
511 | public function toArray(): array
512 | $date = (new Jalalian(1397, 6, 24))->toArray()
513 | // output: (
514 | // [year] => 1397
515 | // [month] => 6
516 | // [day] => 24
517 | // [dayOfWeek] => 0
518 | // [dayOfYear] => 179
519 | // [hour] => 0
520 | // [minute] => 0
521 | // [second] => 0
522 | // [micro] => 0
523 | // [timestamp] => 1536969600
524 | // [formatted] => 1397-06-24 00:00:00
525 | // [timezone] =>
526 | // )
527 | ```
528 |
529 | ---
530 | ```php
531 | public function getDayOfWeek(): int
532 |
533 | $date = (new Jalalian(1397, 5, 24))->getDayOfWeek()
534 | // output: 0
535 |
536 | ```
537 |
538 | ---
539 | ```php
540 | public function isSunday(): bool
541 |
542 | $date = (new Jalalian(1397, 6, 24))->isSunday()
543 | // output: false
544 |
545 | ```
546 |
547 | ---
548 | ```php
549 | public function isMonday(): bool
550 |
551 | $date = (new Jalalian(1397, 6, 26))->isMonday()
552 | // output: true
553 |
554 | ```
555 |
556 | ---
557 | ```php
558 | public function isTuesday(): bool
559 |
560 | $date = (new Jalalian(1397, 6, 24))->isTuesday()
561 | // output: false
562 |
563 | ```
564 |
565 | ---
566 | ```php
567 | public function isWednesday(): bool
568 |
569 | $date = (new Jalalian(1397, 6, 24))->isWednesday()
570 | // output: false
571 |
572 | ```
573 |
574 | ---
575 | ```php
576 | public function isThursday(): bool
577 |
578 | $date = (new Jalalian(1397, 6, 22))->isThursday()
579 | // output: true
580 |
581 | ```
582 |
583 | ---
584 | ```php
585 | public function getDayOfYear(): int
586 |
587 | $date = (new Jalalian(1397, 5, 24))->getDayOfYear()
588 | // output: 179
589 |
590 | ```
591 |
592 | ---
593 | ```php
594 | public function toString(): string
595 | $date = (new Jalalian(1397, 5, 24))->isPast()
596 | // output: 1397-05-24 00:00:00
597 |
598 | ```
599 |
600 | ---
601 | ```php
602 | public function format(string $format): string
603 |
604 | $date = (new Jalalian(1397, 5, 24))->format('y')
605 | // output: 1397
606 | // see php date formats
607 |
608 | ```
609 |
610 | ---
611 | ```php
612 | public function __toString(): string
613 |
614 | // Alias of toString()
615 | ```
616 |
617 | ---
618 | ```php
619 | public function ago(): string
620 |
621 | ```
622 |
623 | ---
624 | ```php
625 | public function getTimestamp(): int
626 |
627 | ```
628 |
629 | ---
630 | ```php
631 | public function getNextWeek(): Jalalian
632 |
633 | ```
634 |
635 | ---
636 | ```php
637 | public function getNextMonth(): Jalalian
638 |
639 | ```
640 |
641 | ---
642 |
643 | ### CalendarUtils
644 | ---
645 |
646 |
647 | #### `checkDate($year, $month, $day, [$isJalali = true])`
648 | ```php
649 | // Check jalali date
650 | \Morilog\Jalali\CalendarUtils::checkDate(1391, 2, 30, true); // true
651 |
652 | // Check jalali date
653 | \Morilog\Jalali\CalendarUtils::checkDate(2016, 5, 7); // false
654 |
655 | // Check gregorian date
656 | \Morilog\Jalali\CalendarUtils::checkDate(2016, 5, 7, false); // true
657 | ```
658 | ---
659 | #### `toJalali($gYear, $gMonth, $gDay)`
660 | ```php
661 | \Morilog\Jalali\CalendarUtils::toJalali(2016, 5, 7); // [1395, 2, 18]
662 | ```
663 | ---
664 | #### `toGregorian($jYear, $jMonth, $jDay)`
665 | ```php
666 | \Morilog\Jalali\CalendarUtils::toGregorian(1395, 2, 18); // [2016, 5, 7]
667 | ```
668 | ---
669 | #### `strftime($format, [$timestamp = false, $timezone = null])`
670 | ```php
671 | CalendarUtils::strftime('Y-m-d', strtotime('2016-05-8')); // 1395-02-19
672 | ```
673 | ---
674 | #### `createDateTimeFromFormat($format, $jalaiTimeString)`
675 | ```php
676 | $Jalalian = '1394/11/25 15:00:00';
677 |
678 | // get instance of \DateTime
679 | $dateTime = \Morilog\Jalali\CalendarUtils::createDatetimeFromFormat('Y/m/d H:i:s', $Jalalian);
680 |
681 | ```
682 | ---
683 | #### `createCarbonFromFormat($format, $jalaiTimeString)`
684 | ```php
685 | $Jalalian = '1394/11/25 15:00:00';
686 |
687 | // get instance of \Carbon\Carbon
688 | $carbon = \Morilog\Jalali\CalendarUtils::createCarbonFromFormat('Y/m/d H:i:s', $Jalalian);
689 |
690 | ```
691 | ---
692 | #### `convertNumbers($string)`
693 | ```php
694 | // convert latin to persian
695 | $date = \Morilog\Jalali\CalendarUtils::strftime('Y-m-d', strtotime('2016-05-8')); // 1395-02-19
696 | \Morilog\Jalali\CalendarUtils::convertNumbers($date); // ۱۳۹۵-۰۲-۱۹
697 |
698 | // convert persian to latin
699 | $dateString = \Morilog\Jalali\CalendarUtils::convertNumbers('۱۳۹۵-۰۲-۱۹', true); // 1395-02-19
700 | \Morilog\Jalali\CalendarUtils::createCarbonFromFormat('Y-m-d', $dateString)->format('Y-m-d'); //2016-05-8
701 | ```
702 |
703 | ---
704 | #### `Carbon api-difference`
705 |
706 | You can convert date/time to [briannesbitt/carbon](https://github.com/briannesbitt/carbon), thus being able to use it's [API](https://carbon.nesbot.com/docs/) to work with PHP DateTime class.
707 |
708 | ##### [Difference](https://carbon.nesbot.com/docs/#api-difference) in months:
709 |
710 | ```php
711 | // convert persian to Carbon
712 | $date = \Morilog\Jalali\Jalalian::fromFormat('Y-m-d', "1395-02-19")->toCarbon();
713 | // ->toString() => Sun May 08 2016 00:00:00 GMT+0000
714 |
715 | // Add 4 months to Carbon
716 | $dateAdd4Months = $date->addMonths(4);
717 |
718 | // Difference in months
719 | $dateAdd4Months->DiffInMonths($date); //4
720 | $dateAdd4Months->floatDiffInMonths($date); //4.0
721 | ```
722 |
723 | ---
724 | ## Formatting ##
725 |
726 | For help in building your formats, checkout the [PHP strftime() docs](http://php.net/manual/en/function.strftime.php).
727 |
728 | ## Notes ##
729 |
730 | The class relies on ``strtotime()`` to make sense of your strings, and ``strftime()`` to handle the formatting. Always check the ``time()`` output to see if you get false timestamps, it which case, means the class couldn't understand what you were asking it to do.
731 |
732 | ## License ##
733 | - This bundle is created based on [Laravel-Date](https://github.com/swt83/laravel-date) by [Scott Travis](https://github.com/swt83) (MIT Licensed).
734 | - [Jalali (Shamsi) DateTime](https://github.com/sallar/CalendarUtils) class included in the package is created by [Sallar Kaboli](http://sallar.me) and is released under the MIT License.
735 | - This package is created and modified by [Morteza Parvini](http://morilog.ir) for Laravel >= 5 and is released under the MIT License.
736 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "moree-dev/jalali",
3 | "description": "This Package helps developers to easily work with Jalali (Shamsi or Iranian) dates in PHP applications, based on Jalali (Shamsi) DateTime class.",
4 | "license": "MIT",
5 | "type": "library",
6 | "authors": [
7 | {
8 | "name": "Milad Rey",
9 | "email": "miladr@gmail.com"
10 | }, {
11 | "name": "Morteza Parvini",
12 | "email": "m.parvini@outlook.com"
13 | }
14 | ],
15 | "keywords": ["Laravel","Date","Datetime","Jalali", "Morilog"],
16 | "require": {
17 | "php": "^7.0 | ^8.0",
18 | "nesbot/carbon": "^1.21 || ^2.0 || ^3.0",
19 | "beberlei/assert": "^3.0"
20 | },
21 | "require-dev" : {
22 | "phpunit/phpunit": ">4.0"
23 | },
24 | "autoload": {
25 | "psr-4": {
26 | "Morilog\\Jalali\\": "src"
27 | },
28 | "files": [
29 | "src/helpers.php"
30 | ]
31 | },
32 | "autoload-dev": {
33 | "psr-4": {
34 | "Morilog\\Jalali\\Tests\\": "tests/"
35 | }
36 | },
37 | "minimum-stablity": "dev"
38 | }
39 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 | ./tests/
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/CalendarUtils.php:
--------------------------------------------------------------------------------
1 | setDate($year, $month, $day);
89 |
90 |
91 | return $georgianDate;
92 | }
93 |
94 | /**
95 | * Checks whether a Jalaali date is valid or not.
96 | *
97 | * @param int $jy
98 | * @param int $jm
99 | * @param int $jd
100 | * @return bool
101 | */
102 | public static function isValidateJalaliDate($jy, $jm, $jd)
103 | {
104 | return $jy >= -61 && $jy <= 3177
105 | && $jm >= 1 && $jm <= 12
106 | && $jd >= 1 && $jd <= self::jalaliMonthLength($jy, $jm);
107 | }
108 |
109 | /**
110 | * Checks whether a date is valid or not.
111 | *
112 | * @param $year
113 | * @param $month
114 | * @param $day
115 | * @param bool $isJalali
116 | * @return bool
117 | */
118 | public static function checkDate($year, $month, $day, $isJalali = true)
119 | {
120 | return $isJalali === true ? self::isValidateJalaliDate($year, $month, $day) : checkdate($month, $day, $year);
121 | }
122 |
123 | /**
124 | * Is this a leap year or not?
125 | *
126 | * @param $jy
127 | * @return bool
128 | */
129 | public static function isLeapJalaliYear($jy)
130 | {
131 | return self::jalaliCal($jy)['leap'] === 0;
132 | }
133 |
134 | /**
135 | * Number of days in a given month in a Jalaali year.
136 | *
137 | * @param int $jy
138 | * @param int $jm
139 | * @return int
140 | */
141 | public static function jalaliMonthLength($jy, $jm)
142 | {
143 | if ($jm <= 6) {
144 | return 31;
145 | }
146 |
147 | if ($jm <= 11) {
148 | return 30;
149 | }
150 |
151 | return self::isLeapJalaliYear($jy) ? 30 : 29;
152 | }
153 |
154 |
155 | /**
156 | * This function determines if the Jalaali (Persian) year is
157 | * leap (366-day long) or is the common year (365 days), and
158 | * finds the day in March (Gregorian calendar) of the first
159 | * day of the Jalaali year (jy).
160 | *
161 | * @param int $jy Jalaali calendar year (-61 to 3177)
162 | * @return array
163 | * leap: number of years since the last leap year (0 to 4)
164 | * gy: Gregorian year of the beginning of Jalaali year
165 | * march: the March day of Farvardin the 1st (1st day of jy)
166 | * @see: http://www.astro.uni.torun.pl/~kb/Papers/EMP/PersianC-EMP.htm
167 | * @see: http://www.fourmilab.ch/documents/calendar/
168 | */
169 | public static function jalaliCal($jy)
170 | {
171 | $breaks = [
172 | -61, 9, 38, 199, 426, 686, 756, 818, 1111, 1181, 1210, 1635, 2060, 2097, 2192, 2262, 2324, 2394, 2456, 3178
173 | ];
174 |
175 | $breaksCount = count($breaks);
176 |
177 | $gy = $jy + 621;
178 | $leapJ = -14;
179 | $jp = $breaks[0];
180 |
181 | if ($jy < $jp || $jy >= $breaks[$breaksCount - 1]) {
182 | throw new \InvalidArgumentException('Invalid Jalali year : ' . $jy);
183 | }
184 |
185 | $jump = 0;
186 |
187 | for ($i = 1; $i < $breaksCount; $i += 1) {
188 | $jm = $breaks[$i];
189 | $jump = $jm - $jp;
190 |
191 | if ($jy < $jm) {
192 | break;
193 | }
194 |
195 | $leapJ = $leapJ + self::div($jump, 33) * 8 + self::div(self::mod($jump, 33), 4);
196 |
197 | $jp = $jm;
198 | }
199 |
200 | $n = $jy - $jp;
201 |
202 | $leapJ = $leapJ + self::div($n, 33) * 8 + self::div(self::mod($n, 33) + 3, 4);
203 |
204 | if (self::mod($jump, 33) === 4 && $jump - $n === 4) {
205 | $leapJ += 1;
206 | }
207 |
208 | $leapG = self::div($gy, 4) - self::div((self::div($gy, 100) + 1) * 3, 4) - 150;
209 |
210 | $march = 20 + $leapJ - $leapG;
211 |
212 | if ($jump - $n < 6) {
213 | $n = $n - $jump + self::div($jump + 4, 33) * 33;
214 | }
215 |
216 | $leap = self::mod(self::mod($n + 1, 33) - 1, 4);
217 |
218 | if ($leap === -1) {
219 | $leap = 4;
220 | }
221 |
222 | return [
223 | 'leap' => $leap,
224 | 'gy' => $gy,
225 | 'march' => $march
226 | ];
227 | }
228 |
229 | /**
230 | * @param $a
231 | * @param $b
232 | */
233 | public static function div($a, $b): int
234 | {
235 | return intdiv($a, $b);
236 | }
237 |
238 | /**
239 | * @param $a
240 | * @param $b
241 | * @return mixed
242 | */
243 | public static function mod($a, $b): int
244 | {
245 | return $a - intdiv($a, $b) * $b;
246 | }
247 |
248 | /**
249 | * @param $jdn
250 | * @return array
251 | */
252 | public static function d2g($jdn)
253 | {
254 | $j = 4 * $jdn + 139361631;
255 | $j += self::div(self::div(4 * $jdn + 183187720, 146097) * 3, 4) * 4 - 3908;
256 | $i = self::div(self::mod($j, 1461), 4) * 5 + 308;
257 |
258 | $gd = self::div(self::mod($i, 153), 5) + 1;
259 | $gm = self::mod(self::div($i, 153), 12) + 1;
260 | $gy = self::div($j, 1461) - 100100 + self::div(8 - $gm, 6);
261 |
262 | return [$gy, $gm, $gd];
263 | }
264 |
265 | /**
266 | * Calculates the Julian Day number from Gregorian or Julian
267 | * calendar dates. This integer number corresponds to the noon of
268 | * the date (i.e. 12 hours of Universal Time).
269 | * The procedure was tested to be good since 1 March, -100100 (of both
270 | * calendars) up to a few million years into the future.
271 | *
272 | * @param int $gy Calendar year (years BC numbered 0, -1, -2, ...)
273 | * @param int $gm Calendar month (1 to 12)
274 | * @param int $gd Calendar day of the month (1 to 28/29/30/31)
275 | * @return int Julian Day number
276 | */
277 | public static function g2d($gy, $gm, $gd)
278 | {
279 | return (self::div(($gy + self::div($gm - 8, 6) + 100100) * 1461, 4)
280 | + self::div(153 * self::mod($gm + 9, 12) + 2, 5)
281 | + $gd - 34840408
282 | ) - self::div(self::div($gy + 100100 + self::div($gm - 8, 6), 100) * 3, 4) + 752;
283 | }
284 |
285 | /**
286 | * Converts a date of the Jalaali calendar to the Julian Day number.
287 | *
288 | * @param int $jy Jalaali year (1 to 3100)
289 | * @param int $jm Jalaali month (1 to 12)
290 | * @param int $jd Jalaali day (1 to 29/31)
291 | * @return int Julian Day number
292 | */
293 | public static function j2d($jy, $jm, $jd)
294 | {
295 | $jCal = self::jalaliCal($jy);
296 |
297 | return self::g2d($jCal['gy'], 3, $jCal['march']) + ($jm - 1) * 31 - self::div($jm, 7) * ($jm - 7) + $jd - 1;
298 | }
299 |
300 |
301 | /**
302 | * Converts the Julian Day number to a date in the Jalaali calendar.
303 | *
304 | * @param int $jdn Julian Day number
305 | * @return array
306 | * 0: Jalaali year (1 to 3100)
307 | * 1: Jalaali month (1 to 12)
308 | * 2: Jalaali day (1 to 29/31)
309 | */
310 | public static function d2j($jdn)
311 | {
312 | $gy = self::d2g($jdn)[0];
313 | $jy = $gy - 621;
314 | $jCal = self::jalaliCal($jy);
315 | $jdn1f = self::g2d($gy, 3, $jCal['march']);
316 |
317 | $k = $jdn - $jdn1f;
318 |
319 | if ($k >= 0) {
320 | if ($k <= 185) {
321 | $jm = 1 + self::div($k, 31);
322 | $jd = self::mod($k, 31) + 1;
323 |
324 | return [$jy, $jm, $jd];
325 | } else {
326 | $k -= 186;
327 | }
328 | } else {
329 | $jy -= 1;
330 | $k += 179;
331 |
332 | if ($jCal['leap'] === 1) {
333 | $k += 1;
334 | }
335 | }
336 |
337 | $jm = 7 + self::div($k, 30);
338 | $jd = self::mod($k, 30) + 1;
339 |
340 | return [$jy, $jm, $jd];
341 | }
342 |
343 | /**
344 | * @param $format
345 | * @param bool $stamp
346 | * @param bool $timezone
347 | * @return mixed
348 | */
349 | public static function date($format, $stamp = false, $timezone = null)
350 | {
351 | $stamp = ($stamp !== false) ? $stamp : time();
352 | $dateTime = static::createDateTime($stamp, $timezone);
353 |
354 |
355 | //Find what to replace
356 | $chars = (preg_match_all('/([a-zA-Z]{1})/', $format, $chars)) ? $chars[0] : array();
357 |
358 | //Intact Keys
359 | $intact = array('B', 'h', 'H', 'g', 'G', 'i', 's', 'I', 'U', 'u', 'Z', 'O', 'P');
360 | $intact = self::filterArray($chars, $intact);
361 | $intactValues = array();
362 |
363 | foreach ($intact as $k => $v) {
364 | $intactValues[$k] = $dateTime->format($v);
365 | }
366 | //End Intact Keys
367 |
368 | //Changed Keys
369 | list($year, $month, $day) = array($dateTime->format('Y'), $dateTime->format('n'), $dateTime->format('j'));
370 | list($jYear, $jMonth, $jDay) = self::toJalali($year, $month, $day);
371 |
372 | $keys = array(
373 | 'd',
374 | 'D',
375 | 'j',
376 | 'l',
377 | 'N',
378 | 'S',
379 | 'w',
380 | 'z',
381 | 'W',
382 | 'F',
383 | 'm',
384 | 'M',
385 | 'n',
386 | 't',
387 | 'L',
388 | 'o',
389 | 'Y',
390 | 'y',
391 | 'a',
392 | 'A',
393 | 'c',
394 | 'r',
395 | 'e',
396 | 'T'
397 | );
398 | $keys = self::filterArray($chars, $keys, array('z'));
399 | $values = array();
400 |
401 | foreach ($keys as $k => $key) {
402 | $v = '';
403 | switch ($key) {
404 | //Day
405 | case 'd':
406 | $v = sprintf("%02d", $jDay);
407 | break;
408 | case 'D':
409 | $v = self::getDayNames($dateTime->format('D'), true);
410 | break;
411 | case 'j':
412 | $v = $jDay;
413 | break;
414 | case 'l':
415 | $v = self::getDayNames($dateTime->format('l'));
416 | break;
417 | case 'N':
418 | $v = self::getDayNames($dateTime->format('l'), false, 1, true);
419 | break;
420 | case 'S':
421 | $v = 'ام';
422 | break;
423 | case 'w':
424 | $v = self::getDayNames($dateTime->format('l'), false, 1, true) - 1;
425 | break;
426 | case 'z':
427 | if ($jMonth > 6) {
428 | $v = 186 + (($jMonth - 6 - 1) * 30) + $jDay;
429 | } else {
430 | $v = (($jMonth - 1) * 31) + $jDay;
431 | }
432 | self::$temp['z'] = $v;
433 | break;
434 | //Week
435 | case 'W':
436 | $v = is_int(self::$temp['z'] / 7) ? (self::$temp['z'] / 7) : intval(self::$temp['z'] / 7 + 1);
437 | break;
438 | //Month
439 | case 'F':
440 | $v = self::getMonthName($jMonth);
441 | break;
442 | case 'm':
443 | $v = sprintf("%02d", $jMonth);
444 | break;
445 | case 'M':
446 | $v = self::getMonthName($jMonth, true);
447 | break;
448 | case 'n':
449 | $v = $jMonth;
450 | break;
451 | case 't':
452 | $v = ($jMonth == 12) ? (self::isLeapJalaliYear($jYear) ? 30 : 29) : ($jMonth > 6 ? 30 : 31);
453 | break;
454 | //Year
455 | case 'L':
456 | $tmpObj = static::createDateTime(time() - 31536000, $timezone);
457 | $v = $tmpObj->format('L');
458 | break;
459 | case 'o':
460 | case 'Y':
461 | $v = $jYear;
462 | break;
463 | case 'y':
464 | $v = $jYear % 100;
465 | if ($v < 10) {
466 | $v = '0' . $v;
467 | }
468 | break;
469 | //Time
470 | case 'a':
471 | $v = ($dateTime->format('a') == 'am') ? 'ق.ظ' : 'ب.ظ';
472 | break;
473 | case 'A':
474 | $v = ($dateTime->format('A') == 'AM') ? 'قبل از ظهر' : 'بعد از ظهر';
475 | break;
476 | //Full Dates
477 | case 'c':
478 | $v = $jYear . '-' . sprintf("%02d", $jMonth) . '-' . sprintf("%02d", $jDay) . 'T';
479 | $v .= $dateTime->format('H') . ':' . $dateTime->format('i') . ':' . $dateTime->format('s') . $dateTime->format('P');
480 | break;
481 | case 'r':
482 | $v = self::getDayNames($dateTime->format('D'), true) . ', ' . sprintf(
483 | "%02d",
484 | $jDay
485 | ) . ' ' . self::getMonthName($jMonth, true);
486 | $v .= ' ' . $jYear . ' ' . $dateTime->format('H') . ':' . $dateTime->format('i') . ':' . $dateTime->format('s') . ' ' . $dateTime->format('P');
487 | break;
488 | //Timezone
489 | case 'e':
490 | $v = $dateTime->format('e');
491 | break;
492 | case 'T':
493 | $v = $dateTime->format('T');
494 | break;
495 | }
496 | $values[$k] = $v;
497 | }
498 | //End Changed Keys
499 |
500 | //Merge
501 | $keys = array_merge($intact, $keys);
502 | $values = array_merge($intactValues, $values);
503 |
504 | return strtr($format, array_combine($keys, $values));
505 | }
506 |
507 | /**
508 | * @param $format
509 | * @param bool $stamp
510 | * @param null $timezone
511 | * @return mixed
512 | */
513 | public static function strftime($format, $stamp = false, $timezone = null)
514 | {
515 | $str_format_code = array(
516 | "%a",
517 | "%A",
518 | "%d",
519 | "%e",
520 | "%j",
521 | "%u",
522 | "%w",
523 | "%U",
524 | "%V",
525 | "%W",
526 | "%b",
527 | "%B",
528 | "%h",
529 | "%m",
530 | "%C",
531 | "%g",
532 | "%G",
533 | "%y",
534 | "%Y",
535 | "%H",
536 | "%I",
537 | "%l",
538 | "%M",
539 | "%p",
540 | "%P",
541 | "%r",
542 | "%R",
543 | "%S",
544 | "%T",
545 | "%X",
546 | "%z",
547 | "%Z",
548 | "%c",
549 | "%D",
550 | "%F",
551 | "%s",
552 | "%x",
553 | "%n",
554 | "%t",
555 | "%%",
556 | );
557 |
558 | $date_format_code = array(
559 | "D",
560 | "l",
561 | "d",
562 | "j",
563 | "z",
564 | "N",
565 | "w",
566 | "W",
567 | "W",
568 | "W",
569 | "M",
570 | "F",
571 | "M",
572 | "m",
573 | "y",
574 | "y",
575 | "y",
576 | "y",
577 | "Y",
578 | "H",
579 | "h",
580 | "g",
581 | "i",
582 | "A",
583 | "a",
584 | "h:i:s A",
585 | "H:i",
586 | "s",
587 | "H:i:s",
588 | "h:i:s",
589 | "H",
590 | "H",
591 | "D j M H:i:s",
592 | "d/m/y",
593 | "Y-m-d",
594 | "U",
595 | "d/m/y",
596 | "\n",
597 | "\t",
598 | "%",
599 | );
600 |
601 | //Change Strftime format to Date format
602 | $format = str_replace($str_format_code, $date_format_code, $format);
603 |
604 | //Convert to date
605 | return self::date($format, $stamp, $timezone);
606 | }
607 |
608 | private static function getDayNames($day, $shorten = false, $len = 1, $numeric = false)
609 | {
610 | switch (strtolower($day)) {
611 | case 'sat':
612 | case 'saturday':
613 | $ret = 'شنبه';
614 | $n = 1;
615 | break;
616 | case 'sun':
617 | case 'sunday':
618 | $ret = 'یکشنبه';
619 | $n = 2;
620 | break;
621 | case 'mon':
622 | case 'monday':
623 | $ret = 'دوشنبه';
624 | $n = 3;
625 | break;
626 | case 'tue':
627 | case 'tuesday':
628 | $ret = 'سهشنبه';
629 | $n = 4;
630 | break;
631 | case 'wed':
632 | case 'wednesday':
633 | $ret = 'چهارشنبه';
634 | $n = 5;
635 | break;
636 | case 'thu':
637 | case 'thursday':
638 | $ret = 'پنجشنبه';
639 | $n = 6;
640 | break;
641 | case 'fri':
642 | case 'friday':
643 | $ret = 'جمعه';
644 | $n = 7;
645 | break;
646 | default:
647 | $ret = '';
648 | $n = -1;
649 | }
650 |
651 | return ($numeric) ? $n : (($shorten) ? mb_substr($ret, 0, $len, 'UTF-8') : $ret);
652 | }
653 |
654 | private static function getMonthName($month, $shorten = false, $len = 3)
655 | {
656 | $monthIndex = ((int)$month) -1 ;
657 | $monthName = static::$monthNames[$monthIndex];
658 | return ($shorten) ? mb_substr($monthName, 0, $len, 'UTF-8') : $monthName;
659 | }
660 |
661 | private static function filterArray($needle, $haystack, $always = array())
662 | {
663 | foreach ($haystack as $k => $v) {
664 | if (!in_array($v, $needle) && !in_array($v, $always)) {
665 | unset($haystack[$k]);
666 | }
667 | }
668 |
669 |
670 | return $haystack;
671 | }
672 |
673 |
674 | /**
675 | * @param $format
676 | * @param $date
677 | * @return array
678 | */
679 | public static function parseFromFormat($format, $date)
680 | {
681 | // reverse engineer date formats
682 | $keys = array(
683 | 'Y' => array('year', '\d{4}'),
684 | 'y' => array('year', '\d{2}'),
685 | 'm' => array('month', '\d{2}'),
686 | 'n' => array('month', '\d{1,2}'),
687 | 'M' => array('month', '[A-Z][a-z]{3}'),
688 | 'F' => array('month', '[A-Z][a-z]{2,8}'),
689 | 'd' => array('day', '\d{2}'),
690 | 'j' => array('day', '\d{1,2}'),
691 | 'D' => array('day', '[A-Z][a-z]{2}'),
692 | 'l' => array('day', '[A-Z][a-z]{6,9}'),
693 | 'u' => array('hour', '\d{1,6}'),
694 | 'h' => array('hour', '\d{2}'),
695 | 'H' => array('hour', '\d{2}'),
696 | 'g' => array('hour', '\d{1,2}'),
697 | 'G' => array('hour', '\d{1,2}'),
698 | 'i' => array('minute', '\d{2}'),
699 | 's' => array('second', '\d{2}'),
700 | );
701 |
702 | // convert format string to regex
703 | $regex = '';
704 | $chars = str_split($format);
705 | foreach ($chars as $n => $char) {
706 | $lastChar = isset($chars[$n - 1]) ? $chars[$n - 1] : '';
707 | $skipCurrent = '\\' == $lastChar;
708 | if (!$skipCurrent && isset($keys[$char])) {
709 | $regex .= '(?P<' . $keys[$char][0] . '>' . $keys[$char][1] . ')';
710 | } else {
711 | if ('\\' == $char) {
712 | $regex .= $char;
713 | } else {
714 | $regex .= preg_quote($char);
715 | }
716 | }
717 | }
718 |
719 | $dt = array();
720 | $dt['error_count'] = 0;
721 | // now try to match it
722 | if (preg_match('#^' . $regex . '$#', $date, $dt)) {
723 | foreach ($dt as $k => $v) {
724 | if (is_int($k)) {
725 | unset($dt[$k]);
726 | }
727 | }
728 | if (!CalendarUtils::checkdate($dt['month'], $dt['day'], $dt['year'], false)) {
729 | $dt['error_count'] = 1;
730 | }
731 | } else {
732 | $dt['error_count'] = 1;
733 | }
734 | $dt['errors'] = array();
735 | $dt['fraction'] = '';
736 | $dt['warning_count'] = 0;
737 | $dt['warnings'] = array();
738 | $dt['is_localtime'] = 0;
739 | $dt['zone_type'] = 0;
740 | $dt['zone'] = 0;
741 | $dt['is_dst'] = '';
742 |
743 | if (strlen($dt['year']) == 2) {
744 | $now = Jalalian::forge('now');
745 | $x = $now->format('Y') - $now->format('y');
746 | $dt['year'] += $x;
747 | }
748 |
749 | $dt['year'] = isset($dt['year']) ? (int)$dt['year'] : 0;
750 | $dt['month'] = isset($dt['month']) ? (int)$dt['month'] : 0;
751 | $dt['day'] = isset($dt['day']) ? (int)$dt['day'] : 0;
752 | $dt['hour'] = isset($dt['hour']) ? (int)$dt['hour'] : 0;
753 | $dt['minute'] = isset($dt['minute']) ? (int)$dt['minute'] : 0;
754 | $dt['second'] = isset($dt['second']) ? (int)$dt['second'] : 0;
755 |
756 | return $dt;
757 | }
758 |
759 | /**
760 | * @param $format
761 | * @param $str
762 | * @param null $timezone
763 | * @return \DateTime
764 | */
765 | public static function createDatetimeFromFormat($format, $str, $timezone = null)
766 | {
767 | $pd = self::parseFromFormat($format, $str);
768 | $gd = self::toGregorian($pd['year'], $pd['month'], $pd['day']);
769 | $date = self::createDateTime('now', $timezone);
770 | $date->setDate($gd[0], $gd[1], $gd[2]);
771 | $date->setTime($pd['hour'], $pd['minute'], $pd['second']);
772 |
773 | return $date;
774 | }
775 |
776 | /**
777 | * @param $format
778 | * @param $str
779 | * @param null $timezone
780 | * @return Carbon
781 | */
782 | public static function createCarbonFromFormat($format, $str, $timezone = null)
783 | {
784 | $dateTime = self::createDatetimeFromFormat($format, $str, $timezone);
785 |
786 | return Carbon::createFromTimestamp($dateTime->getTimestamp(), $dateTime->getTimezone());
787 | }
788 |
789 | /**
790 | * Convert Latin numbers to persian numbers and vice versa
791 | *
792 | * @param string $string
793 | * @param boolean $toEnglish, default is false to save compatiblity
794 | * @return string
795 | */
796 | public static function convertNumbers($string, $toLatin = false)
797 | {
798 | $farsi_array = array("۰", "۱", "۲", "۳", "۴", "۵", "۶", "۷", "۸", "۹");
799 | $english_array = array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
800 | if (!$toLatin) {
801 | return str_replace($english_array, $farsi_array, $string);
802 | }
803 | return str_replace($farsi_array, $english_array, $string);
804 | }
805 |
806 | /**
807 | * @param $timestamp
808 | * @param null $timezone
809 | * @return \DateTime|static
810 | */
811 | public static function createDateTime($timestamp = null, $timezone = null)
812 | {
813 | $timezone = static::createTimeZone($timezone);
814 |
815 | if ($timestamp === null) {
816 | return Carbon::now($timezone);
817 | }
818 |
819 |
820 | if ($timestamp instanceof \DateTimeInterface) {
821 | return $timestamp;
822 | }
823 |
824 | if (is_string($timestamp)) {
825 | return new \DateTime($timestamp, $timezone);
826 | }
827 |
828 | if (is_numeric($timestamp)) {
829 | return Carbon::createFromTimestamp($timestamp, $timezone);
830 | }
831 |
832 |
833 | throw new \InvalidArgumentException('timestamp is not valid');
834 | }
835 |
836 | /**
837 | * @param null $timezone
838 | * @return \DateTimeZone|null
839 | */
840 | public static function createTimeZone($timezone = null)
841 | {
842 | if ($timezone instanceof \DateTimeZone) {
843 | return $timezone;
844 | }
845 |
846 | if ($timezone === null) {
847 | return new \DateTimeZone(date_default_timezone_get());
848 | }
849 |
850 | if (is_string($timezone)) {
851 | return new \DateTimeZone($timezone);
852 | }
853 |
854 |
855 | throw new \InvalidArgumentException('timezone is not valid');
856 | }
857 | }
858 |
--------------------------------------------------------------------------------
/src/Converter.php:
--------------------------------------------------------------------------------
1 | format("Y/m/d");
25 | }
26 |
27 | /**
28 | * Format the instance as a readable date
29 | *
30 | * @return string
31 | */
32 | public function toFormattedDateString()
33 | {
34 | return $this->format('j F Y');
35 | }
36 |
37 | /**
38 | * Format the instance with the day, and a readable date
39 | *
40 | * @return string
41 | */
42 | public function toFormattedDayDateString()
43 | {
44 | return $this->format('l j F Y');
45 | }
46 |
47 | /**
48 | * Format the instance as time
49 | *
50 | * @param string $unitPrecision
51 | *
52 | * @return string
53 | */
54 | public function toTimeString($unitPrecision = 'second')
55 | {
56 | return $this->format(static::getTimeFormatByPrecision($unitPrecision));
57 | }
58 |
59 | /**
60 | * Format the instance as date and time
61 | *
62 | * @param string $unitPrecision
63 | *
64 | * @return string
65 | */
66 | public function toDateTimeString($unitPrecision = 'second')
67 | {
68 | return $this->format('Y/m/d ' . static::getTimeFormatByPrecision($unitPrecision));
69 | }
70 |
71 | /**
72 | * Format the instance as a readable date and time
73 | *
74 | * @param string $unitPrecision
75 | *
76 | * @return string
77 | */
78 | public function toFormattedDateTimeString($unitPrecision = 'second')
79 | {
80 | return $this->format('j F Y ' . static::getTimeFormatByPrecision($unitPrecision));
81 | }
82 |
83 | /**
84 | * Return a format from H:i to H:i:s.u according to given unit precision.
85 | *
86 | * @param string $unitPrecision "minute", "second", "millisecond" or "microsecond"
87 | *
88 | * @return string
89 | */
90 | public static function getTimeFormatByPrecision($unitPrecision)
91 | {
92 | switch (Date::singularUnit($unitPrecision)) {
93 | case 'minute':
94 | return 'H:i';
95 | case 'second':
96 | return 'H:i:s';
97 | case 'm':
98 | case 'millisecond':
99 | return 'H:i:s.v';
100 | case 'µ':
101 | case 'microsecond':
102 | return 'H:i:s.u';
103 | }
104 |
105 | throw new UnitException('Precision unit expected among: minute, second, millisecond and microsecond.');
106 | }
107 |
108 | /**
109 | * Format the instance as date and time T-separated with no timezone
110 | * echo Jalalian::now()->toDateTimeLocalString('minute'); // You can specify precision among: minute, second, millisecond and microsecond
111 | * ```
112 | *
113 | * @param string $unitPrecision
114 | *
115 | * @return string
116 | */
117 | public function toDateTimeLocalString($unitPrecision = 'second')
118 | {
119 | return $this->format('Y-m-d\T' . static::getTimeFormatByPrecision($unitPrecision));
120 | }
121 |
122 | /**
123 | * Format the instance with day, date and time
124 | *
125 | * @param string $unitPrecision
126 | *
127 | * @return string
128 | */
129 | public function toDayDateTimeString($unitPrecision = 'second')
130 | {
131 | return $this->format('l j F Y ' . static::getTimeFormatByPrecision($unitPrecision));
132 | }
133 |
134 | /**
135 | * Format the instance with the year, and a readable month
136 | *
137 | * @return string
138 | */
139 | public function toFormattedMonthYearString()
140 | {
141 | return $this->format('F Y');
142 | }
143 |
144 | }
145 |
--------------------------------------------------------------------------------
/src/Jalalian.php:
--------------------------------------------------------------------------------
1 | 6) {
62 | Assertion::between($day, 1, 30);
63 | }
64 |
65 | if (!CalendarUtils::isLeapJalaliYear($year) && $month === 12) {
66 | Assertion::between($day, 1, 29);
67 | }
68 | Assertion::between($hour, 0, 24);
69 | Assertion::between($minute, 0, 59);
70 | Assertion::between($second, 0, 59);
71 |
72 | $this->year = $year;
73 | $this->month = $month;
74 | $this->day = $day;
75 | $this->hour = $hour;
76 | $this->minute = $minute;
77 | $this->second = $second;
78 | $this->timezone = $timezone;
79 | }
80 |
81 | public static function now(?\DateTimeZone $timeZone = null): Jalalian
82 | {
83 | return static::fromCarbon(Carbon::now($timeZone));
84 | }
85 |
86 | /**
87 | * @param Carbon $carbon
88 | * @return Jalalian
89 | */
90 | public static function fromCarbon(Carbon $carbon): Jalalian
91 | {
92 | $jDate = CalendarUtils::toJalali($carbon->year, $carbon->month, $carbon->day);
93 |
94 | return new static(
95 | $jDate[0],
96 | $jDate[1],
97 | $jDate[2],
98 | $carbon->hour,
99 | $carbon->minute,
100 | $carbon->second,
101 | $carbon->getTimezone()
102 | );
103 | }
104 |
105 | public static function fromFormat(string $format, string $timestamp, ?\DateTimeZone $timeZone = null): Jalalian
106 | {
107 | return static::fromCarbon(CalendarUtils::createCarbonFromFormat($format, $timestamp, $timeZone));
108 | }
109 |
110 | public static function forge($timestamp, ?\DateTimeZone $timeZone = null): Jalalian
111 | {
112 | return static::fromDateTime($timestamp, $timeZone);
113 | }
114 |
115 | /**
116 | * @param \DateTimeInterface| string $dateTime
117 | * @param \DateTimeZone|null $timeZone
118 | * @return Jalalian
119 | */
120 | public static function fromDateTime($dateTime, ?\DateTimeZone $timeZone = null): Jalalian
121 | {
122 | if (is_numeric($dateTime)) {
123 | return static::fromCarbon(Carbon::createFromTimestamp($dateTime, $timeZone));
124 | }
125 |
126 | return static::fromCarbon(new Carbon($dateTime, $timeZone));
127 | }
128 |
129 | public function getFirstDayOfWeek(): Jalalian
130 | {
131 | return (new static(
132 | $this->getYear(),
133 | $this->getMonth(),
134 | $this->getDay(),
135 | $this->getHour(),
136 | $this->getMinute(),
137 | $this->getSecond(),
138 | $this->getTimezone()
139 | ))->subDays($this->getDayOfWeek());
140 | }
141 |
142 | public function getFirstDayOfMonth(): Jalalian
143 | {
144 | return new static(
145 | $this->getYear(),
146 | $this->getMonth(),
147 | 1,
148 | $this->getHour(),
149 | $this->getMinute(),
150 | $this->getSecond(),
151 | $this->getTimezone()
152 | );
153 | }
154 |
155 | public function getFirstDayOfYear(): Jalalian
156 | {
157 | return new static(
158 | $this->getYear(),
159 | 1,
160 | 1,
161 | $this->getHour(),
162 | $this->getMinute(),
163 | $this->getSecond(),
164 | $this->getTimezone()
165 | );
166 | }
167 |
168 | public function getFirstDayOfQuarter(): Jalalian
169 | {
170 | return new static(
171 | $this->getYear(),
172 | ($this->getQuarter() - 1) * Carbon::MONTHS_PER_QUARTER + 1,
173 | 1,
174 | $this->getHour(),
175 | $this->getMinute(),
176 | $this->getSecond(),
177 | $this->getTimezone()
178 | );
179 | }
180 |
181 | public function getEndDayOfWeek(): Jalalian
182 | {
183 | $endWeek = $this->addDays(6 - $this->getDayOfWeek());
184 |
185 | return (new static(
186 | $endWeek->getYear(),
187 | $endWeek->getMonth(),
188 | $endWeek->getDay(),
189 | $endWeek->getHour(),
190 | $endWeek->getMinute(),
191 | $endWeek->getSecond(),
192 | $endWeek->getTimezone()
193 | ));
194 | }
195 |
196 | public function getEndDayOfMonth(): Jalalian
197 | {
198 | return new static(
199 | $this->getYear(),
200 | $this->getMonth(),
201 | $this->getDaysOf($this->getMonth()),
202 | $this->getHour(),
203 | $this->getMinute(),
204 | $this->getSecond(),
205 | $this->getTimezone()
206 | );
207 | }
208 |
209 | public function getEndDayOfYear(): Jalalian
210 | {
211 | return new static(
212 | $this->getYear(),
213 | 12,
214 | $this->getDaysOf(12),
215 | $this->getHour(),
216 | $this->getMinute(),
217 | $this->getSecond(),
218 | $this->getTimezone()
219 | );
220 | }
221 |
222 | public function getEndDayOfQuarter(): Jalalian
223 | {
224 | return new static(
225 | $this->getYear(),
226 | $this->getQuarter() * Carbon::MONTHS_PER_QUARTER,
227 | $this->getDaysOf($this->getQuarter() * Carbon::MONTHS_PER_QUARTER),
228 | $this->getHour(),
229 | $this->getMinute(),
230 | $this->getSecond(),
231 | $this->getTimezone()
232 | );
233 | }
234 |
235 | public function getMonthDays()
236 | {
237 | if ($this->getMonth() <= 6) {
238 | return 31;
239 | }
240 |
241 | if ($this->getMonth() < 12 || $this->isLeapYear()) {
242 | return 30;
243 | }
244 |
245 | return 29;
246 | }
247 |
248 | /**
249 | * @return int
250 | */
251 | public function getMonth(): int
252 | {
253 | return $this->month;
254 | }
255 |
256 | public function isLeapYear(): bool
257 | {
258 | return CalendarUtils::isLeapJalaliYear($this->getYear());
259 | }
260 |
261 | /**
262 | * @return int
263 | */
264 | public function getYear()
265 | {
266 | return $this->year;
267 | }
268 |
269 | public function getQuarter(): int
270 | {
271 | return (int) ceil($this->getMonth() / Carbon::MONTHS_PER_QUARTER);
272 | }
273 |
274 | public function subMonths(int $months = 1): Jalalian
275 | {
276 | Assertion::greaterOrEqualThan($months, 1);
277 |
278 | $diff = ($this->getMonth() - $months);
279 |
280 | if ($diff >= 1) {
281 | $day = $this->getDay();
282 | $targetMonthDays = $this->getDaysOf($diff);
283 | $targetDay = $day <= $targetMonthDays ? $day : $targetMonthDays;
284 |
285 | return new static(
286 | $this->getYear(),
287 | $diff,
288 | $targetDay,
289 | $this->getHour(),
290 | $this->getMinute(),
291 | $this->getSecond(),
292 | $this->getTimezone()
293 | );
294 | }
295 |
296 | $years = abs((int)($diff / 12));
297 | $date = $years > 0 ? $this->subYears($years) : clone $this;
298 | $diff = 12 - abs($diff % 12) - $date->getMonth();
299 |
300 | return $diff > 0 ? $date->subYears(1)->addMonths($diff) : $date->subYears(1);
301 | }
302 |
303 | /**
304 | * @return int
305 | */
306 | public function getDay(): int
307 | {
308 | return $this->day;
309 | }
310 |
311 | public function getDaysOf(int $monthNumber = 1): int
312 | {
313 | Assertion::between($monthNumber, 1, 12);
314 |
315 | $months = [
316 | 1 => 31,
317 | 2 => 31,
318 | 3 => 31,
319 | 4 => 31,
320 | 5 => 31,
321 | 6 => 31,
322 | 7 => 30,
323 | 8 => 30,
324 | 9 => 30,
325 | 10 => 30,
326 | 11 => 30,
327 | 12 => $this->isLeapYear() ? 30 : 29,
328 | ];
329 |
330 | return $months[$monthNumber];
331 | }
332 |
333 | /**
334 | * @return int
335 | */
336 | public function getHour(): int
337 | {
338 | return $this->hour;
339 | }
340 |
341 | /**
342 | * @return int
343 | */
344 | public function getMinute(): int
345 | {
346 | return $this->minute;
347 | }
348 |
349 | /**
350 | * @return int
351 | */
352 | public function getSecond(): int
353 | {
354 | return $this->second;
355 | }
356 |
357 | /**
358 | * @return \DateTimeZone|null
359 | */
360 | public function getTimezone()
361 | {
362 | return $this->timezone;
363 | }
364 |
365 | public function subYears(int $years = 1): Jalalian
366 | {
367 | Assertion::greaterOrEqualThan($years, 1);
368 |
369 | return new static(
370 | $this->getYear() - $years,
371 | $this->getMonth(),
372 | $this->getDay(),
373 | $this->getHour(),
374 | $this->getMinute(),
375 | $this->getSecond(),
376 | $this->getTimezone()
377 | );
378 | }
379 |
380 | public function addMonths(int $months = 1): Jalalian
381 | {
382 | Assertion::greaterOrEqualThan($months, 1);
383 |
384 | $years = (int)($months / 12);
385 | $months = (int)($months % 12);
386 | $date = $years > 0 ? $this->addYears($years) : clone $this;
387 |
388 | while ($months > 0) {
389 | $nextMonth = ($date->getMonth() + 1) % 12;
390 | $nextMonthDays = $date->getDaysOf($nextMonth === 0 ? 12 : $nextMonth);
391 | $nextMonthDay = $date->getDay() <= $nextMonthDays ? $date->getDay() : $nextMonthDays;
392 |
393 | $days = ($date->getMonthDays() - $date->getDay()) + $nextMonthDay;
394 |
395 | $date = $date->addDays($days);
396 | $months--;
397 | }
398 |
399 | return $date;
400 | }
401 |
402 | public function addYears(int $years = 1): Jalalian
403 | {
404 | Assertion::greaterOrEqualThan($years, 1);
405 |
406 | $year = $this->getYear() + $years;
407 | if (false === CalendarUtils::isLeapJalaliYear($year) && $this->getMonth() === 12 && $this->getDay() === $this->getDaysOf(12)) {
408 | $day = 29;
409 | } else {
410 | $day = $this->getDay();
411 | }
412 |
413 | return new static(
414 | $year,
415 | $this->getMonth(),
416 | $day,
417 | $this->getHour(),
418 | $this->getMinute(),
419 | $this->getSecond(),
420 | $this->getTimezone()
421 | );
422 | }
423 |
424 | public function subDays(int $days = 1): Jalalian
425 | {
426 | return static::fromCarbon($this->toCarbon()->subDays($days));
427 | }
428 |
429 | public function subDay(): Jalalian
430 | {
431 | return $this->subDays(1);
432 | }
433 |
434 | /**
435 | * @return Carbon
436 | */
437 | public function toCarbon(): Carbon
438 | {
439 | $gDate = CalendarUtils::toGregorian($this->getYear(), $this->getMonth(), $this->getDay());
440 | $carbon = Carbon::createFromDate($gDate[0], $gDate[1], $gDate[2], $this->getTimezone());
441 |
442 | $carbon->setTime($this->getHour(), $this->getMinute(), $this->getSecond());
443 |
444 | return $carbon;
445 | }
446 |
447 | public function addHours(int $hours = 1): Jalalian
448 | {
449 | return static::fromCarbon($this->toCarbon()->addHours($hours));
450 | }
451 |
452 | public function subHours(int $hours = 1): Jalalian
453 | {
454 | return static::fromCarbon($this->toCarbon()->subHours($hours));
455 | }
456 |
457 | public function addMinutes(int $minutes = 1): Jalalian
458 | {
459 | return static::fromCarbon($this->toCarbon()->addMinutes($minutes));
460 | }
461 |
462 | public function subMinutes(int $minutes = 1): Jalalian
463 | {
464 | return static::fromCarbon($this->toCarbon()->subMinutes($minutes));
465 | }
466 |
467 | public function addSeconds(int $secs = 1): Jalalian
468 | {
469 | return static::fromCarbon($this->toCarbon()->addSeconds($secs));
470 | }
471 |
472 | public function subSeconds(int $secs = 1): Jalalian
473 | {
474 | return static::fromCarbon($this->toCarbon()->subSeconds($secs));
475 | }
476 |
477 | public function equalsTo(Jalalian $other): bool
478 | {
479 | return $this->equalsToCarbon($other->toCarbon());
480 | }
481 |
482 | public function equalsToCarbon(Carbon $carbon): bool
483 | {
484 | return $this->toCarbon()->equalTo($carbon);
485 | }
486 |
487 | public function greaterThan(Jalalian $other): bool
488 | {
489 | return $this->greaterThanCarbon($other->toCarbon());
490 | }
491 |
492 | public function greaterThanCarbon(Carbon $carbon): bool
493 | {
494 | return $this->toCarbon()->greaterThan($carbon);
495 | }
496 |
497 | public function lessThan(Jalalian $other): bool
498 | {
499 | return $this->lessThanCarbon($other->toCarbon());
500 | }
501 |
502 | public function lessThanCarbon(Carbon $carbon): bool
503 | {
504 | return $this->toCarbon()->lessThan($carbon);
505 | }
506 |
507 | public function greaterThanOrEqualsTo(Jalalian $other): bool
508 | {
509 | return $this->greaterThanOrEqualsToCarbon($other->toCarbon());
510 | }
511 |
512 | public function greaterThanOrEqualsToCarbon(Carbon $carbon): bool
513 | {
514 | return $this->toCarbon()->greaterThanOrEqualTo($carbon);
515 | }
516 |
517 | public function lessThanOrEqualsTo(Jalalian $other): bool
518 | {
519 | return $this->lessThanOrEqualsToCarbon($other->toCarbon());
520 | }
521 |
522 | public function lessThanOrEqualsToCarbon(Carbon $carbon): bool
523 | {
524 | return $this->toCarbon()->lessThanOrEqualTo($carbon);
525 | }
526 |
527 | public function isStartOfWeek(): bool
528 | {
529 | return $this->isSaturday();
530 | }
531 |
532 | public function isSaturday(): bool
533 | {
534 | return $this->isDayOfWeek(Carbon::SATURDAY);
535 | }
536 |
537 | public function isDayOfWeek(int $day): bool
538 | {
539 | Assertion::between($day, 0, 6);
540 | return $this->toCarbon()->isDayOfWeek($day);
541 | }
542 |
543 | public function isEndOfWeek(): bool
544 | {
545 | return $this->isFriday();
546 | }
547 |
548 | public function isFriday(): bool
549 | {
550 | return $this->isDayOfWeek(Carbon::FRIDAY);
551 | }
552 |
553 | public function isToday(): bool
554 | {
555 | return $this->toCarbon()->isToday();
556 | }
557 |
558 | public function isTomorrow(): bool
559 | {
560 | return $this->toCarbon()->isTomorrow();
561 | }
562 |
563 | public function isYesterday(): bool
564 | {
565 | return $this->toCarbon()->isYesterday();
566 | }
567 |
568 | public function isFuture(): bool
569 | {
570 | return $this->toCarbon()->isFuture();
571 | }
572 |
573 | public function isPast(): bool
574 | {
575 | return $this->toCarbon()->isPast();
576 | }
577 |
578 | public function toArray(): array
579 | {
580 | return [
581 | 'year' => $this->year,
582 | 'month' => $this->month,
583 | 'day' => $this->day,
584 | 'dayOfWeek' => $this->getDayOfWeek(),
585 | 'dayOfYear' => $this->getDayOfYear(),
586 | 'hour' => $this->hour,
587 | 'minute' => $this->minute,
588 | 'second' => $this->second,
589 | 'micro' => $this->toCarbon()->micro,
590 | 'timestamp' => $this->toCarbon()->timestamp,
591 | 'formatted' => $this->toString(),
592 | 'timezone' => $this->timezone,
593 | ];
594 | }
595 |
596 | public function getDayOfWeek(): int
597 | {
598 | if ($this->isSaturday()) {
599 | return 0;
600 | }
601 |
602 | if ($this->isSunday()) {
603 | return 1;
604 | }
605 |
606 | if ($this->isMonday()) {
607 | return 2;
608 | }
609 |
610 | if ($this->isTuesday()) {
611 | return 3;
612 | }
613 |
614 | if ($this->isWednesday()) {
615 | return 4;
616 | }
617 |
618 | if ($this->isThursday()) {
619 | return 5;
620 | }
621 |
622 | return 6;
623 | }
624 |
625 | public function isSunday(): bool
626 | {
627 | return $this->isDayOfWeek(Carbon::SUNDAY);
628 | }
629 |
630 | public function isMonday(): bool
631 | {
632 | return $this->isDayOfWeek(Carbon::MONDAY);
633 | }
634 |
635 | public function isTuesday(): bool
636 | {
637 | return $this->isDayOfWeek(Carbon::TUESDAY);
638 | }
639 |
640 | public function isWednesday(): bool
641 | {
642 | return $this->isDayOfWeek(Carbon::WEDNESDAY);
643 | }
644 |
645 | public function isThursday(): bool
646 | {
647 | return $this->isDayOfWeek(Carbon::THURSDAY);
648 | }
649 |
650 | public function getDayOfYear(): int
651 | {
652 | $dayOfYear = 0;
653 | for ($m = 1; $m < $this->getMonth(); $m++) {
654 | if ($m <= 6) {
655 | $dayOfYear += 31;
656 | continue;
657 | }
658 |
659 | if ($m < 12) {
660 | $dayOfYear += 30;
661 | continue;
662 | }
663 | }
664 |
665 | return $dayOfYear + $this->getDay();
666 | }
667 |
668 | public function toString(): string
669 | {
670 | return $this->format('Y-m-d H:i:s');
671 | }
672 |
673 | public function format(string $format): string
674 | {
675 | return CalendarUtils::strftime($format, $this->toCarbon());
676 | }
677 |
678 | public function __toString(): string
679 | {
680 | return $this->toString();
681 | }
682 |
683 | public function ago(): string
684 | {
685 | $now = time();
686 | $time = $this->getTimestamp();
687 |
688 | // catch error
689 | if (!$time) {
690 | return false;
691 | }
692 |
693 | // build period and length arrays
694 | $periods = ['ثانیه', 'دقیقه', 'ساعت', 'روز', 'هفته', 'ماه', 'سال', 'قرن'];
695 | $lengths = [60, 60, 24, 7, 4.35, 12, 10];
696 |
697 | // get difference
698 | $difference = $now - $time;
699 |
700 | // set descriptor
701 | if ($difference < 0) {
702 | $difference = abs($difference); // absolute value
703 | $negative = true;
704 | }
705 |
706 | // do math
707 | for ($j = 0; $difference >= $lengths[$j] and $j < count($lengths) - 1; $j++) {
708 | $difference /= $lengths[$j];
709 | }
710 |
711 | // round difference
712 | $difference = intval(round($difference));
713 |
714 | // return
715 | return number_format($difference) . ' ' . $periods[$j] . ' ' . (isset($negative) ? '' : 'پیش');
716 | }
717 |
718 | public function getTimestamp(): int
719 | {
720 | return $this->toCarbon()->getTimestamp();
721 | }
722 |
723 | public function getNextWeek(): Jalalian
724 | {
725 | return $this->addDays(7);
726 | }
727 |
728 | public function getLastWeek(): Jalalian
729 | {
730 | return $this->subDays(7);
731 | }
732 |
733 | public function addDays(int $days = 1): Jalalian
734 | {
735 | return static::fromCarbon($this->toCarbon()->addDays($days));
736 | }
737 |
738 | public function addDay(): Jalalian
739 | {
740 | return $this->addDays(1);
741 | }
742 |
743 | public function getNextMonth(): Jalalian
744 | {
745 | return $this->addMonths(1);
746 | }
747 |
748 | public function getLastMonth(): Jalalian
749 | {
750 | return $this->subMonths(1);
751 | }
752 |
753 | public function getWeekOfMonth(): int
754 | {
755 | return floor(($this->day + 5 - $this->getDayOfWeek()) / 7) + 1;
756 | }
757 | }
758 |
--------------------------------------------------------------------------------
/src/helpers.php:
--------------------------------------------------------------------------------
1 | assertTrue(CalendarUtils::checkDate(1391, 2, 30, true));
16 | $this->assertFalse(CalendarUtils::checkDate(1395, 13, 10, true));
17 | $this->assertFalse(CalendarUtils::checkDate(1395, 12, 31, true));
18 | $this->assertFalse(CalendarUtils::checkDate(2015, 12, 31, true));
19 | }
20 |
21 | public function testToJalali()
22 | {
23 | $this->assertTrue(CalendarUtils::toJalali(2016, 5, 7) === [1395, 2, 18]);
24 | $this->assertFalse(CalendarUtils::toJalali(2015, 5, 7) === [1394, 2, 18]);
25 | }
26 |
27 | public function testToGregorian()
28 | {
29 | $this->assertTrue(CalendarUtils::toGregorian(1395, 2, 18) === [2016, 5, 7]);
30 | $this->assertFalse(CalendarUtils::toGregorian(1394, 2, 18) === [2015, 5, 7]);
31 | }
32 |
33 | public function testIsLeapJalaliYear()
34 | {
35 | $this->assertTrue(CalendarUtils::isLeapJalaliYear(1395));
36 | $this->assertFalse(CalendarUtils::isLeapJalaliYear(1394));
37 | }
38 |
39 | public function testStrftime()
40 | {
41 | $table = [
42 | [
43 | '2016-05-08',
44 | 'Y-m-d',
45 | '1395-02-19'
46 | ],
47 | [
48 | '2022-03-24',
49 | 'y-m-d',
50 | '01-01-04'
51 | ],
52 | [
53 | '2023-03-24',
54 | 'y-m-D',
55 | '02-01-ج'
56 | ],
57 | ];
58 |
59 | foreach ($table as $row) {
60 | list($dateTimeString, $format, $expected) = $row;
61 | $timestamp = strtotime($dateTimeString);
62 | $this->assertEquals($expected, CalendarUtils::strftime($format, $timestamp));
63 | }
64 | }
65 |
66 | public function testFormatMonthName()
67 | {
68 | $months = range(1, 12);
69 |
70 | // Should returns iranian months name as default
71 | foreach ($months as $month) {
72 | $date = sprintf('1401/%d/10', $month);
73 | $actual = Jalalian::fromFormat('Y/n/d', $date)->format('F');
74 | $expected = CalendarUtils::IRANIAN_MONTHS_NAME[$month - 1];
75 | $this->assertEquals($expected, $actual);
76 | }
77 |
78 | // Should returns afghan months name when set
79 | CalendarUtils::useAfghanMonthsName();
80 | foreach ($months as $month) {
81 | $date = sprintf('1401/%d/10', $month);
82 | $actual = Jalalian::fromFormat('Y/n/d', $date)->format('F');
83 | $expected = CalendarUtils::AFGHAN_MONTHS_NAME[$month - 1];
84 | $this->assertEquals($expected, $actual);
85 | }
86 |
87 | // Should returns afghan months name when set
88 | CalendarUtils::useIranianMonthsName();
89 | foreach ($months as $month) {
90 | $date = sprintf('1401/%d/10', $month);
91 | $actual = Jalalian::fromFormat('Y/n/d', $date)->format('F');
92 | $expected = CalendarUtils::IRANIAN_MONTHS_NAME[$month - 1];
93 | $this->assertEquals($expected, $actual);
94 | }
95 | }
96 |
97 | public function test_parseFromPersian()
98 | {
99 | $jalaliDate = '1393/03/27';
100 | $date = CalendarUtils::parseFromFormat('Y/m/d', $jalaliDate);
101 |
102 | $this->assertEquals(1393, $date['year']);
103 | $this->assertEquals(03, $date['month']);
104 | $this->assertEquals(27, $date['day']);
105 |
106 | $date = CalendarUtils::parseFromFormat('Y-m-d H:i:s', '1395-03-15 21:00:00');
107 | $this->assertEquals(21, $date['hour']);
108 | $this->assertEquals(0, $date['minute']);
109 | $this->assertEquals(0, $date['second']);
110 | }
111 |
112 | public function testCreateDateTimeFormFormat()
113 | {
114 | $jdate = '1394/11/25 15:00:00';
115 | $gDateTime = CalendarUtils::createDatetimeFromFormat('Y/m/d H:i:s', $jdate);
116 |
117 | $this->assertTrue($gDateTime instanceof \DateTime);
118 |
119 | $this->assertTrue('2016-02-14 15:00:00' === $gDateTime->format('Y-m-d H:i:s'));
120 | }
121 |
122 | public function testCreateCarbonFormFormat()
123 | {
124 | $jdate = '1394/11/25 15:00:00';
125 | $carbon = CalendarUtils::createCarbonFromFormat('Y/m/d H:i:s', $jdate);
126 |
127 | $this->assertTrue($carbon instanceof \Carbon\Carbon);
128 | $this->assertTrue($carbon->day === 14);
129 | $this->assertTrue('2016-02-14 15:00:00' === $carbon->format('Y-m-d H:i:s'));
130 |
131 | $jalaiDateFormatted = Jalalian::fromDateTime($carbon->toDateString())->format('Y-m-d H:i:s');
132 | $jalaiDateTimeFormatted = Jalalian::fromDateTime($carbon->toDateTimeString())->format('Y-m-d H:i:s');
133 | $this->assertFalse($jalaiDateFormatted === '1394-11-25 15:00:00');
134 | $this->assertTrue($jalaiDateTimeFormatted === '1394-11-25 15:00:00');
135 |
136 | // Test support years after 1416
137 | $carbon = CalendarUtils::createCarbonFromFormat('Y/m/d', '1417/10/11');
138 | $this->assertEquals('2039-01-01', $carbon->format('Y-m-d'));
139 | }
140 |
141 | public function testTimezone()
142 | {
143 | date_default_timezone_set('Asia/Tehran');
144 | $tehranDate = Jalalian::now();
145 | $tehranHour = $tehranDate->format('H');
146 | $tehranMin = $tehranDate->format('i');
147 |
148 | date_default_timezone_set('UTC');
149 | $utcDate = Jalalian::now();
150 | $utcHour = $utcDate->format('H');
151 | $utcMin = $utcDate->format('i');
152 |
153 | $tzOffset = $this->getTimeZoneOffset('Asia/Tehran', 'UTC');
154 |
155 | $this->assertTrue((((($utcHour * 60) + $utcMin) * 60) - ((($tehranHour * 60) + $tehranMin) * 60)) === $tzOffset);
156 | }
157 |
158 |
159 | private function getTimeZoneOffset($remote_tz, $origin_tz = null)
160 | {
161 | if ($origin_tz === null) {
162 | if (!is_string($origin_tz = date_default_timezone_get())) {
163 | return false; // A UTC timestamp was returned -- bail out!
164 | }
165 | }
166 | $origin_dtz = new DateTimeZone($origin_tz);
167 | $remote_dtz = new DateTimeZone($remote_tz);
168 | $origin_dt = new DateTime("now", $origin_dtz);
169 | $remote_dt = new DateTime("now", $remote_dtz);
170 | $offset = $origin_dtz->getOffset($origin_dt) - $remote_dtz->getOffset($remote_dt);
171 |
172 | return $offset;
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/tests/HelperTest.php:
--------------------------------------------------------------------------------
1 | assertTrue(function_exists('jdate'));
13 |
14 | $jdate = jdate('now');
15 | $this->assertTrue($jdate instanceof Jalalian);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tests/JalalianTest.php:
--------------------------------------------------------------------------------
1 | assertTrue($jDate instanceof Jalalian);
15 | $this->assertEquals($jDate->getDay(), 25);
16 | $this->assertEquals($jDate->getYear(), 1397);
17 | $this->assertEquals($jDate->getMonth(), 1);
18 |
19 | $this->assertEquals($jDate->format('Y-m-d H:i:s'), '1397-01-25 00:00:00');
20 | }
21 |
22 | public function testGetDayOfYear()
23 | {
24 | $jDate = new Jalalian(1397, 1, 25);
25 | $this->assertEquals($jDate->getDayOfYear(), 25);
26 |
27 | $jDate = new Jalalian(1397, 5, 20);
28 | $this->assertEquals($jDate->getDayOfYear(), 144);
29 |
30 | $jDate = new Jalalian(1397, 7, 3);
31 | $this->assertEquals($jDate->getDayOfYear(), 189);
32 |
33 | $jDate = new Jalalian(1397, 12, 29);
34 | $this->assertEquals($jDate->getDayOfYear(), 365);
35 |
36 | $jDate = new Jalalian(1395, 12, 30);
37 | $this->assertTrue($jDate->isLeapYear());
38 | $this->assertEquals($jDate->getDayOfYear(), 366);
39 | }
40 |
41 | public function testModifiers()
42 | {
43 | $jDate = new Jalalian(1397, 1, 18);
44 |
45 | $this->assertEquals($jDate->addYears()->getYear(), 1398);
46 | $this->assertEquals($jDate->addMonths(11)->getMonth(), 12);
47 | $this->assertEquals($jDate->addMonths(11)->addDays(20)->getMonth(), 1);
48 | $this->assertEquals($jDate->subDays(8)->getNextMonth()->getMonth(), 2);
49 |
50 | $jDate = Jalalian::fromCarbon(Carbon::createFromDate(2019, 1, 1));
51 | $this->assertEquals($jDate->addMonths(4)->getYear(), 1398);
52 |
53 | $jDate = new Jalalian(1397, 1, 31);
54 | $this->assertEquals($jDate->addMonths(1)->getDay(), 31);
55 | $this->assertEquals($jDate->addYears(3)->getDay(), 31);
56 | $this->assertEquals($jDate->addMonths(36)->toString(), $jDate->addYears(3)->toString());
57 | $this->assertEquals($jDate->subYears(10)->toString(), (new Jalalian(1387, 1, 31))->toString());
58 | $this->assertTrue($jDate->subYears(2)->subMonths(34)->equalsTo(new Jalalian(1392, 03, 31)));
59 |
60 | $jDate = (new Jalalian(1397, 6, 11))->subMonths(1);
61 | $this->assertEquals($jDate->getMonth(), 5);
62 |
63 | $this->assertEquals((new Jalalian(1397, 7, 1))->subMonths(1)->getMonth(), 6);
64 |
65 | $jDate = Jalalian::now();
66 | $month = $jDate->getMonth();
67 | if ($month > 1) {
68 | $this->assertEquals($month - 1, $jDate->subMonths()->getMonth());
69 | }
70 |
71 |
72 | $jDate = Jalalian::fromFormat('Y-m-d', '1397-12-12');
73 | $this->assertEquals('1398-01-12', $jDate->addMonths(1)->format('Y-m-d'));
74 |
75 | $jDate = Jalalian::fromFormat('Y-m-d', '1397-11-30');
76 | $this->assertEquals('1397-12-29', $jDate->addMonths(1)->format('Y-m-d'));
77 |
78 | $jDate = Jalalian::fromFormat('Y-m-d', '1397-06-30');
79 | $this->assertEquals('1397-07-30', $jDate->addMonths(1)->format('Y-m-d'));
80 |
81 | $jDate = Jalalian::fromFormat('Y-m-d', '1397-06-31');
82 | $this->assertEquals('1397-07-30', $jDate->addMonths(1)->format('Y-m-d'));
83 |
84 | $jDate = Jalalian::fromFormat('Y-m-d', '1395-12-30');
85 | $this->assertEquals('1399-12-30', $jDate->addMonths(48)->format('Y-m-d'));
86 |
87 | $jDate = Jalalian::fromFormat('Y-m-d', '1395-12-30');
88 | $this->assertEquals('1398-12-29', $jDate->addMonths(36)->format('Y-m-d'));
89 | }
90 |
91 | public function testForge()
92 | {
93 | $jDate = Jalalian::forge(strtotime('now'));
94 | $this->assertTrue($jDate instanceof Jalalian);
95 | $this->assertTrue($jDate->getTimestamp() === strtotime('now'));
96 |
97 | $jDate = Jalalian::forge(1333857600);
98 | $this->assertEquals($jDate->toString(), '1391-01-20 04:00:00');
99 |
100 | $jDate = Jalalian::forge('last monday');
101 | $this->assertTrue($jDate instanceof Jalalian);
102 |
103 | $jDate = Jalalian::forge(1552608000);
104 | $this->assertEquals('1397-12-24', $jDate->format('Y-m-d'));
105 | }
106 |
107 | public function testMaximumYearFormatting()
108 | {
109 | $jDate = Jalalian::fromFormat('Y-m-d', '1800-12-01');
110 | $this->assertEquals(1800, $jDate->getYear());
111 | $this->assertEquals($jDate->format('Y-m-d'), '1800-12-01');
112 |
113 | // issue-110
114 | $jDate = Jalalian::fromFormat('Y-m-d', '1416-12-01');
115 | $this->assertEquals(1416, $jDate->format('Y'));
116 | }
117 |
118 | public function testGetWeekOfMonth()
119 | {
120 | $jDate = new Jalalian(1400, 1, 8);
121 | $this->assertEquals($jDate->getWeekOfMonth(), 2);
122 |
123 | $jDate = new Jalalian(1400, 5, 13);
124 | $this->assertEquals($jDate->getWeekOfMonth(), 3);
125 |
126 | $jDate = new Jalalian(1390, 11, 11);
127 | $this->assertEquals($jDate->getWeekOfMonth(), 2);
128 |
129 | $jDate = new Jalalian(1395, 7, 20);
130 | $this->assertEquals($jDate->getWeekOfMonth(), 4);
131 |
132 | $jDate = new Jalalian(1401, 1, 5);
133 | $this->assertEquals($jDate->getWeekOfMonth(), 1);
134 |
135 | $jDate = new Jalalian(1390, 8, 7);
136 | $this->assertEquals($jDate->getWeekOfMonth(), 2);
137 |
138 |
139 | $jDate = new Jalalian(1390, 8, 27);
140 | $this->assertEquals($jDate->getWeekOfMonth(), 4);
141 |
142 | $jDate = new Jalalian(1390, 7, 1);
143 | $this->assertEquals($jDate->getWeekOfMonth(), 1);
144 |
145 | $jDate = new Jalalian(1390, 7, 2);
146 | $this->assertEquals($jDate->getWeekOfMonth(), 2);
147 |
148 | $jDate = new Jalalian(1390, 7, 30);
149 | $this->assertEquals($jDate->getWeekOfMonth(), 6);
150 |
151 | $jDate = new Jalalian(1390, 6, 15);
152 | $this->assertEquals($jDate->getWeekOfMonth(), 3);
153 |
154 | $jDate = new Jalalian(1390, 6, 25);
155 | $this->assertEquals($jDate->getWeekOfMonth(), 4);
156 |
157 | $jDate = new Jalalian(1390, 6, 26);
158 | $this->assertEquals($jDate->getWeekOfMonth(), 5);
159 |
160 | $jDate = new Jalalian(1401, 3, 7);
161 | $this->assertEquals($jDate->getWeekOfMonth(), 2);
162 | }
163 |
164 | public function testGetFirstDayOfWeek()
165 | {
166 | $jDate = new Jalalian(1401, 1, 23);
167 | $this->assertEquals($jDate->getFirstDayOfWeek()->format('Y-m-d'), '1401-01-20');
168 |
169 | $jDate = new Jalalian(1395, 4, 24);
170 | $this->assertEquals($jDate->getFirstDayOfWeek()->format('Y-m-d'), '1395-04-19');
171 |
172 | $jDate = new Jalalian(1398, 11, 7);
173 | $this->assertEquals($jDate->getFirstDayOfWeek()->format('Y-m-d'), '1398-11-05');
174 |
175 | $jDate = new Jalalian(1400, 8, 19);
176 | $this->assertEquals($jDate->getFirstDayOfWeek()->format('Y-m-d'), '1400-08-15');
177 | }
178 |
179 | public function testGetFirstDayOfMonth()
180 | {
181 | $jDate = new Jalalian(1401, 1, 23);
182 | $this->assertEquals($jDate->getFirstDayOfMonth()->format('Y-m-d'), '1401-01-01');
183 |
184 | $jDate = new Jalalian(1390, 5, 14);
185 | $this->assertEquals($jDate->getFirstDayOfMonth()->format('Y-m-d'), '1390-05-01');
186 |
187 | $jDate = new Jalalian(1399, 2, 29);
188 | $this->assertEquals($jDate->getFirstDayOfMonth()->format('Y-m-d'), '1399-02-01');
189 |
190 | $jDate = new Jalalian(1398, 10, 10);
191 | $this->assertEquals($jDate->getFirstDayOfMonth()->format('Y-m-d'), '1398-10-01');
192 | }
193 |
194 | public function testGetFirstDayOfYear()
195 | {
196 | $jDate = new Jalalian(1401, 6, 11);
197 | $this->assertEquals($jDate->getFirstDayOfYear()->format('Y-m-d'), '1401-01-01');
198 |
199 | $jDate = new Jalalian(1399, 11, 28);
200 | $this->assertEquals($jDate->getFirstDayOfYear()->format('Y-m-d'), '1399-01-01');
201 |
202 | $jDate = new Jalalian(1394, 1, 12);
203 | $this->assertEquals($jDate->getFirstDayOfYear()->format('Y-m-d'), '1394-01-01');
204 |
205 | $jDate = new Jalalian(1393, 9, 5);
206 | $this->assertEquals($jDate->getFirstDayOfYear()->format('Y-m-d'), '1393-01-01');
207 | }
208 |
209 | public function testAddDay()
210 | {
211 | $jDate = new Jalalian(1401, 6, 31);
212 | $this->assertEquals($jDate->addDay()->format('Y-m-d'), '1401-07-01');
213 | }
214 |
215 | public function testSubDay()
216 | {
217 | $jDate = new Jalalian(1401, 6, 1);
218 | $this->assertEquals($jDate->subDay()->format('Y-m-d'), '1401-05-31');
219 | }
220 |
221 | public function testGetLastWeek()
222 | {
223 | $jDate = new Jalalian(1401, 6, 8);
224 | $this->assertEquals($jDate->getLastWeek()->format('Y-m-d'), '1401-06-01');
225 | }
226 |
227 | public function testGetLastMonth()
228 | {
229 | $jDate = new Jalalian(1401, 6, 8);
230 | $this->assertEquals($jDate->getLastMonth()->format('Y-m-d'), '1401-05-08');
231 | }
232 |
233 | public function testGetFirstDayOfQuarter()
234 | {
235 | $jDate = new Jalalian(1402, 1, 25);
236 | $this->assertEquals('1402-01-01', $jDate->getFirstDayOfQuarter()->format('Y-m-d'));
237 |
238 | $jDate = new Jalalian(1402, 2, 25);
239 | $this->assertEquals('1402-01-01', $jDate->getFirstDayOfQuarter()->format('Y-m-d'));
240 |
241 | $jDate = new Jalalian(1402, 3, 25);
242 | $this->assertEquals('1402-01-01', $jDate->getFirstDayOfQuarter()->format('Y-m-d'));
243 |
244 | $jDate = new Jalalian(1402, 4, 25);
245 | $this->assertEquals('1402-04-01', $jDate->getFirstDayOfQuarter()->format('Y-m-d'));
246 |
247 | $jDate = new Jalalian(1402, 5, 25);
248 | $this->assertEquals('1402-04-01', $jDate->getFirstDayOfQuarter()->format('Y-m-d'));
249 |
250 | $jDate = new Jalalian(1402, 6, 25);
251 | $this->assertEquals('1402-04-01', $jDate->getFirstDayOfQuarter()->format('Y-m-d'));
252 |
253 | $jDate = new Jalalian(1402, 7, 25);
254 | $this->assertEquals('1402-07-01', $jDate->getFirstDayOfQuarter()->format('Y-m-d'));
255 |
256 | $jDate = new Jalalian(1402, 8, 25);
257 | $this->assertEquals('1402-07-01', $jDate->getFirstDayOfQuarter()->format('Y-m-d'));
258 |
259 | $jDate = new Jalalian(1402, 9, 25);
260 | $this->assertEquals('1402-07-01', $jDate->getFirstDayOfQuarter()->format('Y-m-d'));
261 |
262 | $jDate = new Jalalian(1402, 10, 25);
263 | $this->assertEquals('1402-10-01', $jDate->getFirstDayOfQuarter()->format('Y-m-d'));
264 |
265 | $jDate = new Jalalian(1402, 11, 19);
266 | $this->assertEquals('1402-10-01', $jDate->getFirstDayOfQuarter()->format('Y-m-d'));
267 |
268 | $jDate = new Jalalian(1402, 12, 25);
269 | $this->assertEquals('1402-10-01', $jDate->getFirstDayOfQuarter()->format('Y-m-d'));
270 |
271 | }
272 |
273 | public function testGetEndDayOfWeek()
274 | {
275 | /*
276 | * --------------------------------
277 | * Day 1402 (March 2024)
278 | * --------------------------------
279 | * Sat Sun Mon Tue Wed Thu Fri
280 | * --------------------------------
281 | * 1
282 | * 2 3 4 5 6 7 8
283 | * 9 10 11 12 13 14 15
284 | * 16 17 18 19 20 21 22
285 | * 23 24 25 26 27 28 29
286 | * 30
287 | * -------------------------------
288 | */
289 |
290 | $jDate = new Jalalian(1402, 10, 25);
291 | $this->assertEquals('1402-10-29', $jDate->getEndDayOfWeek()->format('Y-m-d'));
292 |
293 | $jDate = new Jalalian(1402, 10, 29);
294 | $this->assertEquals('1402-10-29', $jDate->getEndDayOfWeek()->format('Y-m-d'));
295 |
296 | $jDate = new Jalalian(1402, 10, 23);
297 | $this->assertEquals('1402-10-29', $jDate->getEndDayOfWeek()->format('Y-m-d'));
298 |
299 | $jDate = new Jalalian(1402, 12, 29);
300 | $this->assertEquals('1403-01-03', $jDate->getEndDayOfWeek()->format('Y-m-d'));
301 |
302 | }
303 |
304 | public function testGetEndDayOfMonth()
305 | {
306 | /*
307 | * --------------------------------
308 | * Day 1402 (March 2024)
309 | * --------------------------------
310 | * Sat Sun Mon Tue Wed Thu Fri
311 | * --------------------------------
312 | * 1
313 | * 2 3 4 5 6 7 8
314 | * 9 10 11 12 13 14 15
315 | * 16 17 18 19 20 21 22
316 | * 23 24 25 26 27 28 29
317 | * 30
318 | * -------------------------------
319 | */
320 |
321 | $jDate = new Jalalian(1402, 10, 25);
322 | $this->assertEquals('1402-10-30', $jDate->getEndDayOfMonth()->format('Y-m-d'));
323 |
324 | $jDate = new Jalalian(1402, 04, 12);
325 | $this->assertEquals('1402-04-31', $jDate->getEndDayOfMonth()->format('Y-m-d'));
326 |
327 | $jDate = new Jalalian(1402, 12, 25);
328 | $this->assertEquals('1402-12-29', $jDate->getEndDayOfMonth()->format('Y-m-d'));
329 | }
330 |
331 | public function testGetEndDayOfYear()
332 | {
333 | $jDate = new Jalalian(1402, 10, 25);
334 | $this->assertEquals('1402-12-29', $jDate->getEndDayOfYear()->format('Y-m-d'));
335 |
336 | // LeapYear
337 | $jDate = new Jalalian(1403, 10, 25);
338 | $this->assertEquals('1403-12-30', $jDate->getEndDayOfYear()->format('Y-m-d'));
339 | }
340 |
341 | public function testGetEndDayOfQuarter()
342 | {
343 | $jDate = new Jalalian(1402, 10, 25);
344 | $this->assertEquals('1402-12-29', $jDate->getEndDayOfQuarter()->format('Y-m-d'));
345 |
346 | $jDate = new Jalalian(1402, 2, 25);
347 | $this->assertEquals('1402-03-31', $jDate->getEndDayOfQuarter()->format('Y-m-d'));
348 |
349 |
350 | $jDate = new Jalalian(1402, 6, 01);
351 | $this->assertEquals('1402-06-31', $jDate->getEndDayOfQuarter()->format('Y-m-d'));
352 |
353 |
354 | $jDate = new Jalalian(1402, 9, 01);
355 | $this->assertEquals('1402-09-30', $jDate->getEndDayOfQuarter()->format('Y-m-d'));
356 | }
357 |
358 | public function testGetQuarter()
359 | {
360 | $jDate = new Jalalian(1402, 10, 25);
361 | $this->assertEquals(4, $jDate->getQuarter());
362 |
363 | $jDate = new Jalalian(1402, 1, 01);
364 | $this->assertEquals(1, $jDate->getQuarter());
365 |
366 |
367 | $jDate = new Jalalian(1402, 05, 24);
368 | $this->assertEquals(2, $jDate->getQuarter());
369 |
370 | $jDate = new Jalalian(1402, 7, 23);
371 | $this->assertEquals(3, $jDate->getQuarter());
372 | }
373 | }
374 |
--------------------------------------------------------------------------------