├── .gitignore ├── src ├── Validator │ ├── ValidatorInterface.php │ ├── ValidIntegerValidator.php │ ├── LeapYearValidator.php │ └── DateValidator.php ├── Holiday │ ├── HolidayInterface.php │ ├── Christmas.php │ └── Easter.php ├── Exception │ └── InvalidDateException.php ├── Converter │ ├── Converter.php │ ├── ToJdnConverter.php │ └── FromJdnConverter.php ├── Operations │ ├── Initiator.php │ ├── Processor.php │ └── Formatter.php ├── DateTimeFactory.php ├── DateTime.php └── Constants.php ├── phpunit.xml.dist ├── tests ├── Exception │ └── InvalidDateExceptionTest.php ├── Validator │ ├── LeapYearValidatorTest.php │ └── DateValidatorTest.php ├── Converter │ ├── FromJdnConverterTest.php │ ├── ToJdnConverterTest.php │ └── ConverterTest.php ├── Holiday │ ├── ChristmasTest.php │ └── EasterTest.php └── DateTimeTest.php ├── composer.json ├── .github └── workflows │ └── tests.yml ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | .phpunit.result.cache 3 | /vendor 4 | -------------------------------------------------------------------------------- /src/Validator/ValidatorInterface.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | tests 11 | 12 | 13 | 14 | 15 | 16 | src 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/Exception/InvalidDateExceptionTest.php: -------------------------------------------------------------------------------- 1 | expectExceptionMessage('Invalid date was given'); 13 | $this->expectException(InvalidDateException::class); 14 | 15 | throw new InvalidDateException(); 16 | } 17 | 18 | public function test_by_catching_the_exception() 19 | { 20 | try { 21 | throw new InvalidDateException(); 22 | } catch (\Exception $e) { 23 | $this->assertTrue($e instanceof InvalidDateException); 24 | $this->assertEquals($e->getMessage(), 'Invalid date was given'); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Validator/LeapYearValidator.php: -------------------------------------------------------------------------------- 1 | year = $year; 23 | } 24 | 25 | /** 26 | * @return bool true if valid 27 | */ 28 | public function isValid() 29 | { 30 | return 31 | $this->isValidInteger($this->year) && 32 | $this->isValidLeapYear($this->year); 33 | } 34 | 35 | /** 36 | * @param $year int 37 | * 38 | * @return bool true if valid 39 | */ 40 | protected function isValidLeapYear($year) 41 | { 42 | return ($year + 1) % 4 === 0; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Validator/LeapYearValidatorTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($expected, $validator->isValid()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Converter/FromJdnConverterTest.php: -------------------------------------------------------------------------------- 1 | expectException(TypeError::class); 18 | 19 | new FromJdnConverter($jdn); 20 | } 21 | 22 | /** 23 | * @dataProvider validJDNDataProvider 24 | * 25 | * @param $jdn 26 | * @param $year 27 | * @param $month 28 | * @param $day 29 | */ 30 | public function test_conversion_from_jdn($jdn, $year, $month, $day) 31 | { 32 | $converter = new FromJdnConverter($jdn); 33 | 34 | $this->assertEquals($day, $converter->getDay()); 35 | $this->assertEquals($month, $converter->getMonth()); 36 | $this->assertEquals($year, $converter->getYear()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "andegna/calender", 3 | "type": "library", 4 | "description": "If you ever want to convert Ethiopian Calender to any other calender system (like the Gregorian Calender) this is the right package for you. And by the way it also support Amharic date formatting and much much more.", 5 | "keywords": ["ethiopian", "date", "calender", "JDN"], 6 | "license": "MIT", 7 | "support": { 8 | "issues": "https://github.com/andegna/calender/issues", 9 | "source": "https://github.com/andegna/calender" 10 | }, 11 | "authors": [ 12 | { 13 | "name": "Sam As End", 14 | "email": "4sam21@gmail.com" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=7.3", 19 | "geezify/geezify-php": "^1.2" 20 | }, 21 | "require-dev": { 22 | "phpunit/phpunit": "^9.5" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "Andegna\\": "src/", 27 | "Andegna\\PHPUnit\\": "tests/" 28 | } 29 | }, 30 | "scripts": { 31 | "test": "phpunit --coverage-clover=coverage.clover" 32 | }, 33 | "extra": { 34 | "branch-alias": { 35 | "develop": "2.4-dev" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Validate composer.json and composer.lock 18 | run: composer validate --strict 19 | 20 | - name: Cache Composer packages 21 | id: composer-cache 22 | uses: actions/cache@v2 23 | with: 24 | path: vendor 25 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 26 | restore-keys: | 27 | ${{ runner.os }}-php- 28 | 29 | - name: Install dependencies 30 | run: composer install --prefer-dist --no-progress 31 | 32 | - name: Run test suite 33 | env: 34 | XDEBUG_MODE: coverage 35 | run: composer run-script test 36 | 37 | - name: Upload Scrutinizer coverage 38 | uses: sudo-bot/action-scrutinizer@latest 39 | with: 40 | cli-args: "--format=php-clover coverage.clover" 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Samson Endale 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Holiday/Christmas.php: -------------------------------------------------------------------------------- 1 | dateTimeZone = $dateTimeZone; 27 | } 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function get(int $year) 33 | { 34 | $day = 29; 35 | 36 | if ($this->wasAfterALeapYear($year)) { 37 | $day = 28; 38 | } 39 | 40 | return DateTimeFactory::of($year, self::TAHISAS, $day, 0, 0, 0, $this->dateTimeZone); 41 | } 42 | 43 | private function wasAfterALeapYear(int $year): bool 44 | { 45 | $previous = $year - 1; 46 | 47 | return (new LeapYearValidator($previous))->isValid(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Converter/Converter.php: -------------------------------------------------------------------------------- 1 | day; 37 | } 38 | 39 | /** 40 | * Get the month. 41 | * 42 | * @return int 43 | */ 44 | public function getMonth(): int 45 | { 46 | return $this->month; 47 | } 48 | 49 | /** 50 | * Get the year. 51 | * 52 | * @return int 53 | */ 54 | public function getYear(): int 55 | { 56 | return $this->year; 57 | } 58 | 59 | /** 60 | * Get the JDN. 61 | * 62 | * @return int 63 | */ 64 | public function getJdn(): int 65 | { 66 | return $this->jdn; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tests/Holiday/ChristmasTest.php: -------------------------------------------------------------------------------- 1 | christmas = new Christmas(new \DateTimeZone('Africa/Addis_Ababa')); 20 | } 21 | 22 | /** 23 | * Eastern Orthodox churches christmas dates. 24 | * 25 | * @return array 26 | */ 27 | public function christmasDatesDataProvider() 28 | { 29 | return [ 30 | [2010, 29], 31 | [2011, 29], 32 | [2012, 28], 33 | [2013, 29], 34 | [2014, 29], 35 | [2015, 29], 36 | [2016, 28], 37 | [2017, 29], 38 | ]; 39 | } 40 | 41 | /** 42 | * @dataProvider christmasDatesDataProvider 43 | * 44 | * @param $year 45 | * @param $day 46 | */ 47 | public function test_christmas_date($year, $day) 48 | { 49 | $ethiopian = $this->christmas->get($year); 50 | 51 | $this->assertEquals($year, $ethiopian->getYear()); 52 | $this->assertEquals(4, $ethiopian->getMonth()); 53 | $this->assertEquals($day, $ethiopian->getDay()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/Converter/ToJdnConverterTest.php: -------------------------------------------------------------------------------- 1 | expectException(TypeError::class); 22 | 23 | new ToJdnConverter($day, $month, $year); 24 | } 25 | 26 | /** 27 | * @dataProvider illegalDateDataProvider 28 | * 29 | * @param $month 30 | * @param $day 31 | * @param $year 32 | * @param $ignore 33 | */ 34 | public function test_me_too($month, $day, $year, $ignore) 35 | { 36 | $this->expectException(InvalidDateException::class); 37 | 38 | new ToJdnConverter($day, $month, $year); 39 | } 40 | 41 | /** 42 | * @dataProvider validJDNDataProvider 43 | * 44 | * @param $jdn 45 | * @param $year 46 | * @param $month 47 | * @param $day 48 | */ 49 | public function test_conversion_to_jdn($jdn, $year, $month, $day) 50 | { 51 | $converter = new ToJdnConverter($day, $month, $year); 52 | 53 | $this->assertEquals($jdn, $converter->getJdn()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Holiday/Easter.php: -------------------------------------------------------------------------------- 1 | dateTimeZone = $dateTimeZone; 25 | } 26 | 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | public function get(int $year) 31 | { 32 | // convert the Ethiopian year to a Julian year 33 | // ሚያዝያ 1 is just a random day after the Gregorian new year 34 | $julian_year = (int) DateTimeFactory::of($year, 8, 1) 35 | ->toGregorian()->format('Y'); 36 | 37 | // get the number of days from vernal equinox to the Easter in the given Julian year 38 | $days_after = easter_days($julian_year, CAL_EASTER_ALWAYS_JULIAN); 39 | 40 | // get the JDN of vernal equinox (March 21) in the given Julian year 41 | $jdn = juliantojd(3, 21, $julian_year); 42 | 43 | // add the number of days to Easter 44 | $jdn += $days_after; 45 | 46 | // create a Ethiopian Date to JDN converter 47 | $con = new FromJdnConverter($jdn); 48 | 49 | // create an Ethiopian date from the converter 50 | return DateTimeFactory::of( 51 | $con->getYear(), 52 | $con->getMonth(), 53 | $con->getDay(), 54 | 0, 55 | 0, 56 | 0, 57 | $this->dateTimeZone 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Converter/ToJdnConverter.php: -------------------------------------------------------------------------------- 1 | set($day, $month, $year); 25 | } 26 | 27 | /** 28 | * Set the date for processing. 29 | * 30 | * @param $day 31 | * @param $month 32 | * @param $year 33 | * 34 | * @throws \Andegna\Exception\InvalidDateException 35 | * 36 | * @return $this 37 | */ 38 | public function set(int $day, int $month, int $year) 39 | { 40 | $validator = new DateValidator($day, $month, $year); 41 | 42 | if (!$validator->isValid()) { 43 | throw new InvalidDateException(); 44 | } 45 | 46 | $this->day = $day; 47 | $this->month = $month; 48 | $this->year = $year; 49 | 50 | $this->jdn = (int) static::process($day, $month, $year); 51 | 52 | return $this; 53 | } 54 | 55 | /** 56 | * @param $day 57 | * @param $month 58 | * @param $year 59 | * 60 | * @return int 61 | */ 62 | protected static function process($day, $month, $year) 63 | { 64 | return 65 | (1723856 + 365) + 66 | 365 * ($year - 1) + 67 | (int) ($year / 4) + 68 | 30 * $month + 69 | $day - 31; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Converter/FromJdnConverter.php: -------------------------------------------------------------------------------- 1 | set($jdn); 22 | } 23 | 24 | /** 25 | * Set the JDN for processing. 26 | * 27 | * @param $jdn 28 | * 29 | * @throws \Andegna\Exception\InvalidDateException 30 | * 31 | * @return $this 32 | */ 33 | public function set(int $jdn) 34 | { 35 | if (!$this->isValidInteger($jdn)) { 36 | throw new InvalidDateException(); 37 | } 38 | 39 | $this->jdn = $jdn; 40 | 41 | $date = static::process($jdn); 42 | 43 | return $this->setDate($date); 44 | } 45 | 46 | /** 47 | * @param $jdn integer 48 | * 49 | * @return array 50 | */ 51 | protected static function process(int $jdn): array 52 | { 53 | $r = (($jdn - 1723856) % 1461); 54 | $n = ($r % 365) + 365 * (int) ($r / 1460); 55 | 56 | $year = 4 * (int) (($jdn - 1723856) / 1461) + 57 | (int) ($r / 365) - (int) ($r / 1460); 58 | $month = (int) ($n / 30) + 1; 59 | $day = ($n % 30) + 1; 60 | 61 | return compact('day', 'month', 'year'); 62 | } 63 | 64 | /** 65 | * @param $date array 66 | * 67 | * @return $this|Converter 68 | */ 69 | protected function setDate(array $date): Converter 70 | { 71 | $this->day = $date['day']; 72 | $this->month = $date['month']; 73 | $this->year = $date['year']; 74 | 75 | return $this; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/Validator/DateValidatorTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($expected, $validator->isValid()); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Validator/DateValidator.php: -------------------------------------------------------------------------------- 1 | day = $day; 43 | $this->month = $month; 44 | $this->year = $year; 45 | } 46 | 47 | /** 48 | * validate the ethiopian date. 49 | * 50 | * @return bool true if valid 51 | */ 52 | public function isValid() 53 | { 54 | $validators = [ 55 | 'isDateValuesIntegers', 56 | 'isValidDayRange', 57 | 'isValidMonthRange', 58 | 'isValidPagumeDayRange', 59 | 'isValidLeapDay', 60 | ]; 61 | 62 | return array_reduce($validators, function ($result, $validator) { 63 | return $result && $this->{$validator}(); 64 | }, true); 65 | } 66 | 67 | /** 68 | * @return bool 69 | */ 70 | protected function isValidDayRange() 71 | { 72 | return $this->day >= self::FIRST_DAY && 73 | $this->day <= self::LAST_DAY; 74 | } 75 | 76 | /** 77 | * @return bool 78 | */ 79 | protected function isValidMonthRange() 80 | { 81 | return $this->month >= self::FIRST_MONTH && 82 | $this->month <= self::LAST_MONTH; 83 | } 84 | 85 | /** 86 | * @return bool 87 | */ 88 | protected function isValidPagumeDayRange() 89 | { 90 | if ($this->month === self::LAST_MONTH) { 91 | return $this->day <= self::PAGUME_LEAP_YEAR_LAST_DAY; 92 | } 93 | 94 | return true; 95 | } 96 | 97 | /** 98 | * @return bool 99 | */ 100 | protected function isValidLeapDay() 101 | { 102 | if ($this->month === self::LAST_MONTH && 103 | $this->day === self::PAGUME_LEAP_YEAR_LAST_DAY 104 | ) { 105 | return (new LeapYearValidator($this->year))->isValid(); 106 | } 107 | 108 | return true; 109 | } 110 | 111 | /** 112 | * @return bool 113 | */ 114 | protected function isDateValuesIntegers() 115 | { 116 | return $this->isValidInteger($this->day, $this->month, $this->year); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /tests/Holiday/EasterTest.php: -------------------------------------------------------------------------------- 1 | easter = new Easter(new \DateTimeZone('Africa/Addis_Ababa')); 27 | } 28 | 29 | /** 30 | * Eastern Orthodox churches easter dates. 31 | * 32 | * @link https://en.wikipedia.org/wiki/Computus 33 | * 34 | * @return array 35 | */ 36 | public function easterDatesDataProvider() 37 | { 38 | return [ 39 | [1997, 'April 27'], 40 | [1998, 'April 19'], 41 | [1999, 'April 11'], 42 | [2000, 'April 30'], 43 | [2001, 'April 15'], 44 | [2002, 'May 5'], 45 | [2003, 'April 27'], 46 | [2004, 'April 11'], 47 | [2005, 'May 1'], 48 | [2006, 'April 23'], 49 | [2007, 'April 8'], 50 | [2008, 'April 27'], 51 | [2009, 'April 19'], 52 | [2010, 'April 4'], 53 | [2011, 'April 24'], 54 | [2012, 'April 15'], 55 | [2013, 'May 5'], 56 | [2014, 'April 20'], 57 | [2015, 'April 12'], 58 | [2016, 'May 1'], 59 | [2017, 'April 16'], 60 | [2018, 'April 8'], 61 | [2019, 'April 28'], 62 | [2020, 'April 19'], 63 | [2021, 'May 2'], 64 | [2022, 'April 24'], 65 | [2023, 'April 16'], 66 | [2024, 'May 5'], 67 | [2025, 'April 20'], 68 | [2026, 'April 12'], 69 | [2027, 'May 2'], 70 | [2028, 'April 16'], 71 | [2029, 'April 8'], 72 | [2030, 'April 28'], 73 | [2031, 'April 13'], 74 | [2032, 'May 2'], 75 | [2033, 'April 24'], 76 | [2034, 'April 9'], 77 | [2035, 'April 29'], 78 | [2036, 'April 20'], 79 | [2037, 'April 5'], 80 | ]; 81 | } 82 | 83 | /** 84 | * @dataProvider easterDatesDataProvider 85 | * 86 | * @param $year 87 | * @param $day_month 88 | */ 89 | public function test_easter_date($year, $day_month) 90 | { 91 | $gregorian = \DateTime::createFromFormat('F j Y', "$day_month $year"); 92 | 93 | $expected = new DateTime($gregorian); 94 | 95 | $ethiopian = $this->easter->get($expected->getYear()); 96 | 97 | $this->assertEquals($expected->getYear(), $ethiopian->getYear()); 98 | $this->assertEquals($expected->getMonth(), $ethiopian->getMonth()); 99 | $this->assertEquals($expected->getDay(), $ethiopian->getDay()); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Operations/Initiator.php: -------------------------------------------------------------------------------- 1 | getJdnFromBase($this->dateTime); 24 | 25 | $converter = new FromJdnConverter($jdn); 26 | 27 | $this->setDateFromConverter($converter); 28 | $this->cacheTimestamp(); 29 | $this->computeFields(); 30 | } 31 | 32 | /** 33 | * Return the JDN of the given gregorian date time. 34 | * 35 | * @param GregorianDateTime $dateTime 36 | * 37 | * @return int 38 | */ 39 | protected function getJdnFromBase(GregorianDateTime $dateTime): int 40 | { 41 | $year = $dateTime->format('Y'); 42 | $month = $dateTime->format('m'); 43 | $day = $dateTime->format('d'); 44 | 45 | return gregoriantojd($month, $day, $year); 46 | } 47 | 48 | /** 49 | * Set the converted year, month and day from the given converter. 50 | * 51 | * @param Converter $converter 52 | * 53 | * @return void 54 | */ 55 | protected function setDateFromConverter(Converter $converter) 56 | { 57 | $this->year = $converter->getYear(); 58 | $this->month = $converter->getMonth(); 59 | $this->day = $converter->getDay(); 60 | } 61 | 62 | /** 63 | * Set the timestamp field. 64 | */ 65 | protected function cacheTimestamp() 66 | { 67 | $this->timestamp = $this->dateTime->getTimestamp(); 68 | } 69 | 70 | /** 71 | * Computer the available properties. 72 | * 73 | * @return void 74 | */ 75 | protected function computeFields() 76 | { 77 | $this->computeLeapYear(); 78 | $this->computeDayOfYear(); 79 | $this->computeDaysInMonth(); 80 | $this->cacheDayOfWeek(); 81 | } 82 | 83 | /** 84 | * Compute the leapYear property. 85 | * 86 | * @return void 87 | */ 88 | protected function computeLeapYear() 89 | { 90 | $leapYear = new LeapYearValidator($this->year); 91 | 92 | $this->leapYear = $leapYear->isValid(); 93 | } 94 | 95 | /** 96 | * Compute the dayOfYear property. 97 | * 98 | * @return void 99 | */ 100 | protected function computeDayOfYear() 101 | { 102 | $this->dayOfYear = ($this->month - 1) * 30 + $this->day; 103 | } 104 | 105 | /** 106 | * Compute the daysInMonth property. 107 | * 108 | * @return void 109 | */ 110 | protected function computeDaysInMonth() 111 | { 112 | $this->daysInMonth = $this->month === 13 ? ($this->leapYear ? 6 : 5) : 30; 113 | } 114 | 115 | /** 116 | * cache the dayOfWeek property. 117 | * 118 | * @return void 119 | */ 120 | protected function cacheDayOfWeek() 121 | { 122 | $this->dayOfWeek = (int) $this->dateTime->format('N'); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /tests/Converter/ConverterTest.php: -------------------------------------------------------------------------------- 1 | getJdn(); 73 | 74 | // The gregorian date in "month/day/year" format 75 | $gregorian = jdtogregorian($jdn); 76 | 77 | $dateTimeZone = self::checkForDateTimeZone($dateTimeZone); 78 | 79 | $base = new GregorianDateTime( 80 | "$gregorian $hour:$minute:$second", 81 | $dateTimeZone 82 | ); 83 | 84 | return new DateTime($base); 85 | } 86 | 87 | /** 88 | * @param int $timestamp timestamp like @see time() 89 | * @param DateTimeZone|null $dateTimeZone the timezone 90 | * 91 | * @return DateTime the datetime u wanted 92 | */ 93 | public static function fromTimestamp($timestamp, DateTimeZone $dateTimeZone = null) 94 | { 95 | $base = new GregorianDateTime( 96 | date('Y-m-d H:i:s', $timestamp), 97 | self::checkForDateTimeZone($dateTimeZone) 98 | ); 99 | 100 | return new DateTime($base); 101 | } 102 | 103 | /** 104 | * Just for convenience. 105 | * 106 | * @param GregorianDateTime $gregorian 107 | * 108 | * @return DateTime the datetime u wanted 109 | */ 110 | public static function fromDateTime(GregorianDateTime $gregorian) 111 | { 112 | return new DateTime($gregorian); 113 | } 114 | 115 | /** 116 | * Just for convenience. 117 | * 118 | * @param Converter $con 119 | * @param DateTimeZone|null $dateTimeZone 120 | * 121 | * @return DateTime the datetime u wanted 122 | */ 123 | public static function fromConverter(Converter $con, DateTimeZone $dateTimeZone = null) 124 | { 125 | return static::of( 126 | $con->getYear(), 127 | $con->getMonth(), 128 | $con->getDay(), 129 | 0, 130 | 0, 131 | 0, 132 | $dateTimeZone 133 | ); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/Operations/Processor.php: -------------------------------------------------------------------------------- 1 | 12 | * works on dateTime property. 13 | */ 14 | trait Processor 15 | { 16 | /** 17 | * @param mixed $dateTime 18 | * 19 | * @return mixed 20 | */ 21 | protected function fixForChaining($dateTime = false) 22 | { 23 | if ($dateTime === false) { 24 | return $dateTime; 25 | } 26 | 27 | $this->updateComputedFields(); 28 | 29 | return $this; 30 | } 31 | 32 | /** 33 | * Adds an amount of days, months, years, hours, minutes and seconds to a DateTime object. 34 | * 35 | * @param DateInterval $interval 36 | * 37 | * @return DateTime|bool 38 | */ 39 | public function add(DateInterval $interval) 40 | { 41 | return $this->fixForChaining($this->dateTime->add($interval)); 42 | } 43 | 44 | /** 45 | * Subtracts an amount of days, months, years, hours, minutes and seconds from a DateTime object. 46 | * 47 | * @param DateInterval $interval 48 | * 49 | * @return DateTime|bool 50 | */ 51 | public function sub(DateInterval $interval) 52 | { 53 | return$this->fixForChaining($this->dateTime->sub($interval)); 54 | } 55 | 56 | /** 57 | * Alters the timestamp. 58 | * 59 | * @param $modify 60 | * 61 | * @return DateTime|bool 62 | */ 63 | public function modify($modify) 64 | { 65 | return $this->fixForChaining($this->dateTime->modify($modify)); 66 | } 67 | 68 | /** 69 | * Returns the difference between two DateTime objects. 70 | * 71 | * @param $datetime 72 | * @param bool $absolute 73 | * 74 | * @return DateInterval 75 | */ 76 | public function diff($datetime, $absolute = false) 77 | { 78 | if ($datetime instanceof DateTime) { 79 | return $this->dateTime->diff($datetime->toGregorian(), $absolute); 80 | } 81 | 82 | return $this->dateTime->diff($datetime, $absolute); 83 | } 84 | 85 | /** 86 | * Sets the date. 87 | * 88 | * @param $year 89 | * @param $month 90 | * @param $day 91 | * 92 | * @return DateTime 93 | */ 94 | public function setDate($year, $month, $day) 95 | { 96 | $jdn = (new ToJdnConverter($day, $month, $year))->getJdn(); 97 | 98 | $gregorian = jdtogregorian($jdn); 99 | 100 | list($month, $day, $year) = explode('/', $gregorian); 101 | 102 | $this->fixForChaining($this->dateTime->setDate((int) $year, (int) $month, (int) $day)); 103 | } 104 | 105 | /** 106 | * Sets the time. 107 | * 108 | * @param $hour 109 | * @param $minute 110 | * @param int $second 111 | * 112 | * @return DateTime 113 | */ 114 | public function setTime($hour, $minute, $second = 0) 115 | { 116 | return $this->fixForChaining($this->dateTime->setTime($hour, $minute, $second)); 117 | } 118 | 119 | /** 120 | * Sets the date and time based on an Unix timestamp. 121 | * 122 | * @param $unixtimestamp 123 | * 124 | * @return DateTime 125 | */ 126 | public function setTimestamp($unixtimestamp) 127 | { 128 | return $this->fixForChaining($this->dateTime->setTimestamp($unixtimestamp)); 129 | } 130 | 131 | /** 132 | * Sets the time zone for the DateTime object. 133 | * 134 | * @param $timezone 135 | * 136 | * @return DateTime 137 | */ 138 | public function setTimezone($timezone) 139 | { 140 | return $this->fixForChaining($this->dateTime->setTimezone($timezone)); 141 | } 142 | 143 | /** 144 | * Returns the timezone offset. 145 | * 146 | * @return int 147 | */ 148 | public function getOffset() 149 | { 150 | return $this->dateTime->getOffset(); 151 | } 152 | 153 | /** 154 | * Return time zone relative to given DateTime. 155 | * 156 | * @return DateTimeZone 157 | */ 158 | public function getTimezone() 159 | { 160 | return $this->dateTime->getTimezone(); 161 | } 162 | 163 | /** 164 | * The __wakeup handler. 165 | */ 166 | public function __wakeup() 167 | { 168 | return $this->dateTime->__wakeup(); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/DateTime.php: -------------------------------------------------------------------------------- 1 | year; 49 | } 50 | 51 | /** 52 | * Returns the Ethiopian Month. 53 | * 54 | * @return int month 55 | */ 56 | public function getMonth() 57 | { 58 | return $this->month; 59 | } 60 | 61 | /** 62 | * Returns the Ethiopian Day. 63 | * 64 | * @return int day 65 | */ 66 | public function getDay() 67 | { 68 | return $this->day; 69 | } 70 | 71 | /** 72 | * Returns true if the Year is a leap. 73 | * 74 | * @return bool leap year 75 | */ 76 | public function isLeapYear() 77 | { 78 | return $this->leapYear; 79 | } 80 | 81 | /** 82 | * Returns the day Of the Year. 83 | * 84 | * Day of the year is the number of days(inclusive) 85 | * have passed since the new year 86 | * 87 | * Eg. 'ኅዳር 29' day of the year is 89 88 | * 89 | * @return int day Of the Year 90 | */ 91 | public function getDayOfYear() 92 | { 93 | return $this->dayOfYear; 94 | } 95 | 96 | /** 97 | * Returns number of days in the given year. 98 | * 99 | * It's 30 except 'ጳጉሜን' 100 | * 101 | * @return int days in the month 102 | */ 103 | public function getDaysInMonth() 104 | { 105 | return $this->daysInMonth; 106 | } 107 | 108 | /** 109 | * Returns the Hour. 110 | * 111 | * @return int hour 112 | */ 113 | public function getHour() 114 | { 115 | return (int) $this->dateTime->format('G'); 116 | } 117 | 118 | /** 119 | * Returns the Minute. 120 | * 121 | * @return int minute 122 | */ 123 | public function getMinute() 124 | { 125 | return (int) $this->dateTime->format('i'); 126 | } 127 | 128 | /** 129 | * Returns the Second. 130 | * 131 | * @return int second 132 | */ 133 | public function getSecond() 134 | { 135 | return (int) $this->dateTime->format('s'); 136 | } 137 | 138 | /** 139 | * Returns the Micro. 140 | * 141 | * @return int micro 142 | */ 143 | public function getMicro() 144 | { 145 | return (int) $this->dateTime->format('u'); 146 | } 147 | 148 | /** 149 | * Returns the Day of the week. 150 | * 151 | * 1 (for ሰኞ) through 7 (for እሑድ) 152 | * 153 | * @return int day of the week 154 | */ 155 | public function getDayOfWeek() 156 | { 157 | return (int) $this->dateTime->format('N'); 158 | } 159 | 160 | /** 161 | * Returns the Timestamp. 162 | * 163 | * @see time() 164 | * 165 | * @return int timestamp 166 | */ 167 | public function getTimestamp() 168 | { 169 | return (int) $this->dateTime->format('U'); 170 | } 171 | 172 | /** 173 | * DateTime constructor. 174 | * 175 | * @param GregorianDateTime|null $dateTime 176 | */ 177 | public function __construct(GregorianDateTime $dateTime = null) 178 | { 179 | if (null === $dateTime) { 180 | $this->dateTime = new GregorianDateTime('now'); 181 | } else { 182 | $this->dateTime = $dateTime; 183 | } 184 | 185 | $this->updateComputedFields(); 186 | } 187 | 188 | /** 189 | * Return the clone of the base gregorian datetime. 190 | * 191 | * @return GregorianDateTime 192 | */ 193 | public function toGregorian() 194 | { 195 | return clone $this->dateTime; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /tests/DateTimeTest.php: -------------------------------------------------------------------------------- 1 | dateTime = DateTimeFactory::of(1986, 3, 21); 29 | $this->otherDateTime = new DateTime($this->dateTime->toGregorian()->add(new \DateInterval('P2Y4DT6H8M'))); 30 | $this->yetAnotherDateTime = DateTimeFactory::of(2000, 1, 1, 0, 0, 0, new \DateTimeZone('America/Los_Angeles'))->sub(new \DateInterval('PT1H')); 31 | } 32 | 33 | public function test_my_birth_day() 34 | { 35 | $this->assertEquals(1986, $this->dateTime->getYear()); 36 | $this->assertEquals(3, $this->dateTime->getMonth()); 37 | $this->assertEquals(21, $this->dateTime->getDay()); 38 | 39 | $this->assertEquals(81, $this->dateTime->getDayOfYear()); 40 | $this->assertEquals(30, $this->dateTime->getDaysInMonth()); 41 | $this->assertEquals(false, $this->dateTime->isLeapYear()); 42 | 43 | $this->assertEquals(2, $this->dateTime->getDayOfWeek()); 44 | $this->assertEquals(754606800, $this->dateTime->getTimestamp()); 45 | $this->assertEquals(10800, $this->dateTime->getOffset()); 46 | $this->assertEquals('Africa/Addis_Ababa', $this->dateTime->getTimezone()->getName()); 47 | 48 | $this->assertEquals( 49 | 'ማክሰኞ, 21-ኅዳ-1986 00:00:00 የምስራቅ አፍሪካ ሰዓት (አዲስ አበባ)', 50 | $this->dateTime->format(DATE_COOKIE) 51 | ); 52 | 53 | $this->assertEquals( 54 | 'ማክሰኞ፣ ኅዳር 21 ቀን 00:00:00 እኩለ፡ሌሊት የምስራቅ አፍሪካ ሰዓት (አዲስ አበባ) 1986 ዓ/ም', 55 | $this->dateTime->format(Constants::DATE_ETHIOPIAN) 56 | ); 57 | 58 | $this->assertEquals( 59 | 'ሰኞ፣ ኅዳር 24 ቀን (ተክለ፡ሐይማኖት) 06:08:00 ጡዋት የምስራቅ አፍሪካ ሰዓት (አዲስ አበባ) 1988 (ዮሐንስ) ዓ/ም', 60 | $this->otherDateTime->format(Constants::DATE_ETHIOPIAN_ORTHODOX) 61 | ); 62 | 63 | $this->assertEquals( 64 | 'ማክሰኞ, 21-ኅዳ-1986 00:00:00 የምስራቅ አፍሪካ ሰዓት (አዲስ አበባ)', 65 | $this->dateTime->format(DATE_COOKIE) 66 | ); 67 | 68 | $this->assertEquals( 69 | 'ማክሰኞ፣ ጳጉሜን ፮ ቀን (ኢያሱስ) 23:00:00 እኩለ፡ሌሊት የፓስፊክ ሰዓት አቆጣጠር (ሎስ አንጀለስ) ፲፱፻፺፱ (ሉቃስ) ዓ/ም', 70 | $this->yetAnotherDateTime->format(Constants::DATE_GEEZ_ORTHODOX) 71 | ); 72 | } 73 | 74 | public function test_date_connector() 75 | { 76 | foreach (Constants::FORMAT_MAPPER as $format => $function) { 77 | $this->assertEquals($this->dateTime->{$function}(), $this->dateTime->format($format)); 78 | } 79 | } 80 | 81 | public function test_processor_trait() 82 | { 83 | $this->yetAnotherDateTime->add(new \DateInterval('P1D')); 84 | $this->assertEquals( 85 | 'ረቡዕ, 01-መስ-2000 23:00:00 የፓስፊክ ሰዓት አቆጣጠር (ሎስ አንጀለስ)', 86 | $this->yetAnotherDateTime->format(DATE_COOKIE) 87 | ); 88 | 89 | $this->yetAnotherDateTime->sub(new \DateInterval('P3D')); 90 | $this->assertEquals( 91 | 'እሑ, 04 ጳጉ 1999 23:00:00 -0700', 92 | $this->yetAnotherDateTime->format(DATE_RFC2822) 93 | ); 94 | 95 | $this->yetAnotherDateTime->modify('+1 hour'); 96 | $this->assertEquals( 97 | 'ሰኞ, 05-ጳጉ-1999 00:00:00 የፓስፊክ ሰዓት አቆጣጠር (ሎስ አንጀለስ)', 98 | $this->yetAnotherDateTime->format(DATE_COOKIE) 99 | ); 100 | 101 | $this->yetAnotherDateTime->setDate(2005, 5, 5); 102 | 103 | $this->assertEquals( 104 | '20050505', 105 | $this->yetAnotherDateTime->format('Ymd') 106 | ); 107 | 108 | $this->yetAnotherDateTime->setTimestamp(1494567149); 109 | $this->assertEquals( 110 | 'ሐሙስ, 03-ግን-2009 22:32:29 የፓስፊክ ሰዓት አቆጣጠር (ሎስ አንጀለስ)', 111 | $this->yetAnotherDateTime->format(DATE_COOKIE) 112 | ); 113 | 114 | $this->yetAnotherDateTime->setTime(6, 0, 0); 115 | $this->assertEquals( 116 | 'ሐሙስ 3th of ግንቦት 2009 06:00:00 ጡዋት', 117 | $this->yetAnotherDateTime->format('l j\t\h \o\f F Y h:i:s A') 118 | ); 119 | 120 | $this->yetAnotherDateTime->setTimezone(new \DateTimeZone('America/Detroit')); 121 | $this->assertEquals( 122 | 'ሐሙስ the 3ኛው, ምስራቃዊ ሰዓት አቆጣጠር (ዲትሮይት)', 123 | $this->yetAnotherDateTime->format('l \t\h\e jS, T') 124 | ); 125 | } 126 | 127 | public function test_datetime_diff() 128 | { 129 | $diff = $this->dateTime->diff($this->otherDateTime); 130 | 131 | $this->assertDiff($diff); 132 | 133 | $diff = $this->dateTime->diff($this->otherDateTime->toGregorian()); 134 | 135 | $this->assertDiff($diff); 136 | } 137 | 138 | /** 139 | * @param $diff 140 | */ 141 | protected function assertDiff($diff) 142 | { 143 | $this->assertEquals(2, $diff->y); 144 | $this->assertEquals(0, $diff->m); 145 | $this->assertEquals(4, $diff->d); 146 | 147 | $this->assertEquals(6, $diff->h); 148 | $this->assertEquals(8, $diff->i); 149 | $this->assertEquals(0, $diff->s); 150 | 151 | $this->assertEquals(734, $diff->days); 152 | } 153 | 154 | public function test_serialize() 155 | { 156 | $serialized = serialize($this->dateTime); 157 | 158 | /** @var DateTime $unserialized */ 159 | $unserialized = unserialize($serialized); 160 | 161 | $diff = $unserialized->diff($this->otherDateTime); 162 | 163 | $this->assertDiff($diff); 164 | 165 | $this->assertEquals( 166 | 'ማክሰኞ፣ ኅዳር 21 ቀን 00:00:00 እኩለ፡ሌሊት የምስራቅ አፍሪካ ሰዓት (አዲስ አበባ) 1986 ዓ/ም', 167 | $this->dateTime->format(Constants::DATE_ETHIOPIAN) 168 | ); 169 | } 170 | 171 | public function test_datetime_processor_chaining() 172 | { 173 | $this->expectWarning(); 174 | $this->expectExceptionMessage('DateTime::modify(): Failed to parse time string (lorem ipsum) at position 0 (l): The timezone could not be found in the database'); 175 | 176 | $this->assertFalse($this->dateTime->modify('lorem ipsum')); 177 | } 178 | 179 | public function test_gregorian_time() 180 | { 181 | $dates = [ 182 | new DateTime(), 183 | DateTimeFactory::now(), 184 | DateTimeFactory::fromDateTime(new \DateTime('yesterday')), 185 | DateTimeFactory::fromTimestamp(1494567149), 186 | DateTimeFactory::fromConverter(new ToJdnConverter(20, 9, 1983)), 187 | ]; 188 | 189 | foreach ($dates as $date) { 190 | $ethiopian = $date; 191 | $gregorian = $ethiopian->toGregorian(); 192 | 193 | $this->assertEquals($ethiopian->getHour(), (int) $gregorian->format('H')); 194 | $this->assertEquals($ethiopian->getMinute(), (int) $gregorian->format('i')); 195 | $this->assertEquals($ethiopian->getSecond(), (int) $gregorian->format('s')); 196 | $this->assertEquals($ethiopian->getMicro(), (int) $gregorian->format('u')); 197 | } 198 | } 199 | 200 | public function test_year_name() 201 | { 202 | $this->assertEquals('ዮሐንስ', DateTimeFactory::of(2008, 1, 1)->format('X')); 203 | $this->assertEquals('ማቴዎስ', DateTimeFactory::of(2009, 1, 1)->format('X')); 204 | $this->assertEquals('ማርቆስ', DateTimeFactory::of(2010, 1, 1)->format('X')); 205 | 206 | $this->assertEquals('ሉቃስ', DateTimeFactory::of(2011, 13, 6)->format('X')); 207 | 208 | $this->assertEquals('ዮሐንስ', DateTimeFactory::of(2012, 1, 1)->format('X')); 209 | $this->assertEquals('ማቴዎስ', DateTimeFactory::of(2013, 1, 1)->format('X')); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/Operations/Formatter.php: -------------------------------------------------------------------------------- 1 | getValueOfFormatCharacter($format_char, $skip_next); 36 | } 37 | 38 | // remove null bits if they exist 39 | return str_replace("\0", '', $result); 40 | } 41 | 42 | /** 43 | * Return the value of the format character. 44 | * 45 | * @param string $name of the field 46 | * @param bool $skip 47 | * 48 | * @return string 49 | */ 50 | protected function getValueOfFormatCharacter(string $name, bool &$skip = false): string 51 | { 52 | if (($r = $this->shouldWeSkip($name, $skip)) !== false) { 53 | return ''.$r; 54 | } 55 | 56 | if ($this->isOverrideFormatCharacter($name)) { 57 | return ''.$this->{Constants::FORMAT_MAPPER[$name]}(); 58 | } 59 | 60 | return $this->dateTime->format($name); 61 | } 62 | 63 | /** 64 | * (01-30) Day of the month, 2 digits with leading zeros. 65 | * 66 | * @return string 67 | */ 68 | public function getDayTwoDigit(): string 69 | { 70 | $day = $this->getValueOfFormatCharacter('j'); 71 | 72 | return strlen($day) === 1 ? "0$day" : $day; 73 | } 74 | 75 | /** 76 | * (ሰኞ-እሑድ) A full textual representation of the day of the week. 77 | * 78 | * return string 79 | */ 80 | public function getTextualDay(): string 81 | { 82 | return Constants::WEEK_NAME[$this->getDayOfWeek()]; 83 | } 84 | 85 | /** 86 | * (ሰኞ-እሑ) A textual representation of a day, two letters. 87 | * 88 | * return string 89 | */ 90 | public function getTextualDayShort(): string 91 | { 92 | $week = $this->getValueOfFormatCharacter('l'); 93 | 94 | return mb_substr($week, 0, 2, 'UTF-8'); 95 | } 96 | 97 | /** 98 | * (መስከረም-ጳጉሜን) A full textual representation of a month. 99 | * 100 | * @return string 101 | */ 102 | public function getTextualMonth(): string 103 | { 104 | return Constants::MONTHS_NAME[$this->getMonth()]; 105 | } 106 | 107 | /** 108 | * (መስ - ጳጉ) A short textual representation of a month, two letters. 109 | * 110 | * @return string 111 | */ 112 | public function getTextualMonthShort(): string 113 | { 114 | $F = $this->getValueOfFormatCharacter('F'); 115 | 116 | return mb_substr($F, 0, 2, 'UTF-8'); 117 | } 118 | 119 | /** 120 | * (01-13) Numeric representation of a month, with leading zeros. 121 | * 122 | * @return string 123 | */ 124 | public function getMonthTwoDigit(): string 125 | { 126 | $n = $this->getValueOfFormatCharacter('n'); 127 | 128 | return (strlen($n) === 1) ? "0$n" : "$n"; 129 | } 130 | 131 | /** 132 | * (1 or 0) Whether it's a leap year. 133 | * 134 | * @return string 135 | */ 136 | public function getLeapYearString(): string 137 | { 138 | return $this->isLeapYear() ? '1' : '0'; 139 | } 140 | 141 | /** 142 | * returns 97 for the year 1997. 143 | * 144 | * @return string 145 | */ 146 | public function getYearShort(): string 147 | { 148 | $Y = $this->getValueOfFormatCharacter('Y'); 149 | 150 | return mb_substr($Y, strlen($Y) - 2, 2); 151 | } 152 | 153 | /** 154 | * Return the amharic equivalent of AM & PM. 155 | * 156 | * (እኩለ፡ሌሊት-ምሽት) 157 | * 158 | * It suppose to format 'Ante meridiem' and 'Post meridiem' 159 | * But we Ethiopians classify the day in ten parts 160 | * and we don't have Uppercase and Lowercase letters 161 | * 162 | * @link http://web.archive.org/web/20140331152859/http://ethiopic.org/Calendars/ 163 | * 164 | * @return string 165 | */ 166 | public function getTimeOfDay(): string 167 | { 168 | $array = [ 169 | 'እኩለ፡ሌሊት' => [23, 0], 170 | 'ውደቀት' => [1, 2, 3], 171 | 'ንጋት' => [4, 5], 172 | 'ጡዋት' => [6, 7, 8], 173 | 'ረፋድ' => [9, 10, 11], 174 | 'እኩለ፡ቀን' => [12], 175 | 'ከሰዓት፡በኋላ' => [13, 14, 15], 176 | 'ወደማታ' => [16, 17], 177 | 'ሲደነግዝ' => [18, 19], 178 | 'ምሽት' => [20, 21, 22], 179 | ]; 180 | 181 | $hour = $this->getHour(); 182 | 183 | $result = array_filter($array, function ($value) use ($hour) { 184 | return in_array($hour, $value, true); 185 | }); 186 | 187 | return key($result); 188 | } 189 | 190 | /** 191 | * 1 (for 'ልደታ'), 2 (for አባ፡ጉባ), ... 30 (for ማርቆስ). 192 | * 193 | * @return string the ethiopian orthodox day name 194 | */ 195 | public function getOrthodoxDay(): string 196 | { 197 | return Constants::ORTHODOX_DAY_NAME[$this->getDay()]; 198 | } 199 | 200 | /** 201 | * ዓ/ም or ዓ/ዓ. 202 | * 203 | * @return string 204 | */ 205 | public function getTextualEra(): string 206 | { 207 | return $this->getYear() > 0 ? 'ዓ/ም' : 'ዓ/ዓ'; 208 | } 209 | 210 | /** 211 | * ማቴዎስ, ማርቆስ, ሉቃስ or ዮሐንስ. 212 | * 213 | * @return string the ethiopian orthodox year name 214 | */ 215 | public function getOrthodoxYear(): string 216 | { 217 | return Constants::ORTHODOX_YEAR_NAME[$this->getYear() % 4]; 218 | } 219 | 220 | /** 221 | * Return the year in geez number. 222 | * 223 | * @throws \Geezify\Exception\NotAnIntegerArgumentException 224 | * 225 | * @return string 226 | */ 227 | public function getYearInGeez(): string 228 | { 229 | return Geezify::create()->toGeez($this->getYear()); 230 | } 231 | 232 | /** 233 | * Return the day in geez number. 234 | * 235 | * @return string 236 | */ 237 | public function getDayInGeez(): string 238 | { 239 | return Geezify::create()->toGeez($this->getDay()); 240 | } 241 | 242 | /** 243 | * Return an empty string. 244 | * 245 | * @return string 246 | */ 247 | public function getTimeZoneString(): string 248 | { 249 | $name = $this->getTimezone()->getName(); 250 | 251 | return Constants::TIME_ZONES[$name] ?? sprintf('ጂ ኤም ቲ%s', $name); 252 | } 253 | 254 | /** 255 | * Return `ኛው` rather than st,nd,rd, and th. 256 | * 257 | * @return string 258 | */ 259 | public function getOrdinalSuffix(): string 260 | { 261 | return 'ኛው'; 262 | } 263 | 264 | /** 265 | * RFC 2822 formatted date. 266 | * 267 | * @return string 268 | */ 269 | public function getFormattedDate(): string 270 | { 271 | return $this->format(DATE_RFC2822); 272 | } 273 | 274 | /** 275 | * @param string $name 276 | * 277 | * @return bool 278 | */ 279 | protected function isOverrideFormatCharacter($name): bool 280 | { 281 | return array_key_exists($name, Constants::FORMAT_MAPPER); 282 | } 283 | 284 | /** 285 | * @param string $name 286 | * @param bool $skip 287 | * 288 | * @return string|false 289 | */ 290 | protected function shouldWeSkip($name, &$skip) 291 | { 292 | if ($this->shouldWeSkip4Real($name, $skip)) { 293 | return $this->skipCharacter($name, $skip); 294 | } 295 | 296 | return false; 297 | } 298 | 299 | /** 300 | * @param $name 301 | * @param $skip 302 | * 303 | * @return bool 304 | */ 305 | protected function shouldWeSkip4Real($name, &$skip): bool 306 | { 307 | return $this->isSkipCharacter($name) || $skip; 308 | } 309 | 310 | /** 311 | * @param $name 312 | * @param $skip 313 | * 314 | * @return string 315 | */ 316 | protected function skipCharacter($name, &$skip): string 317 | { 318 | if ($skip) { 319 | $skip = false; 320 | 321 | return $name; 322 | } 323 | 324 | if ($this->isSkipCharacter($name)) { 325 | $skip = true; 326 | } 327 | 328 | return ''; 329 | } 330 | 331 | /** 332 | * @param $name 333 | * 334 | * @return bool 335 | */ 336 | protected function isSkipCharacter($name): bool 337 | { 338 | return $name === '\\'; 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Andegna Calender ![From Ethiopia](https://img.shields.io/badge/From-Ethiopia-brightgreen.svg) 2 | 3 | ![Build Status](https://github.com/andegna/calender/actions/workflows/tests.yml/badge.svg) 4 | [![StyleCI](https://styleci.io/repos/30183050/shield)](https://styleci.io/repos/30183050) 5 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/andegna/calender/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/andegna/calender/?branch=master) 6 | [![Code Coverage](https://scrutinizer-ci.com/g/andegna/calender/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/andegna/calender/?branch=master) 7 | [![Total Downloads](https://poser.pugx.org/andegna/calender/d/total.svg)](https://packagist.org/packages/andegna/calender) 8 | [![Latest Stable Version](https://poser.pugx.org/andegna/calender/v/stable.svg)](https://packagist.org/packages/andegna/calender) 9 | [![Latest Unstable Version](https://poser.pugx.org/andegna/calender/v/unstable.svg)](https://packagist.org/packages/andegna/calender) 10 | [![License](https://poser.pugx.org/andegna/calender/license.svg)](https://packagist.org/packages/andegna/calender) 11 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fandegna%2Fcalender.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fandegna%2Fcalender?ref=badge_shield) 12 | 13 | [![SymfonyInsight](https://insight.symfony.com/projects/22a1a0d1-9999-4771-90eb-a79271985256/big.svg)](https://insight.symfony.com/projects/22a1a0d1-9999-4771-90eb-a79271985256) 14 | 15 | > If you ever want to convert **Ethiopian Calender** to any other calendar system 16 | > (like the Gregorian Calendar) this is the right (well-tested, well designed, high quality) package for you. 17 | > 18 | > And by the way it also supports Amharic date formatting and much much more. 19 | 20 | 21 | 22 | - [Basic Usage](#basic-usage-hammer) 23 | - [Requirement](#requirement) 24 | - [Installation](#installation) 25 | - [Conversion](#conversion) 26 | - [From Ethiopian Date](#from-Ethiopian-Date) 27 | - [From Timestamp](#from-timestamp) 28 | - [From DateTime object](#from-dateTime-object) 29 | - [From date string](#from-date-string) 30 | - [From the system time](#from-the-system-time) 31 | - [To DateTime](#to-datetime) 32 | - [Low level Conversion](#low-level-conversion) 33 | - [How PHP calendar conversion works](#how-php-calendar-conversion-works) 34 | - [From JDN](#from-jdn) 35 | - [To JDN](#to-jdn) 36 | - [Practical Example](#practical-example) 37 | - [Manipulation](#manipulation) 38 | - [Warning](#warning) 39 | - [Manipulating the internal date time](#manipulating-the-internal-date-time) 40 | - [Formatting](#formatting) 41 | - [Introduction](#introduction) 42 | - [Additional character formats](#additional-character-formats) 43 | - [Constants](#constants) 44 | - [Holidays](#holidays) 45 | - [Easter](#easter) 46 | - [Validators](#validators) 47 | - [Contributing](#contributing) 48 | 49 | 50 | ## Basic Usage :hammer: [↑](#top) 51 | 52 | Just to give you the 10,000-foot view (:airplane:) of the package. 53 | 54 | ```php 55 | // create a gregorian date using PHP's built-in DateTime class 56 | $gregorian = new DateTime('next monday'); 57 | 58 | // just pass it to Andegna\DateTime constractor and you will get $ethiopian date 59 | $ethipic = new Andegna\DateTime($gregorian); 60 | ``` 61 | 62 | Format it 63 | 64 | ```php 65 | // format it 66 | // ሰኞ፣ ግንቦት ፯ ቀን (ሥላሴ) 00:00:00 እኩለ፡ሌሊት EAT ፳፻፱ (ማርቆስ) ዓ/ም 67 | echo $ethipic->format(Andegna\Constants::DATE_GEEZ_ORTHODOX); 68 | 69 | // ሰኞ፣ ግንቦት 07 ቀን 00:00:00 እኩለ፡ሌሊት EAT 2009 ዓ/ም 70 | echo $ethipic->format(Andegna\Constants::DATE_ETHIOPIAN); 71 | ``` 72 | 73 | Modify it 74 | 75 | ```php 76 | $ethipic->modify('+8 hours'); 77 | $ethipic->sub(new DateInterval('PT30M')); // 30 minutes 78 | 79 | // ሰኞ, 07-ግን-2009 07:30:00 EAT 80 | echo $ethipic->format(DATE_COOKIE); 81 | ``` 82 | 83 | Get what you want :wink: 84 | 85 | ```php 86 | echo $ethipic->getYear(); // 2009 87 | echo $ethipic->getMonth(); // 9 88 | echo $ethipic->getDay(); // 7 89 | 90 | echo $ethipic->getTimestamp(); // 1494822600 91 | 92 | // turn it back to gregorian 93 | // Monday, 15-May-2017 07:30:00 EAT 94 | echo $ethipic->toGregorian()->format(DATE_COOKIE); 95 | ``` 96 | 97 | 98 | ## Requirement [↑](#top) 99 | 100 | Andegna Calender requires `php: >=7.0` with fire and blood :fire: :dragon:. 101 | 102 | Please notice the name of this package is `andegna/calender` not `andegna/calendar`. 103 | It's a spelling mistake am not intending to fix. 104 | 105 | 106 | ## Installation [↑](#top) 107 | 108 | **Andegna Calender** utilizes [Composer](https://getcomposer.org/) to manage its dependencies. 109 | So, before using this, make sure you have Composer installed on your machine. 110 | 111 | > Composer is a tool for dependency management in PHP. It allows you to declare the libraries your project depends on and it will manage (install/update) them for you. 112 | 113 | If you never used composer before :flushed:, PLEASE read some [intro here](https://getcomposer.org/doc/00-intro.md) before you write any PHP code again. 114 | 115 | ```bash 116 | composer require andegna/calender 117 | ``` 118 | 119 | 120 | ## Conversion [↑](#top) 121 | 122 | Before we talk about calendar conversion, we better know how the `Andegna\DateTime` class works internally. 123 | 124 | The `Andegna\DateTime` class is just a wrapper around PHP's built-in [`DateTime`](http://uk1.php.net/manual/en/class.datetime.php) 125 | object and implements the PHP [`DateTimeInterface`](http://uk1.php.net/manual/en/class.datetimeinterface.php) 126 | (OK! I lied on one part but trust me you don't wanna know that :smile:). 127 | 128 | So `Andegna\DateTime` keep hold of the gregorian date and overrides the `format`, `getTimestamp`, `add`, 'diff' and such methods to give you an Ethiopian Calendar equivalent. 129 | 130 | That's how it basically works. 131 | 132 | 133 | ### From Ethiopian Date [↑](#top) 134 | 135 | You can create an `Andegna\DateTime` from a given Ethiopia Date. 136 | 137 | Let's "create" 138 | 139 | ```php 140 | $millennium = Andegna\DateTimeFactory::of(2000, 1, 1); 141 | 142 | // ረቡዕ፣ መስከረም 01 ቀን (ልደታ) 00:00:00 እኩለ፡ሌሊት EAT 2000 (ማቴዎስ) ዓ/ም 143 | echo $millennium->format(\Andegna\Constants::DATE_ETHIOPIAN_ORTHODOX).PHP_EOL; 144 | 145 | // Wednesday, 12-Sep-2007 00:00:00 EAT 146 | echo $millennium->toGregorian()->format(DATE_COOKIE).PHP_EOL; 147 | 148 | $fall_of_derg = Andegna\DateTimeFactory::of(1983, 9, 20, 7, 43, 21, new DateTimeZone('Africa/Addis_Ababa')); 149 | 150 | // ማክሰኞ፣ ግንቦት 20 ቀን (ሕንፅተ) 07:43:21 ጡዋት EAT 1983 (ዮሐንስ) ዓ/ም 151 | echo $fall_of_derg->format(\Andegna\Constants::DATE_ETHIOPIAN_ORTHODOX).PHP_EOL; 152 | 153 | // Tuesday, 28-May-1991 07:43:21 EAT 154 | echo $fall_of_derg->toGregorian()->format(DATE_COOKIE).PHP_EOL; 155 | ``` 156 | 157 | 158 | ### From Timestamp [↑](#top) 159 | 160 | Let's assume you have a timestamp from same were probably `time()` function or from some kind of database. 161 | 162 | You can get `Andegna\DateTime` object like this 163 | ```php 164 | $timestamp = time(); // or some other place ¯\_(ツ)_/¯ 165 | 166 | $ethipic = Andegna\DateTimeFactory::fromTimestamp($timestamp); 167 | ``` 168 | 169 | And you are done. You can also or pass a `DateTimeZone` object if you want too 170 | 171 | ```php 172 | $sheger = new DateTimeZone('Africa/Addis_Ababa'); 173 | 174 | $ethiopic = Andegna\DateTimeFactory::fromTimestamp($timestamp, $sheger); 175 | ``` 176 | 177 | 178 | ### From DateTime object [↑](#top) 179 | 180 | If you already have a `DateTime` object, just give it to me :smile: 181 | 182 | ```php 183 | $gregorian = new DateTime('Thu, 11 May 2017 19:01:26 GMT'); 184 | $ethiopic = Andegna\DateTimeFactory::fromDateTime($gregorian); 185 | 186 | // or just pass it through the constractor 187 | $ethiopic = new Andegna\DateTime(new DateTime('next sunday')); 188 | ``` 189 | 190 | 191 | ### From date string [↑](#top) 192 | 193 | This is not actually part of this package but someone probably having a hard time. 194 | 195 | It generally boils down to two options. Do you know the format of the date string or not. 196 | 197 | If you know the format of the date (you probably should) you can create a gregorian `DateTime` 198 | like this 199 | 200 | ```php 201 | // passing the format followed by the date string you got 202 | $gregorian1 = DateTime::createFromFormat('j-M-Y', '15-Feb-2009'); 203 | $gregorian2 = DateTime::createFromFormat('m-j-Y', '12-31-1999'); 204 | $gregorian3 = DateTime::createFromFormat('Y-m-d H:i:s', '2009-02-15 15:16:17'); 205 | ``` 206 | 207 | To figure out the format please check [this link](http://php.net/manual/en/datetime.createfromformat.php) or search for "PHP date function". 208 | 209 | But if don't know the format is or don't care to figure it out just try to pass it to the DateTime constructor. It will "probably" figure out the format of the date string 210 | 211 | ```php 212 | $gregorian1 = new DateTime('next sunday'); 213 | $gregorian2 = new DateTime('yesterday'); 214 | $gregorian3 = new DateTime('1999-12-31'); 215 | $gregorian4 = new DateTime('2123-12-31 12:34:56'); 216 | 217 | $gregorian_bad = new DateTime('12-31-1999'); // this one probably fails 218 | ``` 219 | 220 | 221 | ### From the system time [↑](#top) 222 | 223 | You obviously can do this 224 | ```php 225 | $gregorian = new DateTime('now'); 226 | $ethiopic = Andegna\DateTimeFactory::fromDateTime($gregorian); 227 | 228 | // but we provided some shortcuts 229 | $now1 = \Andegna\DateTimeFactory::now(); 230 | $now2 = new DateTime(); 231 | 232 | // if you wanna specify time zone 233 | $sheger = new DateTimeZone('Africa/Addis_Ababa'); 234 | $now3 = \Andegna\DateTimeFactory::now($sheger); 235 | ``` 236 | 237 | 238 | ### To DateTime [↑](#top) 239 | 240 | If you want the internal `DateTime` object (A.K.A convert to gregorian calendar). 241 | 242 | ```php 243 | // create some Ethiopian date how ever you want 244 | $ethiopian_date = \Andegna\DateTimeFactory::now(); 245 | 246 | // you get a PHP DateTime object to play with 247 | $gregorian = $now->toGregorian(); 248 | ``` 249 | > Warning: the returned DateTime object is just a clone. So changes/modification to the returned object will not affect the Ethiopic date. 250 | 251 | 252 | ## Low level Conversion [↑](#top) 253 | 254 | If you are a geek like me, you are probably interested in Calendar coz it has Astronomy, Maths, and History. 255 | 256 | 257 | ### How PHP calendar conversion works [↑](#top) 258 | 259 | > The calendar extension presents a series of functions to simplify converting between different calendar formats (except Ethiopian). 260 | > The intermediary or standard it is based on is the **Julian Day Count**. The Julian Day Count is a count of days starting from January 1st, 4713 B.C. 261 | > To convert between calendar systems, you must first convert to Julian Day Count, then to the calendar system of your choice. 262 | > Julian Day Count is very different from the Julian Calendar! For more information on Julian Day Count, visit » [http://www.hermetic.ch/cal_stud/jdn.htm](http://www.hermetic.ch/cal_stud/jdn.htm). 263 | > For more information on calendar systems visit » [http://www.fourmilab.ch/documents/calendar/](http://www.fourmilab.ch/documents/calendar/). Excerpts from this page are included in these instructions and are in quotes. 264 | 265 | Those words are straight from the [php docs](http://php.net/manual/en/intro.calendar.php). 266 | 267 | So we need to implement two things to convert Ethiopian date to any other calendar. 268 | 1. Convert Ethiopian Date To Julian Date Count 269 | 2. Convert Julian Date Count To Ethiopian Date 270 | 271 | If you wanna know the algorithms check out the only reliable resource at [this link](http://web.archive.org/web/20140331152859/http://ethiopic.org/Calendars/). 272 | 273 | or check out my gist on GitHub at [this link](https://gist.github.com/SamAsEnd/70f2587c002070d2a1985f0741111554) 274 | 275 | 276 | ### From JDN [↑](#top) 277 | 278 | ```php 279 | use Andegna\Converter\FromJdnConverter; 280 | 281 | $jdn = 2457886; 282 | $converter = new FromJdnConverter($jdn); 283 | 284 | $day = $converter->getDay(); // 4 285 | $month = $converter->getMonth(); // 9 286 | $year = $converter->getYear(); // 2009 287 | ``` 288 | 289 | 290 | ### To JDN [↑](#top) 291 | 292 | ```php 293 | use Andegna\Converter\ToJdnConverter; 294 | 295 | $converter = new ToJdnConverter(21,3,1986); 296 | echo $jdn = $converter->getJdn(); // 2449322 297 | ``` 298 | 299 | 300 | ### Practical Example [↑](#top) 301 | 302 | Now with those handy tools in our hand, we can convert Ethiopian date to any other date. 303 | 304 | Let's convert to Jewish for example 305 | 306 | ```php 307 | $et = new Andegna\DateTime(); 308 | 309 | // ዓርብ, 04-ግን-2009 14:41:00 EAT 310 | echo $et->format(DATE_COOKIE); 311 | 312 | // create a Ethiopian Date `ToJdnConverter` 313 | $converter = new Andegna\Converter\ToJdnConverter($et->getDay(), $et->getMonth(), $et->getYear()); 314 | 315 | // convert it to jdn 316 | $jdn = $converter->getJdn(); 317 | 318 | // use the built-in php function to convert the jdn to the jewish calendar 319 | $jewish_date1 = jdtojewish($jdn); 320 | 321 | // 9/16/5777 322 | echo $jewish_date1; 323 | 324 | // it also support formating for the return string 325 | $jewish_date2 = jdtojewish($jdn, true, CAL_JEWISH_ADD_ALAFIM_GERESH); 326 | 327 | // convert the return string to utf-8 328 | $jewish_date2 = iconv ('WINDOWS-1255', 'UTF-8', $jewish_date2); 329 | 330 | // טז אייר ה'תשעז 331 | echo $jewish_date2; 332 | ``` 333 | 334 | Let's convert Julian Calendar to Ethiopian Calendar as a second example 335 | 336 | ```php 337 | $day = 29; 338 | $month = 4; 339 | $year = 2017; 340 | 341 | // get the jdn using the built in php function 342 | $jdn = juliantojd($month, $day, $year); 343 | 344 | // convert the jd to Ethiopian Date 345 | $converter = new Andegna\Converter\FromJdnConverter($jdn); 346 | 347 | // create a `Andegna\DateTime` from the converted date 348 | $ethiopic = Andegna\DateTimeFactory::fromConverter($converter); 349 | 350 | // ዓርብ, 04-ግን-2009 00:00:00 EAT 351 | echo $ethiopic->format(DATE_COOKIE); 352 | 353 | // Friday, 12-May-2017 00:00:00 EAT 354 | echo $ethiopic->toGregorian()->format(DATE_COOKIE); 355 | ``` 356 | 357 | #### List of supported calendars built into PHP 358 | - French Republican Calendar 359 | - Gregorian Calendar 360 | - Jewish calendar 361 | - Julian Calendar 362 | - Unix (I know what you are thinking. It's not a calendar but it handy) 363 | 364 | Click [here](http://php.net/manual/en/ref.calendar.php) to read more about those calendar function 365 | 366 | 367 | ## Manipulation [↑](#top) 368 | 369 | 370 | ### Warning [↑](#top) 371 | 372 | > The DateTime processor works on the internal gregorian `DateTime`. And manipulating months and years probably give an expected results. 373 | 374 | 375 | ### Manipulating the internal date time [↑](#top) 376 | 377 | You probably need to read about [DateTimeInterval](http://php.net/manual/en/class.dateinterval.php) if you don't already know. 378 | 379 | To give you a short summary, `DateInterval` implements the [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) durations. 380 | 381 | Durations are a component of time intervals and define the amount of intervening time in a time interval. 382 | Durations are represented by the format **`P[n]Y[n]M[n]DT[n]H[n]M[n]S`** or **`P[n]W`**. 383 | 384 | In these representations, the [n] is replaced by the value for each of the date and time elements that follow the [n]. 385 | 386 | Leading zeros are not required. The capital letters P, Y, M, W, D, T, H, M, and S are designators for each of the date and time elements and are not replaced. 387 | 388 | - **P** is the duration designator (for the period) placed at the start of the duration representation. 389 | - **Y** is the year designator that follows the value for the number of years. 390 | - **M** is the month designator that follows the value for the number of months. 391 | - **W** is the week designator that follows the value for the number of weeks. 392 | - **D** is the day designator that follows the value for the number of days. 393 | - **T** is the time designator that precedes the time components of the representation. 394 | - **H** is the hour designator that follows the value for the number of hours. 395 | - **M** is the minute designator that follows the value for the number of minutes. 396 | - **S** is the second designator that follows the value for the number of seconds. 397 | 398 | > For example, **"P3Y6M4DT12H30M5S"** represents a duration of "three years, six months, four days, twelve hours, thirty minutes, and five seconds". 399 | 400 | ```php 401 | // let's start from today 402 | $today = new Andegna\DateTime(); 403 | 404 | // Adds an amount of days, months, years, hours, minutes and seconds to a DateTime object 405 | $tomorrow = $today->add(new DateInterval('P1D')); 406 | $after_some_days = $today->add(new DateInterval('P10DT4H')); 407 | $after_6_hours = $today->add(new DateInterval('PT6H')); 408 | 409 | // Subtracts an amount of days, months, years, hours, minutes and seconds from a DateTime object 410 | $yesterday = $today->sub(new DateInterval('P1D')); 411 | $before_some_days = $today->sub(new DateInterval('P10DT4H')); 412 | $before_6_hours = $today->sub(new DateInterval('PT6H')); 413 | 414 | // Alters the DateTime object 415 | $tomorrow = $today->modify('+1 day'); 416 | $yesterday = $today->modify('-1 day'); 417 | $after_30_minutes = $today->modify('+30 minutes'); 418 | $after_30_minutes = $today->modify('next sunday'); 419 | ``` 420 | 421 | If you want to get the difference between dates 422 | 423 | ```php 424 | $today = new Andegna\DateTime(); 425 | $tomorrow = (new Andegna\DateTime())->add(new DateInterval('P1DT2M3S')); 426 | 427 | $diff = $today->diff($tomorrow); // returns a DateTimeInterval 428 | var_dump($diff); 429 | ``` 430 | 431 | will output something like this 432 | ``` 433 | object(DateInterval)[9] 434 | ... 435 | public 'd' => int 1 436 | public 'h' => int 0 437 | public 'i' => int 2 438 | public 's' => int 3 439 | ... 440 | ``` 441 | 442 | ## Formatting [↑](#top) 443 | 444 | 445 | ### Introduction [↑](#top) 446 | 447 | PHP built-in `DateTime` class has a `format` method used to format dates. 448 | 449 | > Read about the format method [here](http://uk1.php.net/manual/en/datetime.format.php) 450 | > 451 | > Read out the format characters [here](http://uk1.php.net/manual/en/function.date.php) 452 | 453 | If you read or already know how PHP date function works, you already know how exactly the formatting works. 454 | 455 | ```php 456 | $now = new Andegna\DateTime(); 457 | 458 | // Let's play a game. It's called "Spot the difference" 459 | echo $now->format(DATE_COOKIE); // ዓርብ, 04-ግን-2009 02:09:52 EAT 460 | echo $now->toGregorian()->format(DATE_COOKIE); // Friday, 12-May-2017 02:09:52 EAT 461 | 462 | echo $now->format(DATE_ATOM); // 2009-09-04EAT02:09:52+03:00 463 | echo $now->toGregorian()->format(DATE_ATOM); // 2017-05-12T02:09:52+03:00 464 | 465 | echo $now->format('F j ቀን Y'); // ግንቦት 4 ቀን 2009 466 | echo $now->toGregorian()->format('F j ቀን Y'); // May 12 ቀን 2017 467 | 468 | echo $now->format('H:i:s A'); // 10:09:17 ረፋድ 469 | echo $now->toGregorian()->format('H:i:s A'); // 10:09:17 AM 470 | ``` 471 | 472 | ### Additional character formats [↑](#top) 473 | 474 | | Format Character | Description | Example | 475 | | ---------------- | ------------- | ------- | 476 | | x | Orthodox day name | 1 => ልደታ, 12 => ሚካኤል | 477 | | X | Orthodox year name | ማቴዎስ, ማርቆስ, ሉቃስ or ዮሐንስ | 478 | | E | Era in Amharic | ዓ/ዓ or ዓ/ም | 479 | | K | Year in geez numeber | ፳፻፱ | 480 | | V | Day in geez numebr | ፪ | 481 | 482 | 483 | ### Constants [↑](#top) 484 | 485 | We have already defined some handy constants to print as it's custom in Ethiopia :heart: . 486 | 487 | ```php 488 | $date = new Andegna\DateTime(); 489 | 490 | // ዓርብ፣ ግንቦት 04 ቀን 02:35:45 ውደቀት EAT 2009 ዓ/ም 491 | echo $date->format(Andegna\Constants::DATE_ETHIOPIAN); 492 | 493 | $date->modify('+8 hours'); 494 | 495 | // ዓርብ፣ ግንቦት 04 ቀን (ዮሐንስ) 10:35:45 ረፋድ EAT 2009 (ማርቆስ) ዓ/ም 496 | echo $date->format(Andegna\Constants::DATE_ETHIOPIAN_ORTHODOX); 497 | 498 | $date->modify('+1 year'); 499 | 500 | // ቅዳሜ፣ ግንቦት ፬ ቀን 10:35:45 ረፋድ EAT ፳፻፲ ዓ/ም 501 | echo $date->format(Andegna\Constants::DATE_GEEZ); 502 | 503 | $date->modify('-3 years')->modify('+1 day'); 504 | 505 | // ረቡዕ፣ ግንቦት ፭ ቀን (አቦ) 10:35:45 ረፋድ EAT ፳፻፯ (ዮሐንስ) ዓ/ም 506 | echo $date->format(Andegna\Constants::DATE_GEEZ_ORTHODOX); 507 | ``` 508 | 509 | As you saw 3D, the constants all start with `DATE_` and followed by `ETHIOPIAN` or `GEEZ`. 510 | 511 | The one followed by `GEEZ` will return the day and the year in geez and the `ETHIOPIAN` with spit an ASCII numbers which we Ethiopian always use. 512 | 513 | Lastly, if you append `_ORTHODOX` you will get the orthodox day name and orthodox year name. 514 | 515 | 516 | ## Holidays [↑](#top) 517 | 518 | 519 | ### Easter [↑](#top) 520 | 521 | Calculating easter date feels like shooting a moving target. And everyone thinks calculating easter date is like impossible, some think like it's only possible if you are a deeply religious and some think it's decided by the church. But calculating easter date ( also called Computus) is not that much complex. 522 | 523 | In the simplest form, Easter is the first Sunday following the full moon that follows the northern spring (vernal) equinox. 524 | 525 | That sounds complex and was hard for the ages but not for the 21st century. 526 | 527 | If you are interested in HOW it's calculated, I will post it on my upcoming blog. 528 | You can read [this](https://en.wikipedia.org/wiki/Computus) in the meanwhile. 529 | 530 | Let's see how you can get the easter date for a given year 531 | 532 | ```php 533 | $easter = new Andegna\Holiday\Easter(); 534 | 535 | // እሑድ፣ ሚያዝያ 08 ቀን 00:00:00 እኩለ፡ሌሊት EAT 2009 ዓ/ም 536 | echo $easter->get(2009)->format(Andegna\Constants::DATE_ETHIOPIAN).PHP_EOL; 537 | 538 | // or just ... 539 | foreach ([2006,2007,2008,2009,2010,2011,2012] as $year) { 540 | echo $easter->get($year)->format(Andegna\Constants::DATE_ETHIOPIAN).PHP_EOL; 541 | } 542 | ``` 543 | will output 544 | ``` 545 | እሑድ፣ ሚያዝያ 12 ቀን 00:00:00 እኩለ፡ሌሊት EAT 2006 ዓ/ም 546 | እሑድ፣ ሚያዝያ 04 ቀን 00:00:00 እኩለ፡ሌሊት EAT 2007 ዓ/ም 547 | እሑድ፣ ሚያዝያ 23 ቀን 00:00:00 እኩለ፡ሌሊት EAT 2008 ዓ/ም 548 | እሑድ፣ ሚያዝያ 08 ቀን 00:00:00 እኩለ፡ሌሊት EAT 2009 ዓ/ም 549 | እሑድ፣ መጋቢት 30 ቀን 00:00:00 እኩለ፡ሌሊት EAT 2010 ዓ/ም 550 | እሑድ፣ ሚያዝያ 20 ቀን 00:00:00 እኩለ፡ሌሊት EAT 2011 ዓ/ም 551 | እሑድ፣ ሚያዝያ 11 ቀን 00:00:00 እኩለ፡ሌሊት EAT 2012 ዓ/ም 552 | ``` 553 | 554 | 555 | ## Validators [↑](#top) 556 | 557 | Validation. You probably need that too. 558 | 559 | To check if Ethiopia date (given `day`, `month` and `year`) is valid you need to do all this 560 | 561 | - Check if the `day` is between `1` and `30` inclusive 562 | - Check if the `month` is between `1` and `13` inclusive 563 | - If the `month` is `13` check if the `day` is between `1` and `6` inclusive 564 | - If the `month` is `13` and the `day` is `6` check if the year is a leap year 565 | 566 | or you can use our validator 567 | 568 | #### DateValidator 569 | 570 | ```php 571 | use Andegna\Validator\DateValidator; 572 | 573 | // true 574 | $is_valid1 = (new DateValidator(15,9, 2009))->isValid(); 575 | 576 | // false 577 | $is_valid2 = (new DateValidator(6,13, 2009))->isValid(); 578 | ``` 579 | 580 | #### LeapYearValidator 581 | 582 | ```php 583 | use Andegna\Validator\LeapYearValidator; 584 | 585 | // false 586 | $is_valid3 = (new LeapYearValidator(2009))->isValid(); 587 | 588 | // true 589 | $is_valid4 = (new LeapYearValidator(2007))->isValid(); 590 | ``` 591 | 592 | 593 | ## Contributing [↑](#top) 594 | 595 | Fork it 596 | Create your feature branch (git checkout -b my-new-feature) 597 | Commit your changes (git commit -am 'Add some feature') 598 | Push to the branch (git push origin my-new-feature) 599 | Create new Pull Request 600 | 601 | 602 | ## License 603 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fandegna%2Fcalender.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fandegna%2Fcalender?ref=badge_large) 604 | -------------------------------------------------------------------------------- /src/Constants.php: -------------------------------------------------------------------------------- 1 | 'መስከረም', 'ጥቅምት', 'ኅዳር', 'ታኅሣሥ', 'ጥር', 'የካቲት', 18 | 'መጋቢት', 'ሚያዝያ', 'ግንቦት', 'ሰኔ', 'ሐምሌ', 'ነሐሴ', 'ጳጉሜን', ]; 19 | 20 | const WEEK_NAME = [1 => 'ሰኞ', 'ማክሰኞ', 'ረቡዕ', 'ሐሙስ', 'ዓርብ', 'ቅዳሜ', 'እሑድ']; 21 | 22 | const ORTHODOX_YEAR_NAME = ['ዮሐንስ', 'ማቴዎስ', 'ማርቆስ', 'ሉቃስ']; 23 | 24 | const ORTHODOX_DAY_NAME = [ 25 | 1 => 'ልደታ', 'አባ፡ጉባ', 'በእታ', 'ዮሐንስ', 'አቦ', 26 | 'ኢያሱስ', 'ሥላሴ', 'አባ፡ኪሮስ', 'ቶማስ', 'መስቀል', 27 | 'ሐና፡ማርያም', 'ሚካኤል', 'እግዚሐር፡አብ', 'አቡነ፡አረጋዊ', 28 | 'ቂርቆስ', 'ኪዳነ፡ምሕረት', 'እስጢፋኖስ', 'ተክለ፡አልፋ', 29 | 'ገብርኤል', 'ሕንፅተ', 'ማርያም', 'ኡራኤል', 'ጊዮርጊስ', 30 | 'ተክለ፡ሐይማኖት', 'መርቆርዮስ', 'ዮሴፍ', 'መድኀኔ፡ዓለም', 31 | 'አማኑኤል', 'ባለ፡እግዚአብሔር', 'ማርቆስ', 32 | ]; 33 | 34 | const FORMAT_MAPPER = [ 35 | 'j' => 'getDay', // 1 - 30 36 | 'd' => 'getDayTwoDigit', // 01 - 30 37 | 'l' => 'getTextualDay', // ሰኞ - እሑድ 38 | 'D' => 'getTextualDayShort', // ሰኞ - እሑ 39 | 'w' => 'getDayOfWeek', // 0 እሑድ - 6 ሰኞ 40 | 'z' => 'getDayOfYear', // 0 - 365 41 | 42 | 'F' => 'getTextualMonth', // መስ - ጳጉ 43 | 'M' => 'getTextualMonthShort', // መስ - ጳጉ 44 | 'n' => 'getMonth', // 1 - 13 45 | 'm' => 'getMonthTwoDigit', // 01 - 13 46 | 't' => 'getDaysInMonth', // 5 - 30 47 | 48 | 'L' => 'getLeapYearString', // 0 - 1 49 | 'Y' => 'getYear', // 2009 50 | 'y' => 'getYearShort', // 09 51 | 'a' => 'getTimeOfDay', // እኩለ፡ሌሊት-ምሽት 52 | 'A' => 'getTimeOfDay', // እኩለ፡ሌሊት-ምሽት 53 | 54 | // custom 55 | 'x' => 'getOrthodoxDay', // ልደታ - ማርቆስ 56 | 'X' => 'getOrthodoxYear', // ማቴዎስ - ዮሐንስ 57 | 'E' => 'getTextualEra', // ዓ/ዓ, ዓ/ም 58 | 59 | 'K' => 'getYearInGeez', // ፳፻፱ 60 | 'V' => 'getDayInGeez', // ፪ 61 | 62 | 'e' => 'getTimeZoneString', // የምስራቅ አፍሪካ ሰዓት (አዲስ አበባ) 63 | 'T' => 'getTimeZoneString', // የምስራቅ አፍሪካ ሰዓት (አዲስ አበባ) 64 | 65 | 'S' => 'getOrdinalSuffix', // ኛው 66 | 'r' => 'getFormattedDate', // ቅዳ, 01 መስ 2008 00:00:00 +0330 67 | ]; 68 | 69 | const TIME_ZONES = [ 70 | 'Africa/Abidjan' => 'ግሪንዊች ማዕከላዊ ሰዓት (አቢጃን)', 71 | 'Africa/Accra' => 'ግሪንዊች ማዕከላዊ ሰዓት (አክራ)', 72 | 'Africa/Addis_Ababa' => 'የምስራቅ አፍሪካ ሰዓት (አዲስ አበባ)', 73 | 'Africa/Algiers' => 'የመካከለኛው አውሮፓ ሰዓት (አልጀርስ)', 74 | 'Africa/Asmera' => 'የምስራቅ አፍሪካ ሰዓት (አስመራ)', 75 | 'Africa/Bamako' => 'ግሪንዊች ማዕከላዊ ሰዓት (ባማኮ)', 76 | 'Africa/Bangui' => 'የምዕራብ አፍሪካ ሰዓት (ባንጉኢ)', 77 | 'Africa/Banjul' => 'ግሪንዊች ማዕከላዊ ሰዓት (ባንጁል)', 78 | 'Africa/Bissau' => 'ግሪንዊች ማዕከላዊ ሰዓት (ቢሳኦ)', 79 | 'Africa/Blantyre' => 'የመካከለኛው አፍሪካ ሰዓት (ብላንታየር)', 80 | 'Africa/Brazzaville' => 'የምዕራብ አፍሪካ ሰዓት (ብራዛቪል)', 81 | 'Africa/Bujumbura' => 'የመካከለኛው አፍሪካ ሰዓት (ቡጁምብራ)', 82 | 'Africa/Cairo' => 'የምስራቃዊ አውሮፓ ሰዓት (ካይሮ)', 83 | 'Africa/Casablanca' => 'የምዕራባዊ አውሮፓ ሰዓት (ካዛብላንካ)', 84 | 'Africa/Ceuta' => 'የመካከለኛው አውሮፓ ሰዓት (ሲኡታ)', 85 | 'Africa/Conakry' => 'ግሪንዊች ማዕከላዊ ሰዓት (ኮናክሬ)', 86 | 'Africa/Dakar' => 'ግሪንዊች ማዕከላዊ ሰዓት (ዳካር)', 87 | 'Africa/Dar_es_Salaam' => 'የምስራቅ አፍሪካ ሰዓት (ዳሬ ሰላም)', 88 | 'Africa/Djibouti' => 'የምስራቅ አፍሪካ ሰዓት (ጅቡቲ)', 89 | 'Africa/Douala' => 'የምዕራብ አፍሪካ ሰዓት (ዱአላ)', 90 | 'Africa/El_Aaiun' => 'የምዕራባዊ አውሮፓ ሰዓት (ኤል አዩአን)', 91 | 'Africa/Freetown' => 'ግሪንዊች ማዕከላዊ ሰዓት (ፍሪታውን)', 92 | 'Africa/Gaborone' => 'የመካከለኛው አፍሪካ ሰዓት (ጋቦሮን)', 93 | 'Africa/Harare' => 'የመካከለኛው አፍሪካ ሰዓት (ሃራሬ)', 94 | 'Africa/Johannesburg' => 'የደቡብ አፍሪካ መደበኛ ሰዓት (ጆሃንስበርግ)', 95 | 'Africa/Juba' => 'የምስራቅ አፍሪካ ሰዓት (ጁባ)', 96 | 'Africa/Kampala' => 'የምስራቅ አፍሪካ ሰዓት (ካምፓላ)', 97 | 'Africa/Khartoum' => 'የመካከለኛው አፍሪካ ሰዓት (ካርቱም)', 98 | 'Africa/Kigali' => 'የመካከለኛው አፍሪካ ሰዓት (ኪጋሊ)', 99 | 'Africa/Kinshasa' => 'የምዕራብ አፍሪካ ሰዓት (ኪንሻሳ)', 100 | 'Africa/Lagos' => 'የምዕራብ አፍሪካ ሰዓት (ሌጎስ)', 101 | 'Africa/Libreville' => 'የምዕራብ አፍሪካ ሰዓት (ሊበርቪል)', 102 | 'Africa/Lome' => 'ግሪንዊች ማዕከላዊ ሰዓት (ሎሜ)', 103 | 'Africa/Luanda' => 'የምዕራብ አፍሪካ ሰዓት (ሉአንዳ)', 104 | 'Africa/Lubumbashi' => 'የመካከለኛው አፍሪካ ሰዓት (ሉቡምባሺ)', 105 | 'Africa/Lusaka' => 'የመካከለኛው አፍሪካ ሰዓት (ሉሳካ)', 106 | 'Africa/Malabo' => 'የምዕራብ አፍሪካ ሰዓት (ማላቡ)', 107 | 'Africa/Maputo' => 'የመካከለኛው አፍሪካ ሰዓት (ማፑቱ)', 108 | 'Africa/Maseru' => 'የደቡብ አፍሪካ መደበኛ ሰዓት (ማሴሩ)', 109 | 'Africa/Mbabane' => 'የደቡብ አፍሪካ መደበኛ ሰዓት (ምባባኔ)', 110 | 'Africa/Mogadishu' => 'የምስራቅ አፍሪካ ሰዓት (ሞቃዲሹ)', 111 | 'Africa/Monrovia' => 'ግሪንዊች ማዕከላዊ ሰዓት (ሞንሮቪያ)', 112 | 'Africa/Nairobi' => 'የምስራቅ አፍሪካ ሰዓት (ናይሮቢ)', 113 | 'Africa/Ndjamena' => 'የምዕራብ አፍሪካ ሰዓት (ንጃሜና)', 114 | 'Africa/Niamey' => 'የምዕራብ አፍሪካ ሰዓት (ኒያሜይ)', 115 | 'Africa/Nouakchott' => 'ግሪንዊች ማዕከላዊ ሰዓት (ኑአክቾት)', 116 | 'Africa/Ouagadougou' => 'ግሪንዊች ማዕከላዊ ሰዓት (ኡጋዱጉ)', 117 | 'Africa/Porto-Novo' => 'የምዕራብ አፍሪካ ሰዓት (ፖርቶ - ኖቮ)', 118 | 'Africa/Sao_Tome' => 'ግሪንዊች ማዕከላዊ ሰዓት (ሳኦ ቶሜ)', 119 | 'Africa/Tripoli' => 'የምስራቃዊ አውሮፓ ሰዓት (ትሪፖሊ)', 120 | 'Africa/Tunis' => 'የመካከለኛው አውሮፓ ሰዓት (ቱኒዝ)', 121 | 'Africa/Windhoek' => 'የመካከለኛው አፍሪካ ሰዓት (ዊንድሆክ)', 122 | 'America/Adak' => 'የሃዋይ አሌኡት ሰዓት አቆጣጠር (አዳክ)', 123 | 'America/Anchorage' => 'የአላስካ ሰዓት አቆጣጠር (አንኮራጅ)', 124 | 'America/Anguilla' => 'የአትላንቲክ የሰዓት አቆጣጠር (አንጉይላ)', 125 | 'America/Antigua' => 'የአትላንቲክ የሰዓት አቆጣጠር (አንቲጓ)', 126 | 'America/Araguaina' => 'የብራዚላዊ ሰዓት አቆጣጠር (አራጉየና)', 127 | 'America/Argentina/La_Rioja' => 'የአርጀንቲና የሰዓት አቆጣጠር (ላ ሪኦጃ)', 128 | 'America/Argentina/Rio_Gallegos' => 'የአርጀንቲና የሰዓት አቆጣጠር (ሪዮ ጋሌጎስ)', 129 | 'America/Argentina/Salta' => 'የአርጀንቲና የሰዓት አቆጣጠር (ሳልታ)', 130 | 'America/Argentina/San_Juan' => 'የአርጀንቲና የሰዓት አቆጣጠር (ሳን ጁአን)', 131 | 'America/Argentina/San_Luis' => 'የአርጀንቲና ምስራቃዊ ሰዓት አቆጣጠር (ሳን ሊውስ)', 132 | 'America/Argentina/Tucuman' => 'የአርጀንቲና የሰዓት አቆጣጠር (ቱኩማን)', 133 | 'America/Argentina/Ushuaia' => 'የአርጀንቲና የሰዓት አቆጣጠር (ኡሹአኢ)', 134 | 'America/Aruba' => 'የአትላንቲክ የሰዓት አቆጣጠር (አሩባ)', 135 | 'America/Asuncion' => 'የፓራጓይ ሰዓት (አሱንሲዮን)', 136 | 'America/Bahia' => 'የብራዚላዊ ሰዓት አቆጣጠር (ባሂአ)', 137 | 'America/Bahia_Banderas' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ባሂያ ባንደራስ)', 138 | 'America/Barbados' => 'የአትላንቲክ የሰዓት አቆጣጠር (ባርቤዶስ)', 139 | 'America/Belem' => 'የብራዚላዊ ሰዓት አቆጣጠር (ቤለም)', 140 | 'America/Belize' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ቤሊዝ)', 141 | 'America/Blanc-Sablon' => 'የአትላንቲክ የሰዓት አቆጣጠር (ብላንክ- ሳብሎን)', 142 | 'America/Boa_Vista' => 'የአማዞን ሰዓት አቆጣጠር (ቦአ ቪስታ)', 143 | 'America/Bogota' => 'የኮሎምቢያ ሰዓት (ቦጎታ)', 144 | 'America/Boise' => 'የተራራ የሰዓት አቆጣጠር (ቦይዝ)', 145 | 'America/Buenos_Aires' => 'የአርጀንቲና የሰዓት አቆጣጠር (ቦነስ አይረስ)', 146 | 'America/Cambridge_Bay' => 'የተራራ የሰዓት አቆጣጠር (ካምብሪጅ ቤይ)', 147 | 'America/Campo_Grande' => 'የአማዞን ሰዓት አቆጣጠር (ካምፖ ግራንዴ)', 148 | 'America/Cancun' => 'ምስራቃዊ ሰዓት አቆጣጠር (ካንኩን)', 149 | 'America/Caracas' => 'የቬኔዝዌላ ሰዓት (ካራካስ)', 150 | 'America/Catamarca' => 'የአርጀንቲና የሰዓት አቆጣጠር (ካታማርካ)', 151 | 'America/Cayenne' => 'የፈረንሳይ ጉያና ሰዓት (ካይንኤ)', 152 | 'America/Cayman' => 'ምስራቃዊ ሰዓት አቆጣጠር (ካይማን)', 153 | 'America/Chicago' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ቺካጎ)', 154 | 'America/Chihuahua' => 'የሜክሲኮ ፓሲፊክ ሰዓት አቆጣጠር (ቺሁዋውአ)', 155 | 'America/Coral_Harbour' => 'ምስራቃዊ ሰዓት አቆጣጠር (አቲኮካን)', 156 | 'America/Cordoba' => 'የአርጀንቲና የሰዓት አቆጣጠር (ኮርዶባ)', 157 | 'America/Costa_Rica' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ኮስታሪካ)', 158 | 'America/Creston' => 'የተራራ የሰዓት አቆጣጠር (ክረስተን)', 159 | 'America/Cuiaba' => 'የአማዞን ሰዓት አቆጣጠር (ኩየአባ)', 160 | 'America/Curacao' => 'የአትላንቲክ የሰዓት አቆጣጠር (ኩራሳዎ)', 161 | 'America/Danmarkshavn' => 'ግሪንዊች ማዕከላዊ ሰዓት (ዳንማርክሻቭን)', 162 | 'America/Dawson' => 'የፓስፊክ ሰዓት አቆጣጠር (ዳውሰን)', 163 | 'America/Dawson_Creek' => 'የተራራ የሰዓት አቆጣጠር (ዳውሰን ክሬክ)', 164 | 'America/Denver' => 'የተራራ የሰዓት አቆጣጠር (ዴንቨር)', 165 | 'America/Detroit' => 'ምስራቃዊ ሰዓት አቆጣጠር (ዲትሮይት)', 166 | 'America/Dominica' => 'የአትላንቲክ የሰዓት አቆጣጠር (ዶሜኒካ)', 167 | 'America/Edmonton' => 'የተራራ የሰዓት አቆጣጠር (ኤድመንተን)', 168 | 'America/Eirunepe' => 'ብራዚል ጊዜ (ኢሩኔፕ)', 169 | 'America/El_Salvador' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ኤልሳልቫዶር)', 170 | 'America/Fort_Nelson' => 'የተራራ የሰዓት አቆጣጠር (ፎርት ኔልሰን)', 171 | 'America/Fortaleza' => 'የብራዚላዊ ሰዓት አቆጣጠር (ፎርታሌዛ)', 172 | 'America/Glace_Bay' => 'የአትላንቲክ የሰዓት አቆጣጠር (ግሌስ ቤይ)', 173 | 'America/Godthab' => 'የምዕራብ ግሪንላንድ ሰዓት (ጋድታብ)', 174 | 'America/Goose_Bay' => 'የአትላንቲክ የሰዓት አቆጣጠር (ጉዝ ቤይ)', 175 | 'America/Grand_Turk' => 'ምስራቃዊ ሰዓት አቆጣጠር (ግራንድ ተርክ)', 176 | 'America/Grenada' => 'የአትላንቲክ የሰዓት አቆጣጠር (ግሬናዳ)', 177 | 'America/Guadeloupe' => 'የአትላንቲክ የሰዓት አቆጣጠር (ጕዳሉፕ)', 178 | 'America/Guatemala' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ጓቲማላ)', 179 | 'America/Guayaquil' => 'የኢኳዶር ሰዓት (ጉያኩይል)', 180 | 'America/Guyana' => 'የጉያና ሰዓት', 181 | 'America/Halifax' => 'የአትላንቲክ የሰዓት አቆጣጠር (ሃሊፋክስ)', 182 | 'America/Havana' => 'ኩባ ሰዓት (ሃቫና)', 183 | 'America/Hermosillo' => 'የሜክሲኮ ፓሲፊክ ሰዓት አቆጣጠር (ኸርሞዚሎ)', 184 | 'America/Indiana/Knox' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ኖክስ, ኢንዲያና)', 185 | 'America/Indiana/Marengo' => 'ምስራቃዊ ሰዓት አቆጣጠር (ማሬንጎ, ኢንዲያና)', 186 | 'America/Indiana/Petersburg' => 'ምስራቃዊ ሰዓት አቆጣጠር (ፒተርስበርግ, ኢንዲያና)', 187 | 'America/Indiana/Tell_City' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ቴል ከተማ, ኢንዲያና)', 188 | 'America/Indiana/Vevay' => 'ምስራቃዊ ሰዓት አቆጣጠር (ቪቫይ, ኢንዲያና)', 189 | 'America/Indiana/Vincennes' => 'ምስራቃዊ ሰዓት አቆጣጠር (ቪንቼንስ, ኢንዲያና)', 190 | 'America/Indiana/Winamac' => 'ምስራቃዊ ሰዓት አቆጣጠር (ዊናማክ, ኢንዲያና)', 191 | 'America/Indianapolis' => 'ምስራቃዊ ሰዓት አቆጣጠር (ኢንዲያናፖሊስ)', 192 | 'America/Inuvik' => 'የተራራ የሰዓት አቆጣጠር (ኢኑቪክ)', 193 | 'America/Iqaluit' => 'ምስራቃዊ ሰዓት አቆጣጠር (ኢኳሊውት)', 194 | 'America/Jamaica' => 'ምስራቃዊ ሰዓት አቆጣጠር (ጃማይካ)', 195 | 'America/Jujuy' => 'የአርጀንቲና የሰዓት አቆጣጠር (ጁጁይ)', 196 | 'America/Juneau' => 'የአላስካ ሰዓት አቆጣጠር (ጁኒዩ)', 197 | 'America/Kentucky/Monticello' => 'ምስራቃዊ ሰዓት አቆጣጠር (ሞንቲሴሎ, ኪንታኪ)', 198 | 'America/Kralendijk' => 'የአትላንቲክ የሰዓት አቆጣጠር (ክራለንዲይክ)', 199 | 'America/La_Paz' => 'የቦሊቪያ ሰዓት (ላ ፓዝ)', 200 | 'America/Lima' => 'የፔሩ ሰዓት (ሊማ)', 201 | 'America/Los_Angeles' => 'የፓስፊክ ሰዓት አቆጣጠር (ሎስ አንጀለስ)', 202 | 'America/Louisville' => 'ምስራቃዊ ሰዓት አቆጣጠር (ሊውስቪል)', 203 | 'America/Lower_Princes' => 'የአትላንቲክ የሰዓት አቆጣጠር (የታችኛው ልዑል ሩብ)', 204 | 'America/Maceio' => 'የብራዚላዊ ሰዓት አቆጣጠር (ሜሲኦ)', 205 | 'America/Managua' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ማናጉአ)', 206 | 'America/Manaus' => 'የአማዞን ሰዓት አቆጣጠር (ማናኡስ)', 207 | 'America/Marigot' => 'የአትላንቲክ የሰዓት አቆጣጠር (ማርጎት)', 208 | 'America/Martinique' => 'የአትላንቲክ የሰዓት አቆጣጠር (ማርቲኒክ)', 209 | 'America/Matamoros' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ማታሞሮስ)', 210 | 'America/Mazatlan' => 'የሜክሲኮ ፓሲፊክ ሰዓት አቆጣጠር (ማዛትላን)', 211 | 'America/Mendoza' => 'የአርጀንቲና የሰዓት አቆጣጠር (ሜንዶዛ)', 212 | 'America/Menominee' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ሜኖሚኒ)', 213 | 'America/Merida' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ሜሪዳ)', 214 | 'America/Metlakatla' => 'የአላስካ ሰዓት አቆጣጠር (መትላካትላ)', 215 | 'America/Mexico_City' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ሜክሲኮ ከተማ)', 216 | 'America/Miquelon' => 'ቅዱስ የፒዬር እና ሚኴሎን ሰዓት (ሚኮውሎን)', 217 | 'America/Moncton' => 'የአትላንቲክ የሰዓት አቆጣጠር (ሞንክቶን)', 218 | 'America/Monterrey' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ሞንተርሬይ)', 219 | 'America/Montevideo' => 'የኡራጓይ ሰዓት (ሞንቴቪድዮ)', 220 | 'America/Montreal' => 'ካናዳ ጊዜ (Montreal)', 221 | 'America/Montserrat' => 'የአትላንቲክ የሰዓት አቆጣጠር (ሞንትሴራት)', 222 | 'America/Nassau' => 'ምስራቃዊ ሰዓት አቆጣጠር (ናሳው)', 223 | 'America/New_York' => 'ምስራቃዊ ሰዓት አቆጣጠር (ኒውዮርክ)', 224 | 'America/Nipigon' => 'ምስራቃዊ ሰዓት አቆጣጠር (ኒፒጎን)', 225 | 'America/Nome' => 'የአላስካ ሰዓት አቆጣጠር (ኖሜ)', 226 | 'America/Noronha' => 'የኖሮንሃ ሰዓት አቆጣጠር (ኖሮኛ)', 227 | 'America/North_Dakota/Beulah' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ቤኡላህ, ሰሜን ዳኮታ)', 228 | 'America/North_Dakota/Center' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (መካከለኛ, ሰሜን ዳኮታ)', 229 | 'America/North_Dakota/New_Salem' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (አዲስ ሳሌም, ሰሜን ዳኮታ)', 230 | 'America/Ojinaga' => 'የተራራ የሰዓት አቆጣጠር (ኦዪናጋ)', 231 | 'America/Panama' => 'ምስራቃዊ ሰዓት አቆጣጠር (ፓናማ)', 232 | 'America/Pangnirtung' => 'ምስራቃዊ ሰዓት አቆጣጠር (ፓንግኒርተንግ)', 233 | 'America/Paramaribo' => 'የሱሪናም ሰዓት (ፓራማሪቦ)', 234 | 'America/Phoenix' => 'የተራራ የሰዓት አቆጣጠር (ፊኒክስ)', 235 | 'America/Port-au-Prince' => 'ምስራቃዊ ሰዓት አቆጣጠር (ፖርት ኦ ፕሪንስ)', 236 | 'America/Port_of_Spain' => 'የአትላንቲክ የሰዓት አቆጣጠር (የእስፔን ወደብ)', 237 | 'America/Porto_Velho' => 'የአማዞን ሰዓት አቆጣጠር (ፔትሮ ቬልሆ)', 238 | 'America/Puerto_Rico' => 'የአትላንቲክ የሰዓት አቆጣጠር (ፖርቶሪኮ)', 239 | 'America/Punta_Arenas' => 'የቺሊ ሰዓት (ፑንታ አሬናስ)', 240 | 'America/Rainy_River' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ሬኒ ሪቨር)', 241 | 'America/Rankin_Inlet' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ራንኪን ኢንሌት)', 242 | 'America/Recife' => 'የብራዚላዊ ሰዓት አቆጣጠር (ረሲፍ)', 243 | 'America/Regina' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ረጂና)', 244 | 'America/Resolute' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ሪዞሊዩት)', 245 | 'America/Rio_Branco' => 'ብራዚል ጊዜ (ሪዮ ብራንኮ)', 246 | 'America/Santa_Isabel' => 'ሰሜናዊ ምእራብ የሜክሲኮ ሰዓት አቆጣጠር (ሳንታ ኢዛቤል)', 247 | 'America/Santarem' => 'የብራዚላዊ ሰዓት አቆጣጠር (ሳንታሬም)', 248 | 'America/Santiago' => 'የቺሊ ሰዓት (ሳንቲያጎ)', 249 | 'America/Santo_Domingo' => 'የአትላንቲክ የሰዓት አቆጣጠር (ሳንቶ ዶሚንጎ)', 250 | 'America/Sao_Paulo' => 'የብራዚላዊ ሰዓት አቆጣጠር (ሳኦ ፖሎ)', 251 | 'America/Scoresbysund' => 'የምስራቅ ግሪንላንድ ሰዓት (ስኮርስባይሰንድ)', 252 | 'America/Sitka' => 'የአላስካ ሰዓት አቆጣጠር (ሲትካ)', 253 | 'America/St_Barthelemy' => 'የአትላንቲክ የሰዓት አቆጣጠር (ቅድስት ቤርተሎሜ)', 254 | 'America/St_Johns' => 'የኒውፋውንድላንድ የሰዓት አቆጣጠር (ቅዱስ ዮሐንስ)', 255 | 'America/St_Kitts' => 'የአትላንቲክ የሰዓት አቆጣጠር (ቅዱስ ኪትስ)', 256 | 'America/St_Lucia' => 'የአትላንቲክ የሰዓት አቆጣጠር (ቅድስት ሉሲያ)', 257 | 'America/St_Thomas' => 'የአትላንቲክ የሰዓት አቆጣጠር (ቅዱስ ቶማስ)', 258 | 'America/St_Vincent' => 'የአትላንቲክ የሰዓት አቆጣጠር (ቅዱስ ቪንሰንት)', 259 | 'America/Swift_Current' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (የሐዋላ ገንዘብ)', 260 | 'America/Tegucigalpa' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ቴጉሲጋልፓ)', 261 | 'America/Thule' => 'የአትላንቲክ የሰዓት አቆጣጠር (ቱሌ)', 262 | 'America/Thunder_Bay' => 'ምስራቃዊ ሰዓት አቆጣጠር (ተንደር ቤይ)', 263 | 'America/Tijuana' => 'የፓስፊክ ሰዓት አቆጣጠር (ቲጁአና)', 264 | 'America/Toronto' => 'ምስራቃዊ ሰዓት አቆጣጠር (ቶሮንቶ)', 265 | 'America/Tortola' => 'የአትላንቲክ የሰዓት አቆጣጠር (ቶርቶላ)', 266 | 'America/Vancouver' => 'የፓስፊክ ሰዓት አቆጣጠር (ቫንኮቨር)', 267 | 'America/Whitehorse' => 'የፓስፊክ ሰዓት አቆጣጠር (ኋይትሆርስ)', 268 | 'America/Winnipeg' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር (ዊኒፔግ)', 269 | 'America/Yakutat' => 'የአላስካ ሰዓት አቆጣጠር (ያኩታት)', 270 | 'America/Yellowknife' => 'የተራራ የሰዓት አቆጣጠር (የሎውናይፍ)', 271 | 'Antarctica/Casey' => 'የምስራቃዊ አውስትራሊያ ሰዓት አቆጣጠር (ካዚይ)', 272 | 'Antarctica/Davis' => 'የዴቪስ ሰዓት (ዳቪስ)', 273 | 'Antarctica/DumontDUrville' => 'የዱሞንት-ዱርቪል ሰዓት (ደሞንት ዲኡርቪል)', 274 | 'Antarctica/Macquarie' => 'የማከሪ ደሴት ሰዓት', 275 | 'Antarctica/Mawson' => 'የማውሰን ሰዓት (ናውሰን)', 276 | 'Antarctica/McMurdo' => 'የኒው ዚላንድ ሰዓት (ማክመርዶ)', 277 | 'Antarctica/Palmer' => 'የቺሊ ሰዓት (ፓልመር)', 278 | 'Antarctica/Rothera' => 'የሮቴራ ሰዓት', 279 | 'Antarctica/Syowa' => 'የሲዮዋ ሰዓት (ስዮዋ)', 280 | 'Antarctica/Troll' => 'ግሪንዊች ማዕከላዊ ሰዓት (ትሮል)', 281 | 'Antarctica/Vostok' => 'የቮስቶክ ሰዓት (ቭስቶክ)', 282 | 'Arctic/Longyearbyen' => 'የመካከለኛው አውሮፓ ሰዓት (ሎንግይርባየን)', 283 | 'Asia/Aden' => 'የዓረቢያ ሰዓት (ኤደን)', 284 | 'Asia/Almaty' => 'የምስራቅ ካዛኪስታን ሰዓት (አልማትይ)', 285 | 'Asia/Amman' => 'የምስራቃዊ አውሮፓ ሰዓት (አማን)', 286 | 'Asia/Anadyr' => 'የአናድይር ሰዓት አቆጣጠር', 287 | 'Asia/Aqtau' => 'የምዕራብ ካዛኪስታን ሰዓት (አኩታኡ)', 288 | 'Asia/Aqtobe' => 'የምዕራብ ካዛኪስታን ሰዓት (አኩቶቤ)', 289 | 'Asia/Ashgabat' => 'የቱርክመኒስታን ሰዓት (አሽጋባት)', 290 | 'Asia/Atyrau' => 'የምዕራብ ካዛኪስታን ሰዓት (አትይራኡ)', 291 | 'Asia/Baghdad' => 'የዓረቢያ ሰዓት (ባግዳድ)', 292 | 'Asia/Bahrain' => 'የዓረቢያ ሰዓት (ባህሬን)', 293 | 'Asia/Baku' => 'የአዘርባጃን ሰዓት (ባኩ)', 294 | 'Asia/Bangkok' => 'የኢንዶቻይና ሰዓት (ባንኮክ)', 295 | 'Asia/Barnaul' => 'ሩስያ ጊዜ (ባርናኡል)', 296 | 'Asia/Beirut' => 'የምስራቃዊ አውሮፓ ሰዓት (ቤሩት)', 297 | 'Asia/Bishkek' => 'የኪርጊስታን ሰዓት (ቢሽኬክ)', 298 | 'Asia/Brunei' => 'የብሩኔይ ዳሩሳላም ሰዓት (ብሩናይ)', 299 | 'Asia/Calcutta' => 'የህንድ መደበኛ ሰዓት (ኮልካታ)', 300 | 'Asia/Chita' => 'ያኩትስክ የሰዓት አቆጣጠር (ቺታ)', 301 | 'Asia/Choibalsan' => 'የቾይባልሳ ሰዓት አቆጣጠር (ቾይባልሳን)', 302 | 'Asia/Colombo' => 'የህንድ መደበኛ ሰዓት (ኮሎምቦ)', 303 | 'Asia/Damascus' => 'የምስራቃዊ አውሮፓ ሰዓት (ደማስቆ)', 304 | 'Asia/Dhaka' => 'የባንግላዴሽ ሰዓት (ዳካ)', 305 | 'Asia/Dili' => 'የምስራቅ ቲሞር ሰዓት (ዲሊ)', 306 | 'Asia/Dubai' => 'የባህረሰላጤ መደበኛ ሰዓት (ዱባይ)', 307 | 'Asia/Dushanbe' => 'የታጂኪስታን ሰዓት (ደሻንቤ)', 308 | 'Asia/Famagusta' => 'የምስራቃዊ አውሮፓ ሰዓት (ፋማጉስታ)', 309 | 'Asia/Gaza' => 'የምስራቃዊ አውሮፓ ሰዓት (ጋዛ)', 310 | 'Asia/Hebron' => 'የምስራቃዊ አውሮፓ ሰዓት (ኬብሮን)', 311 | 'Asia/Hong_Kong' => 'የሆንግ ኮንግ ሰዓት', 312 | 'Asia/Hovd' => 'የሆቭድ ሰዓት አቆጣጠር', 313 | 'Asia/Irkutsk' => 'የኢርኩትስክ ሰዓት አቆጣጠር', 314 | 'Asia/Jakarta' => 'የምዕራባዊ ኢንዶኔዢያ ሰዓት (ጃካርታ)', 315 | 'Asia/Jayapura' => 'የምስራቃዊ ኢንዶኔዢያ ሰዓት (ጃያፑራ)', 316 | 'Asia/Jerusalem' => 'የእስራኤል ሰዓት (እየሩሳሌም)', 317 | 'Asia/Kabul' => 'የአፍጋኒስታን ሰዓት (ካቡል)', 318 | 'Asia/Kamchatka' => 'የካምቻትካ ሰዓት አቆጣጠር', 319 | 'Asia/Karachi' => 'የፓኪስታን ሰዓት (ካራቺ)', 320 | 'Asia/Katmandu' => 'የኔፓል ሰዓት (ካትማንዱ)', 321 | 'Asia/Khandyga' => 'ያኩትስክ የሰዓት አቆጣጠር (ካንዲጋ)', 322 | 'Asia/Krasnoyarsk' => 'የክራስኖያርስክ ሰዓት አቆጣጠር', 323 | 'Asia/Kuala_Lumpur' => 'የማሌይዢያ ሰዓት (ኩዋላ ላምፑር)', 324 | 'Asia/Kuching' => 'የማሌይዢያ ሰዓት (ኩቺንግ)', 325 | 'Asia/Kuwait' => 'የዓረቢያ ሰዓት (ኩዌት)', 326 | 'Asia/Macau' => 'የቻይና ሰዓት (ማካኡ)', 327 | 'Asia/Magadan' => 'የማጋዳን የሰዓት አቆጣጠር', 328 | 'Asia/Makassar' => 'የመካከለኛው ኢንዶኔዢያ ሰዓት (ማካሳር)', 329 | 'Asia/Manila' => 'የፊሊፒን ሰዓት (ማኒላ)', 330 | 'Asia/Muscat' => 'የባህረሰላጤ መደበኛ ሰዓት (ሙስካት)', 331 | 'Asia/Nicosia' => 'የምስራቃዊ አውሮፓ ሰዓት (ኒኮሲአ)', 332 | 'Asia/Novokuznetsk' => 'የክራስኖያርስክ ሰዓት አቆጣጠር (ኖቮኩትዝኔክ)', 333 | 'Asia/Novosibirsk' => 'የኖቮሲብሪስክ የሰዓት አቆጣጠር (ኖቮሲቢሪስክ)', 334 | 'Asia/Omsk' => 'የኦምስክ የሰዓት አቆጣጠር', 335 | 'Asia/Oral' => 'የምዕራብ ካዛኪስታን ሰዓት (ኦራል)', 336 | 'Asia/Phnom_Penh' => 'የኢንዶቻይና ሰዓት (ፍኖም ፔንህ)', 337 | 'Asia/Pontianak' => 'የምዕራባዊ ኢንዶኔዢያ ሰዓት (ፖንቲአናክ)', 338 | 'Asia/Pyongyang' => 'የኮሪያ ሰዓት (ፕዮንግያንግ)', 339 | 'Asia/Qatar' => 'የዓረቢያ ሰዓት (ኳታር)', 340 | 'Asia/Qostanay' => 'የምስራቅ ካዛኪስታን ሰዓት (ኮስታናይ)', 341 | 'Asia/Qyzylorda' => 'የምዕራብ ካዛኪስታን ሰዓት (ኩይዚሎርዳ)', 342 | 'Asia/Rangoon' => 'የሚያንማር ሰዓት (ያንጎን)', 343 | 'Asia/Riyadh' => 'የዓረቢያ ሰዓት (ሪያድ)', 344 | 'Asia/Saigon' => 'የኢንዶቻይና ሰዓት (ሆ ቺ ሚንህ ከተማ)', 345 | 'Asia/Sakhalin' => 'የሳክሃሊን ሰዓት አቆጣጠር', 346 | 'Asia/Samarkand' => 'የኡዝቤኪስታን ሰዓት (ሳማርካንድ)', 347 | 'Asia/Seoul' => 'የኮሪያ ሰዓት (ሴኦል)', 348 | 'Asia/Shanghai' => 'የቻይና ሰዓት (ሻንጋይ)', 349 | 'Asia/Singapore' => 'የሲንጋፒር መደበኛ ሰዓት (ሲንጋፖር)', 350 | 'Asia/Srednekolymsk' => 'የማጋዳን የሰዓት አቆጣጠር (ስሬድኔስኮልምስክ)', 351 | 'Asia/Taipei' => 'የታይፔይ ሰዓት (ታይፓይ)', 352 | 'Asia/Tashkent' => 'የኡዝቤኪስታን ሰዓት (ታሽኬንት)', 353 | 'Asia/Tbilisi' => 'የጂዮርጂያ ሰዓት (ትብሊሲ)', 354 | 'Asia/Tehran' => 'የኢራን ሰዓት (ቴህራን)', 355 | 'Asia/Thimphu' => 'የቡታን ሰዓት (ቲምፉ)', 356 | 'Asia/Tokyo' => 'የጃፓን ሰዓት (ቶኪዮ)', 357 | 'Asia/Tomsk' => 'ሩስያ ጊዜ (ቶምስክ)', 358 | 'Asia/Ulaanbaatar' => 'የኡላን ባቶር ጊዜ (ኡላአንባአታር)', 359 | 'Asia/Urumqi' => 'ቻይና ጊዜ (ኡሩምኪ)', 360 | 'Asia/Ust-Nera' => 'የቭላዲቮስቶክ የሰዓት አቆጣጠር (ኡስት-ኔራ)', 361 | 'Asia/Vientiane' => 'የኢንዶቻይና ሰዓት (ቬንቲአን)', 362 | 'Asia/Vladivostok' => 'የቭላዲቮስቶክ የሰዓት አቆጣጠር', 363 | 'Asia/Yakutsk' => 'ያኩትስክ የሰዓት አቆጣጠር', 364 | 'Asia/Yekaterinburg' => 'የየካተሪንበርግ ሰዓት አቆጣጠር', 365 | 'Asia/Yerevan' => 'የአርመኒያ ሰዓት (ይሬቫን)', 366 | 'Atlantic/Azores' => 'የአዞረስ ሰዓት', 367 | 'Atlantic/Bermuda' => 'የአትላንቲክ የሰዓት አቆጣጠር (ቤርሙዳ)', 368 | 'Atlantic/Canary' => 'የምዕራባዊ አውሮፓ ሰዓት (ካናሪ)', 369 | 'Atlantic/Cape_Verde' => 'የኬፕ ቨርዴ ሰዓት (ኬፕ ቬርደ)', 370 | 'Atlantic/Faeroe' => 'የምዕራባዊ አውሮፓ ሰዓት (ፋሮእ)', 371 | 'Atlantic/Madeira' => 'የምዕራባዊ አውሮፓ ሰዓት (ማዴራ)', 372 | 'Atlantic/Reykjavik' => 'ግሪንዊች ማዕከላዊ ሰዓት (ሬይክጃቪክ)', 373 | 'Atlantic/South_Georgia' => 'የደቡብ ጂዮርጂያ ሰዓት (ደቡብ ጆርጂያ)', 374 | 'Atlantic/St_Helena' => 'ግሪንዊች ማዕከላዊ ሰዓት (ቅድስት ሄለና)', 375 | 'Atlantic/Stanley' => 'የፋልክላንድ ደሴቶች ሰዓት (ስታንሌይ)', 376 | 'Australia/Adelaide' => 'የመካከለኛው አውስትራሊያ ሰዓት አቆጣጠር (አዴሌእድ)', 377 | 'Australia/Brisbane' => 'የምዕራባዊ አውስትራሊያ የሰዓት አቆጣጠር (ብሪስቤን)', 378 | 'Australia/Broken_Hill' => 'የመካከለኛው አውስትራሊያ ሰዓት አቆጣጠር (ብሮክን ሂል)', 379 | 'Australia/Currie' => 'የምዕራባዊ አውስትራሊያ የሰዓት አቆጣጠር (ከሪ)', 380 | 'Australia/Darwin' => 'የመካከለኛው አውስትራሊያ ሰዓት አቆጣጠር (ዳርዊን)', 381 | 'Australia/Eucla' => 'የአውስትራሊያ መካከለኛ ምስራቃዊ ሰዓት አቆጣጠር (ኡክላ)', 382 | 'Australia/Hobart' => 'የምዕራባዊ አውስትራሊያ የሰዓት አቆጣጠር (ሆባርት)', 383 | 'Australia/Lindeman' => 'የምዕራባዊ አውስትራሊያ የሰዓት አቆጣጠር (ሊንድማን)', 384 | 'Australia/Lord_Howe' => 'የሎርድ ሆዌ የሰዓት አቆጣጠር (ሎርድ ሆዊ)', 385 | 'Australia/Melbourne' => 'የምዕራባዊ አውስትራሊያ የሰዓት አቆጣጠር (ሜልቦርን)', 386 | 'Australia/Perth' => 'የምስራቃዊ አውስትራሊያ ሰዓት አቆጣጠር (ፐርዝ)', 387 | 'Australia/Sydney' => 'የምዕራባዊ አውስትራሊያ የሰዓት አቆጣጠር (ሲድኒ)', 388 | 'CST6CDT' => 'የሰሜን አሜሪካ የመካከለኛ ሰዓት አቆጣጠር', 389 | 'EST5EDT' => 'ምስራቃዊ ሰዓት አቆጣጠር', 390 | 'Etc/GMT' => 'ግሪንዊች ማዕከላዊ ሰዓት', 391 | 'Etc/UTC' => 'የተቀነባበረ ሁለገብ ሰዓት', 392 | 'Europe/Amsterdam' => 'የመካከለኛው አውሮፓ ሰዓት (አምስተርዳም)', 393 | 'Europe/Andorra' => 'የመካከለኛው አውሮፓ ሰዓት (አንዶራ)', 394 | 'Europe/Astrakhan' => 'የሞስኮ ሰዓት አቆጣጠር (አስትራክሃን)', 395 | 'Europe/Athens' => 'የምስራቃዊ አውሮፓ ሰዓት (አቴንስ)', 396 | 'Europe/Belgrade' => 'የመካከለኛው አውሮፓ ሰዓት (ቤልግሬድ)', 397 | 'Europe/Berlin' => 'የመካከለኛው አውሮፓ ሰዓት (በርሊን)', 398 | 'Europe/Bratislava' => 'የመካከለኛው አውሮፓ ሰዓት (ብራቲስላቫ)', 399 | 'Europe/Brussels' => 'የመካከለኛው አውሮፓ ሰዓት (ብራሰልስ)', 400 | 'Europe/Bucharest' => 'የምስራቃዊ አውሮፓ ሰዓት (ቡካሬስት)', 401 | 'Europe/Budapest' => 'የመካከለኛው አውሮፓ ሰዓት (ቡዳፔስት)', 402 | 'Europe/Busingen' => 'የመካከለኛው አውሮፓ ሰዓት (ቡሲንገን)', 403 | 'Europe/Chisinau' => 'የምስራቃዊ አውሮፓ ሰዓት (ቺስናኡ)', 404 | 'Europe/Copenhagen' => 'የመካከለኛው አውሮፓ ሰዓት (ኮፐንሃገን)', 405 | 'Europe/Dublin' => 'ግሪንዊች ማዕከላዊ ሰዓት (ደብሊን)', 406 | 'Europe/Gibraltar' => 'የመካከለኛው አውሮፓ ሰዓት (ጂብራልታር)', 407 | 'Europe/Guernsey' => 'ግሪንዊች ማዕከላዊ ሰዓት (ጉርነሲ)', 408 | 'Europe/Helsinki' => 'የምስራቃዊ አውሮፓ ሰዓት (ሄልሲንኪ)', 409 | 'Europe/Isle_of_Man' => 'ግሪንዊች ማዕከላዊ ሰዓት (አይስል ኦፍ ማን)', 410 | 'Europe/Istanbul' => 'ቱርክ ጊዜ (ኢስታንቡል)', 411 | 'Europe/Jersey' => 'ግሪንዊች ማዕከላዊ ሰዓት (ጀርሲ)', 412 | 'Europe/Kaliningrad' => 'የምስራቃዊ አውሮፓ ሰዓት (ካሊኒንግራድ)', 413 | 'Europe/Kiev' => 'የምስራቃዊ አውሮፓ ሰዓት (ኪየቭ)', 414 | 'Europe/Kirov' => 'ሩስያ ጊዜ (ኪሮቭ)', 415 | 'Europe/Lisbon' => 'የምዕራባዊ አውሮፓ ሰዓት (ሊዝበን)', 416 | 'Europe/Ljubljana' => 'የመካከለኛው አውሮፓ ሰዓት (ልጁብልጃና)', 417 | 'Europe/London' => 'ግሪንዊች ማዕከላዊ ሰዓት (ለንደን)', 418 | 'Europe/Luxembourg' => 'የመካከለኛው አውሮፓ ሰዓት (ሉክሰምበርግ)', 419 | 'Europe/Madrid' => 'የመካከለኛው አውሮፓ ሰዓት (ማድሪድ)', 420 | 'Europe/Malta' => 'የመካከለኛው አውሮፓ ሰዓት (ማልታ)', 421 | 'Europe/Mariehamn' => 'የምስራቃዊ አውሮፓ ሰዓት (ሜሪሃምን)', 422 | 'Europe/Minsk' => 'የሞስኮ ሰዓት አቆጣጠር (ሚንስክ)', 423 | 'Europe/Monaco' => 'የመካከለኛው አውሮፓ ሰዓት (ሞናኮ)', 424 | 'Europe/Moscow' => 'የሞስኮ ሰዓት አቆጣጠር', 425 | 'Europe/Oslo' => 'የመካከለኛው አውሮፓ ሰዓት (ኦስሎ)', 426 | 'Europe/Paris' => 'የመካከለኛው አውሮፓ ሰዓት (ፓሪስ)', 427 | 'Europe/Podgorica' => 'የመካከለኛው አውሮፓ ሰዓት (ፖድጎሪካ)', 428 | 'Europe/Prague' => 'የመካከለኛው አውሮፓ ሰዓት (ፕራግ)', 429 | 'Europe/Riga' => 'የምስራቃዊ አውሮፓ ሰዓት (ሪጋ)', 430 | 'Europe/Rome' => 'የመካከለኛው አውሮፓ ሰዓት (ሮም)', 431 | 'Europe/Samara' => 'የሳማራ ሰዓት አቆጣጠር', 432 | 'Europe/San_Marino' => 'የመካከለኛው አውሮፓ ሰዓት (ሳን ማሪኖ)', 433 | 'Europe/Sarajevo' => 'የመካከለኛው አውሮፓ ሰዓት (ሳሪየቮ)', 434 | 'Europe/Saratov' => 'የሞስኮ ሰዓት አቆጣጠር (ሳራቶቭ)', 435 | 'Europe/Simferopol' => 'የሞስኮ ሰዓት አቆጣጠር (ሲምፈሮፖል)', 436 | 'Europe/Skopje' => 'የመካከለኛው አውሮፓ ሰዓት (ስኮፕየ)', 437 | 'Europe/Sofia' => 'የምስራቃዊ አውሮፓ ሰዓት (ሶፊያ)', 438 | 'Europe/Stockholm' => 'የመካከለኛው አውሮፓ ሰዓት (ስቶክሆልም)', 439 | 'Europe/Tallinn' => 'የምስራቃዊ አውሮፓ ሰዓት (ታሊን)', 440 | 'Europe/Tirane' => 'የመካከለኛው አውሮፓ ሰዓት (ቴራን)', 441 | 'Europe/Ulyanovsk' => 'የሞስኮ ሰዓት አቆጣጠር (ኡልያኖቭስክ)', 442 | 'Europe/Uzhgorod' => 'የምስራቃዊ አውሮፓ ሰዓት (ኡዝጎሮድ)', 443 | 'Europe/Vaduz' => 'የመካከለኛው አውሮፓ ሰዓት (ቫዱዝ)', 444 | 'Europe/Vatican' => 'የመካከለኛው አውሮፓ ሰዓት (ቫቲካን)', 445 | 'Europe/Vienna' => 'የመካከለኛው አውሮፓ ሰዓት (ቪየና)', 446 | 'Europe/Vilnius' => 'የምስራቃዊ አውሮፓ ሰዓት (ቪሊነስ)', 447 | 'Europe/Volgograd' => 'የቮልጎራድ የሰዓት አቆጣጠር', 448 | 'Europe/Warsaw' => 'የመካከለኛው አውሮፓ ሰዓት (ዋርሶው)', 449 | 'Europe/Zagreb' => 'የመካከለኛው አውሮፓ ሰዓት (ዛግሬብ)', 450 | 'Europe/Zaporozhye' => 'የምስራቃዊ አውሮፓ ሰዓት (ዛፖሮዚይ)', 451 | 'Europe/Zurich' => 'የመካከለኛው አውሮፓ ሰዓት (ዙሪክ)', 452 | 'Indian/Antananarivo' => 'የምስራቅ አፍሪካ ሰዓት (አንታናናሪቮ)', 453 | 'Indian/Chagos' => 'የህንድ ውቅያኖስ ሰዓት (ቻጎስ)', 454 | 'Indian/Christmas' => 'የገና ደሴት ሰዓት', 455 | 'Indian/Cocos' => 'የኮኮስ ደሴቶች ሰዓት', 456 | 'Indian/Comoro' => 'የምስራቅ አፍሪካ ሰዓት (ኮሞሮ)', 457 | 'Indian/Kerguelen' => 'የፈረንሳይ ደቡባዊ እና አንታርክቲክ ሰዓት (ኬርጉለን)', 458 | 'Indian/Mahe' => 'የሴሸልስ ሰዓት (ማሄ)', 459 | 'Indian/Maldives' => 'የማልዲቭስ ሰዓት (ማልዲቨ)', 460 | 'Indian/Mauritius' => 'የማውሪሺየስ ሰዓት (ሞሪሽየስ)', 461 | 'Indian/Mayotte' => 'የምስራቅ አፍሪካ ሰዓት (ማዮቴ)', 462 | 'Indian/Reunion' => 'የሬዩኒየን ሰዓት', 463 | 'MST7MDT' => 'የተራራ የሰዓት አቆጣጠር', 464 | 'PST8PDT' => 'የፓስፊክ ሰዓት አቆጣጠር', 465 | 'Pacific/Apia' => 'የአፒያ ሰዓት (አፒአ)', 466 | 'Pacific/Auckland' => 'የኒው ዚላንድ ሰዓት (ኦክላንድ)', 467 | 'Pacific/Bougainville' => 'የፓፗ ኒው ጊኒ ሰዓት (ቦጌይንቪል)', 468 | 'Pacific/Chatham' => 'የቻታም ሰዓት', 469 | 'Pacific/Easter' => 'የኢስተር ደሴት ሰዓት (ፋሲካ)', 470 | 'Pacific/Efate' => 'የቫኗቱ ሰዓት (ኢፋቴ)', 471 | 'Pacific/Enderbury' => 'የፊኒክስ ደሴቶች ሰዓት (ኢንደርበሪ)', 472 | 'Pacific/Fakaofo' => 'የቶኬላው ሰዓት (ፋካኦፎ)', 473 | 'Pacific/Fiji' => 'የፊጂ ሰዓት', 474 | 'Pacific/Funafuti' => 'የቱቫሉ ሰዓት (ፈናፉቲ)', 475 | 'Pacific/Galapagos' => 'የጋላፓጎስ ሰዓት', 476 | 'Pacific/Gambier' => 'የጋምቢየር ሰዓት', 477 | 'Pacific/Guadalcanal' => 'የሰለሞን ደሴቶች ሰዓት (ጉዋዳልካናል)', 478 | 'Pacific/Guam' => 'የቻሞሮ መደበኛ ሰዓት (ጉአም)', 479 | 'Pacific/Honolulu' => 'የሃዋይ አሌኡት ሰዓት አቆጣጠር (ሆኖሉሉ)', 480 | 'Pacific/Johnston' => 'የሃዋይ አሌኡት ሰዓት አቆጣጠር (ጆንስተን)', 481 | 'Pacific/Kiritimati' => 'የላይን ደሴቶች ሰዓት (ኪሪቲማቲ)', 482 | 'Pacific/Kosrae' => 'የኮስራኤ ሰዓት (ኮስሬ)', 483 | 'Pacific/Kwajalein' => 'የማርሻል ደሴቶች ሰዓት (ክዋጃሊን)', 484 | 'Pacific/Majuro' => 'የማርሻል ደሴቶች ሰዓት (ማጁሩ)', 485 | 'Pacific/Marquesas' => 'የማርኴሳስ ሰዓት (ማርክዌሳስ)', 486 | 'Pacific/Midway' => 'የሳሞዋ ሰዓት (ሚድወይ)', 487 | 'Pacific/Nauru' => 'የናውሩ ሰዓት', 488 | 'Pacific/Niue' => 'የኒዩዌ ሰዓት (ኒዌ)', 489 | 'Pacific/Norfolk' => 'የኖርፎልክ ደሴቶች ሰዓት', 490 | 'Pacific/Noumea' => 'የኒው ካሌዶኒያ ሰዓት (ናኦሚአ)', 491 | 'Pacific/Pago_Pago' => 'የሳሞዋ ሰዓት (ፓጎ ፓጎ)', 492 | 'Pacific/Palau' => 'የፓላው ሰዓት', 493 | 'Pacific/Pitcairn' => 'የፒትካይርን ሰዓት (ፒትከይርን)', 494 | 'Pacific/Ponape' => 'የፖናፔ ሰዓት (ፖህንፔ)', 495 | 'Pacific/Port_Moresby' => 'የፓፗ ኒው ጊኒ ሰዓት (ፖርት ሞሬስባይ)', 496 | 'Pacific/Rarotonga' => 'የኩክ ደሴቶች ሰዓት (ራሮቶንጋ)', 497 | 'Pacific/Saipan' => 'የቻሞሮ መደበኛ ሰዓት (ሴይፓን)', 498 | 'Pacific/Tahiti' => 'የታሂቲ ሰዓት', 499 | 'Pacific/Tarawa' => 'የጂልበርት ደሴቶች ሰዓት (ታራዋ)', 500 | 'Pacific/Tongatapu' => 'የቶንጋ ሰዓት (ቶንጋታፑ)', 501 | 'Pacific/Truk' => 'የቹክ ሰዓት', 502 | 'Pacific/Wake' => 'የዌክ ደሴት ሰዓት (ዋኬ)', 503 | 'Pacific/Wallis' => 'የዋሊስ እና ፉቱና ሰዓት', 504 | ]; 505 | } 506 | --------------------------------------------------------------------------------