Fixed Timestamp issues after timezone changes

Affected version
2.0.10

Steffen

Well-known member
For example, for dates between 2019-03-29T23:00:00 (Friday 11 PM) and 2019-03-29T23:59:59 (Friday 11:59:59 PM), XenForo shows the day of week as Saturday instead of Friday. See the attached screenshot I made here on XenForo.com.

This happens because PHP code in \XF\Language::getRelativeDateTimeOutput and JavaScript in XF.DynamicDate::refresh assumes that all days have exactly 24 hours (86400 seconds). This is not true when a DST change occurs. I think this is the correct fix (https://www.php.net/manual/en/function.date.php):

Diff:
--- a/src/XF/Language.php
+++ b/src/XF/Language.php
@@ -705,12 +705,7 @@ class Language implements \ArrayAccess
         }
         else if ($timestamp >= $timeRef['week'])
         {
-            $dow = $timeRef['todayDow'] - ceil(($timeRef['today'] - $timestamp) / 86400);
-            if ($dow < 0)
-            {
-                $dow += 7;
-            }
-
+            $dow = date('N', $timestamp) % 7;
             $day = $this->getPhraseCacheRaw('day_' . $this->dowTranslation[$dow]);
 
             return strtr($this->getPhraseCacheRaw('day_x_at_time_y'), [
(You can remove the % 7 if you also change the array key of sunday in the $dowTranslation map from 0 to 7.)

The very same bug exists in the JavaScript code that dynamically refreshes dates:
Diff:
--- a/js/xf/core.js
+++ b/js/xf/core.js
@@ -4736,11 +4736,7 @@ if (window.jQuery === undefined) jQuery = $ = {};
                 {
                     if (dynType !== 'week')
                     {
-                        var calcDow = todayDow - Math.ceil((todayStart - thisTime) / 86400);
-                        if (calcDow < 0)
-                        {
-                            calcDow += 7;
-                        }
+                        var calcDow = new Date(thisTime * 1000).getDay() % 7;
 
                         $el.text(XF.phrase('day_x_at_time_y', {
                             '{day}': XF.phrase('day' + calcDow),
Original bug report here (in german): https://www.computerbase.de/forum/threads/fehlerhafter-zeitstempel-eines-erstellten-posts.1864649/
 

Attachments

Kier

XenForo developer
Staff member
@Steffen it seems to me that for those day-of-week values, we are dealing with an absolute value rather than a relative one, so we should be able to just use the system back-end to get the DoW from the timestamp, with appropriate adjustments (done largely automatically if we stay within the XF context) for timezone... Take a look at this for Language.php
Diff:
Index: src/XF/Language.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/XF/Language.php    (revision 94c80936bd1eb35605db181b8ec268fea314dc97)
+++ src/XF/Language.php    (date 1572954185886)
@@ -627,6 +627,9 @@
     {
         $timeRef = $this->getDayStartTimestamps();
 
+        $dateObj = new \DateTime('@' . $timestamp);
+        $dateObj->setTimezone($this->getTimeZone());
+
         $interval = $timeRef['now'] - $timestamp;
 
         if ($interval < -2)
@@ -655,20 +658,21 @@
                     '{time}' => $time
                 ]);
             }
-            else if ($timestamp < $timeRef['tomorrow'] + 86400)
+            else if ($timestamp < $dateObj->setTimestamp($timeRef['tomorrow'])->modify('+1 day')->format('U')) // day after tomorrow
             {
                 // tomorrow
                 return strtr($this->getPhraseCacheRaw('tomorrow_at_x'), [
                     '{time}' => $time
                 ]);
             }
-            else if ($futureInterval < (7 * 86400))
+            else if ($futureInterval < ($dateObj->setTimestamp(\XF::$time)->modify('+1 week')->format('U') - \XF::$time)) // one week in the future
             {
-                // after tomorrow
+                // after tomorrow but within the next week
                 return $this->getDateTimeOutput($date, $time);
             }
             else if ($getFullDate)
             {
+                // more than a week in the future
                 return $this->getDateTimeOutput($date, $time);
             }
             else
@@ -705,12 +709,7 @@
         }
         else if ($timestamp >= $timeRef['week'])
         {
-            $dow = $timeRef['todayDow'] - ceil(($timeRef['today'] - $timestamp) / 86400);
-            if ($dow < 0)
-            {
-                $dow += 7;
-            }
-
+            $dow = $dateObj->setTimestamp($timestamp)->format('w');
             $day = $this->getPhraseCacheRaw('day_' . $this->dowTranslation[$dow]);
 
             return strtr($this->getPhraseCacheRaw('day_x_at_time_y'), [
 
  • Like
Reactions: Tom

XF Bug Bot

XenForo bug fixer bot
Staff member
Thank you for reporting this issue. It has now been resolved and we are aiming to include it in a future XF release (2.1.5).

Change log:
Quite a large and confusing fix, because we have to quosh our assumption that a day is 86400 seconds long and that the time in 86400 seconds will be the same time tomorrow etc. Instead, we need to use whatever interval calculating tools are provided by the system (for PHP that's `DateTime->modify()` and for JavaScript it involves creating a new `Date` object then setting its date using `Date.setFullYear(year, month, date + offsetDays)`) My tests suggest this is working but there are various edge cases I haven't been able to test that might make it blow up - though hopefully not too spectacularly. The PHP side is definitely more sound than the JS because PHP's DateTime is much more friendly than JS's Date, but I'm carrying through more data from PHP to JS (tomorrow, yesterday and -6 days stamps) so that should alleviate some of the problems... but let's see! - Kier
Any changes made as a result of this issue being resolved may not be rolled out here until later.
 
  • Like
Reactions: Tom
Top