Unverified Commit 64a3f9a3 authored by acrollet's avatar acrollet Committed by GitHub

Update event entities to use Drupal datetime fields (#46)

* Update event entities to use Drupal datetime fields

* Event start and end dates now use UTC timezone

* Event start and end dates now are a single datetime range field

* Add dependency on module “Fullcalendar library”

* Add fullcalendar_library to makefile

* Don't fail build on php 7.2

* Change timezone to UTC

* Remove bat_fullcalendar.install

* Migrate event's dates on the new field

* Use short array syntax

* Event start and end dates now are a single datetime range field
parent 1c1de35d
......@@ -8,6 +8,10 @@ php:
- 7.1
- 7.2
matrix:
allow_failures:
- php: 7.2
addons:
chrome: stable
......
......@@ -6,3 +6,4 @@ package: bat
dependencies:
- bat_unit
- datetime_range
......@@ -6,6 +6,8 @@
* the entity types.
*/
use Drupal\Core\Database\Database;
/**
* Implements hook_requirements().
*/
......@@ -34,3 +36,37 @@ function bat_event_requirements($phase) {
];
}
}
/**
* Migrate event's dates on the new field.
*/
function bat_event_update_8001() {
$events = db_select('event', 'e')
->fields('e', [])
->condition('start', '', '<>')
->execute()
->fetchAll();
foreach ($events as $event) {
$event_object = bat_event_load($event->id);
$start_date = new DateTime();
$start_date->setTimestamp($event->start);
$end_date = new DateTime();
$end_date->setTimestamp($event->end);
$event_object->setStartDate($start_date);
$event_object->setEndDate($end_date);
$event_object->save();
}
}
/**
* Drop old "start" and "end" columns on event table.
*/
function bat_event_update_8002() {
Database::getConnection()->schema()->dropField('event', 'start');
Database::getConnection()->schema()->dropField('event', 'end');
}
......@@ -78,6 +78,60 @@ function bat_event_type_add_event_state_reference($entity) {
}
}
/**
* Create "Event Dates" field.
*/
function bat_event_type_add_event_dates_field($entity) {
$entity_info = \Drupal::entityTypeManager()->getDefinition($entity->target_entity_type);
$field_name = 'event_dates';
$field_storage = FieldStorageConfig::loadByName('bat_event', $field_name);
$field = FieldConfig::loadByName('bat_event', $entity->id(), $field_name);
if (empty($field_storage)) {
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'bat_event',
'type' => 'daterange',
'cardinality' => 1,
'locked' => 1,
'settings' => [
'datetime_type' => 'datetime',
],
]);
$field_storage->save();
}
if (empty($field)) {
$field = FieldConfig::create([
'field_storage' => $field_storage,
'entity_type' => 'bat_event',
'label' => 'Event Dates',
'bundle' => $entity->id(),
'required' => FALSE,
'settings' => [
'datetime_type' => 'datetime',
],
]);
$field->save();
$form_display = \Drupal::entityTypeManager()->getStorage('entity_form_display')->load('bat_event.' . $entity->id() . '.default');
if (!$form_display) {
$form_display = EntityFormDisplay::create([
'targetEntityType' => 'bat_event',
'bundle' => $entity->id(),
'mode' => 'default',
'status' => TRUE,
]);
}
$form_display->setComponent($field_name, [
'type' => 'daterange_default',
'weight' => 1,
]);
$form_display->save();
}
}
/**
* Create fields of type 'Entity Reference' to reference the target entity.
*
......
......@@ -145,30 +145,38 @@ class Event extends ContentEntityBase implements EventInterface {
* {@inheritdoc}
*/
public function getStartDate() {
$date = new \DateTime();
return $date->setTimestamp($this->get('start')->value);
$date = new \DateTime($this->get('event_dates')->value);
return $date;
}
/**
* {@inheritdoc}
*/
public function getEndDate() {
$date = new \DateTime();
return $date->setTimestamp($this->get('end')->value);
$date = new \DateTime($this->get('event_dates')->end_value);
return $date;
}
/**
* {@inheritdoc}
*/
public function setStartDate(\DateTime $date) {
$this->set('start', $date->getTimestamp());
$value = [
'value' => $date->format('Y-m-d\TH:i:00'),
'end_value' => $this->getEndDate()->format('Y-m-d\TH:i:00'),
];
$this->set('event_dates', $value);
}
/**
* {@inheritdoc}
*/
public function setEndDate(\DateTime $date) {
$this->set('end', $date->getTimestamp());
$value = [
'value' => $this->getStartDate()->format('Y-m-d\TH:i:00'),
'end_value' => $date->format('Y-m-d\TH:i:00'),
];
$this->set('event_dates', $value);
}
/**
......@@ -203,7 +211,7 @@ class Event extends ContentEntityBase implements EventInterface {
$this->batStoreSave($unit,
$entity_original->getStartDate(),
$entity_original->getEndDate(),
$entity_original->getEndDate()->sub(new \DateInterval('PT1M')),
$event_type->id(),
$event_type->getEventGranularity(),
$unit->getDefaultValue(),
......@@ -250,7 +258,7 @@ class Event extends ContentEntityBase implements EventInterface {
$this->batStoreSave($unit,
$this->getStartDate(),
$this->getEndDate(),
$this->getEndDate()->sub(new \DateInterval('PT1M')),
$event_type->id(),
$event_type->getEventGranularity(),
$event_value,
......@@ -311,36 +319,6 @@ class Event extends ContentEntityBase implements EventInterface {
->setLabel(t('Changed'))
->setDescription(t('The time that the entity was last edited.'));
$fields['start'] = BaseFieldDefinition::create('created')
->setLabel(t('Start Date'))
->setDescription(t('The time that this event starts.'))
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'timestamp',
'weight' => 0,
])
->setDisplayOptions('form', [
'type' => 'datetime_timestamp',
'weight' => 0,
])
->setDisplayConfigurable('form', TRUE)
->setRequired(TRUE);
$fields['end'] = BaseFieldDefinition::create('created')
->setLabel(t('End Date'))
->setDescription(t('The time that this event ends.'))
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'timestamp',
'weight' => 1,
])
->setDisplayOptions('form', [
'type' => 'datetime_timestamp',
'weight' => 1,
])
->setDisplayConfigurable('form', TRUE)
->setRequired(TRUE);
$fields['type'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Type'))
->setDescription(t('The event type.'))
......
......@@ -96,6 +96,9 @@ class EventType extends ConfigEntityBundleBase implements EventTypeInterface {
// Create all tables necessary for this Event Type.
bat_event_create_event_type_schema($this->id());
// Create a field of type "Date range" for event dates.
bat_event_type_add_event_dates_field($this);
// Create a field of type 'Entity Reference' to reference a Bat Unit.
bat_event_type_add_target_entity_field($this);
......
......@@ -27,30 +27,6 @@ class EventViewsData extends EntityViewsData implements EntityViewsDataInterface
'help' => $this->t('The Event ID.'),
];
$data['event']['start_date'] = [
'title' => $this->t('Start Date'),
'help' => $this->t("A event's start date."),
'field' => [
'float' => TRUE,
'id' => 'bat_event_handler_date_field',
'click sortable' => TRUE,
],
'filter' => [
'id' => 'bat_event_handler_date_filter',
],
];
$data['event']['end_date'] = [
'title' => $this->t('End Date'),
'help' => $this->t("A event's end date."),
'field' => [
'float' => TRUE,
'id' => 'bat_event_handler_date_field',
'click sortable' => TRUE,
],
'filter' => [
'id' => 'bat_event_handler_date_filter',
],
];
$data['event']['type']['field'] = [
'title' => $this->t('Event Type'),
'help' => $this->t('The event type label.'),
......
......@@ -91,24 +91,23 @@ class EventForm extends ContentEntityForm {
$form['created']['#group'] = 'author';
}
if ($entity->isNew()) {
$form['start']['widget'][0]['value']['#default_value'] = '';
$form['end']['widget'][0]['value']['#default_value'] = '';
if ($event_type->getEventGranularity() == 'bat_daily') {
$form['event_dates']['widget'][0]['value']['#date_time_element'] = 'none';
$form['event_dates']['widget'][0]['end_value']['#date_time_element'] = 'none';
}
else {
$form['end']['widget'][0]['value']['#default_value']->add(new \DateInterval('PT1M'));
$form['event_dates']['widget'][0]['value']['#date_increment'] = 60;
$form['event_dates']['widget'][0]['end_value']['#date_increment'] = 60;
}
unset($form['start']['widget'][0]['value']['#description']);
unset($form['end']['widget'][0]['value']['#description']);
$form['event_dates']['widget'][0]['value']['#date_timezone'] = 'UTC';
$form['event_dates']['widget'][0]['end_value']['#date_timezone'] = 'UTC';
if ($event_type->getEventGranularity() == 'bat_daily') {
$form['start']['widget'][0]['value']['#date_time_element'] = 'none';
$form['end']['widget'][0]['value']['#date_time_element'] = 'none';
if (isset($form['event_dates']['widget'][0]['value']['#default_value'])) {
$form['event_dates']['widget'][0]['value']['#default_value']->setTimezone(new \DateTimeZone('UTC'));
}
else {
$form['start']['widget'][0]['value']['#date_increment'] = 60;
$form['end']['widget'][0]['value']['#date_increment'] = 60;
if (isset($form['event_dates']['widget'][0]['end_value']['#default_value'])) {
$form['event_dates']['widget'][0]['end_value']['#default_value']->setTimezone(new \DateTimeZone('UTC'));
}
if (\Drupal::request()->query->get(MainContentViewSubscriber::WRAPPER_FORMAT) == 'drupal_ajax') {
......@@ -130,12 +129,12 @@ class EventForm extends ContentEntityForm {
$values = $form_state->getValues();
$start_date = new \DateTime($values['start'][0]['value']->format('Y-m-d H:i:s'));
$end_date = new \DateTime($values['end'][0]['value']->format('Y-m-d H:i:s'));
$start_date = new \DateTime($values['event_dates'][0]['value']->format('Y-m-d H:i:s'));
$end_date = new \DateTime($values['event_dates'][0]['end_value']->format('Y-m-d H:i:s'));
// The end date must be greater or equal than start date.
if ($end_date < $start_date) {
$form_state->setErrorByName('end', t('End date must be on or after the start date.'));
$form_state->setErrorByName('event_dates', t('End date must be on or after the start date.'));
}
$event_type = bat_event_type_load($this->entity->bundle());
......@@ -183,17 +182,14 @@ class EventForm extends ContentEntityForm {
$event = $this->entity;
$event_type = bat_event_type_load($event->bundle());
$end_date = $event->getEndDate();
if ($event_type->getEventGranularity() == 'bat_daily') {
$start_date = $event->getStartDate()->setTime(0, 0);
$event->setStartDate($start_date);
$end_date->setTime(0, 0);
$end_date = $event->getEndDate()->setTime(0, 0);
$event->setEndDate($end_date);
}
$end_date->sub(new \DateInterval('PT1M'));
$event->setEndDate($end_date);
$status = $event->save();
switch ($status) {
......
......@@ -84,13 +84,13 @@ class EventListBuilder extends EntityListBuilder {
],
'start_date' => [
'data' => $this->t('Start Date'),
'field' => 'start',
'field' => 'event_dates',
'specifier' => 'start',
'class' => [RESPONSIVE_PRIORITY_LOW],
],
'end_date' => [
'data' => $this->t('End Date'),
'field' => 'end',
'field' => 'event_dates',
'specifier' => 'end',
'class' => [RESPONSIVE_PRIORITY_LOW],
],
......@@ -112,7 +112,7 @@ class EventListBuilder extends EntityListBuilder {
$row['id'] = $entity->id();
$row['start_date'] = $entity->getStartDate()->format($date_format);
$row['end_date'] = $entity->getEndDate()->add(new \DateInterval('PT1M'))->format($date_format);
$row['end_date'] = $entity->getEndDate()->format($date_format);
$row['type'] = bat_event_type_load($entity->bundle())->label();
return $row + parent::buildRow($entity);
}
......
<?php
/**
* A extension of the views date handler to allow for some data
* transformations
*
* @ingroup views_field_handlers
*/
namespace Drupal\bat_event\Plugin\views\field;
use Drupal\views\ResultRow;
use Drupal\views\Plugin\views\field\Date;
/**
* @ViewsField("bat_event_handler_date_field")
*/
class BatEventHandlerDateField extends Date {
public function construct() {
parent::construct();
}
public function render(ResultRow $values) {
$value = $this->get_value($values);
$date = new DateTime($value);
if ($this->table == 'bat_events' && $this->field == 'end_date') {
// Add a minute to then end date.
$date->add(new DateInterval('PT1M'));
}
$value = $date->getTimestamp();
$format = $this->options['date_format'];
if (in_array($format, ['custom', 'raw time ago', 'time ago', 'raw time span', 'time span', 'raw time span', 'inverse time span', 'time span'])) {
$custom_format = $this->options['custom_date_format'];
}
if ($value) {
$time_diff = REQUEST_TIME - $value;
switch ($format) {
case 'raw time ago':
return format_interval($time_diff, is_numeric($custom_format) ? $custom_format : 2);
case 'time ago':
return t('%time ago', ['%time' => format_interval($time_diff, is_numeric($custom_format) ? $custom_format : 2)]);
case 'raw time hence':
return format_interval(-$time_diff, is_numeric($custom_format) ? $custom_format : 2);
case 'time hence':
return t('%time hence', ['%time' => format_interval(-$time_diff, is_numeric($custom_format) ? $custom_format : 2)]);
case 'raw time span':
return ($time_diff < 0 ? '-' : '') . format_interval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2);
case 'inverse time span':
return ($time_diff > 0 ? '-' : '') . format_interval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2);
case 'time span':
return t(($time_diff < 0 ? '%time hence' : '%time ago'), ['%time' => format_interval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2)]);
case 'custom':
if ($custom_format == 'r') {
return format_date($value, $format, $custom_format, NULL, 'en');
}
return format_date($value, $format, $custom_format);
default:
return format_date($value, $format);
}
}
}
}
......@@ -24,8 +24,7 @@ class BatEventHandlerDurationField extends FieldPluginBase {
public function render(ResultRow $values) {
$event = $this->getEntity($values);
$value = $event->end->value - $event->start->value;
$value += 60;
$value = $event->getEndDate()->getTimestamp() - $event->getStartDate()->getTimestamp();
return $this->sanitizeValue(\Drupal::service('date.formatter')->formatInterval($value));
}
......
<?php
/**
* @file
* Filter to handle dates stored as a string.
*/
namespace Drupal\bat_event\Plugin\views\filter;
use Drupal\views\Plugin\views\filter\Date;
/**
* @ViewsFilter("bat_event_handler_date_filter")
*/
class BatEventHandlerDateFilter extends Date {
protected function opSimple($field) {
$query_substitutions = views_views_query_substitutions($this->view);
$value = date('Y-m-d', intval(strtotime($this->value['value'], $query_substitutions['***CURRENT_TIME***'])));
$this->query->addWhereExpression($this->options['group'], "$field $this->operator '$value'");
}
protected function opBetween($field) {
// Use the substitutions to ensure a consistent timestamp.
$query_substitutions = views_views_query_substitutions($this->view);
$a = date('Y-m-d', intval(strtotime($this->value['min'], $query_substitutions['***CURRENT_TIME***'])));
$b = date('Y-m-d', intval(strtotime($this->value['max'], $query_substitutions['***CURRENT_TIME***'])));
// This is safe because we are manually scrubbing the values.
// It is necessary to do it this way because $a and $b are formulas when using an offset.
$operator = strtoupper($this->operator);
$this->query->addWhereExpression($this->options['group'], "$field $operator '$a' AND '$b'");
}
}
......@@ -109,11 +109,15 @@ class BatEventUiBulkUpdateForm extends FormBase {
foreach ($units as $unit) {
$event = bat_event_create([
'type' => $event_type->id(),
'start' => $start_date->getTimestamp(),
'end' => $end_date->getTimestamp(),
'uid' => $type->uid->entity->uid->value,
]);
$event_dates = [
'value' => $start_date->format('Y-m-d'),
'end_value' => $end_date->format('Y-m-d'),
];
$event->set('event_dates', $event_dates);
$target_field_name = 'event_' . $event_type->target_entity_type . '_reference';
$event->set($target_field_name, $unit->id());
......
......@@ -7,3 +7,4 @@ package: bat
dependencies:
- bat_api
- bat_event
- fullcalendar_library
<?php
/**
* @file
* Install for BAT Fullcalendar module
*/
/**
* Implements hook_requirements().
*/
function bat_fullcalendar_requirements($phase) {
if ($phase != 'runtime') {
return [];
}
if (!file_exists(DRUPAL_ROOT . '/libraries/fullcalendar/fullcalendar.min.js')) {
return [
'fullcalendar' => [
'title' => t('Fullcalendar'),
'value' => t('FullCalendar Library loaded via CDN'),
'description' => t('BAT is using a CDN to load the Fullcalendar library. To use a local copy, <a target="_blank" href="@url">download</a> the library and extract it into the /libraries/fullcalendar folder in your Drupal installation directory.', ['@url' => 'https://fullcalendar.io/download/']),
'severity' => REQUIREMENT_INFO,
],
];
}
elseif (!file_exists(DRUPAL_ROOT . '/libraries/fullcalendar-scheduler/scheduler.min.js')) {
return [
'fullcalendar' => [
'title' => t('Fullcalendar'),
'value' => t('FullCalendar Scheduler Library loaded via CDN'),
'description' => t('BAT is using a CDN to load the Fullcalendar Scheduler library. To use a local copy, <a target="_blank" href="@url">download</a> the library and extract it into the /libraries/fullcalendar folder in your Drupal installation directory.', ['@url' => 'https://fullcalendar.io/scheduler/']),
'severity' => REQUIREMENT_INFO,
],
];
}
return [
'fullcalendar' => [
'title' => t('Fullcalendar'),
'value' => t('The FullCalendar, FullCalendar Scheduler and the Moment Library are installed'),
'severity' => REQUIREMENT_OK,
],
];
}
fullcalendar:
version: VERSION
js:
/libraries/fullcalendar/lib/moment.min.js: { minified: true }
/libraries/fullcalendar/fullcalendar.min.js: { minified: true }
/libraries/fullcalendar/locale-all.js: { minified: true }
dependencies:
- core/jquery
css:
theme:
css/fullcalendar.theme.css: {}
/libraries/fullcalendar/fullcalendar.min.css: { minified: true }
fullcalendar-scheduler:
bat-fullcalendar-scheduler:
version: VERSION
js:
/libraries/fullcalendar-scheduler/scheduler.min.js: { minified: true }
js/bat_fullcalendar_timeline.js: {}
dependencies:
- bat_fullcalendar/fullcalendar
- fullcalendar_library/fullcalendar-scheduler
- core/drupalSettings
- core/drupal.dialog.ajax
css:
theme:
css/bat_fullcalendar_timeline.css: {}
/libraries/fullcalendar-scheduler/scheduler.min.css: { minified: true }
bat-fullcalendar-scheduler:
version: VERSION
js:
js/bat_fullcalendar_timeline.js: {}
dependencies:
- bat_fullcalendar/fullcalendar-scheduler
css:
theme:
css/bat_modal.css: {}
css/fullcalendar.theme.css: {}
......@@ -7,13 +7,6 @@
* to easily modify it.
*/
define('FULLCALENDAR_JS_CDN', 'https://cdn.jsdelivr.net/npm/fullcalendar@3.7.0/dist/fullcalendar.min.js');
define('FULLCALENDAR_LOCALE_JS_CDN', 'https://cdn.jsdelivr.net/npm/fullcalendar@3.7.0/dist/locale-all.js');
define('FULLCALENDAR_CSS_CDN', 'https://cdn.jsdelivr.net/npm/fullcalendar@3.7.0/dist/fullcalendar.min.css');
define('FULLCALENDAR_SCHEDULER_JS_CDN', 'https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@1.9.0/dist/scheduler.min.js');
define('FULLCALENDAR_SCHEDULER_CSS_CDN', 'https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@1.9.0/dist/scheduler.min.css');
define('MOMENT_JS_CDN', 'https://cdn.jsdelivr.net/npm/moment@2.19.1/moment.min.js');
use Drupal\Core\Ajax\CloseModalDialogCommand;
use Drupal\Core\Template\Attribute;
use Drupal\Component\Utility\Html;
......@@ -292,40 +285,3 @@ function bat_fullcalendar_modal_style($style = 'default') {
return $modal_style;
}
/**
* Implements hook_library_info_alter().
*/
function bat_fullcalendar_library_info_alter(&$libraries, $extension) {
if ($extension == 'bat_fullcalendar') {
if (!file_exists(DRUPAL_ROOT . '/libraries/fullcalendar/lib/moment.min.js')) {
unset($libraries['fullcalendar']['js']['/libraries/fullcalendar/lib/moment.min.js']);
$libraries['fullcalendar']['js'][MOMENT_JS_CDN] = ['type' => 'external', 'minified' => TRUE];
}
if (!file_exists(DRUPAL_ROOT . '/libraries/fullcalendar/fullcalendar.min.js')) {
unset($libraries['fullcalendar']['js']['/libraries/fullcalendar/fullcalendar.min.js']);
$libraries['fullcalendar']['js'][FULLCALENDAR_JS_CDN] = ['type' => 'external', 'minified' => TRUE];
}
if (!file_exists(DRUPAL_ROOT . '/libraries/fullcalendar/locale-all.js')) {
unset($libraries['fullcalendar']['js']['/libraries/fullcalendar/locale-all.js']);
$libraries['fullcalendar']['js'][FULLCALENDAR_LOCALE_JS_CDN] = ['type' => 'external', 'minified' => TRUE];
}
if (!file_exists(DRUPAL_ROOT . '/libraries/fullcalendar/fullcalendar.min.css')) {
unset($libraries['fullcalendar']['css']['theme']['/libraries/fullcalendar/fullcalendar.min.css']);
$libraries['fullcalendar']['css']['theme'][FULLCALENDAR_CSS_CDN] = ['type' => 'external', 'minified' => TRUE];
}
if (!file_exists(DRUPAL_ROOT . '/libraries/fullcalendar-scheduler/scheduler.min.js')) {
unset($libraries['fullcalendar-scheduler']['js']['/libraries/fullcalendar-scheduler/scheduler.min.js']);
$libraries['fullcalendar-scheduler']['js'][FULLCALENDAR_SCHEDULER_JS_CDN] = ['type' => 'external', 'minified' => TRUE];
}
if (!file_exists(DRUPAL_ROOT . '/libraries/fullcalendar-scheduler/scheduler.min.css')) {
unset($libraries['fullcalendar-scheduler']['css']['theme']['/libraries/fullcalendar-scheduler/scheduler.min.css']);
$libraries['fullcalendar-scheduler']['css']['theme'][FULLCALENDAR_SCHEDULER_CSS_CDN] = ['type' => 'external', 'minified' => TRUE];
}
}
}
......@@ -149,12 +149,11 @@ class FullcalendarEventManagerForm extends FormBase {
$event = bat_event_create(['type' => $event_type]);
$event->uid = \Drupal::currentUser()->id();
$event->start = $start_date->getTimestamp();
// Always subtract one minute from the end time. FullCalendar provides
// start and end time with the assumption that the last minute is *excluded*
// while BAT deals with times assuming that the last minute is included.
$end_date->sub(new \DateInterval('PT1M'));
$event->end = $end_date->getTimestamp();
$event_dates = [
'value' => $start_date->format('Y-m-d\TH:i:00'),
'end_value' => $end_date->format('Y-m-d\TH:i:00'),
];
$event->set('event_dates', $event_dates);
$event_type_entity = bat_event_type_load($event_type);
// Construct target entity reference field name using this event type's target entity type.
......@@ -192,12 +191,11 @@ class FullcalendarEventManagerForm extends FormBase {
$event = bat_event_create(['type' => $event_type]);
$event->uid = \Drupal::currentUser()->id();
$event->start = $start_date->getTimestamp();
// Always subtract one minute from the end time. FullCalendar provides
// start and end time with the assumption that the last minute is *excluded*
// while BAT deals with times assuming that the last minute is included.
$end_date->sub(new \DateInterval('PT1M'));
$event->end = $end_date->getTimestamp();
$event_dates = [
'value' => $start_date->format('Y-m-d\TH:i:00'),
'end_value' => $end_date->format('Y-m-d\TH:i:00'),
];
$event->set('event_dates', $event_dates);
$event_type_entity = bat_event_type_load($event_type);
</