Skip to content

Commit

Permalink
Override default timezone in TZIndependentFormatter as UTC
Browse files Browse the repository at this point in the history
The formatter was applying Operating System timezone on top of any offset,
leading to incorrect dates for any values passed to it.

This meant that anything at the start of day at negative GMT timezones, eg. GMT-4
would move back one day, showing incorrect results in the calendar.

Fixed by applying UTC as the timezone for the formatter

Affects issues:
- Possibly fixed #1239
  • Loading branch information
AuroraLS3 committed Jan 6, 2025
1 parent 7a5cb84 commit 6bc8f0d
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.djrapitops.plan.delivery;

import com.djrapitops.plan.delivery.formatting.Formatters;
import com.djrapitops.plan.delivery.rendering.json.graphs.GraphJSONCreator;
import com.djrapitops.plan.delivery.rendering.json.graphs.Graphs;
import com.djrapitops.plan.delivery.webserver.Addresses;
import com.djrapitops.plan.storage.file.PublicHtmlFiles;
Expand All @@ -32,18 +33,20 @@ public class DeliveryUtilities {
private final Lazy<Formatters> formatters;
private final Lazy<Graphs> graphs;
private final Lazy<PublicHtmlFiles> publicHtmlFiles;
private final Lazy<GraphJSONCreator> graphJSONCreator;

@Inject
public DeliveryUtilities(
Lazy<Addresses> addresses,
Lazy<Formatters> formatters,
Lazy<Graphs> graphs,
Lazy<PublicHtmlFiles> publicHtmlFiles
Lazy<PublicHtmlFiles> publicHtmlFiles, Lazy<GraphJSONCreator> graphJSONCreator
) {
this.addresses = addresses;
this.formatters = formatters;
this.graphs = graphs;
this.publicHtmlFiles = publicHtmlFiles;
this.graphJSONCreator = graphJSONCreator;
}

public Addresses getAddresses() {
Expand All @@ -62,4 +65,7 @@ public PublicHtmlFiles getPublicHtmlFiles() {
return publicHtmlFiles.get();
}

public GraphJSONCreator getGraphJSONCreator() {
return graphJSONCreator.get();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.djrapitops.plan.delivery.formatting.Formatter;

import java.text.SimpleDateFormat;
import java.util.TimeZone;

/**
* Formatter for a timestamp in ISO-8601 format without the clock, without applying timezone offset.
Expand All @@ -34,6 +35,7 @@ public String apply(Long date) {

private String format(Long date) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

return dateFormat.format(date);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.formatting.time;

import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.function.Executable;
import utilities.RandomData;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* Tests against https://github.com/plan-player-analytics/Plan/issues/1239 issues.
*
* @author AuroraLS3
*/
class ISO8601NoClockTZIndependentFormatterTest {

ISO8601NoClockTZIndependentFormatter formatter = new ISO8601NoClockTZIndependentFormatter();

@RepeatedTest(10)
@DisplayName("OS timezone is not applied to Formatter")
void osTimeZoneIsNotApplied() {
long startOfDay = RandomData.randomTimeAfter(0L);
startOfDay = startOfDay - (startOfDay % TimeUnit.DAYS.toMillis(1));

ZonedDateTime epoch = Instant.EPOCH.atZone(ZoneId.of("UTC"));
ZonedDateTime date = epoch.plusSeconds(TimeUnit.MILLISECONDS.toSeconds(startOfDay));
String expected = date.getYear() + "-" +
StringUtils.leftPad("" + date.getMonthValue(), 2, '0') + "-" +
StringUtils.leftPad("" + date.getDayOfMonth(), 2, '0');

List<Executable> assertions = new ArrayList<>();
for (int i = 0; i < 24; i++) {
String result = formatter.apply(startOfDay + TimeUnit.HOURS.toMillis(i));
final int added = i;
assertions.add(() -> assertEquals(expected, result, "Incorrect result when added " + added + " hours"));
}
assertAll(assertions);
}
}
9 changes: 9 additions & 0 deletions Plan/common/src/test/java/extension/SeleniumExtension.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.v130.emulation.Emulation;
import org.openqa.selenium.logging.LogType;
import org.openqa.selenium.logging.LoggingPreferences;
import org.testcontainers.shaded.org.awaitility.Awaitility;
Expand Down Expand Up @@ -100,6 +102,13 @@ private ChromeDriver getChromeWebDriver() {
return new ChromeDriver(chromeOptions);
}

public static void setTimeZone(ChromeDriver chromeDriver, String timeZone) {
try (DevTools devTools = chromeDriver.getDevTools()) {
devTools.createSession();
devTools.send(Emulation.setTimezoneOverride(timeZone));
}
}

private LoggingPreferences getLoggingPreferences() {
LoggingPreferences logPrefs = new LoggingPreferences();
logPrefs.enable(LogType.PERFORMANCE, Level.INFO);
Expand Down
12 changes: 12 additions & 0 deletions Plan/common/src/test/java/utilities/TestData.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,18 @@ private static List<FinishedSession> createSessionsForPlayer(UUID uuid) {
return sessions;
}

public static FinishedSession createSession(UUID uuid, ServerUUID serverUUID, long start) {
String[] gms = GMTimes.getGMKeyArray();

ActiveSession sessionOne = new ActiveSession(uuid, serverUUID, start, serverWorldNames[0], gms[0]);

UUID otherUUID = uuid.equals(playerUUID) ? player2UUID : playerUUID;
sessionOne.addPlayerKill(TestData.getPlayerKill(uuid, otherUUID, serverUUID, "Iron Sword", 1234750L));
sessionOne.addPlayerKill(TestData.getPlayerKill(uuid, otherUUID, serverUUID, "Gold Sword", 1234800L));

return sessionOne.toFinishedSession(start + 500L);
}

public static Transaction storeServers() {
return new Transaction() {
@Override
Expand Down

0 comments on commit 6bc8f0d

Please sign in to comment.