format('N'); // 1=Mon ... 5=Fri, 6=Sat, 7=Sun if ($dow === 5) { // Friday itself return $d->format('Y-m-d'); } elseif ($dow === 6) { // Saturday - previous day is Friday $d->modify('-1 day'); return $d->format('Y-m-d'); } elseif ($dow === 7) { // Sunday - 2 days back is Friday $d->modify('-2 days'); return $d->format('Y-m-d'); } else { // Mon(1)->Fri is 3 days prior; Tue(2)->4; Wed(3)->5; Thu(4)->6 $daysBack = $dow + 2; $d->modify("-{$daysBack} days"); return $d->format('Y-m-d'); } } /** * Returns the 5 workdate strings (Y-m-d) for the week starting on $weekStart (a Friday), * in order: Friday, Monday, Tuesday, Wednesday, Thursday. */ function getWeekDates(string $weekStart): array { $fri = new DateTime($weekStart); $dates = [$fri->format('Y-m-d')]; $d = clone $fri; $d->modify('+3 days'); // Monday for ($i = 0; $i < 4; $i++) { $dates[] = $d->format('Y-m-d'); $d->modify('+1 day'); } return $dates; } function dayLabel(string $dateStr): string { $d = new DateTime($dateStr); return $d->format('D, M j'); } function currentWeekStart(): string { return getWeekStart(date('Y-m-d')); } function nextWeekStart(string $weekStart): string { $d = new DateTime($weekStart); $d->modify('+7 days'); return $d->format('Y-m-d'); } /** * Compute the dollar amount owed for a set of day_entries rows. */ function calcOwed(array $entries): float { $total = 0.0; foreach ($entries as $e) { if ($e['status'] === 'full') $total += FULL_DAY_RATE; elseif ($e['status'] === 'half') $total += HALF_DAY_RATE; } return $total; } function e(string $s): string { return htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); } /** * Validates a Y-m-d date string strictly (rejects malformed input that * would otherwise throw when passed to `new DateTime()`). */ function isValidDateStr(?string $s): bool { if (!$s) return false; $d = DateTime::createFromFormat('Y-m-d', $s); return $d !== false && $d->format('Y-m-d') === $s; }