fix: browse page timestamps show UTC instead of user local time (#482)

* fix: browse page timestamps show UTC instead of user local time

Closes #474

* fix: tests
This commit is contained in:
2025-07-15 21:17:53 -06:00
committed by GitHub
parent f3ae187274
commit baf2822b81
4 changed files with 48 additions and 19 deletions

View File

@@ -3,13 +3,13 @@ import json
import os
import shutil
from contextlib import asynccontextmanager
from datetime import datetime
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Literal
import sqlalchemy
from fastapi import HTTPException
from pydantic import BaseModel, ConfigDict, Field
from pydantic import BaseModel, ConfigDict, Field, field_serializer
from reflector.db import database, metadata
from reflector.processors.types import Word as ProcessorWord
from reflector.settings import settings
@@ -82,7 +82,7 @@ transcripts = sqlalchemy.Table(
def generate_transcript_name() -> str:
now = datetime.utcnow()
now = datetime.now(timezone.utc)
return f"Transcript {now.strftime('%Y-%m-%d %H:%M:%S')}"
@@ -150,7 +150,7 @@ class Transcript(BaseModel):
status: str = "idle"
locked: bool = False
duration: float = 0
created_at: datetime = Field(default_factory=datetime.utcnow)
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
title: str | None = None
short_summary: str | None = None
long_summary: str | None = None
@@ -168,6 +168,12 @@ class Transcript(BaseModel):
source_kind: SourceKind
audio_deleted: bool | None = None
@field_serializer("created_at", when_used="json")
def serialize_datetime(self, dt: datetime) -> str:
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
return dt.isoformat()
def add_event(self, event: str, data: BaseModel) -> TranscriptEvent:
ev = TranscriptEvent(event=event, data=data.model_dump())
self.events.append(ev)

View File

@@ -1,4 +1,4 @@
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from typing import Annotated, Literal, Optional
import reflector.auth as auth
@@ -6,7 +6,7 @@ from fastapi import APIRouter, Depends, HTTPException
from fastapi_pagination import Page
from fastapi_pagination.ext.databases import paginate
from jose import jwt
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, field_serializer
from reflector.db.meetings import meetings_controller
from reflector.db.migrate_user import migrate_user
from reflector.db.rooms import rooms_controller
@@ -61,6 +61,13 @@ class GetTranscriptMinimal(BaseModel):
target_language: str | None
reviewed: bool
meeting_id: str | None
@field_serializer("created_at", when_used="json")
def serialize_datetime(self, dt: datetime) -> str:
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
return dt.isoformat()
source_kind: SourceKind
room_id: str | None = None
room_name: str | None = None

View File

@@ -46,7 +46,7 @@ import useSessionUser from "../../lib/useSessionUser";
import NextLink from "next/link";
import { Room, GetTranscriptMinimal } from "../../api";
import Pagination from "./pagination";
import { formatTimeMs } from "../../lib/time";
import { formatTimeMs, formatLocalDate } from "../../lib/time";
import useApi from "../../lib/useApi";
import { useError } from "../../(errors)/errorContext";
import { SourceKind } from "../../api";
@@ -381,15 +381,7 @@ export default function TranscriptBrowser() {
? item.room_name
: item.source_kind}
</Td>
<Td>
{new Date(item.created_at).toLocaleString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric",
})}
</Td>
<Td>{formatLocalDate(item.created_at)}</Td>
<Td>{formatTimeMs(item.duration)}</Td>
<Td>
<Menu closeOnSelect={true}>
@@ -466,9 +458,7 @@ export default function TranscriptBrowser() {
? item.room_name
: item.source_kind}
</Text>
<Text>
Date: {new Date(item.created_at).toLocaleString()}
</Text>
<Text>Date: {formatLocalDate(item.created_at)}</Text>
<Text>Duration: {formatTimeMs(item.duration)}</Text>
</Box>
<Menu>

View File

@@ -28,3 +28,29 @@ export const formatTimeDifference = (seconds: number): string => {
return timeString;
};
export const formatRelativeTime = (dateString: string): string => {
const now = new Date();
const past = new Date(dateString);
const diffMs = now.getTime() - past.getTime();
const diffSeconds = Math.floor(diffMs / 1000);
const diffMinutes = Math.floor(diffSeconds / 60);
const diffHours = Math.floor(diffMinutes / 60);
const diffDays = Math.floor(diffHours / 24);
if (diffSeconds < 60) return `${diffSeconds}s ago`;
if (diffMinutes < 60) return `${diffMinutes}m ago`;
if (diffHours < 24) return `${diffHours}h ago`;
return `${diffDays}d ago`;
};
export const formatLocalDate = (dateString: string): string => {
return new Date(dateString).toLocaleString(navigator.language || "en-US", {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric",
});
};