fix(tz): server-side timezone conversion for all user-facing time displays
The audit showed only 5 of ~11 time-display surfaces converted to the user's timezone; everything else rendered pgx-decoded UTC. The server owns conversion — clients (web/app/KOReader) receive display-ready strings or machine-readable instants and never convert. - Progress page: LastUpdated formatted in the requesting user's tz (was raw UTC). - Analytics: date-range defaults, day buckets, most-active-day, device last-sync, and popular-book last-read all computed in the user's tz — evening sessions no longer land on the next UTC day. Also fixed the end-date boundary: the query bounds with <= and the end bound was midnight, so the end date's own sessions (including 'today') never appeared. - Queue page: new created_at_display field (admin tz) rendered by the template; created_at stays RFC3339 for API consumers. - Admin hash-conflicts SSR bridge: detected date rendered in the admin's tz. - OPDS: .UTC() before the Z-suffixed feed timestamps (honesty fix — correct today only because the session zone happens to be UTC). - KOReader annotation datetimes deliberately untouched (device owns its display).
This commit is contained in:
1 parent
740213f0a4
commit
1601548e6c
8 files changed
+75
-24
No files matched your search
@@ -2,6 +2,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"bookhoard/internal/database"
|
||||
"bookhoard/internal/utils"
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -47,7 +48,11 @@ type QueueItemResponse struct {
|
||||
Status string `json:"status"`
|
||||
ErrorMessage *string `json:"error_message,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
ProcessedAt *string `json:"processed_at,omitempty"`
|
||||
// CreatedAtDisplay is the server-rendered wall-clock string in the
|
||||
// requesting admin's timezone (templates render this; created_at stays
|
||||
// machine-readable RFC3339 for API consumers).
|
||||
CreatedAtDisplay string `json:"created_at_display,omitempty"`
|
||||
ProcessedAt *string `json:"processed_at,omitempty"`
|
||||
}
|
||||
|
||||
func (h *QueueHandler) GetDeviceQueueStats(c *echo.Context) error {
|
||||
@@ -136,7 +141,9 @@ func (h *QueueHandler) ListDeviceQueueItems(c *echo.Context) error {
|
||||
Status: item.Status.String,
|
||||
ErrorMessage: textPtrToString(item.ErrorMessage),
|
||||
CreatedAt: item.CreatedAt.Time.Format("2006-01-02T15:04:05Z07:00"),
|
||||
ProcessedAt: timestamptzPtrToString(item.ProcessedAt),
|
||||
CreatedAtDisplay: utils.FormatTimeInTimezone(
|
||||
item.CreatedAt.Time, user.Timezone.String),
|
||||
ProcessedAt: timestamptzPtrToString(item.ProcessedAt),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -189,7 +196,9 @@ func (h *QueueHandler) GetQueueData(c *echo.Context) ([]QueueItemResponse, error
|
||||
Status: item.Status.String,
|
||||
ErrorMessage: textPtrToString(item.ErrorMessage),
|
||||
CreatedAt: item.CreatedAt.Time.Format("2006-01-02T15:04:05Z07:00"),
|
||||
ProcessedAt: timestamptzPtrToString(item.ProcessedAt),
|
||||
CreatedAtDisplay: utils.FormatTimeInTimezone(
|
||||
item.CreatedAt.Time, user.Timezone.String),
|
||||
ProcessedAt: timestamptzPtrToString(item.ProcessedAt),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user