StrictDateInput
in package
Strict parser of `<input type=datetime-local>` and `<input type=date>` values, plus a server rule that rejects an invalid value on the field before `onSuccess` (so before manager `save()`).
new DateTime($value) and DateTime::createFromFormat() silently normalize a nonexistent
date (2026-02-30 becomes 2026-03-02, warnings unchecked), and new DateTime() accepts
any relative text (next monday) that a browser never sends for these input types. This
parser uses the exact format (createFromFormat with !, which zeroes components outside the
format), checks getLastErrors() and round-trips back to the same string, which also catches
valid but rearranged notations.
NOTE: it does not replace AdminFormHelpersTrait::parseDateTime() and its local copies, which
are still used for texts outside the datetime-local/date contract (for example ProductSet
snapshots in the DB shape Y-m-d H:i:s). Use this class only on fields where the HTML type
guarantees the value.
No seconds: no admin datetime-local field declares step, so the browser always sends
Y-m-d\TH:i; a round-trip to a longer format would always fail on a real request.
Messages are global keys with the // prefix (as in ColumnGate): validation is called
from dozens of forms across modules, and a domain-prefixed key would have to be copied into
every NEON file.
Table of Contents
Constants
- DATE_MESSAGE : string = '//admin.general.strictDate.date'
- Message for an invalid date; global key.
- DATETIME_LOCAL_MESSAGE : string = '//admin.general.strictDate.datetimeLocal'
- Message for an invalid date and time; global key.
- DATE_FORMAT : string = 'Y-m-d'
- DATETIME_LOCAL_FORMAT : string = 'Y-m-d\TH:i'
Methods
- dateRule() : BaseControl
- Server rule for `<input type=date>`; same logic as {@see datetimeLocalRule()}.
- dateRules() : void
- Batch variant of {@see dateRule()}; see {@see datetimeLocalRules()}.
- datetimeLocalRule() : BaseControl
- Server rule for `<input type=datetime-local>`. An empty value passes (Nette skips rules on an optional empty field anyway, see {@see ColumnGate}); the caller's `setRequired()` enforces filling.
- datetimeLocalRules() : void
- Batch variant of {@see datetimeLocalRule()}; see {@see ColumnGate::lengths()} for the name matching rules. A missing control is skipped (fields are sometimes built conditionally).
- keepSeconds() : DateTime|null
- Keeps the seconds of the stored value: `datetime-local` without `step` sends no seconds, so saving unchanged would cut `23:59:59` to `23:59:00`.
- parseDate() : DateTime|null
- Parses `<input type=date>` into `DateTime` (midnight), or null.
- parseDatetimeLocal() : DateTime|null
- Parses `<input type=datetime-local>` (without `step`, so without seconds) into `DateTime`, or null.
- isValidOrEmpty() : bool
- parse() : DateTime|null
Constants
DATE_MESSAGE
Message for an invalid date; global key.
public
string
DATE_MESSAGE
= '//admin.general.strictDate.date'
DATETIME_LOCAL_MESSAGE
Message for an invalid date and time; global key.
public
string
DATETIME_LOCAL_MESSAGE
= '//admin.general.strictDate.datetimeLocal'
DATE_FORMAT
private
string
DATE_FORMAT
= 'Y-m-d'
DATETIME_LOCAL_FORMAT
private
string
DATETIME_LOCAL_FORMAT
= 'Y-m-d\TH:i'
Methods
dateRule()
Server rule for `<input type=date>`; same logic as {@see datetimeLocalRule()}.
public
static dateRule(BaseControl $control[, string $message = self::DATE_MESSAGE ]) : BaseControl
Parameters
- $control : BaseControl
- $message : string = self::DATE_MESSAGE
Return values
BaseControldateRules()
Batch variant of {@see dateRule()}; see {@see datetimeLocalRules()}.
public
static dateRules(Container $container, array<int, string> $names) : void
Parameters
- $container : Container
- $names : array<int, string>
datetimeLocalRule()
Server rule for `<input type=datetime-local>`. An empty value passes (Nette skips rules on an optional empty field anyway, see {@see ColumnGate}); the caller's `setRequired()` enforces filling.
public
static datetimeLocalRule(BaseControl $control[, string $message = self::DATETIME_LOCAL_MESSAGE ]) : BaseControl
Parameters
- $control : BaseControl
- $message : string = self::DATETIME_LOCAL_MESSAGE
Return values
BaseControldatetimeLocalRules()
Batch variant of {@see datetimeLocalRule()}; see {@see ColumnGate::lengths()} for the name matching rules. A missing control is skipped (fields are sometimes built conditionally).
public
static datetimeLocalRules(Container $container, array<int, string> $names) : void
Parameters
- $container : Container
- $names : array<int, string>
keepSeconds()
Keeps the seconds of the stored value: `datetime-local` without `step` sends no seconds, so saving unchanged would cut `23:59:59` to `23:59:00`.
public
static keepSeconds(DateTime|null $submitted, DateTimeInterface|null $original) : DateTime|null
When the submitted value falls into the same minute as the original, the original (with seconds) is returned; otherwise the submitted one. Call it with the entity value before overwriting.
Parameters
- $submitted : DateTime|null
- $original : DateTimeInterface|null
Return values
DateTime|nullparseDate()
Parses `<input type=date>` into `DateTime` (midnight), or null.
public
static parseDate(mixed $value) : DateTime|null
Parameters
- $value : mixed
Return values
DateTime|nullparseDatetimeLocal()
Parses `<input type=datetime-local>` (without `step`, so without seconds) into `DateTime`, or null.
public
static parseDatetimeLocal(mixed $value) : DateTime|null
Parameters
- $value : mixed
Return values
DateTime|nullisValidOrEmpty()
private
static isValidOrEmpty(mixed $value, string $format) : bool
Parameters
- $value : mixed
- $format : string
Return values
boolparse()
private
static parse(mixed $value, string $format) : DateTime|null
Parameters
- $value : mixed
- $format : string