mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-23 00:13:35 +01:00
* CalDAV: fixed all-day events from Thunderbird with timezone were one day longer
using the timezone causes all sorts of problems, therefore ignoring it now and more robust end-time calculation
This commit is contained in:
parent
acdf19e462
commit
4e975aa8f4
@ -377,7 +377,15 @@ class DateTime extends \DateTime
|
|||||||
case 'integer':
|
case 'integer':
|
||||||
case 'ts':
|
case 'ts':
|
||||||
// EGroupware's integer timestamp is NOT the usual UTC timestamp, but has a timezone offset applied!
|
// 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'));
|
// calendar_ical unfortunately sets different timezones, breaking all sorts of things, if we're not setting the TZ back to our server-timezone
|
||||||
|
if (date_default_timezone_get() !== self::$server_timezone->getName())
|
||||||
|
{
|
||||||
|
$tz_backup = date_default_timezone_get();
|
||||||
|
date_default_timezone_set(self::$server_timezone->getName());
|
||||||
|
}
|
||||||
|
$ret = mktime(parent::format('H'),parent::format('i'),parent::format('s'),parent::format('m'),parent::format('d'),parent::format('Y'));
|
||||||
|
if (isset($tz_backup)) date_default_timezone_set($tz_backup);
|
||||||
|
return $ret;
|
||||||
case 'utc': // alias for "U" / timestamp in UTC
|
case 'utc': // alias for "U" / timestamp in UTC
|
||||||
return $this->getTimestamp();
|
return $this->getTimestamp();
|
||||||
case 'object':
|
case 'object':
|
||||||
|
@ -1559,15 +1559,8 @@ class calendar_boupdate extends calendar_bo
|
|||||||
}
|
}
|
||||||
if (!empty($event['end']))
|
if (!empty($event['end']))
|
||||||
{
|
{
|
||||||
$time = new Api\DateTime($event['end'], Api\DateTime::$user_timezone);
|
$time = $this->so->startOfDay(new Api\DateTime($event['end'], Api\DateTime::$user_timezone));
|
||||||
|
$time->add('-1second');
|
||||||
// Check to see if switching timezones changes the date, we'll need to adjust for that
|
|
||||||
$end_event_timezone = clone $time;
|
|
||||||
$time->setServer();
|
|
||||||
$delta = (int)$end_event_timezone->format('z') - (int)$time->format('z');
|
|
||||||
$time->add("$delta days");
|
|
||||||
|
|
||||||
$time->setTime(23, 59, 59);
|
|
||||||
$event['end'] = Api\DateTime::to($time, 'ts');
|
$event['end'] = Api\DateTime::to($time, 'ts');
|
||||||
$save_event['end'] = $time;
|
$save_event['end'] = $time;
|
||||||
}
|
}
|
||||||
@ -1619,8 +1612,8 @@ class calendar_boupdate extends calendar_bo
|
|||||||
{
|
{
|
||||||
if ($event['whole_day'])
|
if ($event['whole_day'])
|
||||||
{
|
{
|
||||||
$time = $this->so->startOfDay(new Api\DateTime($date, Api\DateTime::$user_timezone));
|
// we use so->startOfDay(new Api\DateTime($time, Api\DateTime::$user_time)) as we not yet converted to server-time!
|
||||||
$date = Api\DateTime::to($time, 'ts');
|
$date = $this->so->startOfDay(new Api\DateTime($date, Api\DateTime::$user_timezone))->format('server');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1642,7 +1635,7 @@ class calendar_boupdate extends calendar_bo
|
|||||||
$alarm['time'] = $this->date2ts($alarm['time'], true); // user to server-time
|
$alarm['time'] = $this->date2ts($alarm['time'], true); // user to server-time
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove alarms belonging to not longer existing or rejected participants
|
// remove alarms belonging to no longer existing or rejected participants
|
||||||
if (!empty($alarm['owner']) && isset($expanded['participants']))
|
if (!empty($alarm['owner']) && isset($expanded['participants']))
|
||||||
{
|
{
|
||||||
// Don't auto-delete alarm if for all users
|
// Don't auto-delete alarm if for all users
|
||||||
|
@ -1465,7 +1465,10 @@ class calendar_ical extends calendar_boupdate
|
|||||||
calendar_rrule::rrule2tz($event, $event_info['stored_event']['start'],
|
calendar_rrule::rrule2tz($event, $event_info['stored_event']['start'],
|
||||||
$event_info['stored_event']['tzid']);
|
$event_info['stored_event']['tzid']);
|
||||||
|
|
||||||
$event['tzid'] = $event_info['stored_event']['tzid'];
|
if (empty($event['tzid']) && !empty($event_info['stored_event']['tzid']))
|
||||||
|
{
|
||||||
|
$event['tzid'] = $event_info['stored_event']['tzid'];
|
||||||
|
}
|
||||||
// avoid that iCal changes the organizer, which is not allowed
|
// avoid that iCal changes the organizer, which is not allowed
|
||||||
$event['owner'] = $event_info['stored_event']['owner'];
|
$event['owner'] = $event_info['stored_event']['owner'];
|
||||||
}
|
}
|
||||||
@ -2625,13 +2628,13 @@ class calendar_ical extends calendar_boupdate
|
|||||||
if (isset($attributes['params']['VALUE'])
|
if (isset($attributes['params']['VALUE'])
|
||||||
&& $attributes['params']['VALUE'] == 'DATE')
|
&& $attributes['params']['VALUE'] == 'DATE')
|
||||||
{
|
{
|
||||||
$isDate = true;
|
$event['whole_day'] = $isDate = true;
|
||||||
}
|
}
|
||||||
$dtstart_ts = is_numeric($attributes['value']) ? $attributes['value'] : $this->date2ts($attributes['value']);
|
$dtstart_ts = is_numeric($attributes['value']) ? $attributes['value'] : $this->date2ts($attributes['value']);
|
||||||
$vcardData['start'] = $dtstart_ts;
|
$vcardData['start'] = $dtstart_ts;
|
||||||
|
|
||||||
// set event timezone from dtstart, if specified there
|
// set event timezone from dtstart, if specified there
|
||||||
if (!empty($attributes['params']['TZID']))
|
if (!empty($attributes['params']['TZID']) && !$isDate)
|
||||||
{
|
{
|
||||||
// import TZID, if PHP understands it (we only care about TZID of starttime,
|
// import TZID, if PHP understands it (we only care about TZID of starttime,
|
||||||
// as we store only a TZID for the whole event)
|
// as we store only a TZID for the whole event)
|
||||||
@ -2681,7 +2684,7 @@ class calendar_ical extends calendar_boupdate
|
|||||||
|
|
||||||
case 'DTEND':
|
case 'DTEND':
|
||||||
$dtend_ts = is_numeric($attributes['value']) ? $attributes['value'] : $this->date2ts($attributes['value']);
|
$dtend_ts = is_numeric($attributes['value']) ? $attributes['value'] : $this->date2ts($attributes['value']);
|
||||||
if (date('H:i:s',$dtend_ts) == '00:00:00')
|
if (date('H:i:s',$dtend_ts) == '00:00:00' || isset($attributes['params']['VALUE']) && $attributes['params']['VALUE'] == 'DATE')
|
||||||
{
|
{
|
||||||
$dtend_ts -= 1;
|
$dtend_ts -= 1;
|
||||||
}
|
}
|
||||||
|
@ -59,15 +59,15 @@ if (!defined('WEEK_s')) define('WEEK_s',7*DAY_s);
|
|||||||
* Class to store all calendar data (storage object)
|
* Class to store all calendar data (storage object)
|
||||||
*
|
*
|
||||||
* Tables used by calendar_so:
|
* Tables used by calendar_so:
|
||||||
* - egw_cal: general calendar data: cal_id, title, describtion, locations, range-start and -end dates
|
* - egw_cal: general calendar data: cal_id, title, description, locations, range-start and -end dates
|
||||||
* - egw_cal_dates: start- and enddates (multiple entry per cal_id for recuring events!), recur_exception flag
|
* - egw_cal_dates: start- and enddates (multiple entry per cal_id for recurring events!), recur_exception flag
|
||||||
* - egw_cal_user: participant info including status (multiple entries per cal_id AND startdate for recuring events)
|
* - egw_cal_user: participant info including status (multiple entries per cal_id AND startdate for recuring events)
|
||||||
* - egw_cal_repeats: recur-data: type, interval, days etc.
|
* - egw_cal_repeats: recur-data: type, interval, days etc.
|
||||||
* - egw_cal_extra: custom fields (multiple entries per cal_id possible)
|
* - egw_cal_extra: custom fields (multiple entries per cal_id possible)
|
||||||
*
|
*
|
||||||
* The new UI, BO and SO classes have a strict definition, in which timezone they operate:
|
* The new UI, BO and SO classes have a strict definition, in which timezone they operate:
|
||||||
* UI only operates in user-time, so there have to be no conversation at all !!!
|
* UI only operates in user-time, so there have to be no conversation at all !!!
|
||||||
* BO's functions take and return user-time only (!), they convert internaly everything to servertime, because
|
* BO's functions take and return user-time only (!), they convert internally everything to servertime, because
|
||||||
* SO operates only on server-time
|
* SO operates only on server-time
|
||||||
*
|
*
|
||||||
* DB-model uses egw_cal_user.cal_status='X' for participants who got deleted. They never get returned by
|
* DB-model uses egw_cal_user.cal_status='X' for participants who got deleted. They never get returned by
|
||||||
@ -79,13 +79,13 @@ if (!defined('WEEK_s')) define('WEEK_s',7*DAY_s);
|
|||||||
* All update methods now take care to update modification time of (evtl. existing) series master too,
|
* All update methods now take care to update modification time of (evtl. existing) series master too,
|
||||||
* to force an etag, ctag and sync-token change! Methods not doing that are private to this class.
|
* to force an etag, ctag and sync-token change! Methods not doing that are private to this class.
|
||||||
*
|
*
|
||||||
* range_start/_end in main-table contains start and end of whole event series (range_end is NULL for unlimited recuring events),
|
* range_start/_end in main-table contains start and end of whole event series (range_end is NULL for unlimited recurring events),
|
||||||
* saving the need to always join dates table, to query non-enumerating recuring events (like CalDAV or ActiveSync does).
|
* saving the need to always join dates table, to query non-enumerating recurring events (like CalDAV or ActiveSync does).
|
||||||
* This effectivly stores MIN(cal_start) and MAX(cal_end) permanently as column in main-table and improves speed tremendiously
|
* This effectively stores MIN(cal_start) and MAX(cal_end) permanently as column in main-table and improves speed tremendously
|
||||||
* (few milisecs instead of more then 2 minutes on huge installations)!
|
* (few millisecond instead of more than 2 minutes on huge installations)!
|
||||||
* It's set in calendar_so::save from start and end or recur_enddate, so nothing changes for higher level classes.
|
* It's set in calendar_so::save from start and end or recur_enddate, so nothing changes for higher level classes.
|
||||||
*
|
*
|
||||||
* egw_cal_user.cal_user_id contains since 14.3.001 only an md5-hash of a lowercased raw email address (not rfc822 address!).
|
* egw_cal_user.cal_user_id contains since 14.3.001 only a md5-hash of a lowercased raw email address (not rfc822 address!).
|
||||||
* Real email address and other possible attendee information for iCal or CalDAV are stored in cal_user_attendee.
|
* Real email address and other possible attendee information for iCal or CalDAV are stored in cal_user_attendee.
|
||||||
* This allows a short 32byte ascii cal_user_id and also storing attendee information for accounts and contacts.
|
* This allows a short 32byte ascii cal_user_id and also storing attendee information for accounts and contacts.
|
||||||
* Outside of this class uid for email address is still "e$cn <$email>" or "e$email".
|
* Outside of this class uid for email address is still "e$cn <$email>" or "e$email".
|
||||||
@ -93,6 +93,10 @@ if (!defined('WEEK_s')) define('WEEK_s',7*DAY_s);
|
|||||||
* egw_cal_user.cal_user_id for DB and calendar_so::combine_user($user_type, $user_id, $user_attendee) to generate
|
* egw_cal_user.cal_user_id for DB and calendar_so::combine_user($user_type, $user_id, $user_attendee) to generate
|
||||||
* uid used outside of this class. Both methods are unchanged when using with their default parameters.
|
* uid used outside of this class. Both methods are unchanged when using with their default parameters.
|
||||||
*
|
*
|
||||||
|
* Whole-day-events are stored with a start-time of 00:00:00 and an end-time of 23:59:59 in the server-timezone.
|
||||||
|
* They are logically understood as floating-date events, so in whatever timezone they occur the whole day!
|
||||||
|
* There is NO flag in the schema for whole-day events, they are detected by having the above start- and end-times.
|
||||||
|
*
|
||||||
* @ToDo drop egw_cal_repeats table in favor of a rrule colum in main table (saves always used left join and allows to store all sorts of rrules)
|
* @ToDo drop egw_cal_repeats table in favor of a rrule colum in main table (saves always used left join and allows to store all sorts of rrules)
|
||||||
*/
|
*/
|
||||||
class calendar_so
|
class calendar_so
|
||||||
@ -480,7 +484,7 @@ class calendar_so
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we have a real recurance, if not set $recur_date=0
|
// check if we have a real recurrence, if not set $recur_date=0
|
||||||
if (is_array($ids) || empty($events[(int)$ids]['recur_type']))
|
if (is_array($ids) || empty($events[(int)$ids]['recur_type']))
|
||||||
{
|
{
|
||||||
$recur_date = 0;
|
$recur_date = 0;
|
||||||
@ -3053,7 +3057,7 @@ ORDER BY cal_user_type, cal_usre_id
|
|||||||
* Check if the event is the whole day
|
* Check if the event is the whole day
|
||||||
*
|
*
|
||||||
* @param array $event event (all timestamps in servertime)
|
* @param array $event event (all timestamps in servertime)
|
||||||
* @return boolean true if whole day event within its timezone, false othwerwise
|
* @return boolean true if whole day event within its timezone, false otherwise
|
||||||
*/
|
*/
|
||||||
function isWholeDay($event)
|
function isWholeDay($event)
|
||||||
{
|
{
|
||||||
@ -3072,27 +3076,27 @@ ORDER BY cal_user_type, cal_usre_id
|
|||||||
$timezone = self::$tz_cache[$event['tzid']];
|
$timezone = self::$tz_cache[$event['tzid']];
|
||||||
}
|
}
|
||||||
$start_time = new Api\DateTime($event['start'],Api\DateTime::$server_timezone);
|
$start_time = new Api\DateTime($event['start'],Api\DateTime::$server_timezone);
|
||||||
$start_time->setTimezone($timezone);
|
|
||||||
$end_time = new Api\DateTime($event['end'],Api\DateTime::$server_timezone);
|
$end_time = new Api\DateTime($event['end'],Api\DateTime::$server_timezone);
|
||||||
|
// by our schema-definition whole-day means 00:00-23:59 in server-timezone
|
||||||
|
if ($start_time->format('H:i') === '00:00' && $end_time->format('H:i') === '23:59')
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// as current code used the event-timezone, lets check that too
|
||||||
|
$start_time->setTimezone($timezone);
|
||||||
$end_time->setTimezone($timezone);
|
$end_time->setTimezone($timezone);
|
||||||
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
|
return $start_time->format('H:i') === '00:00' && $end_time->format('H:i') === '23:59';
|
||||||
// '(): ' . $start . '-' . $end);
|
|
||||||
$start = Api\DateTime::to($start_time,'array');
|
|
||||||
$end = Api\DateTime::to($end_time,'array');
|
|
||||||
|
|
||||||
|
|
||||||
return !$start['hour'] && !$start['minute'] && $end['hour'] == 23 && $end['minute'] == 59;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves a datetime to the beginning of the day within timezone
|
* Moves a datetime to the beginning of the day within timezone
|
||||||
*
|
*
|
||||||
* @param Api\DateTime $time the datetime entry
|
* @param Api\DateTime $time the datetime entry
|
||||||
* @param string tz_id timezone
|
* @param ?string $tz_id timezone
|
||||||
*
|
*
|
||||||
* @return DateTime
|
* @return DateTime
|
||||||
*/
|
*/
|
||||||
function &startOfDay(Api\DateTime $time, $tz_id=null)
|
function startOfDay(Api\DateTime $time, $tz_id=null)
|
||||||
{
|
{
|
||||||
if (empty($tz_id))
|
if (empty($tz_id))
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user