Issue:
PM-444: 0013316: Be able to assign users to different time zone
PM-3493: Agregar soporte multiple timezone a los endpoints usando formato fecha ISO 8601
Cause:
New feature
Solution:
Added functionality for time zone
362 lines
13 KiB
PHP
362 lines
13 KiB
PHP
<?php
|
|
namespace ProcessMaker\Util;
|
|
|
|
class DateTime
|
|
{
|
|
const ISO8601 = 'Y-m-d\TH:i:sP';
|
|
|
|
const REGEXPDATE = '[1-9]\d{3}\-(?:0[1-9]|1[0-2])\-(?:0[1-9]|[12][0-9]|3[01])';
|
|
const REGEXPTIME = '(?:[0-1]\d|2[0-3])\:[0-5]\d\:[0-5]\d';
|
|
|
|
/**
|
|
* Get Time Zone Offset by Time Zone ID
|
|
*
|
|
* @param string $timeZoneId Time Zone ID
|
|
*
|
|
* @return int Return the Time Zone Offset; false otherwise
|
|
*/
|
|
public function getTimeZoneOffsetByTimeZoneId($timeZoneId)
|
|
{
|
|
try {
|
|
$dt = new \DateTime(null, new \DateTimeZone($timeZoneId));
|
|
|
|
//Return
|
|
return $dt->getOffset();
|
|
} catch (\Exception $e) {
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Time Zone ID by Time Zone Offset
|
|
*
|
|
* @param int $offset Time Zone Offset
|
|
*
|
|
* @return string Return the Time Zone ID; UTC ID otherwise
|
|
*/
|
|
public function getTimeZoneIdByTimeZoneOffset($offset)
|
|
{
|
|
try {
|
|
foreach (\DateTimeZone::listIdentifiers() as $value) {
|
|
$timeZoneOffset = self::getTimeZoneOffsetByTimeZoneId($value);
|
|
|
|
if ($timeZoneOffset !== false && $timeZoneOffset == $offset) {
|
|
//Return
|
|
return $value;
|
|
}
|
|
}
|
|
|
|
//Return
|
|
return 'UTC';
|
|
} catch (\Exception $e) {
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Time Zone ID by UTC Offset
|
|
*
|
|
* @param string $utcOffset UTC Offset
|
|
*
|
|
* @return string Return the Time Zone ID; UTC ID otherwise
|
|
*/
|
|
public function getTimeZoneIdByUtcOffset($utcOffset)
|
|
{
|
|
try {
|
|
if (preg_match('/^([\+\-])(\d{2}):(\d{2})$/', $utcOffset, $arrayMatch)) {
|
|
$sign = $arrayMatch[1];
|
|
$h = (int)($arrayMatch[2]);
|
|
$m = (int)($arrayMatch[3]);
|
|
} else {
|
|
//Return
|
|
return 'UTC';
|
|
}
|
|
|
|
$offset = (($sign == '+')? '' : '-') . (($h * 60 * 60) + ($m * 60)); //Convert UTC Offset to seconds
|
|
|
|
//Return
|
|
return self::getTimeZoneIdByTimeZoneOffset((int)($offset));
|
|
} catch (\Exception $e) {
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert date from Time Zone to Time Zone
|
|
*
|
|
* @param string $date Date
|
|
* @param string $fromTimeZone Time Zone source
|
|
* @param string $toTimeZone Time Zone to convert
|
|
* @param string $format Format to return date
|
|
*
|
|
* @return string Return date
|
|
*/
|
|
public function convertTimeZone($date, $fromTimeZone, $toTimeZone, $format = 'Y-m-d H:i:s')
|
|
{
|
|
try {
|
|
$dt = new \DateTime($date, new \DateTimeZone($fromTimeZone)); //From Time Zone
|
|
$dt->setTimeZone(new \DateTimeZone($toTimeZone)); //To Time Zone
|
|
|
|
//Return
|
|
return $dt->format($format);
|
|
} catch (\Exception $e) {
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert ISO-8601 to Time Zone
|
|
*
|
|
* @param string $dateIso8601 Date
|
|
* @param string $toTimeZone Time Zone to convert
|
|
* @param string $format Format to return date
|
|
*
|
|
* @return string Return date
|
|
*/
|
|
public function convertIso8601ToTimeZone($dateIso8601, $toTimeZone, $format = 'Y-m-d H:i:s')
|
|
{
|
|
try {
|
|
$fromTimeZone = 'UTC';
|
|
|
|
if (preg_match('/^.+([\+\-]\d{2}:\d{2})$/', $dateIso8601, $arrayMatch)) {
|
|
$fromTimeZone = self::getTimeZoneIdByUtcOffset($arrayMatch[1]);
|
|
}
|
|
|
|
$dt = \DateTime::createFromFormat(self::ISO8601, $dateIso8601, new \DateTimeZone($fromTimeZone)); //From ISO-8601
|
|
$dt->setTimeZone(new \DateTimeZone($toTimeZone)); //To Time Zone
|
|
|
|
//Return
|
|
return $dt->format($format);
|
|
} catch (\Exception $e) {
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert ISO-8601/datetime/array-ISO-8601-datetime-data to Time Zone
|
|
*
|
|
* @param mixed $data Data
|
|
* @param string $fromTimeZone Time Zone source
|
|
* @param string $toTimeZone Time Zone to convert
|
|
* @param array $arrayKey Keys that convert to Time Zone
|
|
* @param string $format Format to return data
|
|
*
|
|
* @return mixed Return data
|
|
*/
|
|
public function convertDataToTimeZone($data, $fromTimeZone, $toTimeZone, array $arrayKey = [], $format = 'Y-m-d H:i:s')
|
|
{
|
|
try {
|
|
$regexpDatetime = '/^' . self::REGEXPDATE . '\s' . self::REGEXPTIME . '$/';
|
|
$regexpIso8601 = '/^' . self::REGEXPDATE . 'T' . self::REGEXPTIME . '[\+\-]\d{2}:\d{2}$/';
|
|
|
|
if (empty($data)) {
|
|
//Return
|
|
return $data;
|
|
}
|
|
|
|
switch (gettype($data)) {
|
|
case 'string':
|
|
if (is_string($data) && preg_match($regexpDatetime, $data)) {
|
|
if ($fromTimeZone != $toTimeZone) {
|
|
$data = self::convertTimeZone($data, $fromTimeZone, $toTimeZone, $format);
|
|
}
|
|
}
|
|
|
|
if (is_string($data) && preg_match($regexpIso8601, $data)) {
|
|
$data = self::convertIso8601ToTimeZone($data, $toTimeZone, $format);
|
|
}
|
|
break;
|
|
case 'array':
|
|
$regexpKey = (!empty($arrayKey))? '/^(?:' . implode('|', $arrayKey) . ')$/i': '';
|
|
|
|
array_walk_recursive(
|
|
$data,
|
|
function (&$value, $key, $arrayData)
|
|
{
|
|
try {
|
|
if ($arrayData['regexpKey'] == '' || preg_match($arrayData['regexpKey'], $key)) {
|
|
if (is_string($value) && preg_match($arrayData['regexpDatetime'], $value)) {
|
|
if ($arrayData['fromTimeZone'] != $arrayData['toTimeZone']) {
|
|
$value = self::convertTimeZone($value, $arrayData['fromTimeZone'], $arrayData['toTimeZone'], $arrayData['format']);
|
|
}
|
|
}
|
|
|
|
if (is_string($value) && preg_match($arrayData['regexpIso8601'], $value)) {
|
|
$value = self::convertIso8601ToTimeZone($value, $arrayData['toTimeZone'], $arrayData['format']);
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
throw $e;
|
|
}
|
|
},
|
|
['fromTimeZone' => $fromTimeZone, 'toTimeZone' => $toTimeZone, 'format' => $format, 'regexpDatetime' => $regexpDatetime, 'regexpIso8601' => $regexpIso8601, 'regexpKey' => $regexpKey]
|
|
);
|
|
break;
|
|
case 'object':
|
|
$data = json_decode(json_encode($data), true);
|
|
$data = self::convertDataToTimeZone($data, $fromTimeZone, $toTimeZone, $arrayKey, $format);
|
|
$data = json_decode(json_encode($data));
|
|
break;
|
|
}
|
|
|
|
//Return
|
|
return $data;
|
|
} catch (\Exception $e) {
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert datetime/array-datetime-data to ISO-8601
|
|
*
|
|
* @param mixed $data Data
|
|
* @param string $fromTimeZone Time Zone source
|
|
* @param string $toTimeZone Time Zone to convert
|
|
* @param array $arrayKey Keys that convert to ISO-8601
|
|
*
|
|
* @return mixed Return data
|
|
*/
|
|
public function convertDataToIso8601($data, $fromTimeZone, $toTimeZone, array $arrayKey = [])
|
|
{
|
|
try {
|
|
$regexpDatetime = '/^' . self::REGEXPDATE . '\s' . self::REGEXPTIME . '$/';
|
|
|
|
if (empty($data)) {
|
|
//Return
|
|
return $data;
|
|
}
|
|
|
|
switch (gettype($data)) {
|
|
case 'string':
|
|
if (is_string($data) && preg_match($regexpDatetime, $data)) {
|
|
if ($fromTimeZone != $toTimeZone) {
|
|
$data = self::convertTimeZone($data, $fromTimeZone, $toTimeZone);
|
|
}
|
|
|
|
$dt = \DateTime::createFromFormat('Y-m-d H:i:s', $data, new \DateTimeZone($toTimeZone));
|
|
|
|
$data = $dt->format(self::ISO8601);
|
|
}
|
|
break;
|
|
case 'array':
|
|
$regexpKey = (!empty($arrayKey))? '/^(?:' . implode('|', $arrayKey) . ')$/i': '';
|
|
|
|
array_walk_recursive(
|
|
$data,
|
|
function (&$value, $key, $arrayData)
|
|
{
|
|
try {
|
|
if (($arrayData['regexpKey'] == '' || preg_match($arrayData['regexpKey'], $key)) &&
|
|
is_string($value) && preg_match($arrayData['regexpDatetime'], $value)
|
|
) {
|
|
if ($arrayData['fromTimeZone'] != $arrayData['toTimeZone']) {
|
|
$value = self::convertTimeZone($value, $arrayData['fromTimeZone'], $arrayData['toTimeZone']);
|
|
}
|
|
|
|
$dt = \DateTime::createFromFormat('Y-m-d H:i:s', $value, new \DateTimeZone($arrayData['toTimeZone']));
|
|
|
|
$value = $dt->format(self::ISO8601);
|
|
}
|
|
} catch (\Exception $e) {
|
|
throw $e;
|
|
}
|
|
},
|
|
['fromTimeZone' => $fromTimeZone, 'toTimeZone' => $toTimeZone, 'regexpDatetime' => $regexpDatetime, 'regexpKey' => $regexpKey]
|
|
);
|
|
break;
|
|
case 'object':
|
|
$data = json_decode(json_encode($data), true);
|
|
$data = self::convertDataToIso8601($data, $fromTimeZone, $toTimeZone, $arrayKey);
|
|
$data = json_decode(json_encode($data));
|
|
break;
|
|
}
|
|
|
|
//Return
|
|
return $data;
|
|
} catch (\Exception $e) {
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert ISO-8601/datetime/array-ISO-8601-datetime-data to UTC
|
|
*
|
|
* @param mixed $data Data
|
|
* @param array $arrayKey Keys that convert to UTC
|
|
* @param string $format Format to return data
|
|
*
|
|
* @return mixed Return data
|
|
*/
|
|
public static function convertDataToUtc($data, array $arrayKey = [], $format = 'Y-m-d H:i:s')
|
|
{
|
|
try {
|
|
if (!(isset($_SESSION['__SYSTEM_UTC_TIME_ZONE__']) && $_SESSION['__SYSTEM_UTC_TIME_ZONE__'])) {
|
|
//Return
|
|
return $data;
|
|
}
|
|
|
|
$fromTimeZone = \ProcessMaker\BusinessModel\User::getUserLoggedTimeZone();
|
|
$toTimeZone = 'UTC';
|
|
|
|
//Return
|
|
return self::convertDataToTimeZone($data, $fromTimeZone, $toTimeZone, $arrayKey, $format);
|
|
} catch (\Exception $e) {
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert UTC to Time Zone
|
|
*
|
|
* @param mixed $data Data
|
|
* @param array $arrayKey Keys that convert to Time Zone
|
|
* @param string $format Format to return data
|
|
*
|
|
* @return mixed Return data
|
|
*/
|
|
public static function convertUtcToTimeZone($data, array $arrayKey = [], $format = 'Y-m-d H:i:s')
|
|
{
|
|
try {
|
|
if (!(isset($_SESSION['__SYSTEM_UTC_TIME_ZONE__']) && $_SESSION['__SYSTEM_UTC_TIME_ZONE__'])) {
|
|
//Return
|
|
return $data;
|
|
}
|
|
|
|
$fromTimeZone = 'UTC';
|
|
$toTimeZone = \ProcessMaker\BusinessModel\User::getUserLoggedTimeZone();
|
|
|
|
//Return
|
|
return self::convertDataToTimeZone($data, $fromTimeZone, $toTimeZone, $arrayKey, $format);
|
|
} catch (\Exception $e) {
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert UTC to ISO-8601
|
|
*
|
|
* @param mixed $data Data
|
|
* @param array $arrayKey Keys that convert to ISO-8601
|
|
*
|
|
* @return mixed Return data
|
|
*/
|
|
public static function convertUtcToIso8601($data, array $arrayKey = [])
|
|
{
|
|
try {
|
|
if (!(isset($_SESSION['__SYSTEM_UTC_TIME_ZONE__']) && $_SESSION['__SYSTEM_UTC_TIME_ZONE__'])) {
|
|
//Return
|
|
return $data;
|
|
}
|
|
|
|
$fromTimeZone = 'UTC';
|
|
$toTimeZone = \ProcessMaker\BusinessModel\User::getUserLoggedTimeZone();
|
|
|
|
//Return
|
|
return self::convertDataToIso8601($data, $fromTimeZone, $toTimeZone, $arrayKey);
|
|
} catch (\Exception $e) {
|
|
throw $e;
|
|
}
|
|
}
|
|
}
|
|
|