attendance['timezone'] ?? null) : null); return new DateTimeZone($tz ?: date_default_timezone_get()); } /** * For a given attendance day (Y-m-d), return the timestamp (Y-m-d H:i:s) * of the end of the SECOND Sunday AFTER that day, in school TZ. * This is when it should hard-lock automatically. */ public static function secondSundayAfterEndOfDay(string $ymd, string $tz = null): string { $zone = self::tz($tz); $day = new DateTimeImmutable($ymd . ' 00:00:00', $zone); // w: 0=Sun, 1=Mon, ... 6=Sat $w = (int) $day->format('w'); $daysToNextSunday = (7 - $w) % 7; // 0 if Sunday -> next Sunday is +7 if ($daysToNextSunday === 0) $daysToNextSunday = 7; $firstSunday = $day->modify("+{$daysToNextSunday} days"); $secondSunday = $firstSunday->modify('+7 days'); return $secondSunday->setTime(23, 59, 59)->format('Y-m-d H:i:s'); } /** * Given "now", compute the cutoff *date* (Y-m-d) that should be published * RIGHT NOW under the "lock the 2nd Sunday backward" rule. * i.e., Sunday of the current week minus 14 days. */ public static function secondSundayBackwardDate(\DateTimeImmutable $now, string $tz = null): string { $zone = self::tz($tz); $n = $now->setTimezone($zone); $w = (int) $n->format('w'); // 0=Sun $thisSunday = $n->setTime(0,0,0)->modify("-{$w} days"); // start of current Sun $twoBack = $thisSunday->modify('-14 days'); // two Sundays ago return $twoBack->format('Y-m-d'); } }