Generato con assistenza AI (Claude Opus 4.8, 1M context). Documento di handoff per una sessione dedicata. Il resto del diario (
metrics.md,04-divergences.md) si aggiorna come sempre a fine di ogni sotto-step. Stato repo all'apertura: steps 0–33 DONE, 594 test, HEAD su main, clippy pulito. Workflow standard [[legacy-port]] + TDD.
date()/DateTime è il primo sotto-sistema che richiede aritmetica di
calendario civile (anni bisestili, giorni del mese, giorno-della-settimana,
ISO week) + parsing di formati data + una piccola gerarchia di classi
native con stato (DateTime mutabile vs DateTimeImmutable). Non è un builtin
puro: serve un design pass prima di scrivere codice. Oracle interamente
disponibile (vedi §1).
Oracle: /tmp/php-src/sapi/cli/php (PHP 8.5.7). Il modulo date è compilato
(a differenza di mbstring): function_exists('date'), class_exists('DateTime'| 'DateTimeImmutable'|'DateInterval'|'DateTimeZone') → tutti true.
Setup test obbligatorio: date_default_timezone_set('UTC'); all'inizio di
ogni snippet (altrimenti warning + timezone di sistema non deterministica).
Usare timestamp fissi per i differenziali.
d=15 D=Sat j=15 l=Saturday N=6 S=th w=6 z=166 W=24
F=June m=06 M=Jun n=6 t=30 L=1 o=2024 Y=2024 y=24
a=pm A=PM B=542 g=12 G=12 h=12 H=12 i=00 s=45 u=000000
e=UTC I=0 O=+0000 P=+00:00 T=UTC Z=0
c=2024-06-15T12:00:45+00:00 r=Sat, 15 Jun 2024 12:00:45 +0000 U=1718452845
- Escape:
\rende letterale il char successivo (date('\\Y=Y',$t)→Y=2024). S= suffisso ordinale inglese (st/nd/rd/th).N=1..7 (lun..dom),w=0..6 (dom..sab).z=day-of-year 0-based.t=giorni nel mese.L=leap.W/o=ISO week/ISO year.B= Swatch internet time.u/v= microsecondi/millisecondi (0 senza frazione).
mktime(0,0,0,6,15,2024) = 1718409600 (h,m,s,month,day,year)
strtotime("2024-06-15 12:00:00") = 1718452800
date("Y-m-d H:i:s", 1718452800) = "2024-06-15 12:00:00"
Altre da coprire: gmdate, gmmktime, checkdate, date_default_timezone_set/get.
$d = new DateTime("2024-06-15 12:30:45"); $d->format("Y-m-d H:i:s") // 2024-06-15 12:30:45
$d->modify("+1 day"); $d->format("Y-m-d") // 2024-06-16
$iv = $d->diff(new DateTime("2024-06-20")); $iv->days; $iv->d; $iv->format("%d days")
$di = new DateTimeImmutable("2024-01-01"); $di->add(new DateInterval("P1M")) // ritorna NUOVO, orig invariato
(new DateTime("2024-06-15"))->getTimestamp()
DateTime::createFromFormat("d/m/Y","15/06/2024")->format("Y-m-d") // 2024-06-15- DateTime è MUTABILE (
modify/add/submutano$this); DateTimeImmutable ritorna una nuova istanza. Distinzione centrale. DateInterval("P1M")= ISO 8601 duration.diff()→ DateInterval cony/m/d/h/i/s/days/invert.
Raccomandazione: crate time 0.3 (Strategy A adapter, [[legacy-port]] — stesso
precedente di regex allo step 27). Pure-Rust, niente dipendenza dal tz-db di
sistema, deterministico, ha civil date math + leap/dow/ordinal. Alternativa
chrono (più completo su parsing/tz ma più pesante). NON hand-rollare
l'aritmetica civile (giorni bisestili, overflow mesi in modify → bug garantiti
e fuori dallo spirito "idiomatic, non porting"). La traduzione dei format char
PHP → output va però scritta a mano (i format char PHP ≠ quelli di time/chrono).
Lo strtotime parsing dei formati relativi va scritto a mano su un subset.
Le classi DateTime hanno stato nativo (un istante) + metodi nativi → non sono prelude-PHP puro (come Exception) né builtin puri. Opzioni:
- (consigliata) Classi native intercettate: registra
DateTime/DateTimeImmutable/DateInterval/DateTimeZonecome classi note; intercettanewe le chiamate di metodo nel valutatore (come già fatto per get_class/ json_decode/preg). Lo stato (timestamp + tz) vive in una proprietà interna dell'oggetto (es. una prop nascosta con l'epoch i64, o un campo nativo nelObject). Riusa la macchina OOP (Zval::Object, dispatch metodi) degli step 19+. - Valuta se serve un nuovo
Zvalo se basta una prop interna sull'Objectesistente. Preferire la prop interna (meno invasivo).
Raccomandazione: solo UTC (+ eventualmente offset fissi +HH:MM) nel primo
giro. Il tz-database completo (America/New_York con DST) è enorme → scope-out
esplicito. I test PHP usano quasi sempre date_default_timezone_set('UTC').
DateTimeZone ridotto a UTC/offset. Documentare in 04-divergences.md.
Il parser di PHP è vastissimo (relative: "next monday", "+1 week", "first day of
next month"; assoluti in mille formati). Scope a un subset: ISO
Y-m-d[ H:i:s], Y/m/d, timestamp @N, now, e i relativi più comuni
(+N day|week|month|year, -N ...). Tutto il resto → scope-out documentato.
createFromFormat invece è deterministico (format esplicito) → più facile.
- 34-1
date()core formatting: dato un timestamp + tz UTC, mappa tutti i format char di §1a. Builtin purodate(string $format, ?int $ts = now). Attenzione:nowrende il risultato non-deterministico → per i test passare sempre il ts. Helperformat_php(epoch, fmt) -> Vec<u8>. Includegmdate(= date in UTC). +molti test (uno per gruppo di char). - 34-2
mktime/gmmktime/checkdate: costruzione timestamp da componenti- validazione data. Puri.
- 34-3
strtotime(subset): ISO assoluti +@N+now+ relativi comuni. Intercettato o puro (no stato se now passato? now usa l'orologio → vedi D-DT5). - 34-4
DateTimecore:new DateTime(str),->format(),->getTimestamp(),->setTimestamp(),setDate/setTime. Classe nativa intercettata (D-DT2). - 34-5
DateTimeImmutable+modify/add/sub(mutabile vs immutabile). - 34-6
DateInterval(P1Y2M3DT4H5M6Sparsing,->format('%...')) +DateTime::diff(). - 34-7
createFromFormat+ rifiniture + corpusext/date/tests.
(Granularità rivedibile: il Decider può accorpare 34-4/5 o spezzare 34-1.)
date('now')enew DateTime()senza arg usano l'orologio reale → non-deterministici. Per il differential, fissare sempre il ts/la stringa. Vedi D-DT5: valutare un hook "ora corrente iniettabile" per testabilità, oppure testare solo i path deterministici (consigliato: solo path con ts esplicito).- D-DT5 —
now: l'orologio reale non è disponibile in modo deterministico nel test harness. Opzioni: (a) scope-out dinow/time()dai differenziali, testare solo input espliciti; (b) iniettare un "clock" fisso nell'Evaluator. Decidere all'inizio. NB:Date.now()/SystemTime::now()esistono in Rust ma rendono i test non riproducibili — preferire (a). DateTimemutabile:$a = new DateTime(...); $b = $a; $b->modify(...)muta anche$a(object handle semantics, già corrette dallo step 19).DateTimeImmutableno.diff()->daysè il totale assoluto di giorni;->dè la componente giorni.->invert= 1 se negativo.createFromFormatritornafalsesu parse-fail (non eccezione, di default).- Overflow in
modify/mktimenormalizza (mktime(0,0,0,13,1,2024)→ gennaio 2025).
- Builtin puri (
date/mktime/checkdate/gmdate) → testare inphp-builtins/tests/builtins.rsout()(registry COMPLETA). NON disponibili ineval.rs::out()(registry vuota). - DateTime OOP (intercettato nel valutatore) → testabile sia in
eval.rssia inbuiltins.rs; usarebuiltins.rsper le asserzioni convar_dump. phpt-runner:PHP_ORACLE=/tmp/php-src/sapi/cli/php target/debug/phpt-runner /tmp/php-src/ext/date/tests/<...>.phptper il corpus. Molti test usanodate_default_timezone_set('UTC')nel--FILE--.- Il binario
phprè uno stub (non esegue script) → differential solo via harness.
- Timezone database completo + DST (solo UTC/offset fissi).
strtotimeformati esotici/locale;IntlDateFormatter; calendari non gregoriani.now/time()deterministici (vedi D-DT5).DatePeriod,DateTimeZoneavanzato, microsecondi reali da clock.- Formattazione locale-dependent (mese/giorno localizzati): PHP
date()è comunque inglese-fisso perD/l/F/M, quindi OK.
- Leggere questo file +
diary/metrics.md(coda) + il resume di memoria. - Decidere D-DT1..D-DT5 col Decider (proporre i default consigliati sopra).
- Aggiungere il crate scelto a
crates/php-runtime/Cargo.toml(o php-builtins per le funzioni pure) —cargo add time/chrono. - Partire da 34-1
date()core in TDD (RED→GREEN per gruppo di format char).