mirror of
				https://github.com/e107inc/e107.git
				synced 2025-10-26 19:21:54 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			285 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			285 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * e107 website system
 | |
|  *
 | |
|  * Copyright (C) 2008-2021 e107 Inc (e107.org)
 | |
|  * Released under the terms and conditions of the
 | |
|  * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
 | |
|  *
 | |
|  */
 | |
| 
 | |
| namespace e107\Shims\Internal;
 | |
| 
 | |
| use DateTimeInterface;
 | |
| use DateTimeZone;
 | |
| 
 | |
| /**
 | |
|  *
 | |
|  */
 | |
| trait StrftimeTrait
 | |
| {
 | |
| 	/**
 | |
| 	 * Polyfill for {@see strftime()}, which was deprecated in PHP 8.1
 | |
| 	 *
 | |
| 	 * The implementation is an approximation that may be wrong for some obscure formatting characters.
 | |
| 	 *
 | |
| 	 * This function will attempt to use the locale features provided by
 | |
| 	 * {@link https://www.php.net/manual/en/book.intl.php ext-intl} and fall back to the PHP-builtin {@see DateTime}.
 | |
| 	 * Note that without ext-intl, formatted times will be in English.
 | |
| 	 *
 | |
| 	 * @param string   $format    The old {@see strftime()} format string
 | |
| 	 * @param int|null $timestamp A Unix epoch timestamp. If null, defaults to the value of {@see time()}.
 | |
| 	 * @return string Datetime formatted according to the provided arguments
 | |
| 	 */
 | |
| 	public static function strftime($format, $timestamp = null)
 | |
| 	{
 | |
| 		if ($timestamp === null) $timestamp = time();
 | |
| 		$datetime = date_create("@$timestamp");
 | |
| 		$datetime->setTimezone(new DateTimeZone(date_default_timezone_get()));
 | |
| 
 | |
| 		foreach (self::getFormatMap() as $strftime_key => $date_format_key)
 | |
| 		{
 | |
| 			if (!is_string($date_format_key) && is_callable($date_format_key))
 | |
| 			{
 | |
| 				$replacement = self::escapeDateTimePattern($date_format_key($datetime));
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				$replacement = $date_format_key;
 | |
| 			}
 | |
| 			$format = str_replace($strftime_key, $replacement, $format);
 | |
| 		}
 | |
| 
 | |
| 		return self::date_format($datetime, $format);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @param DateTimeInterface $datetime
 | |
| 	 * @param string            $format
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	protected static function date_format($datetime, $format)
 | |
| 	{
 | |
| 		if (!extension_loaded('intl'))
 | |
| 		{
 | |
| 			return date_format($datetime, $format);
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		$timezone = 'GMT'.date('P');
 | |
| 		$formatter = new \IntlDateFormatter(
 | |
| 			self::getSensibleLocale(),
 | |
| 			\IntlDateFormatter::NONE,
 | |
| 			\IntlDateFormatter::NONE,
 | |
| 			null, // More accurate timezone. @see https://stackoverflow.com/questions/31707395/why-php-intldateformatter-returns-wrong-date-1-hour
 | |
| 			null,
 | |
| 			$format
 | |
| 		);
 | |
| 
 | |
| 		datefmt_set_timezone($formatter, $timezone);
 | |
| 
 | |
| 		return $formatter->format($datetime);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Try to figure out the e107 locale, falling back to the {@see setlocale()} value, and falling back again to "C"
 | |
| 	 *
 | |
| 	 * @return string An {@link http://www.faqs.org/rfcs/rfc1766 RFC 1766} language tag such as "en_US"
 | |
| 	 */
 | |
| 	protected static function getSensibleLocale()
 | |
| 	{
 | |
| 		if (defined('CORE_LC') && defined('CORE_LC2'))
 | |
| 		{
 | |
| 			return strtolower(CORE_LC) . "_" . strtoupper(CORE_LC2);
 | |
| 		}
 | |
| 
 | |
| 		$setlocale = setlocale(LC_ALL, "0");
 | |
| 		return $setlocale ?: 'C';
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Escape a literal string for use inside a datetime pattern
 | |
| 	 *
 | |
| 	 * Implementation differs depending on whether {@link https://www.php.net/manual/en/book.intl.php ext-intl} is
 | |
| 	 * enabled
 | |
| 	 *
 | |
| 	 * @param string $input
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	protected static function escapeDateTimePattern($input)
 | |
| 	{
 | |
| 		if (extension_loaded('intl'))
 | |
| 		{
 | |
| 			return "'" . str_replace("'", "''", $input) . "'";
 | |
| 		}
 | |
| 
 | |
| 		return chunk_split($input, 1, "\\");
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get the {@see strftime()} format to date format pattern mapping depending on if
 | |
| 	 * {@link https://www.php.net/manual/en/book.intl.php ext-intl} is enabled
 | |
| 	 *
 | |
| 	 * @return array<string, string|callable>
 | |
| 	 */
 | |
| 	protected static function getFormatMap()
 | |
| 	{
 | |
| 		if (extension_loaded('intl'))
 | |
| 		{
 | |
| 			return self::getFormatMapForIntlDateFormatter();
 | |
| 		}
 | |
| 
 | |
| 		return self::getFormatMapForDateTime();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	protected static function getFormatMapForIntlDateFormatter()
 | |
| 	{
 | |
| 		return [
 | |
| 			'%a' => 'eee',
 | |
| 			'%A' => 'eeee',
 | |
| 			'%d' => 'dd',
 | |
| 			'%e' => function($datetime)
 | |
| 			{
 | |
| 				return str_pad(self::date_format($datetime, 'd'), 2, " ", STR_PAD_LEFT);
 | |
| 			},
 | |
| 			'%j' => function($datetime)
 | |
| 			{
 | |
| 				return str_pad(self::date_format($datetime, 'D'), 3, "0", STR_PAD_LEFT);
 | |
| 			},
 | |
| 			'%u' => 'e',
 | |
| 			'%w' => function($datetime)
 | |
| 			{
 | |
| 				return date_format($datetime, 'w');
 | |
| 			},
 | |
| 			'%U' => 'w',
 | |
| 			'%V' => 'ww',
 | |
| 			'%W' => function($datetime)
 | |
| 			{
 | |
| 				return date_format($datetime, 'W');
 | |
| 			},
 | |
| 			'%b' => 'MMM',
 | |
| 			'%B' => 'MMMM',
 | |
| 			'%h' => 'MMM',
 | |
| 			'%m' => 'MM',
 | |
| 			'%C' => function($datetime)
 | |
| 			{
 | |
| 				return (string) ((int) self::date_format($datetime, 'y') / 100);
 | |
| 			},
 | |
| 			'%g' => 'yy',
 | |
| 			'%G' => 'y',
 | |
| 			'%y' => 'yy',
 | |
| 			'%Y' => 'y',
 | |
| 			'%H' => 'HH',
 | |
| 			'%k' => function($datetime)
 | |
| 			{
 | |
| 				return str_pad(self::date_format($datetime, 'H'), 2, " ", STR_PAD_LEFT);
 | |
| 			},
 | |
| 			'%I' => 'hh',
 | |
| 			'%l' => function($datetime)
 | |
| 			{
 | |
| 				return str_pad(self::date_format($datetime, 'h'), 2, " ", STR_PAD_LEFT);
 | |
| 			},
 | |
| 			'%M' => 'mm',
 | |
| 			'%p' => function($datetime)
 | |
| 			{
 | |
| 				return strtoupper(self::date_format($datetime, 'a'));
 | |
| 			},
 | |
| 			'%P' => 'a',
 | |
| 			'%r' => 'hh:mm:ss a',
 | |
| 			'%R' => 'HH:mm',
 | |
| 			'%S' => 'ss',
 | |
| 			'%T' => 'HH:mm:ss',
 | |
| 			'%X' => 'HH:mm:ss',
 | |
| 			'%z' => 'Z',
 | |
| 			'%Z' => 'z',
 | |
| 			'%c' => function($datetime)
 | |
| 			{
 | |
| 				/** @noinspection PhpComposerExtensionStubsInspection */
 | |
| 				return \IntlDateFormatter::formatObject($datetime);
 | |
| 			},
 | |
| 			'%D' => 'MM/dd/yy',
 | |
| 			'%F' => 'y-MM-dd',
 | |
| 			'%s' => function($datetime)
 | |
| 			{
 | |
| 				return date_timestamp_get($datetime);
 | |
| 			},
 | |
| 			'%x' => function($datetime)
 | |
| 			{
 | |
| 				/** @noinspection PhpComposerExtensionStubsInspection */
 | |
| 				return \IntlDateFormatter::formatObject($datetime, \IntlDateFormatter::SHORT);
 | |
| 			},
 | |
| 			'%n' => "\n",
 | |
| 			'%t' => "\t",
 | |
| 			'%%' => "'%'",
 | |
| 		];
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	protected static function getFormatMapForDateTime()
 | |
| 	{
 | |
| 		return [
 | |
| 			'%a' => 'D',
 | |
| 			'%A' => 'l',
 | |
| 			'%d' => 'd',
 | |
| 			'%e' => function($datetime)
 | |
| 			{
 | |
| 				return str_pad(self::date_format($datetime, 'n'), 2, " ", STR_PAD_LEFT);
 | |
| 			},
 | |
| 			'%j' => function($datetime)
 | |
| 			{
 | |
| 				return str_pad(self::date_format($datetime, 'z'), 3, "0", STR_PAD_LEFT);
 | |
| 			},
 | |
| 			'%u' => 'N',
 | |
| 			'%w' => 'w',
 | |
| 			'%U' => 'W',
 | |
| 			'%V' => 'W',
 | |
| 			'%W' => 'W',
 | |
| 			'%b' => 'M',
 | |
| 			'%B' => 'F',
 | |
| 			'%h' => 'M',
 | |
| 			'%m' => 'm',
 | |
| 			'%C' => function($datetime)
 | |
| 			{
 | |
| 				return (string) ((int) self::date_format($datetime, 'Y') / 100);
 | |
| 			},
 | |
| 			'%g' => 'y',
 | |
| 			'%G' => 'Y',
 | |
| 			'%y' => 'y',
 | |
| 			'%Y' => 'Y',
 | |
| 			'%H' => 'H',
 | |
| 			'%k' => function($datetime)
 | |
| 			{
 | |
| 				return str_pad(self::date_format($datetime, 'G'), 2, " ", STR_PAD_LEFT);
 | |
| 			},
 | |
| 			'%I' => 'h',
 | |
| 			'%l' => function($datetime)
 | |
| 			{
 | |
| 				return str_pad(self::date_format($datetime, 'g'), 2, " ", STR_PAD_LEFT);
 | |
| 			},
 | |
| 			'%M' => 'i',
 | |
| 			'%p' => 'A',
 | |
| 			'%P' => 'a',
 | |
| 			'%r' => 'h:i:s A',
 | |
| 			'%R' => 'H:i',
 | |
| 			'%S' => 's',
 | |
| 			'%T' => 'H:i:s',
 | |
| 			'%X' => 'H:i:s',
 | |
| 			'%z' => 'O',
 | |
| 			'%Z' => 'T',
 | |
| 			'%c' => 'r',
 | |
| 			'%D' => 'm/d/y',
 | |
| 			'%F' => 'Y-m-d',
 | |
| 			'%s' => 'U',
 | |
| 			'%x' => 'Y-m-d',
 | |
| 			'%n' => "\n",
 | |
| 			'%t' => "\t",
 | |
| 			'%%' => '\%',
 | |
| 		];
 | |
| 	}
 | |
| }
 |