open-hours
v1.0.0
Published
A lightweight library for parsing, querying, and displaying business hours
Maintainers
Readme
open-hours
A lightweight TypeScript library for parsing, querying, and displaying business hours. Handles complex cases like overnight hours, multiple time ranges per day, timezones, and holidays.
Features
- Zero dependencies - Uses native
IntlAPIs for timezone and formatting - Lightweight - Under 5KB gzipped
- Full TypeScript support - Complete type definitions included
- Timezone aware - Correctly handles business hours across timezones
- Edge case handling - Overnight hours, 24-hour businesses, holidays, temporary closures
- Display helpers - Human-readable status text and formatted hours
Installation
npm install open-hoursQuick Start
import { OpenHours } from 'open-hours';
const hours = OpenHours.create({
monday: [{ open: '09:00', close: '17:00' }],
tuesday: [{ open: '09:00', close: '17:00' }],
wednesday: [{ open: '09:00', close: '17:00' }],
thursday: [{ open: '09:00', close: '17:00' }],
friday: [{ open: '09:00', close: '17:00' }],
saturday: 'closed',
sunday: 'closed',
timezone: 'America/Edmonton'
});
hours.isOpenNow(); // true/false
hours.status(); // 'open' | 'closed' | 'closes-soon' | 'opens-soon'
hours.statusText(); // "Open until 5:00 PM"
hours.todayHours(); // "9:00 AM - 5:00 PM"API Reference
Creating an Instance
OpenHours.create(config, options?)
const hours = OpenHours.create({
monday: [{ open: '09:00', close: '17:00' }],
tuesday: [{ open: '09:00', close: '12:00' }, { open: '13:00', close: '17:00' }],
friday: [{ open: '18:00', close: '02:00' }], // Overnight
sunday: 'closed',
timezone: 'America/Edmonton'
}, {
soonThreshold: 30, // Minutes for 'closes-soon'/'opens-soon' (default: 30)
locale: 'en-US', // Locale for formatting (default: 'en-US')
use24Hour: false // Use 24-hour time format (default: false)
});OpenHours.fromGooglePlaces(data, options)
Parse Google Places API opening_hours format directly:
const hours = OpenHours.fromGooglePlaces(place.opening_hours, {
timezone: 'America/Edmonton'
});Query Methods
isOpenNow(): boolean
Check if the business is currently open.
hours.isOpenNow(); // trueisOpenAt(date: Date): boolean
Check if the business is open at a specific time.
hours.isOpenAt(new Date('2025-01-20T14:00:00')); // truestatus(date?: Date): Status
Get the current status. Returns 'open', 'closed', 'closes-soon', or 'opens-soon'.
hours.status(); // 'open'closesAt(date?: Date): Date | null
Get the next closing time. Returns null if currently closed.
hours.closesAt(); // Date objectopensAt(date?: Date): Date | null
Get the next opening time. Returns null if currently open.
hours.opensAt(); // Date objectnextOpen(date?: Date): Date | null
Get when the business next opens, regardless of current status.
hours.nextOpen(); // Date objectTime Calculations
closesIn(date?: Date): Duration | null
Get time remaining until closing.
hours.closesIn(); // { hours: 2, minutes: 30 }opensIn(date?: Date): Duration | null
Get time remaining until opening.
hours.opensIn(); // { hours: 1, minutes: 15 }Display Helpers
statusText(date?: Date): string
Get human-readable status text.
hours.statusText(); // "Open until 5:00 PM"
// "Closed, opens at 9:00 AM"
// "Closed, opens Monday at 9:00 AM"todayHours(date?: Date): string
Get today's hours formatted for display.
hours.todayHours(); // "9:00 AM - 5:00 PM"
// "6:00 AM - 12:00 PM, 4:00 PM - 10:00 PM"
// "Closed"
// "24 Hours"formatDay(day: DayName): string
Get formatted hours for a specific day.
hours.formatDay('monday'); // "9:00 AM - 5:00 PM"formatWeek(): Record<DayName, string>
Get formatted hours for the entire week.
hours.formatWeek();
// {
// sunday: 'Closed',
// monday: '9:00 AM - 5:00 PM',
// tuesday: '9:00 AM - 5:00 PM',
// ...
// }Serialization
toJSON(): OpenHoursConfig
Serialize back to a plain config object. Useful for storage or transmission.
const json = hours.toJSON();
localStorage.setItem('hours', JSON.stringify(json));
// Later...
const restored = OpenHours.create(JSON.parse(localStorage.getItem('hours')));Advanced Usage
Multiple Time Ranges (Split Hours)
Handle restaurants with lunch breaks:
const restaurant = OpenHours.create({
monday: [
{ open: '06:00', close: '12:00' }, // Breakfast/Lunch
{ open: '16:00', close: '22:00' } // Dinner
],
timezone: 'America/Edmonton'
});
restaurant.todayHours(); // "6:00 AM - 12:00 PM, 4:00 PM - 10:00 PM"
restaurant.isOpenAt(new Date('2025-01-20T14:00:00')); // false (during break)Overnight Hours
Handle bars and nightclubs that close after midnight:
const bar = OpenHours.create({
friday: [{ open: '18:00', close: '02:00' }], // Closes 2 AM Saturday
saturday: [{ open: '18:00', close: '02:00' }], // Closes 2 AM Sunday
timezone: 'America/Edmonton'
});
// Saturday at 1 AM (still open from Friday)
bar.isOpenAt(new Date('2025-01-25T08:00:00Z')); // true24-Hour Businesses
const store = OpenHours.create({
monday: '24hours',
tuesday: '24hours',
// ...
timezone: 'America/Edmonton'
});Holidays
Support for both date-specific and recurring annual holidays:
const store = OpenHours.create({
monday: [{ open: '09:00', close: '21:00' }],
// ... other days
timezone: 'America/Edmonton',
holidays: [
// Recurring holidays (MM-DD format)
{ date: '12-25', name: 'Christmas Day' },
{ date: '01-01', name: "New Year's Day" },
// Date-specific (YYYY-MM-DD format)
{ date: '2025-07-01', name: 'Canada Day' },
// Holiday with special hours
{
date: '12-24',
name: 'Christmas Eve',
ranges: [{ open: '09:00', close: '14:00' }]
}
]
});
// Check Christmas
store.isOpenAt(new Date('2025-12-25T12:00:00')); // false
store.statusText(new Date('2025-12-25T12:00:00')); // "Closed for Christmas Day"Temporary Closures
const store = OpenHours.create({
monday: [{ open: '09:00', close: '17:00' }],
// ...
timezone: 'America/Edmonton',
temporaryClosure: {
start: '2025-02-01',
end: '2025-02-15',
reason: 'Renovations'
}
});
store.statusText(new Date('2025-02-10T12:00:00'));
// "Temporarily closed: Renovations"Timezone Handling
The library correctly handles timezone differences. A business in Calgary will show correct open/closed status regardless of where the user is:
// Business in Calgary (Mountain Time)
const calgaryBusiness = OpenHours.create({
monday: [{ open: '09:00', close: '17:00' }],
timezone: 'America/Edmonton'
});
// User checking from Toronto (Eastern Time)
// When it's 11 AM in Toronto, it's 9 AM in Calgary
const torontoTime = new Date('2025-01-20T16:00:00Z'); // 11 AM EST
calgaryBusiness.isOpenAt(torontoTime); // true (just opened in Calgary)Types
import type {
OpenHoursConfig,
OpenHoursOptions,
TimeRange,
HolidayOverride,
TemporaryClosure,
DayName,
DayInput,
Status,
Duration,
} from 'open-hours';Browser Support
Works in all modern browsers and Node.js 18+. Requires Intl.DateTimeFormat support (available in all modern environments).
Development
# Install dependencies
npm install
# Run tests
npm test
# Build
npm run build
# Type check
npm run typecheckLicense
MIT
