Last updated March 18, 2008. Created on March 18, 2008.
Edited by jfall. Log in to edit this page.

Here's an example of how to apply the theme_date_display_combination override.
In this example, we want to:

  • Theme "all-day events" so the time is not shown (users set time to 0:00 to indicate all day*)
  • Theme "multi-day all-day events" so they show, for example, Jan. 1 - 5, 2008 or Jan. 28 - Feb. 2, 2008
  • Theme "single-day, multi-time events" so they show, for example, Tues. Jan. 1 - 11:00 am - 2:30 pm

(* From a usability perspective, it would be better to add a CCK checkbox field for all-day events, and then utilize that here - this can be done fairly easily and is left as an exercise...)

The primary difficulty here is that the dates are already formatted when we get them. A more elegant solution could be achieved by overriding the theme_date_formatter() function - but that is beyond the scope of this tutorial, which provides a simpler (but slightly "hack'ish") approach.
To this end, I need to make 2 assumptions (be sure they apply to you!):

  1. All date formats used will specify a dash, - , to separate the time from the date. Most date formats do this, but not all. Check the comments in the code for the places this is important.
  2. For "multi-day all-day events" you never want to show the Day name. Thus, the format for this type of date is fixed in the code - the admin settings for date formats are totally ignored for these types of events.

The original idea for this example came from:
This code goes into your template.php file.

function phptemplate_date_display_combination($field, $dates, $node = NULL) {

   // If there is no From date - get out...
  if (empty($dates['value']['formatted'])) { 

  $date1 = $dates['value']['object']->db;  // the "From" date
  $date2 = $dates['value2']['object']->db; // the "To" date

  // Same From and To Date and Time, don't consider the To date...
  if ($date1->timestamp == $date2->timestamp) {
  $sameDay = $date1->parts['year'] == $date2->parts['year'] &&
             $date1->parts['yday'] == $date2->parts['yday'];
  $sameTime = $date1->parts['hours'] == $date2->parts['hours'] &&
              $date1->parts['minutes'] == $date2->parts['minutes'];  // to the nearest  minute
  // All day events have only a from date with a time of 0:00
  $allDay = (empty($date2) && !_isTimeSet($date1));

  // All day, multi-day events have both times set to 0:00
  $multiDay = !_isTimeSet($date1) && !empty($date2) && !_isTimeSet($date2);
  $sameMonth = $date1->parts['mon'] == $date2->parts['mon'];
  if ($allDay) { //this is an all day, single-day event
    // Assumption 1:  stripping time off formatted date based on location of dash!
    $dashPos = strpos($dates['value']['formatted'],'-');  // find start of time portion
    $formattedDate= substr($dates['value']['formatted'],0,$dashPos);  // strip off time
       // Here is an alternate that hard-codes the date format (Jan. 1, 2008) 
       // $formattedDate = date('F j, Y', $date1->timestamp);
    return '<span class="date-display-single">'. $formattedDate .'</span>';
  elseif ($multiDay) { //this is an all day, multi-day event
    // Assumption 2:  The date formats here are hard-coded....
    $startDate = date('F j', $date1->timestamp);
    $endFormat = ($sameMonth?'j, Y':'F j, Y');
    $endDate = date($endFormat, $date2->timestamp);
    return _theme_date_range($startDate, $endDate);
  elseif ($sameDay && !$sameTime) {  //this is a single day, multi-time event
    $startDate = $dates['value']['formatted'];
    $date = $dates['value2']['formatted'];
    $dashPos = strpos($date,'-');  // find start of time portion
    $endDate= substr($date, $dashPos+2, strlen($date));  // grab just the time
       // Here is an alternate that hard-codes the end time format (2:30pm) 
       // $endDate = date('g:ia', $date2->timestamp);
    return _theme_date_range($startDate, $endDate);
  else { //single day/time event or multi-day/multi time event - let default themer do it
    return theme_date_display_combination($field, $dates, $node);

* Helper to determine if the time for a given date is set to 0:00
* Returns true if a 'valid' time is set,  false if time is 0:00
function _isTimeSet($date) {
  return $date->parts['hours']!=0 || $date->parts['minutes']!=0;  // to nearest minute only

* Helper function to theme a pair of dates as:  StartDate - EndDate
* Note extra class added : date-display-short-end
* This can be used to override the default CSS, which puts a line-break between dates.
function _theme_date_range($startDate, $endDate) {
  return '<span class="date-display-start">'. $startDate .
  '</span><span class="date-display-separator"> - </span><span class="date-display-end date-display-short-end">'.
  $endDate .'</span>'; 

Looking for support? Visit the forums, or join #drupal-support in IRC.


jimijamesi’s picture

nice workaround/hack, I tried this out in calendar1.7 date 1.8 it works fine except that the month calendar view still displays 12:00 am (which is the 12 h clock translation of 0:00)

It does suppress the time on the node detail page however!

Any clues as to how to make this work in a default monthly view??

not sure if it has to do with the fact that the cal displays time without the (DMY - time ) format... just (time)

thanks in advance...

jfall’s picture

The example given above overrides the theme function used by the Date module...
I have not tried to override theming on the Calendar itself... but here's my best guess....
The Calendar module provides a series of over-ridable theming functions for its various views (day, week, month, etc.). For example:

function theme_calendar_node_day($node) {
  $output .= '<div class="calendar dayview">'."\n";
  $output .= theme('calendar_stripe_stripe', $node);

  $output .= '<h2 class="title">'. l($node->title, "$node->url", array('title' => t('view this item'))) .'</h2>'."\n";

  $output .= '<div class="times">';
  $output .= '<span class="start">'. $start_label . $node->start_format .'</span>'."\n";
  if ($node->calendar_start != $node->calendar_end && $node->calendar_end) {
    $output .= '<span class="end"> - '. $end_label . $node->end_format .'</span>'."\n";
  $output .= '</div>';
  // ...
  return $output;

Unfortunately, the code to theme the time data is duplicated in each of these functions, so it's a bit messy to override them :-(
If I had a CVS account, I'd offer up a patch to fix that... something like:
function theme_calendar_node_time($node) {
  $output = '<span class="start">'. $start_label . $node->start_format .'</span>'."\n";
  if ($node->calendar_start != $node->calendar_end && $node->calendar_end) {
    $output .= '<span class="end"> - '. $end_label . $node->end_format .'</span>'."\n";
  return $output;

with the appropriate substitutions in the other theming functions to call this method.
(In fact, I might think about breaking this down further to theme the start and end times separately, depending on how the other theming functions work...)

Then it would be easy easy to apply the same types of techniques I used above to override this one little function to theme the time (presumably to nothing for start==end==0)

Please keep in mind - I haven't tried this code AT ALL - this is just an idea.

vito_a’s picture

(* From a usability perspective, it would be better to add a CCK checkbox field for all-day events, and then utilize that here - this can be done fairly easily and is left as an exercise...)

Just did :)

Made a kind of solution for the 'add all day checkbox to date' and 'click on calendar to create event' issues related to the , , and .

The module is at the Date all day module . I'll really appreciate any comments and suggestions.