Usage example of date elements within forms

* Enable the Date API module which is a part of the Date package. Optionally consider adding a dependency in the .info file of your custom module.
* Date API uses hook_elements() to create custom form types. See date_api_elements.inc for a list of form types and configuration options available.

Adding a date form element is then easy.

/**
 * Your form builder.
 */
function mymodule_form($form_state) {
  $form = array();

  // [...snip...] add many fields to your form

  // Creating the date/time element starts here

  // Provide a default date in the format YYYY-MM-DD HH:MM:SS.
  $date = '2008-12-31 00:00:00';

  // For example to record the request time (a timestamp).
  // $date = format_date(REQUEST_TIME, 'custom', 'Y-m-d H:i:s');

  // Provide a format using regular PHP format parts (see documentation on php.net).
  // If you're using a date_select, the format will control the order of the date parts in the selector,
  // rearrange them any way you like. Parts left out of the format will not be displayed to the user.
  $format = 'Y-m-d H:i';
 
  $form['date2'] = array(
     '#type' => 'date_select', // types 'date_text' and 'date_timezone' are also supported. See .inc file.
     '#title' => t('select a date'),
     '#default_value' => $date, 
     '#date_format' => $format,
     '#date_label_position' => 'within', // See other available attributes and what they do in date_api_elements.inc
     '#date_timezone' => 'America/Chicago', // Optional, if your date has a timezone other than the site timezone.
     '#date_increment' => 15, // Optional, used by the date_select and date_popup elements to increment minutes and seconds.
     '#date_year_range' => '-3:+3', // Optional, used to set the year range (back 3 years and forward 3 years is the default).
  );

  // [...snip...] more fields, including the 'submit' button.

  return $form;
}

These are self-validating elements, you shouldn't need to do anything except include them in your form. You pass them parameters for the timezone, format, and a default value in the format YYYY-MM-DD HH:MM:SS, and it will convert your input into the format the element wants, split the date and time into different fields as needed, then accept and validate the user input and convert it back into a string in the same format you originally provided (YYYY-MM-DD HH:MM:SS). So you pass it a string and it will pass back a string by the time you get to your own validation function.

You are responsible for doing your own timezone conversion, the element uses the timezone just so it can create a date object with the right timezone for doing its formatting. So if you need to do timezone conversion, you pull your UTC date out of the database and convert it to a local date, pass that value to the Date element, then take what it returns and convert it back to UTC and store it in the database again.

If the Date Popup module is enabled, a date_popup element can be used. It works like the other elements, but splits the date and time into separate parts. You control whether it uses date or date and time by the format you supply in #date_format. If you don't have any time parts in the format, you won't get a time element. Note that the jquery widget (the 'date' part of the popup) can only accept a limited number of formats, things like Y-m-d or m/d/Y or d.m.Y.

Comments

iongion’s picture

Using the 6.x-2.9 date api module, the code above and even this bellow don't work, somehow the form field does not get populated:

    $dateformat = 'Y-m-d H:i';
    $timestamp = time(); // <- as example, usually this is a db value
    $label = t('Custom date');
  
    $dbv = $timestamp;
    $value = NULL;
    
    // construct date, if db value is empty, default to now
    if (empty($dbv)) {
      $d = new DateTime('@'.$dbv);
      $value = $d->format($dateformat);
    } else {
      $d = new DateTime();
      $value = $d->format($dateformat);
    }
    
    $form['thedatefield'] = array(
      '#type' => 'date_popup',
      '#date_timezone' => date_default_timezone(),
      '#date_format' => $dateformat,
      '#date_year_range' => '-3:+3',
      '#title' => $label,
      '#default_value' => $value,
      '#required' => TRUE,
    );
mauzeh’s picture

Seems to occur when either #date_format or #default_value is not properly formed.

wirka’s picture

I do as above as well but I change the date format to 'd/m/Y' and then not working at all. Does date_popup only accept 'Y-m-d' format?

cposer’s picture

I hope this is the right place to discuss issues:

In order to be able to use the theme_table() - function [or theme('table') for that matter] I use drupal_render() to render the form elements manually and let the theme-function use the output as table cell content.

Now with date_select, this does not work as expected. The parent divs get rendered the way they are supposed to, but the actual select field is rendered outside of the table, namely at the place where it is defined in the code (which means that it is rendered with the rest of the form via drupal_get_form() ).

Here is my example:

$form['employees']['year'] = array(
	'#title' => '',
	'#type' => 'date_select',
	'#date_format' => $format_year,
	'#date_label_position' => 'within',
	'#date_year_range' => '-50:+3',
);
$header = array(
	t('Year'),
);
$rows[] = array(
	drupal_render($form['employees']['year']),
);
$form['employees']['table'] = array (
	'#value' => theme('table', $header, $rows),
);

I reduced the code to the core of the problem. I normally use a foreach-loop and additional textfields.

The table then looks like this.

What am I doing wrong?

rolkos’s picture

It's also possible to set date range like below:

'#date_year_range' => '1900:now',

This will of course allow to pick dates from 1900 till now.

tzed’s picture

When I add a date_select form item into my D7 custom form it allows nonsense dates, e.g. like Feb 31st etc. Is there any built-in way to validate this (on client side) or I need to write my own validation?

Or maybe I'm just doing something wrong:

$form['departure_date'] = array(
'#type' => 'date_select',
'#title' => t('Departure'),
'#default_value' => date("Y-m-d", $now),
'#date_format' => "Y-m-d",
'#date_year_range' => '-0:+2',
'#states' => array(
'visible' => array(':input[name="set_dates"]' => array('value' => 1)),
)

);

Thanks for your thoughts, Tomas

pschuelke’s picture

You could look into using php's checkdate in the form validation hook

agalligani’s picture

Hi - works great! Except for a DOB field the year is sorted in ascending order which is OK but is there an option to reverse sort the year? Otherwise people are scrolling from, say 1915 (unless a default is set) Thx.

amaria’s picture

Use the hook_date_select_process_alter() hook in a custom module to sort the year however you like.

Chisholm Technologies, Inc - Custom Software Development since 1999!
http://www.chisholmtech.com

phponwebsites’s picture

How to set display only current month dates?
Is it possible?
I could hide future dates from current date using below codes:

function phponwebsites_date_popup_process_alter(&$element, &$form_state, &$context) {
    $element['#datepicker_options'] = array(
        'maxDate' => "+0D",
  );
  $element['date'] = date_popup_process_date_part($element);
}

Simillarly, i want to disable past months dates. How to do this?

gibbo1715’s picture

Hi, really like the date API and popup and want to use it in my own module but how do I save the result as a timestamp, currently it saves as a string ( I think) to my DB with year first so I cant just do strtotime... or can I somehow?

Thanks