File size: 2,267 Bytes
7596726
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
export const MINUTE_MS = 60 * 1000;
export const DAY_MS = 24 * 60 * MINUTE_MS;

const WEEKDAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const MONTH_NAMES = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

// Short human-readable label used inside schedule blocks.
export function compactDateTime(value) {
  if (value == null) return '';
  return String(value)
    .replace('T', ' ')
    .replace(/(\d{2}:\d{2}):\d{2}$/, '$1');
}

// Parses backend `NaiveDateTime` strings without letting the browser inject the local timezone.
export function parseDateTimeMs(value) {
  if (value == null || value === '') return null;
  if (typeof value === 'number' && Number.isFinite(value)) return value;

  const normalized = String(value).trim().replace(' ', 'T');
  const match = normalized.match(/^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2}))?)?/);
  if (match) {
    // Preserve backend NaiveDateTime wall-time components without applying the browser timezone.
    return Date.UTC(
      Number(match[1]),
      Number(match[2]) - 1,
      Number(match[3]),
      Number(match[4] || 0),
      Number(match[5] || 0),
      Number(match[6] || 0),
    );
  }

  const parsed = Date.parse(normalized);
  return Number.isFinite(parsed) ? parsed : null;
}

// Guarantees a usable time span for timeline rendering even when data is partial.
export function normalizeShiftBounds(startMs, endMs) {
  if (startMs == null && endMs == null) {
    return { startMs: null, endMs: null };
  }
  let resolvedStart = startMs;
  let resolvedEnd = endMs;
  if (resolvedStart == null) resolvedStart = resolvedEnd;
  if (resolvedEnd == null || resolvedEnd <= resolvedStart) resolvedEnd = resolvedStart + MINUTE_MS;
  return { startMs: resolvedStart, endMs: resolvedEnd };
}

// Returns the UTC midnight used as the start of the visible wall-time day.
export function wallTimeDayStartMs(ms) {
  const date = new Date(ms);
  return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
}

// Formats the day header shown above the timeline.
export function formatAxisDayLabel(ms) {
  const date = new Date(ms);
  return `${WEEKDAY_NAMES[date.getUTCDay()]} ${date.getUTCDate()} ${MONTH_NAMES[date.getUTCMonth()]}`;
}