Date/time values are being casted/parsed in a locale aware fashion
Update: this is the default behavior with the CakePHP application template versions prior to 3.2.5. As of 3.2.5 locale parsing is not enabled by default anymore, which will make the date/time marshalling logic expect a default format of Y-m-d H:i:s
instead.
In the marshalling process, values are being "casted" according to the respective column types. For DATETIME
columns, this is done by the CakeDatabaseTypeDateTimeType
type class.
To be exact, this is done in CakeDatabaseTypeDateTimeType::marshall()
.
With the default app template configuration, DateTimeType
is configured to use locale aware parsing, and since no default locale format is being set, CakeI18nTime::parseDateTime()
will parse the values according to its default "to string format" (Time::$_toStringFormat
), which defaults to the locale aware [IntlDateFormatter::SHORT, IntlDateFormatter::SHORT]
.
So, if for example your locale is set to en_US
, then the value would be parsed with an expected format of M/d/yy, h:mm a
, which your value wouldn't fit, and hence you'd finally end up with null
being set for the entity property.
Make the parser use the proper format
tl;dr
In case the format for the jQuery widget is not being used everywhere in your app, you could for example either temporarily set the proper locale format, or disable locale parsing, like
// for time- or date-only comlumn types you'd use 'time' or 'date' instead of 'datetime'
$dateTimeType = Type::build('datetime')->setLocaleFormat('yyyy/MM/dd HH:mm');
// ...
$intervention = $this->Interventions->patchEntity($intervention, $this->request->data);
// ...
$dateTimeType->setLocaleFormat(null);
or
$dateTimeType = Type::build('datetime')->useLocaleParser(false);
// ...
$intervention = $this->Interventions->patchEntity($intervention, $this->request->data);
// ...
$dateTimeType->useLocaleParser(true);
It should be noted that this will affect all date/time input, not just your starttime
and endtime
fields!
Should the format used by the jQuery widget on the other hand be the format that you wish to use all the way through your app, then changing the default format could do it too, like
use CakeI18nTime;
use CakeI18nFrozenTime;
// To affect date-only columns you'd configure `Date` and `FrozenDate`.
// For time-only columns, see the linked SO question below.
Time::setToStringFormat('yyyy/MM/dd HH:mm');
FrozenTime::setToStringFormat('yyyy/MM/dd HH:mm');
in your bootstrap.php
. Note that there's also Time/FrozenTime::setJsonEncodeFormat()
and Time/FrozenTime::$niceFormat
which you may want/need to modify too.
See also
Convert the input before marshalling it
Another option would be to for example convert the data to Time
instances before the marshalling process. This would avoid possible problems with the previous mentioned solution that would affect all input.
In your InterventionsTable
class (could also be put in a behavior or an external listener):
use CakeEventEvent;
use CakeI18nTime;
...
public function beforeMarshal(Event $event, ArrayObject $data, ArrayObject $options)
{
foreach (['starttime', 'endtime'] as $key) {
if (isset($data[$key]) && is_string($data[$key])) {
$data[$key] = Time::parseDateTime($data[$key], 'yyyy/MM/dd HH:mm');
}
}
}
See also
Cookbook > Database Access & ORM > Saving Data > Modifying Request Data Before Building Entities