CluPilotCloud/app/Support/LocalTime.php

49 lines
1.5 KiB
PHP

<?php
namespace App\Support;
use Illuminate\Support\Carbon;
/**
* The two halves of a wall clock, kept in one place.
*
* A `datetime-local` field has no timezone: it is the numbers a person reads
* off their own clock. That makes it a round trip with two ends, and getting
* one end right is not half a fix — it is a fresh bug. Filling a field from a
* UTC timestamp shows an operator 21:00 for a window that starts at 23:00
* their time; parsing what they type as UTC stores 21:00 for the 19:00 they
* meant. Do both, or neither works.
*
* They live as a pair here so that nobody can change one end without the other
* being in front of them.
*/
final class LocalTime
{
/** The value for a datetime-local input, in the reader's own zone. */
public static function toField(?Carbon $at): string
{
return $at?->local()->format('Y-m-d\TH:i') ?? '';
}
/**
* What a person typed into such a field, as UTC for storage.
*
* Returns null for anything unusable rather than throwing: these fields
* are read on every keystroke, and half of "2026-07-2" is normal.
*/
public static function fromField(string $value): ?Carbon
{
if (trim($value) === '') {
return null;
}
try {
// Parsed IN the display zone — the string carries no offset, so
// without this it would be read as UTC and silently shifted.
return Carbon::parse($value, config('app.display_timezone'))->utc();
} catch (\Throwable) {
return null;
}
}
}