aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Schubert2020-10-31 20:43:09 +0100
committerGitHub2020-10-31 20:43:09 +0100
commitc31a16cb6d9057883cc5f46376c190a8a8736546 (patch)
treedfe4bd1853fffd259f01a2ad920f6ae00cb44bb8
parent84b8eb8048fcdb8e6449583583aa21b7bb90486e (diff)
preserve schedule timezones and show current time + event timezone on the website (#117)
Diffstat (limited to '')
-rw-r--r--assets/css/_schedule.less24
-rw-r--r--assets/css/_structure.less5
-rw-r--r--assets/js/lustiges-script.js44
-rw-r--r--model/Schedule.php195
-rw-r--r--template/assemblies/header.phtml6
-rw-r--r--template/assemblies/schedule.phtml13
6 files changed, 161 insertions, 126 deletions
diff --git a/assets/css/_schedule.less b/assets/css/_schedule.less
index fe512a9..852d5a4 100644
--- a/assets/css/_schedule.less
+++ b/assets/css/_schedule.less
@@ -16,19 +16,21 @@ body .schedule {
.now {
position: absolute;
- left: 0;
- width: 150px;
- height: 100%;
- background-color: @schedule-now-bg;
- font-size: 14px;
pointer-events: none;
-
+ height: 100%;
+ display: flex;
+ left: 0;
z-index: 5;
- span {
- display: block;
- position: absolute;
- right: -28px;
+ .overlay {
+ width: 150px;
+ height: 100%;
+ background-color: @schedule-now-bg;
+ }
+
+ .label {
+ font-size: 14px;
+ padding-left: 5px;
color: @schedule-now;
}
}
@@ -50,7 +52,7 @@ body .schedule {
.inner {
display: block;
- padding: 10px;
+ padding: 15px;
height: 100%;
}
diff --git a/assets/css/_structure.less b/assets/css/_structure.less
index bf9e3b1..eeb1b23 100644
--- a/assets/css/_structure.less
+++ b/assets/css/_structure.less
@@ -55,6 +55,11 @@ nav {
}
}
+ .navbar-time {
+ line-height: 27px;
+ padding: 10px 10px;
+ }
+
.button-wrapper > .btn {
width: 40px;
}
diff --git a/assets/js/lustiges-script.js b/assets/js/lustiges-script.js
index 67ecd04..b1664f3 100644
--- a/assets/js/lustiges-script.js
+++ b/assets/js/lustiges-script.js
@@ -127,6 +127,7 @@ $(function() {
$(function() {
var
$schedule = $('body .schedule'),
+ $time = $('.navbar-time'),
$now = $schedule.find('.now'),
scrollLock = false,
rewindTimeout,
@@ -154,6 +155,23 @@ $(function() {
}
});
+ function formatLocalTime(timestamp, offset) {
+ const d = new Date(timestamp * 1000);
+
+ // js timezone offset is negative
+ const diff = -d.getTimezoneOffset() - offset;
+ d.setUTCMinutes(d.getUTCMinutes() - diff);
+
+ return String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
+ }
+
+ function formatOffset(offset) {
+ const sign = offset < 0 ? "-" : "+";
+ const hours = String(Math.floor(Math.abs(offset)/60)).padStart(2, '0');
+ const minutes = String(Math.abs(offset)%60).padStart(2, '0');
+ return sign + hours + ":" + minutes;
+ }
+
// schedule now-marker & scrolling
function updateProgramView(initial) {
var
@@ -167,12 +185,22 @@ $(function() {
.find('.room')
.first()
.find('.block')
- .filter(function(i, el) {
+ .filter(function(i, el) {
return $(this).data('start') < now;
}).last();
- if($block.length == 0)
- return $now.css('width', 0);
+ // if we are yet to start find first block as reference
+ if (!$block.length)
+ $block = $schedule
+ .find('.room').first()
+ .find('.block').first();
+
+ // still no luck
+ if(!$block.length) {
+ $now.find('.overlay').css('width', 0);
+ $now.find('.label').text('now');
+ return;
+ }
var
// start & end-timestamp
@@ -195,15 +223,19 @@ $(function() {
px_in_display = px - scrollx;
//console.log($block.get(0), new Date(start*1000), new Date(now*1000), new Date(end*1000), normalized, px);
- $now.css('width', px);
+ const eventOffset = parseFloat($block.data('offset')) || 0;
+ const eventTime = formatLocalTime(now, eventOffset);
+ $now.find('.overlay').css('width', px);
+ $now.find('.label').text("now (" + eventTime + ")");
+ $time.text(eventTime + " (" + formatOffset(eventOffset) + ")");
// scrolling is locked by manual interaction
- if(scrollLock)
+ if (scrollLock)
return;
if(
// now marker is > 2/3 of the schedule-display-width
- px_in_display > (displayw * 2/3) ||
+ px_in_display > (displayw * 2/3) ||
// now marker is <1/7 of the schedule-display-width
px_in_display < (displayw/7)
diff --git a/model/Schedule.php b/model/Schedule.php
index d006d1f..739d2c8 100644
--- a/model/Schedule.php
+++ b/model/Schedule.php
@@ -89,31 +89,32 @@ class Schedule
// so to be on the safer side we calculate our own daystart/end here
foreach($schedule->day as $day)
{
- $daystart = PHP_INT_MAX;
- $dayend = 0;
+ $daystart = new DateTimeImmutable('+100 year');
+ $dayend = new DateTimeImmutable('-100 year');
foreach($day->room as $room)
{
$roomName = (string)$room['name'];
if($this->isRoomFiltered($roomName))
continue;
-
+
if(!in_array($roomName, $rooms))
$rooms[] = $roomName;
foreach($room->event as $event)
{
- $start = strtotime((string)$event->date);
- $duration = $this->strToDuration((string)$event->duration);
- $end = $start + $duration;
+ $start = new DateTimeImmutable((string)$event->date);
+ $interval = $this->strToInterval((string)$event->duration);
+ $end = $start->add($interval);
- $daystart = min($daystart, $start);
- $dayend = max($dayend, $end);
+ $daystart = $start < $daystart ? $start : $daystart;
+ $dayend = $end > $dayend ? $end : $dayend;
}
}
- $day['start'] = $daystart;
- $day['end'] = $dayend;
+ // stringify again to store in simplexml
+ $day['start'] = $daystart->format('c');
+ $day['end'] = $dayend->format('c');
}
@@ -123,23 +124,23 @@ class Schedule
$daysSorted[] = $day;
}
- usort($daysSorted, function($a, $b) {
- return (int)$a['start'] - (int)$b['start'];
+ usort($daysSorted, function($a, $b): int {
+ return strcmp($a['start'], $b['start']);
});
$dayidx = 0;
foreach($daysSorted as $day)
{
$dayidx++;
- $daystart = (int)$day['start'];
- $dayend = (int)$day['end'];
+ $daystart = new DateTimeImmutable($day['start']);
+ $dayend = new DateTimeImmutable($day['end']);
$roomidx = 0;
foreach($rooms as $roomName)
{
$roomidx++;
- $laststart = false;
- $lastend = false;
+ $laststart = NULL;
+ $lastend = NULL;
if($this->isRoomFiltered($roomName))
continue;
@@ -147,24 +148,16 @@ class Schedule
$result = $day->xpath("room[@name='".$roomName."']");
if(!$result) {
// this room has no events on this day -> add long gap
- $program[$roomName][] = array(
- 'special' => 'gap',
-
- 'fstart' => date('c', $daystart),
- 'fend' => date('c', $dayend),
-
- 'start' => $daystart,
- 'end' => $dayend,
- 'duration' => $dayend - $daystart,
- );
- $program[$roomName][] = array(
- 'special' => 'daychange',
- 'title' => 'Daychange from Day '.$dayidx.' to '.($dayidx+1),
-
- 'start' => $dayend,
- 'end' => (int)$schedule->day[$dayidx]['start'],
- 'duration' => 60*60,
- );
+ $gap = $this->makeEvent($daystart, $dayend);
+ $gap['special'] = 'gap';
+ $program[$roomName][] = $gap;
+
+ $end = new DateTimeImmutable($schedule->day[$dayidx]['start']);
+ $daychange = $this->makeEvent($dayend, $end);
+ $daychange['special'] = 'daychange';
+ $daychange['title'] = 'Daychange from Day '.$dayidx.' to '.($dayidx+1);
+ $daychange['duration'] = 3600;
+ $program[$roomName][] = $daychange;
continue;
}
$room = $result[0];
@@ -184,103 +177,74 @@ class Schedule
foreach($eventsSorted as $event)
{
- $start = strtotime((string)$event->date);
- $duration = $this->strToDuration((string)$event->duration);
- $end = $start + $duration;
+ $start = new DateTimeImmutable((string)$event->date);
+ $interval = $this->strToInterval((string)$event->duration);
+ $end = $start->add($interval);
// skip duplicate events in fahrplan source
if ( $laststart == $start )
- continue;
+ continue;
if($lastend && $lastend < $start)
{
- // synthesize pause event
- $pauseduration = $start - $lastend;
- $program[$roomName][] = array(
- 'special' => 'pause',
- 'title' => round($pauseduration / 60).' minutes pause',
-
- 'fstart' => date('c', $lastend),
- 'fend' => date('c', $start),
-
- 'start' => $lastend,
- 'end' => $start,
- 'duration' => $pauseduration,
- 'room_known' => $this->isRoomMapped($roomName),
- );
+ // pause between talks
+ $pause = $this->makeEvent($lastend, $start);
+ $pause['special'] = 'pause';
+ $pause['title'] = round($pause['duration'] / 60).' minutes pause';
+ $pause['room_known'] = $this->isRoomMapped($roomName);
+ $program[$roomName][] = $pause;
}
else if(!$lastend && $daystart < $start)
{
- $program[$roomName][] = array(
- 'special' => 'gap',
-
- 'fstart' => date('c', $daystart),
- 'fend' => date('c', $start),
-
- 'start' => $daystart,
- 'end' => $start,
- 'duration' => $start - $daystart,
- 'room_known' => $this->isRoomMapped($roomName),
- );
+ // gap before first talk
+ $gap = $this->makeEvent($daystart, $start);
+ $gap['special'] = 'gap';
+ $gap['room_known'] = $this->isRoomMapped($roomName);
+ $program[$roomName][] = $gap;
}
$personnames = array();
if(isset($event->persons)) foreach($event->persons->person as $person)
$personnames[] = (string)$person;
- $program[$roomName][] = array(
- 'title' => (string)$event->title,
- 'speaker' => implode(', ', $personnames),
-
- 'fstart' => date('c', $start),
- 'fend' => date('c', $end),
-
- 'start' => $start,
- 'end' => $end,
- 'duration' => $duration,
- 'room_known' => $this->isRoomMapped($roomName),
- 'optout' => $this->isOptout($event),
- );
+ // normal talk
+ $talk = $this->makeEvent($start, $end);
+ $talk['title'] = (string)$event->title;
+ $talk['speaker'] = implode(', ', $personnames);
+ $talk['room_known'] = $this->isRoomMapped($roomName);
+ $talk['optout'] = $this->isOptout($event);
+ $program[$roomName][] = $talk;
$laststart = $start;
$lastend = $end;
}
- // synthesize daychange event
if(!$lastend) $lastend = $daystart;
if($lastend < $dayend)
{
- $program[$roomName][] = array(
- 'special' => 'gap',
-
- 'fstart' => date('c', $lastend),
- 'fend' => date('c', $dayend),
-
- 'start' => $lastend,
- 'end' => $dayend,
- 'duration' => $dayend - $lastend,
- );
+ // gap after last talk
+ $gap = $this->makeEvent($lastend, $dayend);
+ $gap['special'] = 'gap';
+ $program[$roomName][] = $gap;
}
if($dayidx < count($schedule->day))
{
- $program[$roomName][] = array(
- 'special' => 'daychange',
- 'title' => 'Daychange from Day '.$dayidx.' to '.($dayidx+1),
-
- 'start' => $dayend,
- 'end' => (int)$schedule->day[$dayidx]['start'],
- 'duration' => 60*60,
- );
+ // daychange
+ $end = new DateTimeImmutable($schedule->day[$dayidx]['start']);
+ $daychange = $this->makeEvent($dayend, $end);
+ $daychange['special'] = 'daychange';
+ $daychange['title'] = 'Daychange from Day '.$dayidx.' to '.($dayidx+1);
+ $daychange['duration'] = 3600;
+ $program[$roomName][] = $daychange;
}
}
}
-
$mapping = $this->getScheduleToRoomSlugMapping();
if($this->getConference()->has('SCHEDULE.ROOMFILTER'))
{
- // sort by roomfilter
+ // determine roomfilter
$roomfilter = $this->getConference()->get('SCHEDULE.ROOMFILTER');
// map roomfilter-rooms to room-slugs
@@ -291,7 +255,7 @@ class Schedule
return $e;
}, $roomfilter);
- // sort according to roomtilter ordering
+ // sort according to roomfilter ordering
uksort($program, function($a, $b) use ($roomfilter) {
return array_search($a, $roomfilter) - array_search($b, $roomfilter);
});
@@ -300,6 +264,32 @@ class Schedule
return $program;
}
+ private function makeEvent(DateTimeImmutable $from, DateTimeImmutable $to): array {
+ return array(
+ 'fstart' => $from->format('c'),
+ 'fend' => $to->format('c'),
+ 'tstart' => $from->format('H:i'),
+ 'tend' => $to->format('H:i'),
+
+ 'start' => $from->getTimestamp(),
+ 'end' => $to->getTimestamp(),
+ 'offset' => $from->getOffset(),
+ 'duration' => $to->getTimestamp() - $from->getTimestamp(),
+ );
+ }
+
+ private function intervalToDuration(DateInterval $interval): int {
+ $one = new DateTimeImmutable();
+ $two = $one->add($interval);
+ return $two->getTimestamp() - $one->getTimestamp();
+ }
+
+ private function strToInterval(string $str): DateInterval
+ {
+ $parts = explode(':', $str);
+ return new DateInterval('PT'.$parts[0].'H'.$parts[1].'M');
+ }
+
public function getDurationSum()
{
@@ -312,23 +302,18 @@ class Schedule
}
-
- private function strToDuration($str)
- {
- $parts = explode(':', $str);
- return ((int)$parts[0] * 60 + (int)$parts[1]) * 60;
- }
-
public function getScheduleUrl()
{
return $this->getConference()->get('SCHEDULE.URL');
}
+
public function getScheduleCache()
{
return sprintf('/tmp/schedule-cache-%s.xml', $this->getConference()->getSlug());
}
+
public function getScheduleToRoomSlugMapping()
{
$mapping = array();
diff --git a/template/assemblies/header.phtml b/template/assemblies/header.phtml
index 43c6494..f7051f1 100644
--- a/template/assemblies/header.phtml
+++ b/template/assemblies/header.phtml
@@ -22,6 +22,12 @@
<span class="fa fa-info"></span>
</a>
</div>
+
+ <? if(isset($room) && $room->hasSchedule()): ?>
+ <div class="navbar-right navbar-time">
+ Current Time
+ </div>
+ <? endif ?>
</div>
</nav>
diff --git a/template/assemblies/schedule.phtml b/template/assemblies/schedule.phtml
index fe0ccbb..2680a44 100644
--- a/template/assemblies/schedule.phtml
+++ b/template/assemblies/schedule.phtml
@@ -1,10 +1,14 @@
<div class="schedule scroll-container">
<div class="scroll-element">
- <div class="now"><span>now</span></div>
+ <? $totalWidth = round($schedule->getDurationSum() / $schedule->getScale()) ?>
+ <div class="now" style="width: <?= h($totalWidth) ?>px">
+ <div class="overlay"></div>
+ <div class="label">now</div>
+ </div>
<? $rooms = $schedule->getSchedule() ?>
<? foreach($rooms as $roomname => $events): ?>
<? $scheduleRoom = $schedule->getMappedRoom($roomname) ?>
- <div class="room <? if(isset($room) && $roomname == $room->getScheduleName()): ?>highlight<? endif ?>" style="width: <?=round($schedule->getDurationSum() / $schedule->getScale())?>px">
+ <div class="room <? if(isset($room) && $roomname == $room->getScheduleName()): ?>highlight<? endif ?>" style="width: <?= h($totalWidth) ?>px">
<? $fromstart = 0; ?>
<? foreach($events as $event): ?>
<div
@@ -12,6 +16,7 @@
style="width: <?=h(round($event['duration'] / $schedule->getScale()))?>px; left: <?=h(round($fromstart / $schedule->getScale()))?>px"
data-start="<?=intval($event['start'])?>"
data-end="<?=intval($event['end'])?>"
+ data-offset="<?=intval($event['offset']/60)?>"
>
<? $fromstart += $event['duration'] ?>
<? if($scheduleRoom): ?>
@@ -42,9 +47,9 @@
<? else: ?>
<? if($event['duration'] > 10*60): /* only display when event is longer as 10 minutes */ ?>
- <h4><?=h(strftime('%H:%M', $event['start']))?>
+ <h4><?=h($event['tstart'])?>
&ndash;
- <?=h(strftime('%H:%M', $event['end']))?>
+ <?=h($event['tend'])?>
&nbsp;in&nbsp;
<?=h($scheduleRoom ? $scheduleRoom->getDisplayShort() : $roomname) ?>
</h4>