Developments

Bukas Hours: A Free WordPress Plugin That Tells Customers If You're Open Right Now

Efraim Ray Efraim Ray 0 Comments
WordPress Plugin Showcase

Before driving to a shop, clinic or municipal office, most people check the website first. Too often they find a static list of hours that ignores the noon break, the holiday and page caching, and they arrive to a locked door. In this breakdown I walk through how I built Bukas Hours, a free WordPress plugin that tells visitors in real time whether a business is open, closing soon or closed.

Bukas is Filipino for open. The plugin handles split shifts, Philippine holidays and special hours, prints Google-ready opening-hours schema, and stays accurate even when a caching plugin serves a page generated hours earlier.

Type WordPress Plugin
Backend PHP 7.4+ / WP 6.3+
Editor Block + Shortcodes
Version v1.0.0
License Free · GPL-2.0+


Try It: Drag the Clock

This is what the plugin renders on a sample bakery's Contact page. Move the time slider or tap a preset and watch the badge, the door sign and the highlighted day change.

panaderialunhaw.example/contact

Sample site · [bukas_status]

Panaderia LunhawSample bakery · Valencia City, Bukidnon

Closed Loading…

Demo time · Manila—

[bukas_hours]

Sample business: Mon–Fri 6–12 and 1–5 with a noon break, Saturday 6–2, Sunday and Bonifacio Day closed.

Build Footprint & Quality Checks

Small-business sites often run on cheap shared hosting, so I kept the plugin light. These are the measured numbers from the v1.0.0 build:

1.4
Front-end JS
KB gzipped
0.9
Front-end CSS
KB gzipped
0
External Requests
no jQuery, no CDN
11
PHP Files Linted
php -l: 0 errors

Technology Stack & Architecture Layers

Platform

WordPress 6.3+ APIs

Settings API for the admin screen, Shortcode API for three shortcodes, and Block API v3 for a dynamic block.

Server Logic

PHP 7.4+ DateTimeImmutable

Timezone-correct schedule engine that turns weekly hours and holiday rows into exact open and close times.

Front End

Vanilla JS & Intl

A 3 KB script recalculates the badge every minute with Intl.DateTimeFormat in the business timezone.

SEO Layer

Schema.org JSON-LD

LocalBusiness markup with openingHoursSpecification and specialOpeningHoursSpecification for holidays.

Key Architectural Decisions

Pillar 01

Cache-Safe Status

PHP embeds the next eight days of opening ranges as timestamps. The browser compares them to the clock, so a page cached at 11:55 AM still says "Closed" at 3:00 AM.

Pillar 02

Sanitize In, Escape Out

Strict HH:MM and date validation, allow-listed timezones and business types, nonces via the Settings API, and late escaping on every output.

Pillar 03

One Pure Logic Class

All time math lives in Bukas_Hours_Schedule, which has no output. Shortcodes, the block and the schema just render its results.

Pillar 04

No Build Step

The block is written against WordPress globals and rendered by PHP, so the plugin zip installs as-is. Assets load only on pages that show hours.

Core Status Logic, Live Badge & Shortcodes

Here are the three pieces that make the badge work: the PHP status check, the browser-side recalculation, and the shortcodes you place on a page.

// includes/class-bukas-hours-schedule.php
public function status( ?DateTimeImmutable $now = null ) {
    $now  = $now ? $now : $this->now();
    $soon = max( 0, (int) $this->settings['closing_soon'] ) * MINUTE_IN_SECONDS;
    $ts   = $now->getTimestamp();

    foreach ( $this->ranges_for( $now ) as $r ) {
        if ( $ts >= $r[0]->getTimestamp() && $ts < $r[1]->getTimestamp() ) {
            $left = $r[1]->getTimestamp() - $ts;
            return array(
                'state'       => ( $left <= $soon ) ? 'closing_soon' : 'open',
                'next_change' => $r[1],
            );
        }
    }

    return array(
        'state'       => 'closed',
        'next_change' => $this->next_opening( $now ),
    );
}
// assets/js/bukas-hours.js — runs in the visitor's browser
/** Pure status calculation; exported on window for testing. */
function compute( cfg, nowTs ) {
    var soon = ( cfg.soon || 0 ) * 60;
    for ( var i = 0; i < cfg.ranges.length; i++ ) {
        var r = cfg.ranges[ i ];
        if ( nowTs >= r[ 0 ] && nowTs < r[ 1 ] ) {
            return { state: r[ 1 ] - nowTs <= soon ? 'closing_soon' : 'open', next: r[ 1 ] };
        }
    }
    for ( var j = 0; j < cfg.ranges.length; j++ ) {
        if ( cfg.ranges[ j ][ 0 ] > nowTs ) {
            return { state: 'closed', next: cfg.ranges[ j ][ 0 ] };
        }
    }
    return { state: 'closed', next: null };
}
<!-- Header: live badge -->
[bukas_status]

<!-- Contact page: weekly hours, today highlighted -->
[bukas_hours]

<!-- Footer or notice: next 5 holidays / special hours -->
[bukas_closures limit="5"]

<!-- Block editor: add the "Bukas Hours" block (Widgets category)
     and choose Badge, Hours, Badge + Hours, or All -->

// Optional: extend the LocalBusiness schema from your theme
add_filter( 'bukas_hours_schema', function ( $data ) {
    $data['priceRange'] = '₱₱';
    return $data;
} );

Development Roadmap & Milestones

Version 1.0.0 is released. The next milestones come straight from the project's roadmap:

1

v1.0.0: Core Plugin (Released)

Weekly hours with noon breaks, holidays and special hours, PH holiday helper, live cache-safe badge, three shortcodes, a dynamic block and LocalBusiness schema.

2

Next: Overnight Shifts & Notices

Support hours that run past midnight (6 PM – 2 AM) and a temporary "Closed today for inventory" banner with an expiry date.

3

Planned: Status REST API

A /wp-json/bukas-hours/v1/status endpoint so chatbots, Messenger auto-replies and n8n workflows can answer "Bukas pa ba kayo?"

4

Planned: Branches & WooCommerce

Multiple locations with their own hours, and an option to show a notice or pause checkout while the store is closed.

User Interface & Feature Showcase

Open now Closes at 5:00 PM Closing soon Closes at 12:00 PM Closed Opens tomorrow at 6:00 AM
Front End

Three States, One Glance

The badge always says what happens next, in your own words or in Filipino.

Settings › Bukas Hours
Mon06:0012:0013:0017:00
Sat06:0014:00--:----:--
SunClosed
Copy Monday to Tue–FriAdd PH regular holidays
wp-admin

Noon Breaks & Holidays in One Screen

Two shifts per day, special hours by date, and a one-click PH holiday helper.

"@type": "Store",
"openingHoursSpecification": [{
  "dayOfWeek": "https://schema.org/Monday",
  "opens": "06:00", "closes": "12:00"
}],
"specialOpeningHoursSpecification": [{
  "validFrom": "2026-11-30",
  "opens": "00:00", "closes": "00:00"
}]
Local SEO

Google-Ready Opening Hours

LocalBusiness JSON-LD on the front page, including holiday closures.

Project Links, Code & Documentation

Download the Plugin

Get bukas-hours.zip and install it in wp-admin under Plugins → Add New → Upload Plugin. Then open Settings → Bukas Hours.

Get v1.0.0

GitHub Repository

Browse the source, open an issue or suggest a feature. The code is free and open source under GPL-2.0-or-later.

View on GitHub

Tutorial & Docs

A beginner-friendly walkthrough of every file, plus deployment steps for LocalWP, wp-env and GitHub releases.

Read the Tutorial
Engineering Takeaway

Small Plugin, Real Problem Solved

A business-hours widget sounds simple until you add lunch breaks, holidays, timezones and page caching. Bukas Hours handles all four so customers stop arriving to a locked door. Want it set up on your business website, or a custom version for multiple branches?

The sample bakery in the demo is fictional. The holiday helper adds only fixed-date regular holidays, so check the year's official proclamation for movable and special non-working days.

Efraim Ray
Written by

Efraim Ray

Software Developer and AI Specialist. BS Information Technology graduate. I pride myself on being conscientious, attentive to detail, and always open to learning — because the best solutions come from listening, taking notes, and exploring what's next.

Comments