More automatic timezone handling schema for new code (hopefully not breaking existing code):

1. SO converts all timestamps to Api\DateTime objects using Api\DateTime::server2user($ts, 'object')
 - Api\Storage and Api\Storage\Base class do that automatic if using 'object' as $timestamp_type constructor parameter
 - if using just Api\Db you need to iterate over your selects manually and apply Api\DateTime::server2user($ts, 'object')
 - timestamps are store in DB in server timezone and above conversation honors that and additionally set the user TZ
2. Rest of the app should keep all timestamps as Api\DateTime objects
 - direct comparison works for Api\DateTime (and PHP \DateTime) as __toString() method automatic converts to UTC timestamps
 - do NOT convert them to timezone-less timestamps and no further timezone conversation needed for output with eTemplate
3. eTemplate2 converts automatic to user timezone for displaying dates and times
 - you need to use <date-time ... data_format="object"/> to get Api\DateTime objects back from eTemplate!
4. Api\Db converts automatic to server timezone when quoting DateTime objects for integer or timestamp columns
5. only output other then eTemplate might need to set a timezone different from the user TZ before calling $ts->format()
This commit is contained in:
Ralf Becker 2021-01-26 10:21:37 +02:00
parent fa26bcb29c
commit 1259ae8d04
3 changed files with 26 additions and 9 deletions

View File

@ -303,28 +303,39 @@ class DateTime extends \DateTime
* Set user timezone, according to user prefs: converts current time to user time
*
* Does nothing if self::$user_timezone is current timezone!
*
* @return self to allow chaining
*/
public function setUser()
{
$this->setTimezone(self::$user_timezone);
return $this;
}
/**
* Set server timezone: converts current time to server time
*
* Does nothing if self::$server_timezone is current timezone!
*
* @return self to allow chaining
*/
public function setServer()
{
$this->setTimezone(self::$server_timezone);
return $this;
}
/**
* Format DateTime object as a specific type or string
*
* EGroupware's integer timestamp is NOT the usual UTC timestamp, but has a timezone offset applied!
* Use $type === 'utc' or getTimestamp() method to get a regular timestamp in UTC.
*
* @param string $type ='' 'integer'|'ts'=timestamp, 'server'=timestamp in servertime, 'string'='Y-m-d H:i:s', 'object'=DateTime,
* 'array'=array with values for keys ('year','month','day','hour','minute','second','full','raw') or string with format
* true = date only, false = time only as in user prefs, '' = date+time as in user prefs
* true = date only, false = time only as in user prefs, '' = date+time as in user prefs, 'utc'=regular timestamp in UTC
* @return int|string|array|datetime see $type
*/
public function format($type='')
@ -353,7 +364,7 @@ class DateTime extends \DateTime
// fall through
case 'integer':
case 'ts':
// ToDo: Check if PHP5.3 getTimestamp does the same, or always returns UTC timestamp
// EGroupware's integer timestamp is NOT the usual UTC timestamp, but has a timezone offset applied!
return mktime(parent::format('H'),parent::format('i'),parent::format('s'),parent::format('m'),parent::format('d'),parent::format('Y'));
case 'utc': // alias for "U" / timestamp in UTC
return $this->getTimestamp();

View File

@ -1461,10 +1461,10 @@ class Db
switch($type)
{
case 'int':
// if DateTime object given, convert it to a unix timestamp (NOT converting the timezone!)
// if DateTime object given, set server-timezone and format it as EGroupware timestamp with offset
if (is_object($value) && ($value instanceof \DateTime))
{
return ($value instanceof DateTime) ? $value->format('ts') : DateTime::to($value,'ts');
return DateTime::user2server($value,'ts');
}
case 'auto':
// atm. (php5.2) php has only 32bit integers, it converts everything else to float.
@ -1497,17 +1497,17 @@ class Db
}
break; // handled like strings
case 'date':
// if DateTime object given, convert it (NOT converting the timezone!)
// if DateTime object given, set server-timezone and format it as string
if (is_object($value) && ($value instanceof \DateTime))
{
return $this->Link_ID->qstr($value->format('Y-m-d'));
return $this->Link_ID->qstr(DateTime::user2server($value,'Y-m-d'));
}
return $this->Link_ID->DBDate($value);
case 'timestamp':
// if DateTime object given, convert it (NOT converting the timezone!)
// if DateTime object given, set server-timezone and format it as string
if (is_object($value) && ($value instanceof \DateTime))
{
return $this->Link_ID->qstr($value->format('Y-m-d H:i:s'));
return $this->Link_ID->qstr(DateTime::user2server($value,'Y-m-d H:i:s'));
}
return $this->Link_ID->DBTimeStamp($value);
}

View File

@ -107,7 +107,12 @@ class Date extends Transformer
{
if (!$value) return $value; // otherwise we will get current date or 1970-01-01 instead of an empty value
if ($this->attrs['data_format'])
// for DateTime objects (regular PHP and Api\DateTime ones), set user timezone
if ($value instanceof \DateTime)
{
$date = Api\DateTime::server2user($value);
}
elseif ($this->attrs['data_format'] && $this->attrs['data_format'] !== 'object')
{
$date = Api\DateTime::createFromFormat($this->attrs['data_format'], $value, Api\DateTime::$user_timezone);
}
@ -154,6 +159,7 @@ class Date extends Transformer
{
try
{
if (substr($value, -1) === 'Z') $value = substr($value, 0, -1);
$date = new Api\DateTime($value);
}
catch(\Exception $e)