View source  
  <?php
namespace Drupal\webform\Element;
use Drupal\Core\Datetime\Entity\DateFormat;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\FormElement;
use Drupal\Core\Form\FormStateInterface;
class WebformTime extends FormElement {
  
  public function getInfo() {
    $class = get_class($this);
    return [
      '#input' => TRUE,
      '#theme' => 'input__webform_time',
      '#process' => [
        [
          $class,
          'processWebformTime',
        ],
      ],
      '#pre_render' => [
        [
          $class,
          'preRenderWebformTime',
        ],
      ],
      '#theme_wrappers' => [
        'form_element',
      ],
      '#timepicker' => FALSE,
      '#time_format' => 'H:i',
      '#size' => 12,
      '#maxlength' => 12,
    ];
  }
  
  public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
    if ($input === FALSE) {
      
      if (!empty($element['#default_value'])) {
        try {
          
          new \DateTime($element['#default_value']);
        } catch (\Exception $exception) {
          \Drupal::messenger()
            ->addError($exception
            ->getMessage());
          return NULL;
        }
        $element['#default_value'] = static::formatTime('H:i', strtotime($element['#default_value']));
        return $element['#default_value'];
      }
      else {
        return NULL;
      }
    }
    return $input;
  }
  
  public static function processWebformTime(&$element, FormStateInterface $form_state, &$complete_form) {
    
    $element += [
      '#element_validate' => [],
    ];
    array_unshift($element['#element_validate'], [
      get_called_class(),
      'validateWebformTime',
    ]);
    $element['#attached']['library'][] = 'webform/webform.element.time';
    $element['#attributes']['data-webform-time-format'] = !empty($element['#time_format']) ? $element['#time_format'] : DateFormat::load('html_time')
      ->getPattern();
    return $element;
  }
  
  public static function validateWebformTime(&$element, FormStateInterface $form_state, &$complete_form) {
    $has_access = Element::isVisibleElement($element);
    $value = $element['#value'];
    if (trim($value) === '') {
      return;
    }
    $time = strtotime($value);
    
    $is_valid_time = $time && ($value === static::formatTime('H:i', $time) || $value === static::formatTime('H:i:s', $time) || $value === static::formatTime($element['#time_format'], $time));
    if (!$is_valid_time) {
      if ($has_access) {
        if (isset($element['#title'])) {
          $form_state
            ->setError($element, t('%name must be a valid time.', [
            '%name' => $element['#title'],
          ]));
        }
        else {
          $form_state
            ->setError($element);
        }
      }
      return;
    }
    $name = empty($element['#title']) ? $element['#parents'][0] : $element['#title'];
    $time_format = $element['#time_format'];
    
    if ($has_access && isset($element['#min'])) {
      $min = strtotime($element['#min']);
      if ($time < $min) {
        $form_state
          ->setError($element, t('%name must be on or after %min.', [
          '%name' => $name,
          '%min' => static::formatTime($time_format, $min),
        ]));
      }
    }
    
    if ($has_access && isset($element['#max'])) {
      $max = strtotime($element['#max']);
      if ($time > $max) {
        $form_state
          ->setError($element, t('%name must be on or before %max.', [
          '%name' => $name,
          '%max' => static::formatTime($time_format, $max),
        ]));
      }
    }
    
    $value = static::formatTime('H:i:s', $time);
    $element['#time_format'] = 'H:i:s';
    $element['#value'] = $value;
    $form_state
      ->setValueForElement($element, $value);
  }
  
  public static function preRenderWebformTime(array $element) {
    if (!empty($element['#timepicker'])) {
      
      $element['#attributes']['type'] = 'text';
      
      if (!empty($element['#value']) && !empty($element['#default_value']) && $element['#value'] === $element['#default_value']) {
        $element['#value'] = static::formatTime($element['#attributes']['data-webform-time-format'], strtotime($element['#value']));
      }
    }
    else {
      $element['#attributes']['type'] = 'time';
    }
    Element::setAttributes($element, [
      'id',
      'name',
      'type',
      'value',
      'size',
      'maxlength',
      'min',
      'max',
      'step',
    ]);
    static::setAttributes($element, [
      'form-time',
      'webform-time',
    ]);
    return $element;
  }
  
  protected static function formatTime($custom_format, $timestamp = NULL) {
    
    $date_formatter = \Drupal::service('date.formatter');
    return $date_formatter
      ->format($timestamp ?: time(), 'custom', $custom_format);
  }
}