├── .gitignore
├── LICENSE
├── README.md
├── StoreHours.class.php
├── StoreHours2.class.php
├── StoreHoursTest.php
├── index.php
└── phpunit.xml.dist
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | nbproject
3 | .*
4 | !/.gitignore
5 | *~
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Cory Etzkorn
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > ⚠️ Note: I am no longer actively maintaining this repo.
2 | Please reach out if you're interested in becoming the primary maintainer.
3 |
4 | PHP Store Hours
5 | ===============
6 |
7 | PHP Store Hours is a simple PHP class that outputs content based on time-of-day and-day-of-week. Simply include the script in any PHP page, adjust opening and closing hours for each day of the week and the script will output content based on the time ranges you specify.
8 |
9 | ### Easily set open hours for each day of the week
10 |
11 | ~~~ php
12 | // REQUIRED
13 | // Define daily open hours
14 | // Must be in 24-hour format, separated by dash
15 | // If closed for the day, leave blank (ex. sunday) or don't add line
16 | // If open multiple times in one day, enter time ranges separated by a comma
17 | $hours = array(
18 | 'mon' => array('11:00-20:30'),
19 | 'tue' => array('11:00-13:00', '18:00-20:30'),
20 | 'wed' => array('11:00-20:30'),
21 | 'thu' => array('11:00-1:30'), // Open late
22 | 'fri' => array('11:00-20:30'),
23 | 'sat' => array('11:00-20:00'),
24 | 'sun' => array() // Closed all day
25 | );
26 | ~~~
27 |
28 | ### Add exceptions for specific dates / holidays
29 |
30 | ~~~ php
31 | // OPTIONAL
32 | // Add exceptions (great for holidays etc.)
33 | // MUST be in format month/day[/year] or year-month-day
34 | // Do not include the year if the exception repeats annually
35 | $exceptions = array(
36 | '2/24' => array('11:00-18:00'),
37 | '10/18' => array('11:00-16:00', '18:00-20:30')
38 | );
39 | ~~~
40 |
41 | ### Customize the final output with shortcodes
42 |
43 | Choose what you'd like to output if you're currently open, currently closed, or closed all day. Shortcodes add dynamic times to your open or closed message.
44 |
45 | ~~~ php
46 | // OPTIONAL
47 | // Place HTML for output below. This is what will show in the browser.
48 | // Use {%hours%} shortcode to add dynamic times to your open or closed message.
49 | $template = array(
50 | 'open' => "
Yes, we're open! Today's hours are {%hours%}.
",
51 | 'closed' => "Sorry, we're closed. Today's hours are {%hours%}.
",
52 | 'closed_all_day' => "Sorry, we're closed today.
",
53 | 'separator' => " - ",
54 | 'join' => " and ",
55 | 'format' => "g:ia", // options listed here: http://php.net/manual/en/function.date.php
56 | 'hours' => "{%open%}{%separator%}{%closed%}"
57 | );
58 | ~~~
59 |
60 | ### Available Methods
61 |
62 | #### render([timestamp = time()])
63 |
64 | This is the default method that outputs the templated content. You'll most likely want to use this.
65 |
66 | ~~~ php
67 | $store_hours = new StoreHours($hours, $exceptions, $template);
68 | $store_hours->render();
69 | ~~~
70 |
71 | #### hours_overview([groupSameDays = false])
72 |
73 | This returns an array with a full list of open hours (for a week without exceptions). Days with same hours will be grouped.
74 |
75 | ~~~ php
76 | $store_hours = new StoreHours($hours, $exceptions, $template);
77 |
78 | echo '';
79 | foreach ($store_hours->hours_overview() as $days => $hours) {
80 | echo '';
81 | echo '' . $days . ' | ';
82 | echo '' . $hours . ' | ';
83 | echo '
';
84 | }
85 | echo '
';
86 | ~~~
87 |
88 | #### hours_today([timestamp = time()])
89 |
90 | This returns an array of the current day's hours.
91 |
92 | ~~~ php
93 | $store_hours = new StoreHours($hours, $exceptions, $template);
94 | $store_hours->hours_today();
95 | ~~~
96 |
97 | #### is_open([timestamp = time()])
98 |
99 | This returns true/false depending on if the store is currently open.
100 |
101 | ~~~ php
102 | $store_hours = new StoreHours($hours, $exceptions, $template);
103 | $store_hours->is_open();
104 | ~~~
105 |
106 | ### Use Cases
107 |
108 | #### Multiple stores / sets of hours
109 |
110 | If you'd like to show multiple sets of hours on the same page, simply invoke two separate instances of `StoreHours`. Remember to set the timezone before each new instance.
111 |
112 | ~~~ php
113 | // New York Hours
114 | date_default_timezone_set('America/New_York');
115 | $nyc_store_hours = new StoreHours($nyc_hours, $nyc_exceptions, $nyc_template);
116 | $nyc_store_hours->render();
117 |
118 | // Los Angeles Hours
119 | date_default_timezone_set('America/Los_Angeles');
120 | $la_store_hours = new StoreHours($la_hours, $la_exceptions, $la_template);
121 | $la_store_hours->render();
122 | ~~~
123 |
124 | ### Testing
125 |
126 | ~~~ bash
127 | $ phpunit
128 | ~~~
129 |
130 | ### Troubleshooting
131 |
132 | If you're getting errors or if times are not rendering as expected, please double check these items before filing an issue on GitHub:
133 |
134 | - Make sure your timezone is configured
135 | - Ensure all exceptions use the month/day format
136 | - Verify that StoreHours.class.php is properly included on the page
137 |
138 | Please report any bugs or issues here on GitHub. I'd love to hear your ideas for improving this script or see how you've used it in your latest project.
139 |
140 | ## Sites using PHP Store Hours
141 |
142 | - [Des Plaines Public Library](http://dppl.org/)
143 | - [The Nevada Discovery Museum](http://www.nvdm.org/)
144 | - [Minne's Diner](http://www.minnesdiner.com/)
145 | - Want to showcase your site? Tweet [@coryetzkorn](http://twitter.com/coryetzkorn)
146 |
--------------------------------------------------------------------------------
/StoreHours.class.php:
--------------------------------------------------------------------------------
1 | exceptions = $exceptions;
49 | $this->config = $config;
50 | $this->yesterdayFlag = false;
51 |
52 | $weekdayToIndex = array(
53 | 'mon' => 1,
54 | 'tue' => 2,
55 | 'wed' => 3,
56 | 'thu' => 4,
57 | 'fri' => 5,
58 | 'sat' => 6,
59 | 'sun' => 7
60 | );
61 |
62 | $this->hours = array();
63 |
64 | foreach ($hours as $key => $value) {
65 | $this->hours[$weekdayToIndex[$key]] = $value;
66 | }
67 |
68 | // Remove empty elements from values (backwards compatibility)
69 | foreach ($this->hours as $key => $value) {
70 | $this->hours[$key] = array_filter($value, function($element)
71 | {
72 | return (trim($element) !== '');
73 | });
74 | }
75 |
76 | // Remove empty elements from values (backwards compatibility)
77 | foreach ($this->exceptions as $key => $value) {
78 | $this->exceptions[$key] = array_filter($value, function($element)
79 | {
80 | return (trim($element) !== '');
81 | });
82 | }
83 |
84 | $defaultConfig = array(
85 | 'separator' => ' - ',
86 | 'join' => ' and ',
87 | 'format' => 'g:ia',
88 | 'overview_weekdays' => array(
89 | 'Mon',
90 | 'Tue',
91 | 'Wed',
92 | 'Thu',
93 | 'Fri',
94 | 'Sat',
95 | 'Sun'
96 | )
97 | );
98 |
99 | $this->config += $defaultConfig;
100 |
101 | }
102 |
103 | /**
104 | *
105 | * @param string $timestamp
106 | * @return boolean
107 | */
108 | private function is_open_at($timestamp = null)
109 | {
110 |
111 | $timestamp = (null !== $timestamp) ? $timestamp : time();
112 | $is_open = false;
113 |
114 | $this->yesterdayFlag = false;
115 |
116 | // Check whether shop's still open from day before
117 | $ts_yesterday = strtotime(date('Y-m-d H:i:s', $timestamp) . ' -1 day');
118 | $yesterday = date('Y-m-d', $ts_yesterday);
119 | $hours_yesterday = $this->hours_today_array($ts_yesterday);
120 |
121 | foreach ($hours_yesterday as $range) {
122 | $range = explode('-', $range);
123 | $start = strtotime($yesterday . ' ' . $range[0]);
124 | $end = strtotime($yesterday . ' ' . $range[1]);
125 | if ($end <= $start) {
126 | $end = strtotime($yesterday . ' ' . $range[1] . ' +1 day');
127 | }
128 | if ($start <= $timestamp && $timestamp <= $end) {
129 | $is_open = true;
130 | $this->yesterdayFlag = true;
131 | break;
132 | }
133 | }
134 |
135 | // Check today's hours
136 | if (!$is_open) {
137 |
138 | $day = date('Y-m-d', $timestamp);
139 | $hours_today_array = $this->hours_today_array($timestamp);
140 |
141 | foreach ($hours_today_array as $range) {
142 | $range = explode('-', $range);
143 | $start = strtotime($day . ' ' . $range[0]);
144 | $end = strtotime($day . ' ' . $range[1]);
145 | if ($end <= $start) {
146 | $end = strtotime($day . ' ' . $range[1] . ' +1 day');
147 | }
148 | if ($start <= $timestamp && $timestamp <= $end) {
149 | $is_open = true;
150 | break;
151 | }
152 | }
153 |
154 | }
155 |
156 | return $is_open;
157 |
158 | }
159 |
160 | /**
161 | *
162 | * @param array $ranges
163 | * @return string
164 | */
165 | private function format_hours(array $ranges)
166 | {
167 |
168 | $hoursparts = array();
169 |
170 | foreach ($ranges as $range) {
171 | $day = '2016-01-01';
172 |
173 | $range = explode('-', $range);
174 | $start = strtotime($day . ' ' . $range[0]);
175 | $end = strtotime($day . ' ' . $range[1]);
176 |
177 | $hoursparts[] = date($this->config['format'], $start) . $this->config['separator'] . date($this->config['format'], $end);
178 | }
179 |
180 | return implode($this->config['join'], $hoursparts);
181 |
182 | }
183 |
184 | /**
185 | *
186 | * @param string $timestamp
187 | * @return array today's hours as array
188 | */
189 | private function hours_today_array($timestamp = null)
190 | {
191 |
192 | $timestamp = (null !== $timestamp) ? $timestamp : time();
193 | $today = strtotime(date('Y-m-d', $timestamp) . ' midnight');
194 | $weekday_short = date('N', $timestamp);
195 | $hours_today_array = array();
196 |
197 | if (isset($this->hours[$weekday_short])) {
198 | $hours_today_array = $this->hours[$weekday_short];
199 | }
200 |
201 | foreach ($this->exceptions as $ex_day => $ex_hours) {
202 | if (strtotime($ex_day) === $today) {
203 | // Today is an exception, use alternate hours instead
204 | $hours_today_array = $ex_hours;
205 | }
206 | }
207 |
208 | return $hours_today_array;
209 |
210 | }
211 |
212 | /**
213 | *
214 | * @return array
215 | */
216 | private function hours_this_week_simple()
217 | {
218 |
219 | $lookup = array_combine(range(1, 7), $this->config['overview_weekdays']);
220 | $ret = array();
221 |
222 | for ($i = 1; $i <= 7; $i++) {
223 | $hours_str = (isset($this->hours[$i]) && count($this->hours[$i]) > 0) ? $this->format_hours($this->hours[$i]) : '-';
224 |
225 | $ret[$lookup[$i]] = $hours_str;
226 | }
227 |
228 | return $ret;
229 |
230 | }
231 |
232 | /**
233 | *
234 | * @return array
235 | */
236 | private function hours_this_week_grouped()
237 | {
238 | $lookup = array_combine(range(1, 7), $this->config['overview_weekdays']);
239 | $blocks = array();
240 |
241 | // Remove empty elements ("closed all day")
242 | $hours = array_filter($this->hours, function($element)
243 | {
244 | return (count($element) > 0);
245 | });
246 |
247 | foreach ($hours as $weekday => $hours2) {
248 | foreach ($blocks as &$block) {
249 | if ($block['hours'] === $hours2) {
250 | $block['days'][] = $weekday;
251 | continue 2;
252 | }
253 | }
254 | unset($block);
255 | $blocks[] = array(
256 | 'days' => array(
257 | $weekday
258 | ),
259 | 'hours' => $hours2
260 | );
261 | }
262 |
263 | // Flatten
264 | $ret = array();
265 | foreach ($blocks as $block) {
266 | // Format days
267 | $keyparts = array();
268 | $keys = $block['days'];
269 | $buffer = array();
270 | $lastIndex = null;
271 | $minGroupSize = 3;
272 |
273 | foreach ($keys as $index) {
274 | if ($lastIndex !== null && $index - 1 !== $lastIndex) {
275 | if (count($buffer) >= $minGroupSize) {
276 | $keyparts[] = $lookup[$buffer[0]] . '-' . $lookup[$buffer[count($buffer) - 1]];
277 | } else {
278 | foreach ($buffer as $b) {
279 | $keyparts[] = $lookup[$b];
280 | }
281 | }
282 | $buffer = array();
283 | }
284 | $buffer[] = $index;
285 | $lastIndex = $index;
286 | }
287 | if (count($buffer) >= $minGroupSize) {
288 | $keyparts[] = $lookup[$buffer[0]] . '-' . $lookup[$buffer[count($buffer) - 1]];
289 | } else {
290 | foreach ($buffer as $b) {
291 | $keyparts[] = $lookup[$b];
292 | }
293 | }
294 | // Combine
295 | $ret[implode(', ', $keyparts)] = $this->format_hours($block['hours']);
296 | }
297 |
298 | return $ret;
299 |
300 | }
301 |
302 | /**
303 | *
304 | * @return string
305 | */
306 | public function is_open()
307 | {
308 |
309 | return $this->is_open_at();
310 |
311 | }
312 |
313 | /**
314 | *
315 | * @return string
316 | */
317 | public function hours_today()
318 | {
319 |
320 | $hours_today = $this->hours_today_array();
321 | return $this->format_hours($hours_today);
322 |
323 | }
324 |
325 | /**
326 | *
327 | * @return array
328 | */
329 | public function hours_this_week($groupSameDays = false)
330 | {
331 |
332 | return (true === $groupSameDays) ? $this->hours_this_week_grouped() : $this->hours_this_week_simple();
333 |
334 | }
335 |
336 | }
337 |
--------------------------------------------------------------------------------
/StoreHours2.class.php:
--------------------------------------------------------------------------------
1 | exceptions = $exceptions;
48 | $this->templates = $templates;
49 | $this->yesterdayFlag = false;
50 |
51 | $weekdayToIndex = array(
52 | 'mon' => 1,
53 | 'tue' => 2,
54 | 'wed' => 3,
55 | 'thu' => 4,
56 | 'fri' => 5,
57 | 'sat' => 6,
58 | 'sun' => 7
59 | );
60 |
61 | $this->hours = array();
62 |
63 | foreach ($hours as $key => $value) {
64 | $this->hours[$weekdayToIndex[$key]] = $value;
65 | }
66 |
67 | // Remove empty elements from values (backwards compatibility)
68 | foreach ($this->hours as $key => $value) {
69 | $this->hours[$key] = array_filter($value, function ($element) {
70 | return (trim($element) !== '');
71 | });
72 | }
73 |
74 | // Remove empty elements from values (backwards compatibility)
75 | foreach ($this->exceptions as $key => $value) {
76 | $this->exceptions[$key] = array_filter($value, function ($element) {
77 | return (trim($element) !== '');
78 | });
79 | }
80 |
81 | $defaultTemplates = array(
82 | 'open' => 'Yes, we\'re open! Today\'s hours are {%hours%}.
',
83 | 'closed' => 'Sorry, we\'re closed. Today\'s hours are {%hours%}.
',
84 | 'closed_all_day' => 'Sorry, we\'re closed.
',
85 | 'separator' => ' - ',
86 | 'join' => ' and ',
87 | 'format' => 'g:ia',
88 | 'hours' => '{%open%}{%separator%}{%closed%}',
89 |
90 | 'overview_separator' => '-',
91 | 'overview_join' => ', ',
92 | 'overview_format' => 'g:ia',
93 | 'overview_weekdays' => array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
94 | );
95 |
96 | $this->templates += $defaultTemplates;
97 | }
98 |
99 | /**
100 | *
101 | * @param string $timestamp
102 | * @return array Today's hours
103 | */
104 | public function hours_today($timestamp = null)
105 | {
106 | $timestamp = (null !== $timestamp) ? $timestamp : time();
107 | $today = strtotime(date('Y-m-d', $timestamp) . ' midnight');
108 | $weekday_short = date('N', $timestamp);
109 |
110 | $hours_today = array();
111 |
112 | if (isset($this->hours[$weekday_short])) {
113 | $hours_today = $this->hours[$weekday_short];
114 | }
115 |
116 | foreach ($this->exceptions as $ex_day => $ex_hours) {
117 | if (strtotime($ex_day) === $today) {
118 | // Today is an exception, use alternate hours instead
119 | $hours_today = $ex_hours;
120 | }
121 | }
122 |
123 | return $hours_today;
124 | }
125 |
126 | /**
127 | *
128 | * @param string $timestamp
129 | * @return boolean
130 | */
131 | public function is_open($timestamp = null)
132 | {
133 | $timestamp = (null !== $timestamp) ? $timestamp : time();
134 |
135 | $is_open = false;
136 | $this->yesterdayFlag = false;
137 |
138 | // Check whether shop's still open from day before
139 |
140 | $ts_yesterday = strtotime(date('Y-m-d H:i:s', $timestamp) . ' -1 day');
141 | $yesterday = date('Y-m-d', $ts_yesterday);
142 | $hours_yesterday = $this->hours_today($ts_yesterday);
143 |
144 | foreach ($hours_yesterday as $range) {
145 | $range = explode('-', $range);
146 | $start = strtotime($yesterday . ' ' . $range[0]);
147 | $end = strtotime($yesterday . ' ' . $range[1]);
148 |
149 | if ($end <= $start) {
150 | $end = strtotime($yesterday . ' ' . $range[1] . ' +1 day');
151 | }
152 |
153 | if ($start <= $timestamp && $timestamp <= $end) {
154 | $is_open = true;
155 | $this->yesterdayFlag = true;
156 | break;
157 | }
158 | }
159 |
160 | // Check today's hours
161 |
162 | if (!$is_open) {
163 | $day = date('Y-m-d', $timestamp);
164 | $hours_today = $this->hours_today($timestamp);
165 |
166 | foreach ($hours_today as $range) {
167 | $range = explode('-', $range);
168 | $start = strtotime($day . ' ' . $range[0]);
169 | $end = strtotime($day . ' ' . $range[1]);
170 |
171 | if ($end <= $start) {
172 | $end = strtotime($day . ' ' . $range[1] . ' +1 day');
173 | }
174 |
175 | if ($start <= $timestamp && $timestamp <= $end) {
176 | $is_open = true;
177 | break;
178 | }
179 | }
180 | }
181 |
182 | return $is_open;
183 | }
184 |
185 | /**
186 | * Prep HTML
187 | *
188 | * @param string $template_name
189 | * @param int $timestamp
190 | */
191 | private function render_html($template_name, $timestamp)
192 | {
193 | $template = $this->templates;
194 | $hours_today = $this->hours_today($timestamp);
195 | $day = date('Y-m-d', $timestamp);
196 | $output = '';
197 |
198 | if (count($hours_today) > 0) {
199 | $hours_template = '';
200 | $first = true;
201 |
202 | foreach ($hours_today as $range) {
203 | $range = explode('-', $range);
204 | $start = strtotime($day . ' ' . $range[0]);
205 | $end = strtotime($day . ' ' . $range[1]);
206 |
207 | if (false === $first) {
208 | $hours_template .= $template['join'];
209 | }
210 |
211 | $hours_template .= $template['hours'];
212 |
213 | $hours_template = str_replace('{%open%}', date($template['format'], $start), $hours_template);
214 | $hours_template = str_replace('{%closed%}', date($template['format'], $end), $hours_template);
215 | $hours_template = str_replace('{%separator%}', $template['separator'], $hours_template);
216 |
217 | $first = false;
218 | }
219 |
220 | $output .= str_replace('{%hours%}', $hours_template, $template[$template_name]);
221 | } else {
222 | $output .= $template['closed_all_day'];
223 | }
224 |
225 | echo $output;
226 | }
227 |
228 | /**
229 | * Output HTML
230 | *
231 | * @param string $timestamp
232 | */
233 | public function render($timestamp = null)
234 | {
235 | $timestamp = (null !== $timestamp) ? $timestamp : time();
236 |
237 | if ($this->is_open($timestamp)) {
238 | // Print yesterday's hours if shop's still open from day before
239 | if ($this->yesterdayFlag) {
240 | $timestamp = strtotime(date('Y-m-d H:i:s', $timestamp) . ' -1 day');
241 | }
242 |
243 | $this->render_html('open', $timestamp);
244 | } else {
245 | $this->render_html('closed', $timestamp);
246 | }
247 | }
248 |
249 | /**
250 | *
251 | * @param array $ranges
252 | * @return string
253 | */
254 | private function hours_overview_format_hours(array $ranges)
255 | {
256 | $hoursparts = array();
257 |
258 | foreach ($ranges as $range) {
259 | $day = '2016-01-01';
260 |
261 | $range = explode('-', $range);
262 | $start = strtotime($day . ' ' . $range[0]);
263 | $end = strtotime($day . ' ' . $range[1]);
264 |
265 | $hoursparts[] = date($this->templates['overview_format'], $start)
266 | . $this->templates['overview_separator']
267 | . date($this->templates['overview_format'], $end);
268 | }
269 |
270 | return implode($this->templates['overview_join'], $hoursparts);
271 | }
272 |
273 | /**
274 | *
275 | */
276 | private function hours_this_week_simple()
277 | {
278 | $lookup = array_combine(range(1, 7), $this->templates['overview_weekdays']);
279 |
280 | $ret = array();
281 |
282 | for ($i = 1; $i <= 7; $i++) {
283 | $hours_str = (isset($this->hours[$i]) && count($this->hours[$i]) > 0)
284 | ? $this->hours_overview_format_hours($this->hours[$i])
285 | : '-';
286 |
287 | $ret[$lookup[$i]] = $hours_str;
288 | }
289 |
290 | return $ret;
291 | }
292 |
293 | /**
294 | *
295 | * @return array
296 | */
297 | private function hours_this_week_grouped()
298 | {
299 | $lookup = array_combine(range(1, 7), $this->templates['overview_weekdays']);
300 |
301 | $blocks = array();
302 |
303 | // Remove empty elements ("closed all day")
304 |
305 | $hours = array_filter($this->hours, function ($element) {
306 | return (count($element) > 0);
307 | });
308 |
309 | foreach ($hours as $weekday => $hours2) {
310 | foreach ($blocks as &$block) {
311 | if ($block['hours'] === $hours2) {
312 | $block['days'][] = $weekday;
313 | continue 2;
314 | }
315 | }
316 | unset($block);
317 |
318 | $blocks[] = array('days' => array($weekday), 'hours' => $hours2);
319 | }
320 |
321 | // Flatten
322 |
323 | $ret = array();
324 |
325 | foreach ($blocks as $block) {
326 | // Format days
327 |
328 | $keyparts = array();
329 | $keys = $block['days'];
330 | $buffer = array();
331 | $lastIndex = null;
332 | $minGroupSize = 3;
333 |
334 | foreach ($keys as $index) {
335 | if ($lastIndex !== null && $index - 1 !== $lastIndex) {
336 | if (count($buffer) >= $minGroupSize) {
337 | $keyparts[] = $lookup[$buffer[0]] . '-' . $lookup[$buffer[count($buffer) - 1]];
338 | } else {
339 | foreach ($buffer as $b) {
340 | $keyparts[] = $lookup[$b];
341 | }
342 | }
343 | $buffer = array();
344 | }
345 |
346 | $buffer[] = $index;
347 |
348 | $lastIndex = $index;
349 | }
350 | if (count($buffer) >= $minGroupSize) {
351 | $keyparts[] = $lookup[$buffer[0]] . '-' . $lookup[$buffer[count($buffer) - 1]];
352 | } else {
353 | foreach ($buffer as $b) {
354 | $keyparts[] = $lookup[$b];
355 | }
356 | }
357 |
358 | // Combine
359 |
360 | $ret[implode(', ', $keyparts)] = $this->hours_overview_format_hours($block['hours']);
361 | }
362 |
363 | return $ret;
364 | }
365 |
366 | /**
367 | *
368 | * @return array
369 | */
370 | public function hours_this_week($groupSameDays = false)
371 | {
372 | return (true === $groupSameDays)
373 | ? $this->hours_this_week_grouped()
374 | : $this->hours_this_week_simple();
375 | }
376 | }
377 |
--------------------------------------------------------------------------------
/StoreHoursTest.php:
--------------------------------------------------------------------------------
1 | array('11:00-20:30'),
26 | 'tue' => array('11:00-13:00', '18:00-20:30'),
27 | 'wed' => array('11:00-20:30'),
28 | 'thu' => array('11:00-1:30'), // Open late
29 | 'fri' => array('11:00-20:30'),
30 | 'sat' => array('11:00-20:00'),
31 | 'sun' => array('') // Closed all day
32 | );
33 |
34 | $exceptions = array(
35 | '2/24' => array('11:00-18:00'),
36 | '10/18' => array('11:00-16:00', '18:00-20:30'),
37 |
38 | '2016-02-01' => array('09:00-11:00')
39 | );
40 |
41 | return new StoreHours($hours, $exceptions);
42 | }
43 |
44 | /**
45 | *
46 | */
47 | public function testHoursTodayMethod()
48 | {
49 | $sh = $this->instantiateWithDefaultData();
50 |
51 | $this->assertEquals(array('11:00-20:30'), $sh->hours_today(strtotime('2016-02-08'))); // mon
52 | $this->assertEquals(array('11:00-13:00', '18:00-20:30'), $sh->hours_today(strtotime('2016-02-09'))); // tue
53 | $this->assertEquals(array('11:00-20:30'), $sh->hours_today(strtotime('2016-02-10'))); // wed
54 | $this->assertEquals(array('11:00-1:30'), $sh->hours_today(strtotime('2016-02-11'))); // thu
55 | $this->assertEquals(array('11:00-20:30'), $sh->hours_today(strtotime('2016-02-12'))); // fri
56 | $this->assertEquals(array('11:00-20:00'), $sh->hours_today(strtotime('2016-02-13'))); // sat
57 | $this->assertEquals(array(), $sh->hours_today(strtotime('2016-02-14'))); // sun
58 |
59 | // Exceptions (dates, not the PHP kind)
60 |
61 | $this->assertEquals(array('11:00-18:00'), $sh->hours_today(strtotime('2016-02-24')));
62 | $this->assertEquals(array('11:00-16:00', '18:00-20:30'), $sh->hours_today(strtotime('2016-10-18')));
63 |
64 | $this->assertEquals(array('09:00-11:00'), $sh->hours_today(strtotime('2016-02-01')));
65 | $this->assertEquals(array('11:00-20:30'), $sh->hours_today(strtotime('2017-02-01'))); // wed
66 | }
67 |
68 | /**
69 | *
70 | */
71 | public function testIsOpenMethod()
72 | {
73 | $sh = $this->instantiateWithDefaultData();
74 |
75 | $this->assertEquals(false, $sh->is_open(strtotime('2016-02-08 10:59:59'))); // mon
76 | $this->assertEquals(true, $sh->is_open(strtotime('2016-02-08 11:00:00')));
77 | $this->assertEquals(true, $sh->is_open(strtotime('2016-02-08 20:30:00')));
78 | $this->assertEquals(false, $sh->is_open(strtotime('2016-02-08 20:30:01')));
79 |
80 | $this->assertEquals(false, $sh->is_open(strtotime('2016-02-09 10:59:59'))); // tue
81 | $this->assertEquals(true, $sh->is_open(strtotime('2016-02-09 11:00:00')));
82 | $this->assertEquals(true, $sh->is_open(strtotime('2016-02-09 13:00:00')));
83 | $this->assertEquals(false, $sh->is_open(strtotime('2016-02-09 13:00:01')));
84 | $this->assertEquals(false, $sh->is_open(strtotime('2016-02-09 17:59:59')));
85 | $this->assertEquals(true, $sh->is_open(strtotime('2016-02-09 18:00:00')));
86 | $this->assertEquals(true, $sh->is_open(strtotime('2016-02-09 20:30:00')));
87 | $this->assertEquals(false, $sh->is_open(strtotime('2016-02-09 20:30:01')));
88 |
89 | // "open late"
90 |
91 | $this->assertEquals(false, $sh->is_open(strtotime('2016-02-11 00:30:00'))); // thu
92 | $this->assertEquals(true, $sh->is_open(strtotime('2016-02-11 23:59:59')));
93 | $this->assertEquals(true, $sh->is_open(strtotime('2016-02-12 00:00:00'))); // fri
94 | $this->assertEquals(true, $sh->is_open(strtotime('2016-02-12 01:30:00')));
95 | $this->assertEquals(false, $sh->is_open(strtotime('2016-02-12 01:30:01')));
96 |
97 | // Exceptions (dates, not the PHP kind)
98 |
99 | $this->assertEquals(false, $sh->is_open(strtotime('2016-02-24 10:59:59')));
100 | $this->assertEquals(true, $sh->is_open(strtotime('2016-02-24 11:00:00')));
101 | $this->assertEquals(true, $sh->is_open(strtotime('2016-02-24 18:00:00')));
102 | $this->assertEquals(false, $sh->is_open(strtotime('2016-02-24 18:00:01')));
103 |
104 | $this->assertEquals(false, $sh->is_open(strtotime('2016-10-18 10:59:59')));
105 | $this->assertEquals(true, $sh->is_open(strtotime('2016-10-18 11:00:00')));
106 | $this->assertEquals(true, $sh->is_open(strtotime('2016-10-18 16:00:00')));
107 | $this->assertEquals(false, $sh->is_open(strtotime('2016-10-18 16:00:01')));
108 | $this->assertEquals(false, $sh->is_open(strtotime('2016-10-18 17:59:59')));
109 | $this->assertEquals(true, $sh->is_open(strtotime('2016-10-18 18:00:00')));
110 | $this->assertEquals(true, $sh->is_open(strtotime('2016-10-18 20:30:00')));
111 | $this->assertEquals(false, $sh->is_open(strtotime('2016-10-18 20:30:01')));
112 |
113 | $this->assertEquals(false, $sh->is_open(strtotime('2016-02-01 08:59:59')));
114 | $this->assertEquals(true, $sh->is_open(strtotime('2016-02-01 09:00:00')));
115 | $this->assertEquals(true, $sh->is_open(strtotime('2016-02-01 11:00:00')));
116 | $this->assertEquals(false, $sh->is_open(strtotime('2016-02-01 11:00:01')));
117 |
118 | $this->assertEquals(false, $sh->is_open(strtotime('2017-02-01 10:59:59'))); // wed
119 | $this->assertEquals(true, $sh->is_open(strtotime('2017-02-01 11:00:00')));
120 | $this->assertEquals(true, $sh->is_open(strtotime('2017-02-01 20:30:00')));
121 | $this->assertEquals(false, $sh->is_open(strtotime('2017-02-01 20:30:01')));
122 | }
123 |
124 | /**
125 | *
126 | */
127 | public function testRenderMethod()
128 | {
129 | $sh = $this->instantiateWithDefaultData();
130 |
131 | ob_start();
132 | $sh->render(strtotime('2016-02-13 14:30:00')); // sat
133 | $this->assertEquals('Yes, we\'re open! Today\'s hours are 11:00am - 8:00pm.
', ob_get_clean());
134 |
135 | ob_start();
136 | $sh->render(strtotime('2016-02-09 14:30:00')); // tue
137 | $this->assertEquals('Sorry, we\'re closed. Today\'s hours are 11:00am - 1:00pm and 6:00pm - 8:30pm.
', ob_get_clean());
138 |
139 | ob_start();
140 | $sh->render(strtotime('2016-02-14 12:00:00')); // sun
141 | $this->assertEquals('Sorry, we\'re closed.
', ob_get_clean());
142 |
143 | // "open late" (if still open, display hours from yesterday)
144 |
145 | ob_start();
146 | $sh->render(strtotime('2016-02-11 23:59:59')); // night from thu->fri, thursday's hours
147 | $this->assertEquals('Yes, we\'re open! Today\'s hours are 11:00am - 1:30am.
', ob_get_clean());
148 |
149 | ob_start();
150 | $sh->render(strtotime('2016-02-12 00:30:00')); // night from thu->fri, thursday's hours
151 | $this->assertEquals('Yes, we\'re open! Today\'s hours are 11:00am - 1:30am.
', ob_get_clean());
152 |
153 | ob_start();
154 | $sh->render(strtotime('2016-02-12 01:30:01')); // closed on friday morning, friday's hours
155 | $this->assertEquals('Sorry, we\'re closed. Today\'s hours are 11:00am - 8:30pm.
', ob_get_clean());
156 |
157 | // Exceptions (dates, not the PHP kind)
158 |
159 | ob_start();
160 | $sh->render(strtotime('2016-02-24 19:00:00'));
161 | $this->assertEquals('Sorry, we\'re closed. Today\'s hours are 11:00am - 6:00pm.
', ob_get_clean());
162 |
163 | ob_start();
164 | $sh->render(strtotime('2016-10-18 19:00:00'));
165 | $this->assertEquals('Yes, we\'re open! Today\'s hours are 11:00am - 4:00pm and 6:00pm - 8:30pm.
', ob_get_clean());
166 |
167 | ob_start();
168 | $sh->render(strtotime('2016-02-01 09:00:00'));
169 | $this->assertEquals('Yes, we\'re open! Today\'s hours are 9:00am - 11:00am.
', ob_get_clean());
170 |
171 | ob_start();
172 | $sh->render(strtotime('2016-02-01 12:00:00'));
173 | $this->assertEquals('Sorry, we\'re closed. Today\'s hours are 9:00am - 11:00am.
', ob_get_clean());
174 |
175 | ob_start();
176 | $sh->render(strtotime('2017-02-01 12:00:00')); // wed
177 | $this->assertEquals('Yes, we\'re open! Today\'s hours are 11:00am - 8:30pm.
', ob_get_clean());
178 | }
179 |
180 | /**
181 | *
182 | */
183 | public function testWithCustomTemplates()
184 | {
185 | $hours = array(
186 | 'mon' => array('09:00-17:00', '17:30-18:00', '19:00-02:30'),
187 | 'thu' => array('17:45-18:00')
188 | );
189 |
190 | $exceptions = array(
191 | '2016-02-15' => array()
192 | );
193 |
194 | $templates = array(
195 | 'open' => 'Open. Hours {%hours%}.',
196 | 'separator' => '-',
197 | 'format' => 'G.i'
198 | );
199 |
200 | $sh = new StoreHours($hours, $exceptions, $templates);
201 |
202 | ob_start();
203 | $sh->render(strtotime('2016-02-08 14:30:00')); // mon
204 | $this->assertEquals('Open. Hours 9.00-17.00 and 17.30-18.00 and 19.00-2.30.', ob_get_clean());
205 |
206 | ob_start();
207 | $sh->render(strtotime('2016-02-11 14:30:00')); // thu
208 | $this->assertEquals('Sorry, we\'re closed. Today\'s hours are 17.45-18.00.
', ob_get_clean());
209 |
210 | ob_start();
211 | $sh->render(strtotime('2016-02-15 14:30:00')); // mon
212 | $this->assertEquals('Sorry, we\'re closed.
', ob_get_clean());
213 | }
214 |
215 | /**
216 | *
217 | */
218 | public function testHoursOverviewSimple()
219 | {
220 | $sh = new StoreHours(array());
221 | $this->assertEquals(array(
222 | 'Mon' => '-',
223 | 'Tue' => '-',
224 | 'Wed' => '-',
225 | 'Thu' => '-',
226 | 'Fri' => '-',
227 | 'Sat' => '-',
228 | 'Sun' => '-'
229 | ), $sh->hours_this_week());
230 |
231 | $sh = new StoreHours(array('fri' => array('')));
232 | $this->assertEquals(array(
233 | 'Mon' => '-',
234 | 'Tue' => '-',
235 | 'Wed' => '-',
236 | 'Thu' => '-',
237 | 'Fri' => '-',
238 | 'Sat' => '-',
239 | 'Sun' => '-'
240 | ), $sh->hours_this_week());
241 |
242 |
243 | $sh = new StoreHours(array(
244 | 'mon' => array('08:00-12:00', '13:00-1:30'),
245 | 'tue' => array('08:00-12:00', '13:00-1:30'),
246 | 'thu' => array('08:00-12:00', '13:00-1:30'),
247 | 'fri' => array('08:00-12:00', '13:00-1:30'),
248 | 'sun' => array('08:00-1:30')
249 | ));
250 | $this->assertEquals(array(
251 | 'Mon' => '8:00am-12:00pm, 1:00pm-1:30am',
252 | 'Tue' => '8:00am-12:00pm, 1:00pm-1:30am',
253 | 'Wed' => '-',
254 | 'Thu' => '8:00am-12:00pm, 1:00pm-1:30am',
255 | 'Fri' => '8:00am-12:00pm, 1:00pm-1:30am',
256 | 'Sat' => '-',
257 | 'Sun' => '8:00am-1:30am'
258 | ), $sh->hours_this_week());
259 | }
260 |
261 | /**
262 | *
263 | */
264 | public function testHoursOverviewGrouped()
265 | {
266 | $sh = new StoreHours(array());
267 | $this->assertEquals(array(), $sh->hours_this_week(true));
268 |
269 | $sh = new StoreHours(array('fri' => array('')));
270 | $this->assertEquals(array(), $sh->hours_this_week(true));
271 |
272 |
273 | $sh = new StoreHours(array(
274 | 'sun' => array('08:00-12:00')
275 | ));
276 | $this->assertEquals(array(
277 | 'Sun' => '8:00am-12:00pm'
278 | ), $sh->hours_this_week(true));
279 |
280 |
281 | $sh = new StoreHours(array(
282 | 'mon' => array('08:00-12:00', '13:00-1:30'),
283 | 'tue' => array('08:00-12:00', '13:00-1:30')
284 | ));
285 | $this->assertEquals(array(
286 | 'Mon, Tue' => '8:00am-12:00pm, 1:00pm-1:30am'
287 | ), $sh->hours_this_week(true));
288 |
289 |
290 | $sh = new StoreHours(array(
291 | 'mon' => array('08:00-12:00', '13:00-1:30'),
292 | 'tue' => array('08:00-12:00', '13:00-1:30'),
293 | 'wed' => array('08:00-12:00', '13:00-1:30')
294 | ));
295 | $this->assertEquals(array(
296 | 'Mon-Wed' => '8:00am-12:00pm, 1:00pm-1:30am'
297 | ), $sh->hours_this_week(true));
298 |
299 |
300 | $sh = new StoreHours(array(
301 | 'mon' => array('08:00-12:00', '13:00-1:30'),
302 | 'tue' => array('08:00-12:00', '13:00-1:30'),
303 | 'thu' => array('08:00-12:00', '13:00-1:30'),
304 | 'fri' => array('08:00-12:00', '13:00-1:30'),
305 | 'sun' => array('08:00-1:30')
306 | ));
307 | $this->assertEquals(array(
308 | 'Mon, Tue, Thu, Fri' => '8:00am-12:00pm, 1:00pm-1:30am',
309 | 'Sun' => '8:00am-1:30am'
310 | ), $sh->hours_this_week(true));
311 |
312 |
313 | $sh = new StoreHours(array(
314 | 'mon' => array('08:00-12:00', '13:00-1:30'),
315 | 'tue' => array('08:00-12:00', '13:00-1:30'),
316 | 'wed' => array('08:00-12:00', '13:00-1:30'),
317 | 'fri' => array('08:00-12:00', '13:00-1:30'),
318 | 'sat' => array('08:00-12:00', '13:00-1:30'),
319 | 'sun' => array('08:00-12:00', '13:00-1:30')
320 | ));
321 | $this->assertEquals(array(
322 | 'Mon-Wed, Fri-Sun' => '8:00am-12:00pm, 1:00pm-1:30am'
323 | ), $sh->hours_this_week(true));
324 | }
325 | }
326 |
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | PHP Store Hours
7 |
8 |
24 |
25 |
26 |
27 |
28 | Gadgets Inc.
29 | Store Hours
30 |
31 | array('11:00-20:30'),
46 | 'tue' => array('11:00-13:00', '18:00-20:30'),
47 | 'wed' => array('11:00-13:00', '18:00-20:30'),
48 | 'thu' => array('11:00-1:30'), // Open late
49 | 'fri' => array('11:00-20:30'),
50 | 'sat' => array('11:00-20:00'),
51 | 'sun' => array('11:00-20:00')
52 | );
53 |
54 | // OPTIONAL
55 | // Add exceptions (great for holidays etc.)
56 | // MUST be in a format month/day[/year] or [year-]month-day
57 | // Do not include the year if the exception repeats annually
58 | $exceptions = array(
59 | '2/24' => array('11:00-18:00'),
60 | '10/18' => array('11:00-16:00', '18:00-20:30')
61 | );
62 |
63 | $config = array(
64 | 'separator' => ' - ',
65 | 'join' => ' and ',
66 | 'format' => 'g:ia',
67 | 'overview_weekdays' => array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
68 | );
69 |
70 | // Instantiate class
71 | $store_hours = new StoreHours($hours, $exceptions, $config);
72 |
73 | // Display open / closed message
74 | if($store_hours->is_open()) {
75 | echo "Yes, we're open! Today's hours are " . $store_hours->hours_today() . ".";
76 | } else {
77 | echo "Sorry, we're closed. Today's hours are " . $store_hours->hours_today() . ".";
78 | }
79 |
80 | // Display full list of open hours (for a week without exceptions)
81 | echo '';
82 | foreach ($store_hours->hours_this_week() as $days => $hours) {
83 | echo '';
84 | echo '' . $days . ' | ';
85 | echo '' . $hours . ' | ';
86 | echo '
';
87 | }
88 | echo '
';
89 |
90 | // Same list, but group days with identical hours
91 | echo '';
92 | foreach ($store_hours->hours_this_week(true) as $days => $hours) {
93 | echo '';
94 | echo '' . $days . ' | ';
95 | echo '' . $hours . ' | ';
96 | echo '
';
97 | }
98 | echo '
';
99 |
100 | ?>
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | StoreHoursTest.php
6 |
7 |
8 |
9 |
10 | StoreHours.class.php
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------