Index: office_hours.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/office_hours/office_hours.module,v
retrieving revision 1.1.2.1.2.13
diff -u -p -r1.1.2.1.2.13 office_hours.module
--- office_hours.module	9 Jul 2010 12:47:18 -0000	1.1.2.1.2.13
+++ office_hours.module	20 Jul 2010 22:23:06 -0000
@@ -15,6 +15,10 @@ function office_hours_theme() {
       'arguments' => array('element' => NULL),
       'file' => 'office_hours.theme.inc',
     ),
+    'office_hours_formatter_grouped' => array(
+      'arguments' => array('element' => NULL),
+      'file' => 'office_hours.theme.inc',
+    ),
     'office_hours_display' => array(
       'arguments' => array('dayname' => NULL,'hours'=> NULL),
       'template' => 'office-hours-display',
@@ -234,6 +238,11 @@ function office_hours_field_formatter_in
       'field types' => array('office_hours'),
       'multiple values' => CONTENT_HANDLE_MODULE,
     ),
+    'grouped' => array(
+      'label' => 'grouped',
+      'field types' => array('office_hours'),
+      'multiple values' => CONTENT_HANDLE_MODULE,
+    ),
   );
 }
 
Index: office_hours.theme.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/office_hours/Attic/office_hours.theme.inc,v
retrieving revision 1.1.2.5
diff -u -p -r1.1.2.5 office_hours.theme.inc
--- office_hours.theme.inc	9 Jul 2010 12:47:18 -0000	1.1.2.5
+++ office_hours.theme.inc	20 Jul 2010 22:23:06 -0000
@@ -43,6 +43,94 @@ function theme_office_hours_formatter_de
   return $output;     
 }
 
+/**
+ * Theme function for 'grouped' text field formatter.
+ */
+function theme_office_hours_formatter_grouped($element) {
+  $output = '';
+  $groups = array();
+  $first = variable_get('date_first_day',0);
+  $field = content_fields($element['#field_name'], $element['#type_name']);
+  $show_closed = $field['showclosed'];
+
+  // get ordered weekdays
+  // @todo should abbr be an option?
+  $weekdays = date_week_days_abbr(TRUE);
+
+  // create hours groups from items
+  foreach (element_children($element) as $key => $elemkey) {
+    $item = $element[$elemkey]['#item'];
+    $day = $element[$elemkey]['#item']['day'];
+    if (isset($day)) {
+      $day_num = check_plain($day);
+      if (is_numeric($day_num)) {
+        $day_num = (int) $day_num;
+        $day_name = $weekdays[$day_num];
+
+        $endhrs = _office_hours_mil_to_tf(check_plain($item['endhours']));
+        $strhrs = _office_hours_mil_to_tf(check_plain($item['starthours']));
+        if ($field['hoursformat']) {
+          $endhrs = _office_hours_convert_to_ampm($endhrs);
+          $strhrs = _office_hours_convert_to_ampm($strhrs);
+        }
+
+        // aggregate by hours
+        $group_id = "$strhrs::$endhrs";
+        if (!isset($groups[$group_id])) {
+          $groups[$group_id]['hours'] = array(array('strhrs' => $strhrs, 'endhrs' => $endhrs));
+        }
+        $groups[$group_id]['days'][$day_name] = $day_num;
+        $group_map[$day_name][$group_id] = $group_id;
+      }
+    }
+  }
+
+  // Add grouped closed days
+  foreach ($weekdays as $day_num => $day_name) {
+    if (!isset($group_map[$day_name])) {
+      $group_id = 'closed';
+      if (!isset($groups[$group_id])) {
+        $groups[$group_id]['hours'] = array(array('closed' => 'closed'));
+      }
+      $groups[$group_id]['days'][$day_name] = $day_num;
+      $group_map[$day_name][$group_id] = $group_id;
+    }
+  }
+
+  // Process groups
+  foreach ($groups as $group_id => &$group) {
+    // set combined days label
+    _office_hours_set_group_label($group);
+  }
+  unset($group);
+
+
+  // adjust translation weekday array for first day of week
+  $weekdays = date_week_days_ordered($weekdays);
+
+  // build output
+  $output = '';
+  $days_rendered = array();
+
+  // output with ordered weekdays to render groups in order
+  foreach ($weekdays as $day_num => $day_name) {
+    if (!isset($days_rendered[$day_name]) && isset($group_map[$day_name])) {
+      foreach ($group_map[$day_name] as $group_id) {
+        if ($show_closed || $group_id != 'closed') {
+          $group = $groups[$group_id];
+          $output .= theme('office_hours_display', $group['label'], $group['hours']);
+          // update rendered array
+          foreach ($group['days'] as $g_day_name => $orig_day_num) {
+            $days_rendered[$g_day_name] = TRUE;
+          }
+        }
+      }
+    }
+  }
+
+  return $output;
+}
+
 function template_preprocess_office_hours_display(&$vars) {
   $vars['starthours1'] = $vars['hours'][0]['strhrs'];
   $vars['endhours1'] = $vars['hours'][0]['endhrs'];
@@ -128,3 +216,49 @@ function theme_office_hours_multiple_val
 
   return $output;
 }
+
+/**
+ * Helper function to set combined days label for hour group
+ */
+function _office_hours_set_group_label(&$group) {
+  // reference days
+  $group_days = &$group['days'];
+  asort($group_days);
+
+  // initialize label
+  list($day_name, $prev_day_num) = each($group_days);
+  $label_start = $day_name;
+  $label_end = '';
+  $label = $label_start;
+
+  // build label
+  while (list($day_name, $day_num) = each($group_days)) {
+    $diff = $day_num - $prev_day_num;
+    if ($diff == 1) {
+      $label_end = $day_name;
+    }
+    else {
+      if ($label_end) {
+        $label .= ' - ' . $label_end;
+      }
+
+      $label_start = $day_name;
+      $label_end = '';
+      $label .= ', ' . $label_start;
+    }
+
+    $prev_day_num = $day_num;
+  }
+
+  // update label if all were consecutive
+  if ($label_end) {
+    $label .= ' - ' . $label_end;
+  }
+
+  // update group
+  $group['label'] = $label;
+
+  // clean up and return
+  unset($group_days);
+  return $label;
+}
