whereBetween mister siste dag fordi SQLite lagrer datokolonner med klokkeslett

Feilen
En Laravel-app hadde en spørring i stil med:
$query->whereBetween('planlagt_dato', ['2026-10-01', '2026-10-31']);
Ser rett ut. Kolonnen er castet til date i modellen, og intervallet dekker hele måneden. Likevel manglet alt som lå på den siste dagen, hver måned, konsekvent.
Hvorfor
SQLite har ikke en egen datotype. Datokolonner lagres som tekst, og i dette tilfellet inneholdt de klokkeslett: 2026-10-01 00:00:00, ikke 2026-10-01. SQLite sammenligner whereBetween på rene strenger. Strengen '2026-10-31' er leksikalsk mindre enn '2026-10-31 00:00:00', så raden med 2026-10-31 00:00:00 faller stille utenfor intervallet. Ingen feilmelding, ingen exception. Bare en rad som ikke er der.
Dette traff to steder i samme app: en dagslisting og et scope som brukes til ruteplanlegging. Begge brukte samme kolonnetype, så begge hadde samme hull.
Fiksen
Ikke endOfDay()-triksing på den ene siden av intervallet. Et halvåpent intervall løser det strukturelt:
$query->where('planlagt_dato', '>=', $fra)
->where('planlagt_dato', '<', $til->copy()->addDay());
>= på startdatoen og < på dagen etter sluttdatoen fanger alt som ligger innenfor, uansett hvilket klokkeslett SQLite har lagret sammen med datoen. Samme scope ble brukt i begge kodestedene som hadde problemet, så fiksen rettet begge med én endring.
Regelen videre: alle kolonner som er castet til date i denne kodebasen behandles med halvåpent intervall, ikke whereBetween, fordi lagringsformatet under kan inneholde klokkeslett selv om applikasjonslaget later som det ikke gjør.
En bieffekt: datoavhengige tester som ble røde på bestemte dager
Flere testklasser satte opp testdata med today()->addDays(5) for å lage en avtale «fem dager fram». Den dagslisten de testet mot viser bare inneværende måned. Treffer today() + 5 over månedsskiftet, forsvinner avtalen fra visningen testen sjekker mot, og testen blir rød, selv om koden ikke er endret og testen var grønn dagen før.
Fiksen er å fryse datoen eksplisitt i testoppsettet (for eksempel med travelTo()) i stedet for å regne relativt til today(). En test som er avhengig av hvilken dato den kjøres på, er en test som vil gi rød CI på akkurat de datoene regnestykket krysser en grense, uten at noen har rørt koden.
Lærdommen
En date-cast i applikasjonslaget er et løfte fra ORM-et, ikke en garanti fra databasen om hvordan verdien ligger lagret. Når en betingelse på en datokolonne gir feil resultat bare i ett av flere scenarioer (her: siste dag i intervallet), er det verdt å se på rå lagringsformat før man leter videre i applikasjonslogikken.


