[#971] Timezone setting now affects calendar and recent days formatting

This commit is contained in:
Rsl1122 2019-04-02 18:52:05 +03:00
parent 0ff83713ec
commit 7dead97213
11 changed files with 104 additions and 58 deletions

View File

@ -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<Integer> 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() {

View File

@ -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());

View File

@ -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<T extends DateHolder> {
@ -33,10 +30,26 @@ public class DateHoldersMutator<T extends DateHolder> {
}
public SortedMap<Long, List<T>> groupByStartOfMinute() {
return groupByStartOfSections(TimeUnit.MINUTES.toMillis(1L));
TreeMap<Long, List<T>> map = new TreeMap<>();
if (dateHolders.isEmpty()) {
return map;
}
private SortedMap<Long, List<T>> groupByStartOfSections(long sectionLength) {
long sectionLenght = TimeUnit.MINUTES.toMillis(1L);
for (T holder : dateHolders) {
long date = holder.getDate();
long startOfSection = date - (date % sectionLenght);
List<T> list = map.getOrDefault(startOfSection, new ArrayList<>());
list.add(holder);
map.put(startOfSection, list);
}
return map;
}
public SortedMap<Long, List<T>> groupByStartOfDay(TimeZone timeZone) {
long twentyFourHours = TimeUnit.DAYS.toMillis(1L);
TreeMap<Long, List<T>> map = new TreeMap<>();
if (dateHolders.isEmpty()) {
@ -45,18 +58,13 @@ public class DateHoldersMutator<T extends DateHolder> {
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<T> list = map.getOrDefault(startOfMinute, new ArrayList<>());
List<T> list = map.getOrDefault(startOfSection, new ArrayList<>());
list.add(holder);
map.put(startOfMinute, list);
map.put(startOfSection, list);
}
return map;
}
public SortedMap<Long, List<T>> groupByStartOfDay() {
long twentyFourHours = TimeUnit.DAYS.toMillis(1L);
SortedMap<Long, List<T>> map = groupByStartOfSections(twentyFourHours);
// Empty map firstKey attempt causes NPE if not checked.
if (!map.isEmpty()) {

View File

@ -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<Point> toPointsWithRemovedOffset(NavigableMap<Long, Integer> 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<Long, Integer> map) {
return (int) map.values().stream()
.mapToInt(i -> i)

View File

@ -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<Long, Integer> newPerDay() {
public TreeMap<Long, Integer> newPerDay(TimeZone timeZone) {
List<DateObj> registerDates = registerDates().stream()
.map(value -> new DateObj<>(value, value))
.collect(Collectors.toList());
SortedMap<Long, List<DateObj>> byDay = new DateHoldersMutator<>(registerDates).groupByStartOfDay();
// Adds timezone offset
SortedMap<Long, List<DateObj>> byDay = new DateHoldersMutator<>(registerDates).groupByStartOfDay(timeZone);
TreeMap<Long, Integer> byDayCounts = new TreeMap<>();
for (Map.Entry<Long, List<DateObj>> entry : byDay.entrySet()) {
byDayCounts.put(entry.getKey(), entry.getValue().size());
byDayCounts.put(
entry.getKey(),
entry.getValue().size()
);
}
return byDayCounts;

View File

@ -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<Long, Integer> uniqueJoinsPerDay() {
SortedMap<Long, List<Session>> byStartOfDay = toDateHoldersMutator().groupByStartOfDay();
public TreeMap<Long, Integer> uniqueJoinsPerDay(TimeZone timeZone) {
// Adds Timezone offset
SortedMap<Long, List<Session>> byStartOfDay = toDateHoldersMutator().groupByStartOfDay(timeZone);
TreeMap<Long, Integer> uniqueJoins = new TreeMap<>();
for (Map.Entry<Long, List<Session>> entry : byStartOfDay.entrySet()) {

View File

@ -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<Long> timeAmountFormatter,
Formatter<Double> decimalFormatter,
Formatter<Double> percentageFormatter
Formatter<Double> 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);

View File

@ -65,7 +65,12 @@ public abstract class DateFormatter implements Formatter<Long> {
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) {

View File

@ -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")
);
}
}

View File

@ -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<Long> timeAmountFormatter;
private final Formatter<Long> yearLongFormatter;
private final Formatter<DateHolder> iso8601Formatter;
private final Formatter<Long> iso8601Formatter;
private final Theme theme;
private final TimeZone timeZone;
private final List<Session> allSessions;
private final long registered;
@ -51,8 +48,9 @@ public class PlayerCalendar {
PlayerContainer container,
Formatter<Long> timeAmountFormatter,
Formatter<Long> yearLongFormatter,
Formatter<DateHolder> iso8601Formatter,
Theme theme
Formatter<Long> 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<String, List<Session>> getSessionsByDay() {
Map<String, List<Session>> sessionsByDay = new HashMap<>();
for (Session session : allSessions) {
String day = iso8601Formatter.apply(session);
String day = iso8601Formatter.apply(session.getDate());
List<Session> 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()) {

View File

@ -39,12 +39,14 @@ public class ServerCalendar {
private final Formatter<Long> iso8601Formatter;
private final Formatter<Long> timeAmountFormatter;
private final Theme theme;
private final TimeZone timeZone;
ServerCalendar(
PlayersMutator mutator, TreeMap<Long, Integer> uniquePerDay, TreeMap<Long, Integer> newPerDay,
Formatter<Long> iso8601Formatter,
Formatter<Long> 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<Long, List<Session>> byStartOfDay = sessionsMutator.toDateHoldersMutator().groupByStartOfDay();
// Adds a timezone offset
SortedMap<Long, List<Session>> byStartOfDay = sessionsMutator.toDateHoldersMutator().groupByStartOfDay(timeZone);
// Has a timezone offset
for (Map.Entry<Long, Integer> 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<Session> sessions = byStartOfDay.getOrDefault(key, new ArrayList<>());
SessionsMutator dayMutator = new SessionsMutator(sessions);