I'm using the Store Hours plugin to output store hours like this:

{% set days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] %}
{% set daysShort = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] %}

<dl class="store-details-hours__list">
  {% for dayHours in entry.hoursOfOperation %}
    <dt class="store-details-hours__day">{{ days[loop.index0] }}</dt>

    {% if dayHours.open|length and dayHours.close|length %}

      <dd class="store-details-hours__hours"
        itemprop="openingHours" content="{{ daysShort[loop.index0] }} {{ dayHours.open|date('h:i') }} - {{ dayHours.close|date('h:i') }}">{{ dayHours.open|date('h:i a') }} - {{ dayHours.close|date('h:i a') }}</dd>
    {% else %}
    <dd class="store-details-hours__hours"
      itemprop="openingHours" content="{{ daysShort[loop.index0] }} Closed">Closed</dd>
    {% endif %}
  {% endfor %}

</dl>

This works okay and outputs like this:

enter image description here

But what I really need is to be able to group the days if the hours are the same so that it looks like this:

enter image description here

It's also important that I can set an attribute tag on the <dd> of content="Mo,Tu,We, 10:00-17:00" So how do I group days of the week and output the hours the way my client wants it to look?

up vote 4 down vote accepted

I think I have a solution to this problem, but it's kind of complicated. There may be a way to simplify this more.

The basic idea is to loop thru your Store Hours field (entry.hoursOfOperation) and build a new array (storeHours) where the days are combined if the current loop's open and close times match the previous loop's.

{% set days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] %}
{% set daysShort = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] %}

{# Setup a new array storeHours to hold all you hashes 
   and prev variables to compare things to in the loop 
 #}
{% set storeHours, prevDays, prevDaysShort, prevOpen, prevClose = [],[],[],"","" %}

{# Loop thru your Store Hours field and build the storeHours array #}
{% for item in entry.hoursOfOperation %}

  {# in the first loop, setup the prevOpen and prevClose variables  #}
  {% if loop.first %}
    {% set prevOpen, prevClose = item.open, item.close %}
  {% endif %}

  {# If the prev hours match the current item's hours, 
     add that day's name to the array of prev days, ["Monday","Tuesday"] etc.
   #}
  {% if prevOpen == item.open and prevClose == item.close %}
    {% set prevDays = prevDays|merge([days[loop.index0]]) %}
    {% set prevDaysShort = prevDaysShort|merge([daysShort[loop.index0]]) %}

  {# If it doesn’t match, add prev variables as a hash to the storeHours array,
     Then set all prev variables with current item
   #}
  {% else %}

    {% set storeHours = storeHours|merge([{
      "days" : prevDays,
      "daysShort" : prevDaysShort,
      "open" : prevOpen,
      "close" : prevClose
    }]) %}

    {% set prevDays = [days[loop.index0]] %}
    {% set prevDaysShort = [daysShort[loop.index0]] %}
    {% set prevOpen = item.open %}
    {% set prevClose = item.close %}

  {% endif %}

  {# If last loop, add prev variables as a hash to the storeHours array
     because there won’t be another loop to compare it to.
   #}
  {% if loop.last %}
    {% set storeHours = storeHours|merge([{
      "days" : prevDays,
      "daysShort" : prevDaysShort,
      "open" : prevOpen,
      "close" : prevClose
    }]) %}
  {% endif %}

{% endfor %}

The storeHours data format will be something like this:

[
  {
    "days" : ["Monday", "Tuesday", "Wednesday"],
    "daysShort" : ["Mo","Tu","We"],
    "open" : "2017-01-21 09:30:00.000000",
    "close" : "2017-01-21 21:00:00.000000"
  },
  {
    "days" : ["Thursday","Friday"],
    "daysShort" : ["Th","Fr"],
    "open" : "2017-01-21 09:00:00.000000",
    "close" : "2017-01-21 17:00:00.000000"
  }
]

Once you've created your storeHours array, you can use it to output your HTML code.

<dl class="store-details-hours__list">
  {% for item in storeHours %}

    <dt class="store-details-hours__day">
      {{ item.days|first }}{% if item.days|length > 1 %} - {{ item.days|last }}{% endif %}
    </dt>    

    {% if item.open|length and item.close|length %}
      <dd class="store-details-hours__hours" itemprop="openingHours" 
        content="{{ item.daysShort|join(',') }} {{ item.open|date('h:i') }} - {{ item.close|date('h:i') }}">
        {{ item.open|date('h:i a') }} - {{ item.close|date('h:i a') }}
      </dd>
    {% else %}
      <dd class="store-details-hours__hours" itemprop="openingHours" 
        content="{{ item.daysShort|join(',') }} Closed">
        Closed
      </dd>
    {% endif %}

  {% endfor %}
</dl>

BONUS ROUND: if you want to make Monday the first day of the week, reorder the days and daysShort arrays at the top like this:

{% set days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] %}
{% set daysShort = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'] %}

Then add this line just below it:

{% set hoursMondayFirst = entry.hoursOfOperation|slice(1,entry.hoursOfOperation|length)|merge(entry.hoursOfOperation|slice(0,1)) %}

That makes a new variable using Twig's slice filter to take Monday - Saturday (slice(1|length)) and merge Sunday at the end (slice(0,1)).

Now just use this as the start of your loop:

{% for item in hoursMondayFirst %}
  • Will try this a little later. Agree it is a little complex. – CreateSean Jan 23 '17 at 18:40
  • That works perfectly. Thank you. – CreateSean Jan 23 '17 at 18:50

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.