File size: 1,715 Bytes
45a105b
 
 
 
 
 
 
 
 
 
adb91eb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Date/time presented in Asia/Riyadh by default (backend-configured tz).
export function formatDateTime(epochSeconds: number, locale = 'en', tz = 'Asia/Riyadh'): string {
  try {
    return new Intl.DateTimeFormat(locale === 'ar' ? 'ar-SA' : 'en-GB', {
      dateStyle: 'medium', timeStyle: 'short', timeZone: tz,
    }).format(new Date(epochSeconds * 1000))
  } catch {
    return new Date(epochSeconds * 1000).toISOString()
  }
}

// Any real transaction is after this floor; anything before it (epoch 0, a placeholder
// like `2`, or `1970-01-01T00:00:00Z`) is treated as "no real timestamp".
const MIN_VALID_MS = Date.UTC(2000, 0, 1)

/**
 * Safe timestamp formatter. Accepts an epoch-seconds number OR an ISO string. Returns `null`
 * for missing/empty/invalid/epoch-equivalent values so the caller can show a localized
 * "Date unavailable" fallback — it NEVER fabricates the current date and never renders 1970.
 */
export function formatDateTimeSafe(
  value: number | string | null | undefined,
  locale = 'en',
  tz = 'Asia/Riyadh',
): string | null {
  if (value === null || value === undefined) return null
  let ms: number
  if (typeof value === 'number') {
    if (!Number.isFinite(value)) return null
    ms = value * 1000                       // backend convention: epoch seconds
  } else {
    const s = value.trim()
    if (s === '') return null
    ms = new Date(s).getTime()              // ISO string (already ms-based)
  }
  if (!Number.isFinite(ms) || ms < MIN_VALID_MS) return null
  try {
    return new Intl.DateTimeFormat(locale === 'ar' ? 'ar-SA' : 'en-GB', {
      dateStyle: 'medium', timeStyle: 'short', timeZone: tz,
    }).format(new Date(ms))
  } catch {
    return null
  }
}