349 lines
9.7 KiB
PHP
349 lines
9.7 KiB
PHP
<?php
|
|
/**
|
|
* @package FOF
|
|
* @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
|
* @license GNU General Public License version 3, or later
|
|
*/
|
|
|
|
namespace FOF40\Date;
|
|
|
|
defined('_JEXEC') || die;
|
|
|
|
use DateTime;
|
|
use DateTimeZone;
|
|
use Exception;
|
|
use FOF40\Container\Container;
|
|
use Joomla\CMS\User\User;
|
|
|
|
/**
|
|
* A helper class to wrangle timezones, as used by Joomla!.
|
|
*
|
|
* @package FOF40\Utils
|
|
*
|
|
* @since 3.1.3
|
|
*/
|
|
class TimezoneWrangler
|
|
{
|
|
/**
|
|
* The default timestamp format string to use when one is not provided
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $defaultFormat = 'Y-m-d H:i:s T';
|
|
|
|
/**
|
|
* When set, this timezone will be used instead of the Joomla! applicable timezone for the user.
|
|
*
|
|
* @var DateTimeZone
|
|
*/
|
|
protected $forcedTimezone;
|
|
|
|
/**
|
|
* Cache of user IDs to applicable timezones
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $userToTimezone = [];
|
|
|
|
/**
|
|
* The component container for which we are created
|
|
*
|
|
* @var Container
|
|
*/
|
|
protected $container;
|
|
|
|
public function __construct(Container $container)
|
|
{
|
|
$this->container = $container;
|
|
}
|
|
|
|
/**
|
|
* Get the default timestamp format to use when one is not provided
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getDefaultFormat(): string
|
|
{
|
|
return $this->defaultFormat;
|
|
}
|
|
|
|
/**
|
|
* Set the default timestamp format to use when one is not provided
|
|
*
|
|
* @param string $defaultFormat
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setDefaultFormat(string $defaultFormat): void
|
|
{
|
|
$this->defaultFormat = $defaultFormat;
|
|
}
|
|
|
|
/**
|
|
* Returns the forced timezone which is used instead of the applicable Joomla! timezone.
|
|
*
|
|
* @return DateTimeZone
|
|
*/
|
|
public function getForcedTimezone(): DateTimeZone
|
|
{
|
|
return $this->forcedTimezone;
|
|
}
|
|
|
|
/**
|
|
* Sets the forced timezone which is used instead of the applicable Joomla! timezone. If the new timezone is
|
|
* different than the existing one we will also reset the user to timezone cache.
|
|
*
|
|
* @param DateTimeZone|string $forcedTimezone
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setForcedTimezone($forcedTimezone): void
|
|
{
|
|
// Are we unsetting the forced TZ?
|
|
if (empty($forcedTimezone))
|
|
{
|
|
$this->forcedTimezone = null;
|
|
$this->resetCache();
|
|
|
|
return;
|
|
}
|
|
|
|
// If the new TZ is a string we have to create an object
|
|
if (is_string($forcedTimezone))
|
|
{
|
|
$forcedTimezone = new DateTimeZone($forcedTimezone);
|
|
}
|
|
|
|
$oldTZ = '';
|
|
|
|
if (is_object($this->forcedTimezone) && ($this->forcedTimezone instanceof DateTimeZone))
|
|
{
|
|
$oldTZ = $this->forcedTimezone->getName();
|
|
}
|
|
|
|
if ($oldTZ === $forcedTimezone->getName())
|
|
{
|
|
return;
|
|
}
|
|
|
|
$this->forcedTimezone = $forcedTimezone;
|
|
|
|
$this->resetCache();
|
|
}
|
|
|
|
/**
|
|
* Reset the user to timezone cache. This is done automatically every time you change the forced timezone.
|
|
*/
|
|
public function resetCache(): void
|
|
{
|
|
$this->userToTimezone = [];
|
|
}
|
|
|
|
/**
|
|
* Get the applicable timezone for a user. If the user is not a guest and they have a timezone set up in their
|
|
* profile it will be used. Otherwise we fall back to the Server Timezone as set up in Global Configuration. If that
|
|
* fails, we use GMT. However, if you have used a non-blank forced timezone that will be used instead, circumventing
|
|
* this calculation. Therefore the returned timezone is one of the following, by descending order of priority:
|
|
* - Forced timezone
|
|
* - User's timezone (explicitly set in their user profile)
|
|
* - Server Timezone (from Joomla's Global Configuration)
|
|
* - GMT
|
|
*
|
|
* @param User|null $user
|
|
*
|
|
* @return DateTimeZone
|
|
*/
|
|
public function getApplicableTimezone(?User $user = null): DateTimeZone
|
|
{
|
|
// If we have a forced timezone use it instead of trying to figure anything out.
|
|
if (is_object($this->forcedTimezone))
|
|
{
|
|
return $this->forcedTimezone;
|
|
}
|
|
|
|
// No user? Get the current user.
|
|
if (is_null($user))
|
|
{
|
|
$user = $this->container->platform->getUser();
|
|
}
|
|
|
|
// If there is a cached timezone return that instead.
|
|
if (isset($this->userToTimezone[$user->id]))
|
|
{
|
|
return $this->userToTimezone[$user->id];
|
|
}
|
|
|
|
// Prefer the user timezone if it's set.
|
|
if (!$user->guest)
|
|
{
|
|
$tz = $user->getParam('timezone', null);
|
|
|
|
if (!empty($tz))
|
|
{
|
|
try
|
|
{
|
|
$this->userToTimezone[$user->id] = new DateTimeZone($tz);
|
|
|
|
return $this->userToTimezone[$user->id];
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the Server Timezone from Global Configuration with a fallback to GMT
|
|
$tz = $this->container->platform->getConfig()->get('offset', 'GMT');
|
|
|
|
try
|
|
{
|
|
$this->userToTimezone[$user->id] = new DateTimeZone($tz);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
// If an invalid timezone was set we get to use GMT
|
|
$this->userToTimezone[$user->id] = new DateTimeZone('GMT');
|
|
}
|
|
|
|
return $this->userToTimezone[$user->id];
|
|
}
|
|
|
|
/**
|
|
* Returns a FOF Date object with its timezone set to the user's applicable timezone.
|
|
*
|
|
* If no user is specified the current user will be used.
|
|
*
|
|
* $time can be a DateTime object (including Date and Joomla Date), an integer (UNIX timestamp) or a date string.
|
|
* If no timezone is specified in a date string we assume it's GMT.
|
|
*
|
|
* @param User $user Applicable user for timezone calculation. Null = current user.
|
|
* @param mixed $time Source time. Leave blank for current date/time.
|
|
*
|
|
* @return Date
|
|
*/
|
|
public function getLocalDateTime(?User $user, $time = null): Date
|
|
{
|
|
$time = empty($time) ? 'now' : $time;
|
|
$date = new Date($time);
|
|
$tz = $this->getApplicableTimezone($user);
|
|
$date->setTimezone($tz);
|
|
|
|
return $date;
|
|
}
|
|
|
|
/**
|
|
* Returns a FOF Date object with its timezone set to GMT.
|
|
*
|
|
* If no user is specified the current user will be used.
|
|
*
|
|
* $time can be a DateTime object (including Date and Joomla Date), an integer (UNIX timestamp) or a date string.
|
|
* If no timezone is specified in a date string we assume it's the user's applicable timezone.
|
|
*
|
|
* @param User $user
|
|
* @param mixed $time
|
|
*
|
|
* @return Date
|
|
*/
|
|
public function getGMTDateTime(?User $user, $time): Date
|
|
{
|
|
$time = empty($time) ? 'now' : $time;
|
|
$tz = $this->getApplicableTimezone($user);
|
|
$date = new Date($time, $tz);
|
|
$gmtTimezone = new DateTimeZone('GMT');
|
|
$date->setTimezone($gmtTimezone);
|
|
|
|
return $date;
|
|
}
|
|
|
|
/**
|
|
* Returns a formatted date string in the user's applicable timezone.
|
|
*
|
|
* If no format is specified we will use $defaultFormat.
|
|
*
|
|
* If no user is specified the current user will be used.
|
|
*
|
|
* $time can be a DateTime object (including Date and Joomla Date), an integer (UNIX timestamp) or a date string.
|
|
* If no timezone is specified in a date string we assume it's GMT.
|
|
*
|
|
* $translate requires you to have loaded the relevant translation file (e.g. en-GB.ini). CMSApplication does that
|
|
* for you automatically. If you're under CLI, a custom WebApplication etc you will probably have to load this file
|
|
* manually.
|
|
*
|
|
* @param string|null $format Timestamp format. If empty $defaultFormat is used.
|
|
* @param User|null $user Applicable user for timezone calculation. Null = current
|
|
* user.
|
|
* @param DateTime|Date|string|int|null $time Source time. Leave blank for current date/time.
|
|
* @param bool $translate Translate day of week and month names?
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getLocalTimeStamp(?string $format = null, ?User $user = null, $time = null, bool $translate = false): string
|
|
{
|
|
$date = $this->getLocalDateTime($user, $time);
|
|
$format = empty($format) ? $this->defaultFormat : $format;
|
|
|
|
return $date->format($format, true, $translate);
|
|
}
|
|
|
|
/**
|
|
* Returns a formatted date string in the GMT timezone.
|
|
*
|
|
* If no format is specified we will use $defaultFormat.
|
|
*
|
|
* If no user is specified the current user will be used.
|
|
*
|
|
* $time can be a DateTime object (including Date and Joomla Date), an integer (UNIX timestamp) or a date string.
|
|
* If no timezone is specified in a date string we assume it's the user's applicable timezone.
|
|
*
|
|
* $translate requires you to have loaded the relevant translation file (e.g. en-GB.ini). CMSApplication does that
|
|
* for you automatically. If you're under CLI, a custom WebApplication etc you will probably have to load this file
|
|
* manually.
|
|
*
|
|
* @param string|null $format Timestamp format. If empty $defaultFormat is used.
|
|
* @param User|null $user Applicable user for timezone calculation. Null = current
|
|
* user.
|
|
* @param DateTime|Date|string|int|null $time Source time. Leave blank for current date/time.
|
|
* @param bool $translate Translate day of week and month names?
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getGMTTimeStamp(?string $format = null, ?User $user = null, $time = null, bool $translate = false): string
|
|
{
|
|
$date = $this->localToGMT($user, $time);
|
|
$format = empty($format) ? $this->defaultFormat : $format;
|
|
|
|
return $date->format($format, true, $translate);
|
|
}
|
|
|
|
/**
|
|
* Convert a local time back to GMT. Returns a Date object with its timezone set to GMT.
|
|
*
|
|
* This is an alias to getGMTDateTime
|
|
*
|
|
* @param string|Date $time
|
|
* @param User|null $user
|
|
*
|
|
* @return Date
|
|
*/
|
|
public function localToGMT($time, ?User $user = null): Date
|
|
{
|
|
return $this->getGMTDateTime($user, $time);
|
|
}
|
|
|
|
/**
|
|
* Convert a GMT time to local timezone. Returns a Date object with its timezone set to the applicable user's TZ.
|
|
*
|
|
* This is an alias to getLocalDateTime
|
|
*
|
|
* @param string|Date $time
|
|
* @param User|null $user
|
|
*
|
|
* @return Date
|
|
*/
|
|
public function GMTToLocal($time, ?User $user = null): Date
|
|
{
|
|
return $this->getLocalDateTime($user, $time);
|
|
}
|
|
}
|