Display activity date in local time (using the time zone that is stored with the activity), not in UTC (#4)
* Display timestamps using the timezone that is stored at the activity (fix 'new Date()' invocation) * Display timestamps using the timezone that is stored at the activity (relative date in timeline views) * Use correct timezone for auto-generated activity title --------- Co-authored-by: Niklas Deutschmann <sonstharmlos@noreply.codeberg.org>
This commit is contained in:
parent
5df4da86a5
commit
102d515b42
5 changed files with 49 additions and 11 deletions
|
|
@ -386,7 +386,8 @@ public class ActivityFileService {
|
|||
activityTitle = parsedData.getTitle();
|
||||
} else {
|
||||
// Generate title if not provided
|
||||
activityTitle = ActivityFormatter.generateActivityTitle(parsedData.getStartTime(), parsedData.getActivityType());
|
||||
activityTitle = ActivityFormatter.generateActivityTitle(parsedData.getStartTime(), parsedData.getTimezone(),
|
||||
parsedData.getActivityType());
|
||||
}
|
||||
|
||||
// Default to PUBLIC if visibility not specified
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -257,7 +256,7 @@ public class FitFileService {
|
|||
private String generateTitle(ParsedActivityData parsedData) {
|
||||
return ActivityFormatter.generateActivityTitle(
|
||||
parsedData.getStartTime(),
|
||||
parsedData.getActivityType()
|
||||
parsedData.getTimezone(), parsedData.getActivityType()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
package net.javahippie.fitpub.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.javahippie.fitpub.model.entity.Activity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.*;
|
||||
|
||||
/**
|
||||
* Utility class for formatting activity-related data for display.
|
||||
*/
|
||||
@Slf4j
|
||||
public class ActivityFormatter {
|
||||
|
||||
/**
|
||||
|
|
@ -47,21 +48,25 @@ public class ActivityFormatter {
|
|||
* Generates a default activity title based on the time of day and activity type.
|
||||
* Format: "[Time of Day] [Activity Type]" (e.g., "Morning Run", "Evening Ride")
|
||||
*
|
||||
* @param startedAt the activity start time
|
||||
* @param startedAt the activity start time
|
||||
* @param timezone the timezone ID of the activity
|
||||
* @param activityType the activity type
|
||||
* @return generated title
|
||||
*/
|
||||
public static String generateActivityTitle(LocalDateTime startedAt, Activity.ActivityType activityType) {
|
||||
public static String generateActivityTitle(LocalDateTime startedAt, String timezone, Activity.ActivityType activityType) {
|
||||
if (startedAt == null || activityType == null) {
|
||||
return "Activity";
|
||||
}
|
||||
|
||||
String timeOfDay = getTimeOfDay(startedAt.toLocalTime());
|
||||
LocalDateTime startedAtLocal = getUtcDateTimeInZone(startedAt, timezone);
|
||||
String timeOfDay = getTimeOfDay(startedAtLocal.toLocalTime());
|
||||
String formattedType = formatActivityType(activityType);
|
||||
|
||||
return timeOfDay + " " + formattedType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Determines the time of day based on the hour.
|
||||
*
|
||||
|
|
@ -81,4 +86,25 @@ public class ActivityFormatter {
|
|||
return "Night";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to convert the given LocalDateTime (which is assumed to be UTC) into a LocalDateTime in the given
|
||||
* timezone
|
||||
*
|
||||
* @param utcDateTime The original date and time (UTC)
|
||||
* @param timezone A timezone ID
|
||||
* @return The original date and time adjusted to the timezone, if the zone ID could be parsed. The original date
|
||||
* and time otherwise
|
||||
*
|
||||
*/
|
||||
private static LocalDateTime getUtcDateTimeInZone(LocalDateTime utcDateTime, String timezone) {
|
||||
try {
|
||||
return utcDateTime.atZone(ZoneOffset.UTC)
|
||||
.withZoneSameInstant(ZoneId.of(timezone))
|
||||
.toLocalDateTime();
|
||||
} catch (DateTimeException e) {
|
||||
log.warn("Invalid time zone ID: {}", timezone);
|
||||
return utcDateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -434,7 +434,7 @@ function formatDateTimeWithTimezone(timestamp, timezone, options = {}) {
|
|||
|
||||
// Parse the timestamp - backend sends LocalDateTime without 'Z'
|
||||
// We need to interpret it in the specified timezone
|
||||
const date = new Date(timestamp);
|
||||
const date = new Date(ensureUTC(timestamp));
|
||||
|
||||
// Default options for date/time display
|
||||
const defaultOptions = {
|
||||
|
|
@ -473,6 +473,17 @@ function formatDateWithTimezone(timestamp, timezone) {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a timestamp will be interpreted as UTC by new Date()
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date (Date time string format)
|
||||
*
|
||||
* @param {string} timestamp - ISO timestamp or LocalDateTime string
|
||||
* @returns {string} The input string, but with a trailing 'Z'
|
||||
*/
|
||||
function ensureUTC(timestamp) {
|
||||
return timestamp.endsWith('Z') ? timestamp : timestamp + 'Z';
|
||||
}
|
||||
|
||||
// Make functions available globally for inline scripts
|
||||
window.FitPub = {
|
||||
createActivityMap,
|
||||
|
|
@ -482,5 +493,6 @@ window.FitPub = {
|
|||
formatDistance,
|
||||
formatPace,
|
||||
formatDateTimeWithTimezone,
|
||||
formatDateWithTimezone
|
||||
formatDateWithTimezone,
|
||||
ensureUTC
|
||||
};
|
||||
|
|
|
|||
|
|
@ -727,7 +727,7 @@ const FitPubTimeline = {
|
|||
* @returns {string} Time ago string
|
||||
*/
|
||||
formatTimeAgo: function(timestamp) {
|
||||
const date = new Date(timestamp);
|
||||
const date = new Date(FitPub.ensureUTC(timestamp));
|
||||
const now = new Date();
|
||||
const secondsAgo = Math.floor((now - date) / 1000);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue