Problem/Motivation

I am trying to implement a datetime-local element but I can't get it to work

All my tests led me to the catch (\Exception $e) in Datetime::valueCallback()
with the error "The date cannot be created from a format."

Steps to reproduce

Build a custom form with the following method in it:

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['datetimepicker'] = [
      '#type' => 'datetime',
      '#date_date_element' => 'datetime-local',
      '#date_time_element' => 'none',
    ];

    $form['submit'] = [
      '#type' => 'submit',
      '#value' => 'Submit',
    ];

    return $form;
  }

Then access your form page, fill the input and submit the form.

Proposed resolution

  1. Alter Datetime::valueCallback() to provide a valid format (ie. $date_format = 'Y-m-d'; and $time_format = 'H:i:s';) to \DateTime::createFromFormat() when '#date_date_element' is 'datetime-local'
  2. Also alter Datetime::processDatetime() to fill $element['#value'] from a 'Y-m-d\TH:i:s' submitted value after the form has been processed.

Remaining tasks

  • Choose whether or not we have to provide a fix when '#date_date_element' is 'datetime' since it's also using an invalid format. If not open a dedicated new issue to fix it
  • Provide tests covering those changes

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

CommentFileSizeAuthor
#6 datetime-local.png88.12 KBmacsim

Issue fork drupal-3505318

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

macsim created an issue. See original summary.

macsim’s picture

Issue summary: View changes
quietone’s picture

Version: 11.1.x-dev » 11.x-dev
cilefen’s picture

The code throwing the exception is:

$date = \DateTime::createFromFormat($format, $time, $datetime_plus->getTimezone());
if (!$date instanceof \DateTime) {
    throw new \InvalidArgumentException('The date cannot be created from a format.');
}

It is in DateTimePlus::createFromFormat. This code returns `false` in the PHP REPL:

\DateTime::createFromFormat('Y-m-d\TH:i', 'now');

So I think your time format string is invalid to the \DateTime library. I hope this helps.

cilefen’s picture

Actually, 'now' can never work. It's just the case that the format has to match the input. What is the input in the cases where this doesn't work?

This works: \DateTime::createFromFormat('Y-m-d H:i', '2008-06-01 12:50')

I think \T is totally unsupported. Anyway, this is PHP's standard library, not Drupal.

macsim’s picture

Version: 11.x-dev » 11.1.x-dev
StatusFileSize
new88.12 KB

Well then it seems that there's a bug somewhere

In Datetime::processDatetime() doc says:

  /**
   * [...]
   * #date_date_format: A date format string that describes the format that
   *     should be displayed to the end user for the date. When using HTML5
   *     elements the format MUST use the appropriate HTML5 format for that
   *     element, no other format will work. See the
   *     DateFormatterInterface::format() function for a list of the possible
   *     formats and HTML5 standards for the HTML5 requirements. Defaults to the
   *     right HTML5 format for the chosen element if an HTML5 element is used,
   *     otherwise defaults to DateFormat::load('html_date')->getPattern().
   * [...]
   */

And in DateFormatterInterface::format() :

  /**
   * [...]
   * @param string $type
   *   (optional) The format to use, one of:
   *   - One of the built-in formats: 'short', 'medium',
   *     'long', 'html_datetime', 'html_date', 'html_time',
   *     'html_yearless_date', 'html_week', 'html_month', 'html_year'.
   *   - The name of a date type defined by a date format config entity.
   *   - The machine name of an administrator-defined date format.
   *   - 'custom', to use $format.
   *   Defaults to 'medium'.
   * @param string $format
   *   (optional) If $type is 'custom', a PHP date format string suitable for
   *   input to date(). Use a backslash to escape ordinary text, so it does not
   *   get interpreted as date format characters.
   * [...]
   */

No matter what the value I give to that "#date_date_format" ("long", "short", "medium", 'html_datetime', "d/m/Y H:i", "Y-m-d H:i", 'd/m/Y', Y-m-d, etc.) - and I've got the same result if I don't set that "#date_date_format" (which leads to a Y-m-d default value) :

  1. the input is always asking for a "d/m/Y H:i" as you can see on the attached screenshot
  2. when I choose a date and time and submit the form, I am always reaching Datetime::valueCallback() with the following values:
    • $input =
       array:1 [▼
        "date" => "2025-02-09T12:00"
      ]
      
    • $date_input = "2025-02-09T12:00"
    • $time_input = ""
    • $date_format = "Y-m-d\TH:i:sO"
    • $time_format = ""
  3. Then I am always catching the Exception with the message "The date cannot be created from a format."
macsim’s picture

Title: Support to use the datetime-local element » datetime-local element is failing on Datetime::valueCallback()
Category: Support request » Bug report
Issue summary: View changes
quietone’s picture

Version: 11.1.x-dev » 11.x-dev

Changes are made on on 11.x (our main development branch) first, and are then back ported as needed according to the Core change policies.

macsim’s picture

Sorry quietone #8 I didn't realised I changed the version

Ok now I undestand why I've got this behavior no matter what I pass to '#date_date_format'
It's because of the Datetime::valueCallback() variables attributions made just before the try/catch:
$date_format = $element['#date_date_format'] != 'none' ? static::getHtml5DateFormat($element) : '';

protected static function getHtml5DateFormat($element) {
  switch ($element['#date_date_element']) {
    case 'date':
      return DateFormat::load('html_date')->getPattern();

    case 'datetime':
    case 'datetime-local':
      return DateFormat::load('html_datetime')->getPattern();

    default:
      return $element['#date_date_format'];
  }
}

and as cilefen said in #4, 'html_datetime' (ie. "Y-m-d\TH:i:sO") is not a valid format to pass to \DateTime::createFromFormat()

macsim’s picture

Status: Active » Needs work
Issue tags: +Need tests

MR provides a fix for the Datetime::ValueCallback() method.

Few thoughts:
- We should maybe find a way to fix it in a less-dirty way (ie. not with that ugly "if" condition)
- It only fixes the problem for the datetime-local element but datetime element also uses the 'html_datetime' format and would break the same way
- Datetime::processDatetime() needs to be updated as well, $element['#value'] is now not correct to display the submitted value in the input done
- We should introduce tests

macsim’s picture

Status: Needs work » Needs review
smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs issue summary update, +Needs Review Queue Initiative

Assuming NR for feedback for approach? Could the issue summary be filled out to clear up for proposed solution. Easier for reviews

It only fixes the problem for the datetime-local element but datetime element also uses the 'html_datetime' format and would break the same way

I'm 50/50 that this should be fixed here too or could be it's own issue. Guess it depends if the code is the same spot.

We should introduce tests

100% thanks for tagging for that!

macsim’s picture

Issue summary: View changes

#13 Yes I wanted some feedback, thank you.

I updated the issue summary ; hopefully it is clear enough and will help resolving the issue.

macsim’s picture

Issue summary: View changes
macsim’s picture

Issue summary: View changes
macsim’s picture

Status: Needs work » Needs review

Added kernel tests for datetime-local

smustgrave’s picture

Status: Needs review » Needs work

Left comments on MR.

macsim’s picture

Status: Needs work » Needs review

Thanks smustgrave.
I applied your suggestions except for the first one since it's how it's done on other core KernelTests.
IMHO it will be easier to maintain the "form + tests" mix rather than to declare the form in another location even if it sounds prettier.

smustgrave’s picture

Thanks, yea personally think it's messy and think the current examples of it aren't good standards. So I'll leave in review for others to take a look.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Need tests, -Needs issue summary update

Won't hold it up longer, will see what the committers think.

catch made their first commit to this issue’s fork.

  • catch committed 6810f24a on 11.x
    Issue #3505318 by macsim, smustgrave, cilefen: datetime-local element is...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Agreed with the form being defined in the kernel test - it keeps everything in one place vs. split between the test and the test module. It's not possible to do this for functional tests, but for kernel/unit tests the more that is kept together the better IMO.

Committed/pushed to 11.x, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.