- Frontend Vue 3 (Composition API) + Vite + Vue Router - Backend Go (stdlib) : API REST todo/notes + proxy RSS + auth token - Docker Compose : SPA nginx + backend + Miniflux + Postgres - Widgets : horloge canvas météo, todo 3 colonnes, notes persistées, agrégateur RSS multi-feeds, pomodoro, recherche DuckDuckGo (Ctrl+K) - Auth : dashboard public, todo/notes protégés par token - Widgets expandables (mode agrandi centré)
89 lines
2.1 KiB
JavaScript
89 lines
2.1 KiB
JavaScript
// Paris timezone helpers + simulated/live clock state.
|
|
//
|
|
// State is module-level (singleton) so all components share the same
|
|
// `simDate` / `isLive` without prop drilling.
|
|
|
|
import { ref, computed } from 'vue'
|
|
|
|
export const TZ = 'Europe/Paris'
|
|
|
|
export function parisComponents(d) {
|
|
const parts = new Intl.DateTimeFormat('fr-FR', {
|
|
timeZone: TZ,
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
hour12: false,
|
|
}).formatToParts(d)
|
|
const get = (t) => parseInt(parts.find((x) => x.type === t).value, 10)
|
|
return {
|
|
year: get('year'),
|
|
month: get('month'),
|
|
day: get('day'),
|
|
hour: get('hour'),
|
|
minute: get('minute'),
|
|
second: get('second'),
|
|
}
|
|
}
|
|
|
|
export function parisDateStr(d) {
|
|
const p = parisComponents(d)
|
|
const pad = (n) => String(n).padStart(2, '0')
|
|
return `${p.year}-${pad(p.month)}-${pad(p.day)}`
|
|
}
|
|
|
|
export function toInputStr(d) {
|
|
const p = parisComponents(d)
|
|
const pad = (n) => String(n).padStart(2, '0')
|
|
return `${p.year}-${pad(p.month)}-${pad(p.day)}T${pad(p.hour)}:${pad(p.minute)}:${pad(p.second)}`
|
|
}
|
|
|
|
export function getParisOffsetMs(d) {
|
|
const fmt = (tz) =>
|
|
new Intl.DateTimeFormat('en-GB', {
|
|
timeZone: tz,
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
hour12: false,
|
|
}).format(d)
|
|
const [uh, um] = fmt('UTC').split(':').map(Number)
|
|
const [ph, pm] = fmt(TZ).split(':').map(Number)
|
|
return ((ph * 60 + pm) - (uh * 60 + um)) * 60000
|
|
}
|
|
|
|
export function parisStrToDate(val) {
|
|
const naive = new Date(val + 'Z')
|
|
return new Date(naive.getTime() - getParisOffsetMs(naive))
|
|
}
|
|
|
|
// --- Shared singleton state ---
|
|
const simDate = ref(null)
|
|
const isLive = ref(true)
|
|
|
|
function getNow() {
|
|
return isLive.value ? new Date() : simDate.value
|
|
}
|
|
|
|
function setSimulated(date) {
|
|
simDate.value = date
|
|
isLive.value = false
|
|
}
|
|
|
|
function goLive() {
|
|
simDate.value = null
|
|
isLive.value = true
|
|
}
|
|
|
|
export function useClockState() {
|
|
return {
|
|
simDate: computed(() => simDate.value),
|
|
isLive: computed(() => isLive.value),
|
|
getNow,
|
|
setSimulated,
|
|
goLive,
|
|
}
|
|
}
|