What is Business Hours & Holiday Schedule Manager?
Business Hours & Holiday Schedule Manager is a complete opening-hours system for WordPress: a weekly schedule builder, a holiday and special-dates manager, a live Open / Closed status badge, and automatic Schema.org markup so search engines can read your hours too. It suits local shops, restaurants, clinics, and any service business — no coding required.
It is entirely self-hosted: your schedule lives in two WordPress options and one small database table, there is no jQuery dependency, no external service, and the plugin never contacts a remote server. Front-end CSS and JavaScript only load on pages that actually display your hours.
[webtend_if_open] / [webtend_if_closed] shortcodes to show content only while you are open or closed. 1.0.3 added multi-day and yearly holidays, compact layouts and the closed-notice bar. See the changelog on WordPress.org for the full list.openingHours and holiday hours — set up in Settings, no code.The weekly schedule builder — Quick fill presets above the list, and Wednesday open for editing with the copy buttons.
System Requirements
The plugin checks its requirements when you activate it. If PHP or WordPress is too old, it deactivates itself and explains why instead of failing. WooCommerce and Elementor are not required.
| Component | Minimum | Notes |
|---|---|---|
| WordPress | 6.2 | Tested up to 7.0 |
| PHP | 7.4 | Checked on activation and on every load |
| WooCommerce | Optional | Runs alongside it with no conflicts; HPOS declared compatible |
| Elementor | Optional | The Business Hours widget only registers if Elementor 3.5+ is active |
.pot file includedInstallation
Method A — WordPress Plugin Directory (Recommended)
Search in WordPress Admin
Go to Plugins → Add New and search for Business Hours & Holiday Schedule Manager.
Install & Activate
Click Install Now, then Activate. Activation creates a small holidays table and sets a default Mon–Fri, 9:00 AM–5:00 PM schedule.
Open the Admin Screen
Click Business Hours in the admin sidebar to set up your real hours.
Method B — Manual Upload
Upload
Download webtend-business-hours.zip, then go to Plugins → Add New → Upload Plugin and install the ZIP. Or unzip it into /wp-content/plugins/.
Activate
Click Activate Plugin. The Business Hours menu appears in the admin sidebar.
Set Your Hours
Use the Weekly Schedule, Holidays and Settings tabs, then add the hours to a page.
Quick Start
After activation you will find Business Hours in your WordPress admin menu.
- On Settings, choose your timezone first — every open/closed calculation depends on it
- On Weekly Schedule, pick a Quick fill preset or toggle the days you are open and click the edit icon to set times or add a split shift, then Save schedule
- On Holidays, add any closed dates or reduced-hours days
- Add the hours to a page with the Business Hours block, the widget, or
[webtend_business_hours] - Check the Shortcodes & Usage tab any time for copy-ready usage examples
Weekly Schedule
Each day has an on/off toggle and a list of time slots. A day that is switched off shows as Closed everywhere. Today's row carries a Today chip so you can see at a glance which day the status badge is reporting on.
Split shifts and lunch breaks
Click the edit icon on a day to open its slot editor. Set the first open and close times, then use + Add split shift to add another slot — up to 4 per day. A morning slot of 09:00–12:00 plus an afternoon slot of 13:00–17:00 gives you a lunch break. Remove any extra slot with its ✕ button.
Quick-fill presets
Above the day list, the Quick fill bar sets up a standard week in one click: Mon–Fri 9–5, Mon–Sat 10–6 or 7 Days 9–9. A preset fills the form only — nothing is stored until you click Save schedule, so you can tweak any day afterwards.
Copy hours to other days
When a day’s editor is open, Apply to all days copies that day’s slots to every day, and Copy to Mon–Fri copies them to the weekdays only. It saves retyping when several days share the same hours.
Saving
Changes are saved over AJAX when you click Save schedule — no page reload. The time chips on each row reflect your edits as you make them.
Holidays & Special Dates
Holidays always override the weekly schedule for their date. Use them for public holidays, staff days, or reduced hours.
Adding a holiday
Pick a Date, give it an optional Label, and leave Closed all day on. Switch it off to reveal opening and closing times for a custom-hours day (for example Christmas Eve, 10:00–14:00). Each date holds one entry — saving the same date again updates it.
The holiday manager — end date and “Repeat every year” on the add form, and multi-day and yearly entries in the list.
Multi-day closures
Add an optional End date to close for several days, such as a winter break. Every day from the start date to the end date (inclusive) uses the holiday setting, and the end date can never be before the start date. The list shows “Through …” on those entries.
Repeat every year
Switch on Repeat every year so a holiday applies to the same dates each year, for example Christmas, so you only enter it once. A range that runs across New Year works too (24 Dec – 2 Jan). A 29 February entry falls on 28 February in non-leap years. A dated entry for a specific day always wins over a yearly one.
Searching and filtering
Use Search by label… to find a holiday by name and the month picker to show a single month (matched on the entry’s start date). Clear resets both. Lists longer than 20 entries are paged, with controls at the bottom of the list. Search and paging update over AJAX, without reloading the screen.
If you pick a date in the past, a warning appears under the date field so you do not add a holiday that will never show.
Removing a holiday
Click the trash icon next to any entry in Upcoming holidays.
Live Status & Timezone
The status badge says whether you are open right now and when that will change. It is rendered on the server, then kept fresh by a lightweight AJAX poll so a visitor who leaves a tab open still sees the right answer.
Refresh interval
The default is 60 seconds. Set it under Settings → Live status refresh interval; enter 0 to turn automatic refresh off entirely.
Opening-time text around holidays
The “Opens …” text looks ahead up to 60 days and skips closed holidays and multi-day closures, so it never says “Opens tomorrow” when tomorrow is a holiday. During a holiday it shows the next real opening time.
Compact layouts
For headers, footers and sidebars you do not need the whole week. Use display="today" for one line with today’s hours (with the badge unless show_status="false"), or display="status" for just the live badge. The block, widget and Elementor widget have a Layout option with the same three choices: full weekly table, today only, status badge only. Both update live like the full table.
Temporary closure (“Close now”)
Power cut, bad weather, someone off sick? Click the Business Hours menu in the WordPress admin bar (or go to Settings → Temporary Closure) and choose:
- Close for the rest of today — closed until your next scheduled opening, which ends the closure by itself
- Close until I reopen — stays closed until you click Reopen now
The badge, “Today” line, notice bar and live refresh all switch to Closed straight away and show your optional Closure message (for example “Closed due to bad weather”). A closure for the rest of today also shows when you reopen. Your weekly schedule and holidays are not changed, so nothing needs undoing. Only administrators (users who can manage options) see the menu or can use the action, and every switch is protected by a nonce.
Closed notice bar
Turn on Settings → Closed Notice Bar to show a thin bar at the top of every page only while you are closed, for example “We’re currently closed. Opens tomorrow at 9:00 AM”. It is off by default and prints nothing while you are open. You can write your own message (the next opening time is added after it) and let visitors dismiss it for the rest of their browser session (no cookies are used). Themes that do not call wp_body_open() get a fixed bar at the bottom of the screen instead. The bar is rendered on the server, so with full-page caching it reflects the time the page was cached. During a temporary closure it shows your closure message.
Labels and time format
Rename the badge text with Open label and Closed label. Leave either blank to use the translated defaults ("Open Now" / "Closed") in your site language. Choose 12-hour, 24-hour, or 12-hour lowercase display.
Timezone, labels, schema options, Business Details, Address, Temporary Closure and the Closed Notice Bar.
The front-end status badge and hours table, with today highlighted.
Schema.org Markup
On every front-end page the plugin prints one JSON-LD script describing your business, built from your settings and weekly schedule. Turn it off with Settings → Schema.org markup.
Business Details
Fill in Settings → Business Details and Address: business type (restaurant, dentist, store and 18 more), business name (blank uses your site title), phone number, price range, and address. Only fields you fill in are output. The theme’s custom logo is added automatically when one is set.
Holiday hours
Upcoming holidays (up to 100 periods, within the next 400 days; multi-day closures and yearly entries are expanded into dated periods) are added as specialOpeningHoursSpecification. A closed day uses 00:00–00:00, the schema.org convention for closed all day; a custom-hours day uses its own times. Switch this off with Include holiday hours. Multi-day closures and yearly entries are expanded into dated periods, and the list is cached and refreshed when you add, change or delete a holiday.
<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Restaurant", "name": "Harbor Street Café", "url": "https://example.com", "telephone": "+1 800 555 0100", "priceRange": "$$", "address": { "@type": "PostalAddress", "streetAddress": "123 Main St", "addressLocality": "Springfield", "addressRegion": "IL", "postalCode": "62701", "addressCountry": "US" }, "openingHours": ["Mo 09:00-13:00", "Mo 14:00-18:00", …], "specialOpeningHoursSpecification": [{ "@type": "OpeningHoursSpecification", "validFrom": "2026-12-25", "validThrough": "2026-12-25", "opens": "00:00", "closes": "00:00", "name": "Christmas Day" }] } </script>
webtend_bh_schema_data filter is still there for anything extra, such as social profile links.Block, Widget & Elementor
Gutenberg block
Insert
In the editor, click + and search Business Hours.
Preview
The block renders a live server-side preview of your real hours.
Options
In the block sidebar, Display Options → Show live status badge toggles the badge. The block also supports wide/full alignment, colors and spacing.
Classic widget
Go to Appearance → Widgets and add Business Hours to any widget area. Set a Title and choose whether to Show live open/closed status. It works in both the block-based widget editor and the classic widgets screen.
Elementor widget
If Elementor 3.5+ is active, a Business Hours widget appears in the Elementor panel. Content controls the status badge toggle. Style adds color controls for the Open and Closed badge (text and background), badge and row typography, day-name, hours, today-highlight and divider colors, and row padding.
Layout (all four placements): choose Full weekly table, Today only or Status badge only.
Shortcode
[webtend_business_hours][webtend_business_hours display="today"][webtend_business_hours display="status"][webtend_business_hours show_status="false"]| Attribute | Default | Notes |
|---|---|---|
| display | full | full, today or status |
| show_status | true | true or false — show the live status badge (always shown for display="status") |
| show_table | true | false hides the weekly table (same as display="status" with the badge); kept for older pages |
| class | — | Extra CSS class on the wrapper |
Paste the shortcode into any page, post, sidebar, or template via do_shortcode(). Assets load only on pages where the shortcode, block or widget actually appears.
Show content only when open or closed
[webtend_if_open]Call us now on 555-0100[/webtend_if_open][webtend_if_closed]We are closed. Leave a message and we will reply tomorrow.[/webtend_if_closed]They use the same status as the badge. Other shortcodes inside them work, but a conditional shortcode cannot contain another of the same name (a WordPress limitation). The content is decided on the server, so with full-page caching it reflects the moment the page was cached.
All Settings
Everything on the Settings tab, plus where the data is stored.
| Setting | Default | Notes |
|---|---|---|
| Timezone | site timezone at install | Any PHP timezone or fixed offset such as +06:00; used for all open/closed calculations |
| Time display format | g:i A | g:i A (9:00 AM), H:i (09:00) or g:i a (9:00 am) |
| Open label | Open Now | Leave blank to use the translated default |
| Closed label | Closed | Leave blank to use the translated default |
| Schema.org markup | on | Prints the JSON-LD block |
| Include holiday hours | on | Adds upcoming holidays to the Schema.org markup |
| Live status refresh interval | 60 | Seconds, 0–3600; 0 turns refresh off |
| Business type | General local business | A schema.org type such as Restaurant, Dentist or Store |
| Business name | site title | Name used in the Schema.org markup |
| Phone number | — | Digits, spaces and + - ( ) only |
| Price range | — | For example $$ |
| Street, City, State/region, Postal code | — | Output as a PostalAddress when any is filled in |
| Country code | — | Two-letter ISO code such as US, GB or BD |
| Show a notice while closed | off | Shows the closed-notice bar at the top of every page while you are closed |
| Notice message | We’re currently closed. | Custom text; the next opening time is added after it (max 160 characters) |
| Allow visitors to dismiss it | on | Adds a dismiss button; remembered for the browser session |
| Closure message | — | Text shown during a temporary closure (max 160 characters); the Close now buttons are on the same screen |
Stored data
| Item | Type | Contains |
|---|---|---|
| wbh_schedule | option | Weekly schedule: enabled flag and time slots per day |
| wbh_settings | option | The settings above |
| wbh_version | option | Installed version, used for upgrades |
| wbh_closure | option | Whether a temporary closure is active, until when, and its message |
| wbh_db_error | option | Set only if the holidays table could not be created; triggers an admin notice |
| {prefix}wbh_holidays | table | Holiday dates, labels, closed flag and custom hours |
Hooks & Filters
For developers and agencies who need to customize output without editing plugin files.
| Hook | Type | Purpose |
|---|---|---|
| webtend_bh_output_schema | filter | Return false to skip the JSON-LD block (for example on specific pages) |
| webtend_bh_schema_type | filter | Change the Schema.org type from LocalBusiness to any other type |
| webtend_bh_schema_data | filter | Add or change any property — for example sameAs or geo — before output |
| webtend_bh_is_open | filter | Change the open/closed decision used by [webtend_if_open] and [webtend_if_closed]; receives the decision and the status |
| webtend_bh_show_notice_bar | filter | Return false to hide the closed-notice bar on specific pages; receives the default decision and the status |
| wbh_get_status | AJAX action | Public (wp_ajax_nopriv_) endpoint the live badge polls; nonce-protected |
add_filter( 'webtend_bh_schema_data', function( $schema ) { $schema['sameAs'] = array( 'https://www.facebook.com/yourpage', 'https://www.instagram.com/yourpage', ); return $schema; } );
add_filter( 'webtend_bh_output_schema', function( $output ) { return is_page( 'contact' ) ? false : $output; } );
add_filter( 'webtend_bh_schema_type', function() { return 'Restaurant'; } );
CSS custom properties (theming)
Every visual token is a custom property on .wbh-widget, so you can restyle the block, widget and shortcode from your theme stylesheet without !important.
| Property | Default | Controls |
|---|---|---|
| --wbh-open | #16a34a | Open status colour |
| --wbh-open-bg | #dcfce7 | Open badge background |
| --wbh-closed | #dc2626 | Closed status colour |
| --wbh-closed-bg | #fee2e2 | Closed badge background |
| --wbh-accent | #4f46e5 | Accent and today highlight |
| --wbh-today-bg | #eef2ff | Today row background |
| --wbh-today-color | inherit | Today row text colour |
| --wbh-text | inherit | Primary text colour |
| --wbh-text-muted | #64748b | Secondary text colour |
| --wbh-border | #e2e8f0 | Table row border colour |
| --wbh-font | inherit | Font family |
| --wbh-font-size | 0.9rem | Base font size |
| --wbh-radius | 6px | Border radius |
| --wbh-row-padding | 8px 0 | Table row padding |
| --wbh-badge-radius | 999px | Status badge border radius |
.wbh-widget {
--wbh-accent: #be123c;
--wbh-today-bg: #ffe4e6;
--wbh-font-size: 1rem;
}The notice bar has two colour tokens on .wbh-notice-bar:
.wbh-notice-bar {
--wbh-notice-bg: #7c2d12;
--wbh-notice-text: #ffffff;
}Frequently Asked Questions
Does this work with WooCommerce?
How does the live status update?
0 to disable automatic refresh.Can I show a lunch break?
What happens on a holiday?
Does it help my SEO?
Are my holidays included in the Schema.org markup?
00:00–00:00. Turn it off with Include holiday hours in Settings.Is it compatible with block themes?
Does it need Elementor?
Does it contact any external server?
Is the plugin translation ready?
webtend-business-hours text domain, and a .pot file is included in /languages.What is removed when I delete the plugin?
wbh_holidays table. Your pages are not modified, but any shortcode left in content will show as plain text.Can I manage hours for more than one location?
Is it accessible?
How do I change the colours or fonts?
--wbh-* custom properties on .wbh-widget in your theme CSS. See Hooks & Filters for the full list.Can I add a holiday that lasts several days?
Do I have to re-enter holidays every year?
How do I show only today’s hours or just the Open Now badge?
[webtend_business_hours display="today"] or display="status", or pick a Layout in the block, widget or Elementor widget.How do I show a message when the business is closed?
How do I close the business quickly without adding a holiday?
How do I show a phone number or button only while I am open?
[webtend_if_open]…[/webtend_if_open], and use [webtend_if_closed]…[/webtend_if_closed] for the closed version. With full-page caching the result reflects the moment the page was cached.Can I put one conditional shortcode inside another?
[webtend_business_hours], work inside them.