| Conditions | 96 |
| Paths | 589 |
| Total Lines | 317 |
| Code Lines | 210 |
| Lines | 79 |
| Ratio | 24.92 % |
| Changes | 0 | ||
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
| 1 | <?php |
||
| 636 | public function parse($dateString, $locale = null, $strict = false) |
||
| 637 | { |
||
| 638 | View Code Duplication | if ($locale instanceof Locale) { |
|
| 639 | $tm = $locale->getContext()->getTranslationManager(); |
||
| 640 | } elseif ($this->context) { |
||
| 641 | $tm = $this->context->getTranslationManager(); |
||
| 642 | if ($locale) { |
||
| 643 | $locale = $tm->getLocale($locale); |
||
| 644 | } else { |
||
| 645 | $locale = $tm->getCurrentLocale(); |
||
| 646 | } |
||
| 647 | } else { |
||
| 648 | throw new \InvalidArgumentException('This DateFormat has not been initialize()d. To be able to pass a string as locale you need to call initialize() or create this DateFormat using TranslationManager::createDateFormat()'); |
||
| 649 | } |
||
| 650 | |||
| 651 | $cal = $tm->createCalendar($locale); |
||
| 652 | // we need to extract the era from the current date and set that |
||
| 653 | // era after reinitialising the calendar because if this is not |
||
| 654 | // done all dates would be BC instead of AD |
||
| 655 | $era = $cal->get(DateDefinitions::ERA); |
||
| 656 | $cal->clear(); |
||
| 657 | $cal->set(DateDefinitions::ERA, $era); |
||
| 658 | |||
| 659 | if ($strict) { |
||
| 660 | $cal->setLenient(false); |
||
| 661 | } |
||
| 662 | |||
| 663 | // TODO: let user chose calendar type when more calendars exist |
||
| 664 | $calendarType = Calendar::GREGORIAN; |
||
| 665 | $datePos = 0; |
||
| 666 | |||
| 667 | $unprocessedTokens = array(); |
||
| 668 | |||
| 669 | $tlCount = count($this->tokenList); |
||
| 670 | for ($i = 0; $i < $tlCount; ++$i) { |
||
| 671 | View Code Duplication | if ($datePos >= strlen($dateString)) { |
|
| 672 | if ($strict) { |
||
| 673 | throw new AgaviException('Input string "' . $dateString . '" is to short'); |
||
| 674 | } |
||
| 675 | break; |
||
| 676 | } |
||
| 677 | |||
| 678 | $token = $this->tokenList[$i]; |
||
| 679 | $type = $this->getTokenType($token); |
||
| 680 | // this and the next token are numbers |
||
| 681 | if ($type == 'number' && $i + 1 < $tlCount && $this->getTokenType($this->tokenList[$i + 1]) == 'number') { |
||
| 682 | $unprocessedTokens = array($token); |
||
| 683 | |||
| 684 | // store all abutting numerical tokens for later processing |
||
| 685 | do { |
||
| 686 | ++$i; |
||
| 687 | $unprocessedTokens[] = $this->tokenList[$i]; |
||
| 688 | } while ($i + 1 < $tlCount && $this->getTokenType($this->tokenList[$i + 1]) == 'number'); |
||
| 689 | |||
| 690 | // retrieve the amount of number characters at our current parse position |
||
| 691 | $numberLen = strspn($dateString, '0123456789', $datePos); |
||
| 692 | |||
| 693 | // calculate the length the numbers should have from the tokens |
||
| 694 | $tokenReqLen = 0; |
||
| 695 | foreach ($unprocessedTokens as $tk) { |
||
| 696 | $tokenReqLen += $tk[1]; |
||
| 697 | } |
||
| 698 | |||
| 699 | // we mimic the ICU behaviour here which only decrements the count of |
||
| 700 | // the first token (from ICU: Take the pattern "HHmmss" as an example. |
||
| 701 | // We will try to parse 2/2/2 characters of the input text, then if that |
||
| 702 | // fails, 1/2/2. We only adjust the width of the leftmost field; the |
||
| 703 | // others remain fixed.) |
||
| 704 | // I'm not sure why they are doing it that way since i think that |
||
| 705 | // decrementing the next tokens when there still aren't enough numbers |
||
| 706 | // wouldn't do any harm (ok, maybe the algorithm is a little harder to |
||
| 707 | // implement ;) - Dominik |
||
| 708 | |||
| 709 | $diff = $tokenReqLen - $numberLen; |
||
| 710 | if ($diff > 0) { |
||
| 711 | // use a reference here so we can simply store the new token length into $tLen |
||
| 712 | $tLen =& $unprocessedTokens[0][1]; |
||
| 713 | if ($diff >= $tLen - 1) { |
||
| 714 | $tLen -= $diff; |
||
| 715 | } else { |
||
| 716 | throw new AgaviException('Not enough digits given in "' . $dateString . '" at pos ' . $datePos . ' (expected ' . $tokenReqLen . ' digits)'); |
||
| 717 | } |
||
| 718 | } |
||
| 719 | |||
| 720 | foreach ($unprocessedTokens as $token) { |
||
| 721 | $dateField = $this->getDateFieldFromTokenType($token[0]); |
||
| 722 | if ($dateField === null) { |
||
| 723 | throw new AgaviException('Token type ' . $token[0] . ' claims to be numerical but has no date field'); |
||
| 724 | } |
||
| 725 | |||
| 726 | $number = (int) substr($dateString, $datePos, $token[1]); |
||
| 727 | |||
| 728 | $datePos += $token[1]; |
||
| 729 | if ($dateField == DateDefinitions::HOUR_OF_DAY && $token[0] == DateFormat::T_HOUR_1_24 && $number == 24) { |
||
| 730 | $number = 0; |
||
| 731 | View Code Duplication | } elseif ($dateField == DateDefinitions::HOUR && $token[0] == DateFormat::T_HOUR_1_12 && $number == 12) { |
|
| 732 | $number = 0; |
||
| 733 | } elseif ($dateField == DateDefinitions::MONTH && $number > 0) { |
||
| 734 | $number -= 1; |
||
| 735 | } |
||
| 736 | |||
| 737 | View Code Duplication | if (self::T_QUARTER == $token[0] || self::T_SA_QUARTER == $token[0]) { |
|
| 738 | // only set the quarter if the date hasn't been set on the calendar object |
||
| 739 | if (!$cal->_isSet(DateDefinitions::MONTH)) { |
||
| 740 | $cal->set($dateField, ($number - 1) * 3); |
||
| 741 | } |
||
| 742 | } else { |
||
| 743 | $cal->set($dateField, $number); |
||
| 744 | } |
||
| 745 | } |
||
| 746 | } elseif ($type == 'number') { |
||
| 747 | $numberLen = strspn($dateString, '0123456789', $datePos); |
||
| 748 | $dateField = $this->getDateFieldFromTokenType($token[0]); |
||
| 749 | if ($dateField === null) { |
||
| 750 | throw new AgaviException('Token type ' . $token[0] . ' claims to be numerical but has no date field'); |
||
| 751 | } |
||
| 752 | if ($strict && $numberLen > 2 && $numberLen > $token[1]) { |
||
| 753 | $numberLen = $token[1]; |
||
| 754 | } |
||
| 755 | if ($dateField == DateDefinitions::MILLISECOND && $numberLen > 3) { |
||
| 756 | // we only store in millisecond precision so only use the first 3 digits of the fractional second |
||
| 757 | $number = (int) substr($dateString, $datePos, 3); |
||
| 758 | } else { |
||
| 759 | $number = (int) substr($dateString, $datePos, $numberLen); |
||
| 760 | } |
||
| 761 | |||
| 762 | $datePos += $numberLen; |
||
| 763 | // since the month is 0-indexed in the calendar and 1-indexed in the |
||
| 764 | // cldr we need to subtract 1 from the month |
||
| 765 | if ($dateField == DateDefinitions::MONTH && $number > 0) { |
||
| 766 | $number -= 1; |
||
| 767 | View Code Duplication | } elseif ($dateField == DateDefinitions::HOUR_OF_DAY && $token[0] == DateFormat::T_HOUR_1_24 && $number == 24) { |
|
| 768 | $number = 0; |
||
| 769 | } elseif ($dateField == DateDefinitions::HOUR && $token[0] == DateFormat::T_HOUR_1_12 && $number == 12) { |
||
| 770 | $number = 0; |
||
| 771 | } elseif ($token[0] == self::T_YEAR && $token[1] == 2 && $numberLen <= 2) { |
||
| 772 | if ($number >= 40) { |
||
| 773 | $number += 1900; |
||
| 774 | } else { |
||
| 775 | $number += 2000; |
||
| 776 | } |
||
| 777 | } |
||
| 778 | |||
| 779 | View Code Duplication | if (self::T_QUARTER == $token[0] || self::T_SA_QUARTER == $token[0]) { |
|
| 780 | // only set the quarter if the date hasn't been set on the calendar object |
||
| 781 | if (!$cal->_isSet(DateDefinitions::MONTH)) { |
||
| 782 | $cal->set($dateField, ($number - 1) * 3); |
||
| 783 | } |
||
| 784 | } else { |
||
| 785 | $cal->set($dateField, $number); |
||
| 786 | } |
||
| 787 | } else { // $type == 'text' |
||
| 788 | $count = $token[1]; |
||
| 789 | switch ($token[0]) { |
||
| 790 | case self::T_TEXT: |
||
| 791 | if (substr_compare($dateString, $token[1], $datePos, strlen($token[1])) == 0) { |
||
| 792 | $datePos += strlen($token[1]); |
||
| 793 | } elseif ($i + 1 == $tlCount && !$strict) { |
||
| 794 | // when the last text token didn't match we don't do anything in non strict mode |
||
| 795 | } else { |
||
| 796 | throw new AgaviException('Unknown character in "' . $dateString . '" at pos ' . $datePos . ' (expected: "' . $token[1] . '", got: "' . substr($dateString, $datePos, strlen($token[1])) . '")'); |
||
| 797 | } |
||
| 798 | break; |
||
| 799 | |||
| 800 | case self::T_ERA: |
||
| 801 | case self::T_DAY_OF_WEEK: |
||
| 802 | case self::T_LOCAL_DAY_OF_WEEK: |
||
| 803 | case self::T_SA_LOCAL_DAY_OF_WEEK: |
||
| 804 | $funcPrefix = ''; |
||
| 805 | |||
| 806 | switch ($token[0]) { |
||
| 807 | case self::T_ERA: |
||
| 808 | $funcPrefix = 'getCalendarEras'; |
||
| 809 | $dataField = DateDefinitions::ERA; |
||
| 810 | break; |
||
| 811 | case self::T_DAY_OF_WEEK: |
||
| 812 | $funcPrefix = 'getCalendarDays'; |
||
| 813 | $dataField = DateDefinitions::DAY_OF_WEEK; |
||
| 814 | break; |
||
| 815 | |||
| 816 | case self::T_LOCAL_DAY_OF_WEEK: |
||
| 817 | case self::T_SA_LOCAL_DAY_OF_WEEK: |
||
| 818 | $funcPrefix = 'getCalendarDays'; |
||
| 819 | $dataField = DateDefinitions::DOW_LOCAL; |
||
| 820 | break; |
||
| 821 | } |
||
| 822 | |||
| 823 | if ($count == 4) { |
||
| 824 | $items = $locale->{$funcPrefix . 'Wide'}($calendarType); |
||
| 825 | } elseif ($count == 5) { |
||
| 826 | $items = $locale->{$funcPrefix . 'Narrow'}($calendarType); |
||
| 827 | } else { |
||
| 828 | $items = $locale->{$funcPrefix . 'Abbreviated'}($calendarType); |
||
| 829 | } |
||
| 830 | $item = null; |
||
| 831 | if ($this->matchStringWithFallbacks($dateString, $items, $datePos, $item)) { |
||
| 832 | $cal->set($dataField, $item); |
||
| 833 | } else { |
||
| 834 | throw new AgaviException('Unknown character in "' . $dateString . '" at pos ' . $datePos . ' (expected one of: ' . implode(', ', $items) . ')'); |
||
| 835 | } |
||
| 836 | break; |
||
| 837 | |||
| 838 | case self::T_AM_PM: |
||
| 839 | $items = array( |
||
| 840 | 0 => $locale->getCalendarAm($calendarType), |
||
| 841 | 1 => $locale->getCalendarPm($calendarType), |
||
| 842 | ); |
||
| 843 | |||
| 844 | $item = null; |
||
| 845 | if ($this->matchStringWithFallbacks($dateString, $items, $datePos, $item)) { |
||
| 846 | $cal->set(DateDefinitions::AM_PM, $item); |
||
| 847 | } else { |
||
| 848 | throw new AgaviException('Unknown character in "' . $dateString . '" at pos ' . $datePos . ' (expected one of: ' . implode(', ', $items) . ')'); |
||
| 849 | } |
||
| 850 | break; |
||
| 851 | |||
| 852 | case self::T_TIMEZONE: |
||
| 853 | case self::T_TIMEZONE_GENERIC: |
||
| 854 | case self::T_TIMEZONE_RFC: |
||
| 855 | $remainder = substr($dateString, $datePos); |
||
| 856 | if (preg_match('#^(GMT)?(\+|-)?(\d{1,2}:\d{1,2}|\d{1,2}\d{1,2})#i', $remainder, $match)) { |
||
| 857 | $datePos += strlen($match[0]); |
||
| 858 | if (strtolower($match[1]) != 'gmt') { |
||
| 859 | $remainder = 'GMT' . $match[0]; |
||
| 860 | } else { |
||
| 861 | $remainder = $match[0]; |
||
| 862 | } |
||
| 863 | $tz = $tm->createTimeZone($remainder); |
||
| 864 | } else { |
||
| 865 | if ($i + 1 != $tlCount && preg_match('#^([a-z/]+)#i', $remainder, $match)) { |
||
| 866 | $remainder = $match[0]; |
||
| 867 | } |
||
| 868 | if (!($tz = $tm->createTimeZone($remainder))) { |
||
| 869 | // try to match a localized timezone string |
||
| 870 | $z = 0; |
||
| 871 | $localizedTzMap = array(); |
||
| 872 | $idToTzMap = array(); |
||
| 873 | $tzNames = $locale->getTimeZoneNames(); |
||
| 874 | foreach ($tzNames as $tzId => $tz) { |
||
| 875 | foreach ($tz as $type => $names) { |
||
| 876 | if (is_array($names)) { |
||
| 877 | foreach ($names as $name) { |
||
| 878 | $localizedTzMap[$z] = $name; |
||
| 879 | $idToTzMap[$z] = $tzId; |
||
| 880 | ++$z; |
||
| 881 | } |
||
| 882 | } |
||
| 883 | } |
||
| 884 | } |
||
| 885 | |||
| 886 | $id = 0; |
||
| 887 | if ($this->matchStringWithFallbacks($dateString, $localizedTzMap, $datePos, $id)) { |
||
| 888 | $tz = $tm->createTimeZone($idToTzMap[$id]); |
||
| 889 | } else { |
||
| 890 | throw new AgaviException('Unknown character in "' . $dateString . '" at pos ' . $datePos . ' (expected one of: ' . implode(', ', $localizedTzMap) . ')'); |
||
| 891 | } |
||
| 892 | } else { |
||
| 893 | $datePos += strlen($remainder); |
||
| 894 | } |
||
| 895 | } |
||
| 896 | |||
| 897 | $cal->setTimeZone($tz); |
||
| 898 | break; |
||
| 899 | |||
| 900 | case self::T_MONTH: |
||
| 901 | View Code Duplication | case self::T_SA_MONTH: |
|
| 902 | if ($count == 3) { |
||
| 903 | $months = $locale->getCalendarMonthsAbbreviated($calendarType); |
||
| 904 | } elseif ($count == 4) { |
||
| 905 | $months = $locale->getCalendarMonthsWide($calendarType); |
||
| 906 | } elseif ($count == 5) { |
||
| 907 | $months = $locale->getCalendarMonthsNarrow($calendarType); |
||
| 908 | } |
||
| 909 | $month = null; |
||
| 910 | if ($this->matchStringWithFallbacks($dateString, $months, $datePos, $month)) { |
||
| 911 | $cal->set(DateDefinitions::MONTH, $month - 1); |
||
| 912 | } else { |
||
| 913 | throw new AgaviException('Unknown character in "' . $dateString . '" at pos ' . $datePos . ' (expected one of: ' . implode(', ', $months) . ')'); |
||
| 914 | } |
||
| 915 | |||
| 916 | break; |
||
| 917 | |||
| 918 | case self::T_QUARTER: |
||
| 919 | View Code Duplication | case self::T_SA_QUARTER: |
|
| 920 | if ($count == 3) { |
||
| 921 | $quarters = $locale->getCalendarQuartersAbbreviated($calendarType); |
||
| 922 | } elseif ($count == 4) { |
||
| 923 | $quarters = $locale->getCalendarQuartersWide($calendarType); |
||
| 924 | } elseif ($count == 5) { |
||
| 925 | $quarters = $locale->getCalendarQuartersNarrow($calendarType); |
||
| 926 | } |
||
| 927 | $quarter = null; |
||
| 928 | if ($this->matchStringWithFallbacks($dateString, $quarters, $datePos, $quarter)) { |
||
| 929 | if (!$cal->_isSet(DateDefinitions::MONTH)) { |
||
| 930 | $cal->set(DateDefinitions::MONTH, ($quarter - 1) * 3); |
||
| 931 | } |
||
| 932 | } else { |
||
| 933 | throw new AgaviException('Unknown character in "' . $dateString . '" at pos ' . $datePos . ' (expected one of: ' . implode(', ', $quarters) . ')'); |
||
| 934 | } |
||
| 935 | break; |
||
| 936 | } |
||
| 937 | } |
||
| 938 | } |
||
| 939 | |||
| 940 | // make sure the calendar has it's time calculated, |
||
| 941 | // so there aren't any strange effects when setting a new timezone |
||
| 942 | // or a new date |
||
| 943 | $cal->getTime(); |
||
| 944 | View Code Duplication | if ($strict) { |
|
| 945 | // calculate the time to get errors for invalid dates |
||
| 946 | if ($datePos < strlen($dateString)) { |
||
| 947 | throw new AgaviException('Input string "' . $dateString . '" has characters after the date'); |
||
| 948 | } |
||
| 949 | } |
||
| 950 | |||
| 951 | return $cal; |
||
| 952 | } |
||
| 953 | |||
| 1100 |
In PHP, under loose comparison (like
==, or!=, orswitchconditions), values of different types might be equal.For
stringvalues, the empty string''is a special case, in particular the following results might be unexpected: