public static function timezone() {
$tzlist = &Civi::$statics[__CLASS__]['tzlist'];
- if(is_null($tzlist)) {
+ if (is_null($tzlist)) {
$tzlist = [];
foreach (timezone_identifiers_list() as $tz) {
// Actual timezone keys for PHP are mapped to human parts.
* @copyright CiviCRM LLC https://civicrm.org/licensing
*/
class CRM_Event_BAO_Event extends CRM_Event_DAO_Event {
+ const tz_fields = ['start_date', 'end_date', 'registration_start_date', 'registration_end_date'];
/**
* Fetch object based on array of properties.
return $return;
}
+ public static function setTimezones(CRM_Event_DAO_Event $event) {
+ // Pre-process time zoned fields into the PHP time zone, which should be the same as the database, to save as timestamp.
+ $timezone_event = ($event->event_tz ?: (!empty($event->id) ? CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $event->id, 'event_tz') : NULL));
+
+ foreach (self::tz_fields as $field) {
+ if(!empty($event->{$field})) {
+ $event->{$field} = CRM_Utils_Date::convertTimeZone($event->{$field}, NULL, $timezone_event);
+ }
+ }
+ }
+
+ public static function resetTimezones(CRM_Event_DAO_Event $event) {
+ // Process time zoned fields into their own time zone
+ $timezone_event = ($event->event_tz ?: (!empty($event->id) ? CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $event->id, 'event_tz') : NULL));
+
+ foreach (self::tz_fields as $field) {
+ if (!empty($event->{$field})) {
+ $event->{$field} = CRM_Utils_Date::convertTimeZone($event->{$field}, $timezone_event);
+ }
+ }
+ }
+
}
$defaults['waitlist_text'] = CRM_Utils_Array::value('waitlist_text', $defaults, ts('This event is currently full. However you can register now and get added to a waiting list. You will be notified if spaces become available.'));
$defaults['template_id'] = $this->_templateId;
+
+ $defaults['event_tz'] = CRM_Utils_Array::value('event_tz', $defaults, CRM_Core_Config::singleton()->userSystem->getTimeZoneString());
+
+ // Convert start and end date defaults to event time zone.
+ if (!empty($defaults['start_date'])) {
+ $defaults['start_date'] = CRM_Utils_Date::convertTimeZone($defaults['start_date'], $defaults['event_tz']);
+ }
+ if (!empty($defaults['end_date'])) {
+ $defaults['end_date'] = CRM_Utils_Date::convertTimeZone($defaults['end_date'], $defaults['event_tz']);
+ }
+
return $defaults;
}
$this->addElement('checkbox', 'is_share', ts('Add footer region with Twitter, Facebook and LinkedIn share buttons and scripts?'));
$this->addElement('checkbox', 'is_map', ts('Include Map to Event Location'));
+ $this->addSelect('event_tz', ['placeholder' => ts('- Select time zone -')], TRUE);
+
$this->add('datepicker', 'start_date', ts('Start'), [], !$this->_isTemplate, ['time' => TRUE]);
$this->add('datepicker', 'end_date', ts('End'), [], FALSE, ['time' => TRUE]);
$params = array_merge($this->controller->exportValues($this->_name), $this->_submitValues);
//format params
- $params['start_date'] = $params['start_date'] ?? NULL;
- $params['end_date'] = $params['end_date'] ?? NULL;
+ $params['start_date'] = !empty($params['start_date']) ? CRM_Utils_Date::convertTimeZone($params['start_date'], NULL, $params['event_tz'] ?? NULL) : $params['start_date'];
+ $params['end_date'] = !empty($params['end_date']) ? CRM_Utils_Date::convertTimeZone($params['end_date'], NULL, $params['event_tz'] ?? NULL) : $params['end_date'];
$params['has_waitlist'] = CRM_Utils_Array::value('has_waitlist', $params, FALSE);
$params['is_map'] = CRM_Utils_Array::value('is_map', $params, FALSE);
$params['is_active'] = CRM_Utils_Array::value('is_active', $params, FALSE);
protected $_profilePostMultiple = [];
protected $_profilePostMultipleAdd = [];
+ protected $_tz;
+
/**
* Set variables up before form is built.
*/
$defaults['thankyou_title'] = CRM_Utils_Array::value('thankyou_title', $defaults, ts('Thank You for Registering'));
$defaults['approval_req_text'] = CRM_Utils_Array::value('approval_req_text', $defaults, ts('Participation in this event requires approval. Submit your registration request here. Once approved, you will receive an email with a link to a web page where you can complete the registration process.'));
+ // Convert start and end date defaults to event time zone.
+ if (!empty($defaults['registration_start_date'])) {
+ $defaults['registration_start_date'] = CRM_Utils_Date::convertTimeZone($defaults['registration_start_date'], $this->_tz ?? NULL);
+ }
+ if (!empty($defaults['registration_end_date'])) {
+ $defaults['registration_end_date'] = CRM_Utils_Date::convertTimeZone($defaults['registration_end_date'], $this->_tz ?? NULL);
+ }
+
return $defaults;
}
$this->add('text', 'registration_link_text', ts('Registration Link Text'));
if (!$this->_isTemplate) {
+ $this->_tz = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $this->_id, 'event_tz') ?? CRM_Core_Config::singleton()->userSystem->getTimeZoneString();
+ $tz = CRM_Core_SelectValues::timezone()[$this->_tz];
+ $this->assign('event_tz', $tz);
$this->add('datepicker', 'registration_start_date', ts('Registration Start Date'), [], FALSE, ['time' => TRUE]);
$this->add('datepicker', 'registration_end_date', ts('Registration End Date'), [], FALSE, ['time' => TRUE]);
}
$params['id'] = $this->_id;
+ if (!isset($this->_tz)) {
+ $this->_tz = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $this->_id, 'event_tz') ?? CRM_Core_Config::singleton()->userSystem->getTimeZoneString();
+ }
+
// format params
$params['is_online_registration'] = CRM_Utils_Array::value('is_online_registration', $params, FALSE);
// CRM-11182
$params['is_multiple_registrations'] = CRM_Utils_Array::value('is_multiple_registrations', $params, FALSE);
$params['allow_same_participant_emails'] = CRM_Utils_Array::value('allow_same_participant_emails', $params, FALSE);
$params['requires_approval'] = CRM_Utils_Array::value('requires_approval', $params, FALSE);
+ $params['registration_start_date'] = !empty($params['registration_start_date']) ? CRM_Utils_Date::convertTimeZone($params['registration_start_date'], NULL, $this->_tz ?? NULL) : $params['registration_start_date'];
+ $params['registration_end_date'] = !empty($params['registration_end_date']) ? CRM_Utils_Date::convertTimeZone($params['registration_end_date'], NULL, $this->_tz ?? NULL) : $params['registration_end_date'];
// reset is_email confirm if not online reg
if (!$params['is_online_registration']) {
}
CRM_Core_DAO::storeValues($dao, $manageEvent[$dao->id]);
+ if (!is_null($dao->event_tz) && $dao->event_tz != CRM_Core_Config::singleton()->userSystem->getTimeZoneString()) {
+ foreach (CRM_Event_BAO_Event::tz_fields as $field) {
+ $manageEvent[$dao->id][$field . '_with_tz'] = CRM_Utils_Date::convertTimeZone($dao->{$field} ?? '', $dao->event_tz);
+ }
+ }
+ $manageEvent[$dao->id]['event_tz'] = $dao->event_tz ? CRM_Core_SelectValues::timezone()[$dao->event_tz] : FALSE;
+
// form all action links
$action = array_sum(array_keys($this->links()));
return $dateObject->format($format);
}
+ /**
+ * Convert a date string between time zones
+ *
+ * @param string $date - date string using $format
+ * @param string $tz_to - new time zone for date
+ * @param string $tz_from - current time zone for date
+ * @param string $format - format string specification as per DateTime::createFromFormat; defaults to 'Y-m-d H:i:s'
+ *
+ * @throws \CRM_Core_Exception
+ *
+ * @return string;
+ */
+ public static function convertTimeZone(string $date, string $tz_to = NULL, string $tz_from = NULL, $format = NULL) {
+ if (!$tz_from) {
+ $tz_from = CRM_Core_Config::singleton()->userSystem->getTimeZoneString();
+ }
+ if (!$tz_to) {
+ $tz_to = CRM_Core_Config::singleton()->userSystem->getTimeZoneString();
+ }
+
+ // Return early if both timezones are the same.
+ if ($tz_from == $tz_to) {
+ return $date;
+ }
+
+ $tz_from = new DateTimeZone($tz_from);
+ $tz_to = new DateTimeZone($tz_to);
+
+ if (is_null($format)) {
+ // Detect "mysql" format dates and adjust format accordingly
+ $format = preg_match('/^(\d{4})(\d{2}){0,5}$/', $date, $m) ? 'YmdHis' : 'Y-m-d H:i:s';
+ }
+
+ try {
+ $date_object = DateTime::createFromFormat('!' . $format, $date, $tz_from);
+ $dt_errors = DateTime::getLastErrors();
+
+ if (!$date_object) {
+ Civi::log()->warning(ts('Attempted to convert time zone with incorrect date format %1', ['%1' => $date]));
+
+ $dt_errors = DateTime::getLastErrors();
+
+ $date_object = new DateTime($date, $tz_from);
+ }
+ elseif ($dt_errors['warning_count'] && array_intersect($dt_errors['warnings'], ['The parsed date was invalid'])) {
+ throw new Exception('The parsed date was invalid');
+ }
+
+ $date_object->setTimezone($tz_to);
+
+ return $date_object->format($format);
+ }
+ catch (Exception $e) {
+ if ($dt_errors['error_count'] || $dt_errors['warning_count']) {
+ throw new CRM_Core_Exception(ts(
+ 'Failed to parse date with %1 errors and %2 warnings',
+ [
+ '%1' => $dt_errors['error_count'],
+ '%2' => $dt_errors['warning_count'],
+ ]
+ ), 0, ['format' => $format, 'date' => $date] + $dt_errors);
+ }
+ else {
+ throw $e;
+ }
+ }
+ }
+
}
<td class="label">{$form.description.label} {if $action == 2}{include file='CRM/Core/I18n/Dialog.tpl' table='civicrm_event' field='description' id=$eventID}{/if}</td>
<td>{$form.description.html}</td>
</tr>
+ <tr class="crm-event-manage-eventinfo-form-block-event_tz">
+ <td class="label">{$form.event_tz.label}</td>
+ <td>{$form.event_tz.html}</td>
+ </tr>
{if !$isTemplate}
<tr class="crm-event-manage-eventinfo-form-block-start_date">
<td class="label">{$form.start_date.label}</td>
<td>{$form.registration_link_text.html} {help id="id-link_text"}</td>
</tr>
{if !$isTemplate}
+ <tr class="crm-event-manage-registration-form-block-event_tz">
+ <td scope="row" class="label" width="20%">{ts}Event Time Zone{/ts}</label></td>
+ <td><output id="event-tz">{$event_tz}</output></td>
+ </tr>
<tr class="crm-event-manage-registration-form-block-registration_start_date">
<td scope="row" class="label" width="20%">{$form.registration_start_date.label}</td>
<td>{$form.registration_start_date.html}</td>
<td class="crm-event-state_province">{$row.state_province}</td>
<td class="crm-event-event_type">{$row.event_type}</td>
<td class="crm-event-is_public">{if $row.is_public eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}</td>
- <td class="crm-event-start_date" data-order="{$row.start_date|crmDate:'%Y-%m-%d'}">{$row.start_date|crmDate:"%b %d, %Y %l:%M %P"}</td>
- <td class="crm-event-end_date" data-order="{$row.end_date|crmDate:'%Y-%m-%d'}">{$row.end_date|crmDate:"%b %d, %Y %l:%M %P"}</td>
+ <td class="crm-event-start_date" data-order="{$row.start_date|crmDate:'%Y-%m-%d %H:%M'}">{$row.start_date|crmDate:"%b %d, %Y %l:%M %P"}{if $row.start_date_with_tz}<br />{$row.start_date_with_tz|crmDate:"%b %d, %Y %l:%M %P"} {$row.event_tz}{elseif !$row.event_tz} <span class="error-message">{ts 1='<i class="crm-i fa-warning"></i>'}%1 No timezone set{/ts}</span>{/if}</td>
+ <td class="crm-event-end_date" data-order="{$row.end_date|crmDate:'%Y-%m-%d %H:%M'}">{$row.end_date|crmDate:"%b %d, %Y %l:%M %P"}{if $row.end_date_with_tz}<br />{$row.end_date_with_tz|crmDate:"%b %d, %Y %l:%M %P"} {$row.event_tz}{/if}</td>
{if call_user_func(array('CRM_Campaign_BAO_Campaign','isCampaignEnable'))}
<td class="crm-event-campaign">{$row.campaign}</td>
{/if}