diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/AnalysisContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/AnalysisContainer.java index 29e81b854..72203e5d6 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/AnalysisContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/AnalysisContainer.java @@ -73,6 +73,7 @@ public class AnalysisContainer extends DynamicDataContainer { private final HtmlTables tables; private final Accordions accordions; private final AnalysisPluginsTabContentCreator pluginsTabContentCreator; + private TimeZone timeZone; public AnalysisContainer( ServerContainer serverContainer, @@ -98,6 +99,9 @@ public class AnalysisContainer extends DynamicDataContainer { this.tables = tables; this.accordions = accordions; this.pluginsTabContentCreator = pluginsTabContentCreator; + + timeZone = config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT"); + addAnalysisSuppliers(); } @@ -232,18 +236,18 @@ public class AnalysisContainer extends DynamicDataContainer { putSupplier(AnalysisKeys.PLAYERS_DAY, () -> getUnsafe(uniqueDay).count()); putSupplier(AnalysisKeys.PLAYERS_WEEK, () -> getUnsafe(uniqueWeek).count()); putSupplier(AnalysisKeys.PLAYERS_MONTH, () -> getUnsafe(uniqueMonth).count()); - putSupplier(AnalysisKeys.AVG_PLAYERS_NEW, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).averageNewPerDay()); - putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_DAY, () -> getUnsafe(newDay).averageNewPerDay()); - putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_WEEK, () -> getUnsafe(newWeek).averageNewPerDay()); - putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_MONTH, () -> getUnsafe(newMonth).averageNewPerDay()); + putSupplier(AnalysisKeys.AVG_PLAYERS_NEW, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).averageNewPerDay(timeZone)); + putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_DAY, () -> getUnsafe(newDay).averageNewPerDay(timeZone)); + putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_WEEK, () -> getUnsafe(newWeek).averageNewPerDay(timeZone)); + putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_MONTH, () -> getUnsafe(newMonth).averageNewPerDay(timeZone)); - putSupplier(AnalysisKeys.UNIQUE_PLAYERS_PER_DAY, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).uniqueJoinsPerDay()); - putSupplier(AnalysisKeys.NEW_PLAYERS_PER_DAY, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).newPerDay()); + putSupplier(AnalysisKeys.UNIQUE_PLAYERS_PER_DAY, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).uniqueJoinsPerDay(timeZone)); + putSupplier(AnalysisKeys.NEW_PLAYERS_PER_DAY, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).newPerDay(timeZone)); putSupplier(AnalysisKeys.UNIQUE_PLAYERS_SERIES, () -> graphs.line().lineGraph( - MutatorFunctions.toPoints(getUnsafe(AnalysisKeys.UNIQUE_PLAYERS_PER_DAY))).toHighChartsSeries() + MutatorFunctions.toPointsWithRemovedOffset(getUnsafe(AnalysisKeys.UNIQUE_PLAYERS_PER_DAY), timeZone)).toHighChartsSeries() ); putSupplier(AnalysisKeys.NEW_PLAYERS_SERIES, () -> graphs.line().lineGraph( - MutatorFunctions.toPoints(getUnsafe(AnalysisKeys.NEW_PLAYERS_PER_DAY))).toHighChartsSeries() + MutatorFunctions.toPointsWithRemovedOffset(getUnsafe(AnalysisKeys.NEW_PLAYERS_PER_DAY), timeZone)).toHighChartsSeries() ); Key retentionDay = new Key<>(Integer.class, "RETENTION_DAY"); @@ -355,10 +359,10 @@ public class AnalysisContainer extends DynamicDataContainer { ); putSupplier(AnalysisKeys.PUNCHCARD_SERIES, () -> graphs.special().punchCard(getUnsafe(sessionsMonth).all()).toHighChartsSeries()); - putSupplier(AnalysisKeys.AVG_PLAYERS, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toAverageUniqueJoinsPerDay()); - putSupplier(AnalysisKeys.AVG_PLAYERS_DAY, () -> getUnsafe(sessionsDay).toAverageUniqueJoinsPerDay()); - putSupplier(AnalysisKeys.AVG_PLAYERS_WEEK, () -> getUnsafe(sessionsWeek).toAverageUniqueJoinsPerDay()); - putSupplier(AnalysisKeys.AVG_PLAYERS_MONTH, () -> getUnsafe(sessionsMonth).toAverageUniqueJoinsPerDay()); + putSupplier(AnalysisKeys.AVG_PLAYERS, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toAverageUniqueJoinsPerDay(timeZone)); + putSupplier(AnalysisKeys.AVG_PLAYERS_DAY, () -> getUnsafe(sessionsDay).toAverageUniqueJoinsPerDay(timeZone)); + putSupplier(AnalysisKeys.AVG_PLAYERS_WEEK, () -> getUnsafe(sessionsWeek).toAverageUniqueJoinsPerDay(timeZone)); + putSupplier(AnalysisKeys.AVG_PLAYERS_MONTH, () -> getUnsafe(sessionsMonth).toAverageUniqueJoinsPerDay(timeZone)); } private void addGraphSuppliers() { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/NetworkContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/NetworkContainer.java index ca1809f9d..8ce401177 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/NetworkContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/NetworkContainer.java @@ -139,7 +139,8 @@ public class NetworkContainer extends DynamicDataContainer { locale, config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD), config.get(TimeSettings.ACTIVE_LOGIN_THRESHOLD), - formatters.timeAmount(), formatters.decimals(), formatters.percentage() + formatters.timeAmount(), formatters.decimals(), formatters.percentage(), + config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT") )); putCachingSupplier(NetworkKeys.HEALTH_INDEX, () -> getUnsafe(healthInformation).getServerHealth()); putCachingSupplier(NetworkKeys.HEALTH_NOTES, () -> getUnsafe(healthInformation).toHtml()); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/DateHoldersMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/DateHoldersMutator.java index 86feab663..d6e98a8af 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/DateHoldersMutator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/DateHoldersMutator.java @@ -18,10 +18,7 @@ package com.djrapitops.plan.data.store.mutators; import com.djrapitops.plan.data.store.objects.DateHolder; -import java.util.ArrayList; -import java.util.List; -import java.util.SortedMap; -import java.util.TreeMap; +import java.util.*; import java.util.concurrent.TimeUnit; public class DateHoldersMutator { @@ -33,10 +30,26 @@ public class DateHoldersMutator { } public SortedMap> groupByStartOfMinute() { - return groupByStartOfSections(TimeUnit.MINUTES.toMillis(1L)); + TreeMap> map = new TreeMap<>(); + + if (dateHolders.isEmpty()) { + return map; + } + + long sectionLenght = TimeUnit.MINUTES.toMillis(1L); + for (T holder : dateHolders) { + long date = holder.getDate(); + long startOfSection = date - (date % sectionLenght); + + List list = map.getOrDefault(startOfSection, new ArrayList<>()); + list.add(holder); + map.put(startOfSection, list); + } + return map; } - private SortedMap> groupByStartOfSections(long sectionLength) { + public SortedMap> groupByStartOfDay(TimeZone timeZone) { + long twentyFourHours = TimeUnit.DAYS.toMillis(1L); TreeMap> map = new TreeMap<>(); if (dateHolders.isEmpty()) { @@ -45,18 +58,13 @@ public class DateHoldersMutator { for (T holder : dateHolders) { long date = holder.getDate(); - long startOfMinute = date - (date % sectionLength); + long dateWithOffset = date + timeZone.getOffset(date); + long startOfSection = dateWithOffset - (dateWithOffset % twentyFourHours); - List list = map.getOrDefault(startOfMinute, new ArrayList<>()); + List list = map.getOrDefault(startOfSection, new ArrayList<>()); list.add(holder); - map.put(startOfMinute, list); + map.put(startOfSection, list); } - return map; - } - - public SortedMap> groupByStartOfDay() { - long twentyFourHours = TimeUnit.DAYS.toMillis(1L); - SortedMap> map = groupByStartOfSections(twentyFourHours); // Empty map firstKey attempt causes NPE if not checked. if (!map.isEmpty()) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/MutatorFunctions.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/MutatorFunctions.java index 6ee33878d..21b802369 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/MutatorFunctions.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/MutatorFunctions.java @@ -21,6 +21,7 @@ import com.djrapitops.plan.utilities.html.graphs.line.Point; import java.util.List; import java.util.Map; import java.util.NavigableMap; +import java.util.TimeZone; import java.util.stream.Collectors; public class MutatorFunctions { @@ -31,6 +32,12 @@ public class MutatorFunctions { .collect(Collectors.toList()); } + public static List toPointsWithRemovedOffset(NavigableMap map, TimeZone timeZone) { + return map.entrySet().stream() + .map(entry -> new Point(entry.getKey() - timeZone.getOffset(entry.getKey()), entry.getValue())) + .collect(Collectors.toList()); + } + public static int average(Map map) { return (int) map.values().stream() .mapToInt(i -> i) diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PlayersMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PlayersMutator.java index c54c57c1c..1b1d60b36 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PlayersMutator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PlayersMutator.java @@ -165,19 +165,23 @@ public class PlayersMutator { return players.size(); } - public int averageNewPerDay() { - return MutatorFunctions.average(newPerDay()); + public int averageNewPerDay(TimeZone timeZone) { + return MutatorFunctions.average(newPerDay(timeZone)); } - public TreeMap newPerDay() { + public TreeMap newPerDay(TimeZone timeZone) { List registerDates = registerDates().stream() .map(value -> new DateObj<>(value, value)) .collect(Collectors.toList()); - SortedMap> byDay = new DateHoldersMutator<>(registerDates).groupByStartOfDay(); + // Adds timezone offset + SortedMap> byDay = new DateHoldersMutator<>(registerDates).groupByStartOfDay(timeZone); TreeMap byDayCounts = new TreeMap<>(); for (Map.Entry> entry : byDay.entrySet()) { - byDayCounts.put(entry.getKey(), entry.getValue().size()); + byDayCounts.put( + entry.getKey(), + entry.getValue().size() + ); } return byDayCounts; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/SessionsMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/SessionsMutator.java index cf6328dbc..de1e11be3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/SessionsMutator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/SessionsMutator.java @@ -159,12 +159,13 @@ public class SessionsMutator { return (long) Median.forList(sessionLengths).calculate(); } - public int toAverageUniqueJoinsPerDay() { - return MutatorFunctions.average(uniqueJoinsPerDay()); + public int toAverageUniqueJoinsPerDay(TimeZone timeZone) { + return MutatorFunctions.average(uniqueJoinsPerDay(timeZone)); } - public TreeMap uniqueJoinsPerDay() { - SortedMap> byStartOfDay = toDateHoldersMutator().groupByStartOfDay(); + public TreeMap uniqueJoinsPerDay(TimeZone timeZone) { + // Adds Timezone offset + SortedMap> byStartOfDay = toDateHoldersMutator().groupByStartOfDay(timeZone); TreeMap uniqueJoins = new TreeMap<>(); for (Map.Entry> entry : byStartOfDay.entrySet()) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java index e45310bc6..8e4f0f6e5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java @@ -36,6 +36,7 @@ import java.util.*; public class NetworkHealthInformation extends AbstractHealthInfo { private final NetworkContainer container; + private final TimeZone timeZone; public NetworkHealthInformation( NetworkContainer container, @@ -44,7 +45,8 @@ public class NetworkHealthInformation extends AbstractHealthInfo { int activeLoginThreshold, Formatter timeAmountFormatter, Formatter decimalFormatter, - Formatter percentageFormatter + Formatter percentageFormatter, + TimeZone timeZone ) { super( container.getUnsafe(NetworkKeys.REFRESH_TIME), @@ -54,6 +56,7 @@ public class NetworkHealthInformation extends AbstractHealthInfo { timeAmountFormatter, decimalFormatter, percentageFormatter ); this.container = container; + this.timeZone = timeZone; calculate(); } @@ -152,12 +155,12 @@ public class NetworkHealthInformation extends AbstractHealthInfo { PlayersMutator serverPlayers = playersMutator.filterPlayedOnServer(serverUUID); PlayersMutator serverRegistered = serverPlayers.filterRegisteredBetween(monthAgo, now); - int averageNewPerDay = serverRegistered.averageNewPerDay(); + int averageNewPerDay = serverRegistered.averageNewPerDay(timeZone); serverContainer.putRawData(AnalysisKeys.AVG_PLAYERS_NEW_MONTH, averageNewPerDay); SessionsMutator serverSessions = new SessionsMutator(serverPlayers.getSessions()) .filterSessionsBetween(monthAgo, now) .filterPlayedOnServer(serverUUID); - int averageUniquePerDay = serverSessions.toAverageUniqueJoinsPerDay(); + int averageUniquePerDay = serverSessions.toAverageUniqueJoinsPerDay(timeZone); int uniquePlayers = serverSessions.toUniquePlayers(); serverContainer.putRawData(AnalysisKeys.AVG_PLAYERS_MONTH, averageUniquePerDay); serverContainer.putRawData(AnalysisKeys.PLAYERS_MONTH, uniquePlayers); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/DateFormatter.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/DateFormatter.java index 6bf1e03fd..df692f256 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/DateFormatter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/DateFormatter.java @@ -65,7 +65,12 @@ public abstract class DateFormatter implements Formatter { protected String replaceRecentDays(long epochMs, String format, String pattern) { long now = System.currentTimeMillis(); - long fromStartOfDay = now % TimeUnit.DAYS.toMillis(1L); + boolean useServerTime = config.isTrue(TimeSettings.USE_SERVER_TIME); + TimeZone timeZone = useServerTime ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT"); + int offset = timeZone.getOffset(epochMs); + + // Time since Start of day: UTC + Timezone % 24 hours + long fromStartOfDay = (now + offset) % TimeUnit.DAYS.toMillis(1L); if (epochMs > now - fromStartOfDay) { format = format.replace(pattern, locale.getString(GenericLang.TODAY)); } else if (epochMs > now - TimeUnit.DAYS.toMillis(1L) - fromStartOfDay) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/CalendarFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/CalendarFactory.java index 343aa770d..0f86460c0 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/CalendarFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/CalendarFactory.java @@ -18,11 +18,14 @@ package com.djrapitops.plan.utilities.html.graphs.calendar; import com.djrapitops.plan.data.store.containers.PlayerContainer; import com.djrapitops.plan.data.store.mutators.PlayersMutator; +import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.system.settings.paths.TimeSettings; import com.djrapitops.plan.system.settings.theme.Theme; import com.djrapitops.plan.utilities.formatting.Formatters; import javax.inject.Inject; import javax.inject.Singleton; +import java.util.TimeZone; import java.util.TreeMap; /** @@ -33,13 +36,16 @@ import java.util.TreeMap; @Singleton public class CalendarFactory { private final Theme theme; + private final PlanConfig config; private final Formatters formatters; @Inject public CalendarFactory( + PlanConfig config, Formatters formatters, Theme theme ) { + this.config = config; this.formatters = formatters; this.theme = theme; } @@ -47,7 +53,8 @@ public class CalendarFactory { public PlayerCalendar playerCalendar(PlayerContainer player) { return new PlayerCalendar( player, - formatters.timeAmount(), formatters.yearLong(), formatters.iso8601NoClock(), theme + formatters.timeAmount(), formatters.yearLong(), formatters.iso8601NoClockLong(), theme, + config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT") ); } @@ -58,7 +65,8 @@ public class CalendarFactory { ) { return new ServerCalendar( mutator, uniquePerDay, newPerDay, - formatters.iso8601NoClockLong(), formatters.timeAmount(), theme + formatters.iso8601NoClockLong(), formatters.timeAmount(), theme, + config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT") ); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/PlayerCalendar.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/PlayerCalendar.java index 5fdd255fc..e28be105a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/PlayerCalendar.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/PlayerCalendar.java @@ -21,15 +21,11 @@ import com.djrapitops.plan.data.container.Session; import com.djrapitops.plan.data.store.containers.PlayerContainer; import com.djrapitops.plan.data.store.keys.PlayerKeys; import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.store.objects.DateHolder; import com.djrapitops.plan.system.settings.theme.Theme; import com.djrapitops.plan.system.settings.theme.ThemeVal; import com.djrapitops.plan.utilities.formatting.Formatter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.TimeUnit; /** @@ -41,8 +37,9 @@ public class PlayerCalendar { private final Formatter timeAmountFormatter; private final Formatter yearLongFormatter; - private final Formatter iso8601Formatter; + private final Formatter iso8601Formatter; private final Theme theme; + private final TimeZone timeZone; private final List allSessions; private final long registered; @@ -51,8 +48,9 @@ public class PlayerCalendar { PlayerContainer container, Formatter timeAmountFormatter, Formatter yearLongFormatter, - Formatter iso8601Formatter, - Theme theme + Formatter iso8601Formatter, + Theme theme, + TimeZone timeZone ) { this.allSessions = container.getValue(PlayerKeys.SESSIONS).orElse(new ArrayList<>()); this.registered = container.getValue(PlayerKeys.REGISTERED).orElse(0L); @@ -61,6 +59,7 @@ public class PlayerCalendar { this.yearLongFormatter = yearLongFormatter; this.iso8601Formatter = iso8601Formatter; this.theme = theme; + this.timeZone = timeZone; } public String toCalendarSeries() { @@ -97,7 +96,7 @@ public class PlayerCalendar { private Map> getSessionsByDay() { Map> sessionsByDay = new HashMap<>(); for (Session session : allSessions) { - String day = iso8601Formatter.apply(session); + String day = iso8601Formatter.apply(session.getDate()); List sessionsOfDay = sessionsByDay.getOrDefault(day, new ArrayList<>()); sessionsOfDay.add(session); @@ -111,11 +110,12 @@ public class PlayerCalendar { for (Session session : allSessions) { String length = timeAmountFormatter.apply(session.getLength()); + Long start = session.getUnsafe(SessionKeys.START); + Long end = session.getValue(SessionKeys.END).orElse(System.currentTimeMillis()); series.append(",{title: 'Session: ").append(length) - .append("',start:").append(session.getUnsafe(SessionKeys.START)) - .append(",end:").append(session.getValue(SessionKeys.END) - .orElse(System.currentTimeMillis())) + .append("',start:").append(start + timeZone.getOffset(start)) + .append(",end:").append(end + timeZone.getOffset(end)) .append("}"); for (PlayerKill kill : session.getPlayerKills()) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/ServerCalendar.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/ServerCalendar.java index cbfb47a0f..d4355902b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/ServerCalendar.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/ServerCalendar.java @@ -39,12 +39,14 @@ public class ServerCalendar { private final Formatter iso8601Formatter; private final Formatter timeAmountFormatter; private final Theme theme; + private final TimeZone timeZone; ServerCalendar( PlayersMutator mutator, TreeMap uniquePerDay, TreeMap newPerDay, Formatter iso8601Formatter, Formatter timeAmountFormatter, - Theme theme + Theme theme, + TimeZone timeZone ) { this.mutator = mutator; this.uniquePerDay = uniquePerDay; @@ -52,6 +54,7 @@ public class ServerCalendar { this.iso8601Formatter = iso8601Formatter; this.timeAmountFormatter = timeAmountFormatter; this.theme = theme; + this.timeZone = timeZone; } public String toCalendarSeries() { @@ -85,15 +88,17 @@ public class ServerCalendar { private void appendSessionRelatedData(StringBuilder series) { SessionsMutator sessionsMutator = new SessionsMutator(mutator.getSessions()); - SortedMap> byStartOfDay = sessionsMutator.toDateHoldersMutator().groupByStartOfDay(); + // Adds a timezone offset + SortedMap> byStartOfDay = sessionsMutator.toDateHoldersMutator().groupByStartOfDay(timeZone); + // Has a timezone offset for (Map.Entry entry : uniquePerDay.entrySet()) { if (entry.getValue() <= 0) { continue; } Long key = entry.getKey(); - String day = iso8601Formatter.apply(key); + String day = iso8601Formatter.apply(key - timeZone.getOffset(entry.getKey()));// Remove the timezone offset since Calendar uses UTC List sessions = byStartOfDay.getOrDefault(key, new ArrayList<>()); SessionsMutator dayMutator = new SessionsMutator(sessions);