Giter VIP home page Giter VIP logo

opening-hours's Introduction

A helper to query and format a set of opening hours

Latest Version on Packagist Software License Tests Coverage Quality Score StyleCI Total Downloads

With spatie/opening-hours you create an object that describes a business' opening hours, which you can query for open or closed on days or specific dates, or use to present the times per day.

spatie/opening-hours can be used directly on Carbon thanks to cmixin/business-time so you can benefit opening hours features directly on your enhanced date objects.

A set of opening hours is created by passing in a regular schedule, and a list of exceptions.

// Add the use at the top of each file where you want to use the OpeningHours class:
use Spatie\OpeningHours\OpeningHours;

$openingHours = OpeningHours::create([
    'monday'     => ['09:00-12:00', '13:00-18:00'],
    'tuesday'    => ['09:00-12:00', '13:00-18:00'],
    'wednesday'  => ['09:00-12:00'],
    'thursday'   => ['09:00-12:00', '13:00-18:00'],
    'friday'     => ['09:00-12:00', '13:00-20:00'],
    'saturday'   => ['09:00-12:00', '13:00-16:00'],
    'sunday'     => [],
    'exceptions' => [
        '2016-11-11' => ['09:00-12:00'],
        '2016-12-25' => [],
        '01-01'      => [],                // Recurring on each 1st of January
        '12-25'      => ['09:00-12:00'],   // Recurring on each 25th of December
    ],
]);

// This will allow you to display things like:

$now = new DateTime('now');
$range = $openingHours->currentOpenRange($now);

if ($range) {
    echo "It's open since ".$range->start()."\n";
    echo "It will close at ".$range->end()."\n";
} else {
    echo "It's closed since ".$openingHours->previousClose($now)->format('l H:i')."\n";
    echo "It will re-open at ".$openingHours->nextOpen($now)->format('l H:i')."\n";
}

The object can be queried for a day in the week, which will return a result based on the regular schedule:

// Open on Mondays:
$openingHours->isOpenOn('monday'); // true

// Closed on Sundays:
$openingHours->isOpenOn('sunday'); // false

It can also be queried for a specific date and time:

// Closed because it's after hours:
$openingHours->isOpenAt(new DateTime('2016-09-26 19:00:00')); // false

// Closed because Christmas was set as an exception
$openingHours->isOpenOn('2016-12-25'); // false

It can also return arrays of opening hours for a week or a day:

// OpeningHoursForDay object for the regular schedule
$openingHours->forDay('monday');

// OpeningHoursForDay[] for the regular schedule, keyed by day name
$openingHours->forWeek();

// Array of day with same schedule for the regular schedule, keyed by day name, days combined by working hours
$openingHours->forWeekCombined();

// OpeningHoursForDay object for a specific day
$openingHours->forDate(new DateTime('2016-12-25'));

// OpeningHoursForDay[] of all exceptions, keyed by date
$openingHours->exceptions();

On construction, you can set a flag for overflowing times across days. For example, for a nightclub opens until 3am on Friday and Saturday:

$openingHours = \Spatie\OpeningHours\OpeningHours::create([
    'overflow' => true,
    'friday'   => ['20:00-03:00'],
    'saturday' => ['20:00-03:00'],
], null);

This allows the API to further at previous day's data to check if the opening hours are open from its time range.

You can add data in definitions then retrieve them:

$openingHours = OpeningHours::create([
    'monday' => [
        'data' => 'Typical Monday',
        '09:00-12:00',
        '13:00-18:00',
    ],
    'tuesday' => [
        '09:00-12:00',
        '13:00-18:00',
        [
            '19:00-21:00',
            'data' => 'Extra on Tuesday evening',
        ],
    ],
    'exceptions' => [
        '2016-12-25' => [
            'data' => 'Closed for Christmas',
        ],
    ],
]);

echo $openingHours->forDay('monday')->data; // Typical Monday
echo $openingHours->forDate(new DateTime('2016-12-25'))->data; // Closed for Christmas
echo $openingHours->forDay('tuesday')[2]->data; // Extra on Tuesday evening

In the example above, data are strings but it can be any kind of value. So you can embed multiple properties in an array.

For structure convenience, the data-hours couple can be a fully-associative array, so the example above is strictly equivalent to the following:

$openingHours = OpeningHours::create([
    'monday' => [
        'hours' => [
            '09:00-12:00',
            '13:00-18:00',
        ],
        'data' => 'Typical Monday',
    ],
    'tuesday' => [
        ['hours' => '09:00-12:00'],
        ['hours' => '13:00-18:00'],
        ['hours' => '19:00-21:00', 'data' => 'Extra on Tuesday evening'],
    ],
    // Open by night from Wednesday 22h to Thursday 7h:
    'wednesday' => ['22:00-24:00'], // use the special "24:00" to reach midnight included
    'thursday' => ['00:00-07:00'],
    'exceptions' => [
        '2016-12-25' => [
            'hours' => [],
            'data'  => 'Closed for Christmas',
        ],
    ],
]);

You can use the separator to to specify multiple days at once, for the week or for exceptions:

$openingHours = OpeningHours::create([
    'monday to friday' => ['09:00-19:00'],
    'saturday to sunday' => [],
    'exceptions' => [
        // Every year
        '12-24 to 12-26' => [
            'hours' => [],
            'data'  => 'Holidays',
        ],
        // Only happening in 2024
        '2024-06-25 to 2024-07-01' => [
            'hours' => [],
            'data'  => 'Closed for works',
        ],
    ],
]);

The last structure tool is the filter, it allows you to pass closures (or callable function/method reference) that take a date as a parameter and returns the settings for the given date.

$openingHours = OpeningHours::create([
    'monday' => [
       '09:00-12:00',
    ],
    'filters' => [
        function ($date) {
            $year         = intval($date->format('Y'));
            $easterMonday = new DateTimeImmutable('2018-03-21 +'.(easter_days($year) + 1).'days');
            if ($date->format('m-d') === $easterMonday->format('m-d')) {
                return []; // Closed on Easter Monday
                // Any valid exception-array can be returned here (range of hours, with or without data)
            }
            // Else the filter does not apply to the given date
        },
    ],
]);

If a callable is found in the "exceptions" property, it will be added automatically to filters so you can mix filters and exceptions both in the exceptions array. The first filter that returns a non-null value will have precedence over the next filters and the filters array has precedence over the filters inside the exceptions array.

Warning: We will loop on all filters for each date from which we need to retrieve opening hours and can neither predicate nor cache the result (can be a random function) so you must be careful with filters, too many filters or long process inside filters can have a significant impact on the performance.

It can also return the next open or close DateTime from a given DateTime.

// The next open datetime is tomorrow morning, because we’re closed on 25th of December.
$nextOpen = $openingHours->nextOpen(new DateTime('2016-12-25 10:00:00')); // 2016-12-26 09:00:00

// The next open datetime is this afternoon, after the lunch break.
$nextOpen = $openingHours->nextOpen(new DateTime('2016-12-24 11:00:00')); // 2016-12-24 13:00:00


// The next close datetime is at noon.
$nextClose = $openingHours->nextClose(new DateTime('2016-12-24 10:00:00')); // 2016-12-24 12:00:00

// The next close datetime is tomorrow at noon, because we’re closed on 25th of December.
$nextClose = $openingHours->nextClose(new DateTime('2016-12-25 15:00:00')); // 2016-12-26 12:00:00

Read the usage section for the full api.

Spatie is a webdesign agency based in Antwerp, Belgium. You'll find an overview of all our open source projects on our website.

Support us

We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on our contact page. We publish all received postcards on our virtual postcard wall.

Installation

You can install the package via composer:

composer require spatie/opening-hours

Usage

The package should only be used through the OpeningHours class. There are also three value object classes used throughout, Time, which represents a single time, TimeRange, which represents a period with a start and an end, and openingHoursForDay, which represents a set of TimeRanges which can't overlap.

Spatie\OpeningHours\OpeningHours

OpeningHours::create(array $data, $timezone = null, $toutputTimezone = null): Spatie\OpeningHours\OpeningHours

Static factory method to fill the set of opening hours.

$openingHours = OpeningHours::create([
    'monday' => ['09:00-12:00', '13:00-18:00'],
    // ...
]);

If no timezone is specified, OpeningHours will just assume you always pass DateTime objects that have already the timezone matching your schedule.

If you pass a $timezone as a second argument or via the array-key 'timezone' (it can be either a DateTimeZone object or a string), then passed dates will be converted to this timezone at the beginning of each method, then if the method return a date object (such as nextOpen, nextClose, previousOpen, previousClose, currentOpenRangeStart or currentOpenRangeEnd), then it's converted back to original timezone before output so the object can reflect a moment in user local time while OpeningHours can stick in its own business timezone.

Alternatively you can also specify both input and output timezone (using second and third argument) or using an array:

$openingHours = OpeningHours::create([
    'monday' => ['09:00-12:00', '13:00-18:00'],
    'timezone' => [
        'input' => 'America/New_York',
        'output' => 'Europe/Oslo',
    ],
]);

OpeningHours::mergeOverlappingRanges(array $schedule) : array

For safety sake, creating OpeningHours object with overlapping ranges will throw an exception unless you pass explicitly 'overflow' => true, in the opening hours array definition. You can also explicitly merge them.

$ranges = [
  'monday' => ['08:00-11:00', '10:00-12:00'],
];
$mergedRanges = OpeningHours::mergeOverlappingRanges($ranges); // Monday becomes ['08:00-12:00']

OpeningHours::create($mergedRanges);
// Or use the following shortcut to create from ranges that possibly overlap:
OpeningHours::createAndMergeOverlappingRanges($ranges);

Not all days are mandatory, if a day is missing, it will be set as closed.

OpeningHours::fill(array $data): Spatie\OpeningHours\OpeningHours

The same as create, but non-static.

$openingHours = (new OpeningHours)->fill([
    'monday' => ['09:00-12:00', '13:00-18:00'],
    // ...
]);

OpeningHours::forWeek(): Spatie\OpeningHours\OpeningHoursForDay[]

Returns an array of OpeningHoursForDay objects for a regular week.

$openingHours->forWeek();

OpeningHours::forWeekCombined(): array

Returns an array of days. Array key is first day with same hours, array values are days that have the same working hours and OpeningHoursForDay object.

$openingHours->forWeekCombined();

OpeningHours::forWeekConsecutiveDays(): array

Returns an array of concatenated days, adjacent days with the same hours. Array key is first day with same hours, array values are days that have the same working hours and OpeningHoursForDay object.

Warning: consecutive days are considered from Monday to Sunday without looping (Monday is not consecutive to Sunday) no matter the days order in initial data.

$openingHours->forWeekConsecutiveDays();

OpeningHours::forDay(string $day): Spatie\OpeningHours\OpeningHoursForDay

Returns an OpeningHoursForDay object for a regular day. A day is lowercase string of the english day name.

$openingHours->forDay('monday');

OpeningHours::forDate(DateTimeInterface $dateTime): Spatie\OpeningHours\OpeningHoursForDay

Returns an OpeningHoursForDay object for a specific date. It looks for an exception on that day, and otherwise it returns the opening hours based on the regular schedule.

$openingHours->forDate(new DateTime('2016-12-25'));

OpeningHours::exceptions(): Spatie\OpeningHours\OpeningHoursForDay[]

Returns an array of all OpeningHoursForDay objects for exceptions, keyed by a Y-m-d date string.

$openingHours->exceptions();

OpeningHours::isOpenOn(string $day): bool

Checks if the business is open (contains at least 1 range of open hours) on a day in the regular schedule.

$openingHours->isOpenOn('saturday');

If the given string is a date, it will check if it's open (contains at least 1 range of open hours) considering both regular day schedule and possible exceptions.

$openingHours->isOpenOn('2020-09-03');
$openingHours->isOpenOn('09-03'); // If year is omitted, current year is used instead

OpeningHours::isClosedOn(string $day): bool

Checks if the business is closed on a day in the regular schedule.

$openingHours->isClosedOn('sunday');

OpeningHours::isOpenAt(DateTimeInterface $dateTime): bool

Checks if the business is open on a specific day, at a specific time.

$openingHours->isOpenAt(new DateTime('2016-26-09 20:00'));

OpeningHours::isClosedAt(DateTimeInterface $dateTime): bool

Checks if the business is closed on a specific day, at a specific time.

$openingHours->isClosedAt(new DateTime('2016-26-09 20:00'));

OpeningHours::isOpen(): bool

Checks if the business is open right now.

$openingHours->isOpen();

OpeningHours::isClosed(): bool

Checks if the business is closed right now.

$openingHours->isClosed();

OpeningHours::isAlwaysOpen(): bool

Checks if the business is open 24/7, has no exceptions and no filters.

if ($openingHours->isAlwaysOpen()) {
    echo 'This business is open all day long every day.';
}

OpeningHours::isAlwaysClosed(): bool

Checks if the business is never open, has no exceptions and no filters.

OpeningHours accept empty array or list with every week day empty with no prejudices.

If it's not a valid state in your domain, you should use this method to throw an exception or show an error.

if ($openingHours->isAlwaysClosed()) {
    throw new RuntimeException('Opening hours missing');
}

OpeningHours::nextOpen

OpeningHours::nextOpen(
    ?DateTimeInterface $dateTime = null,
    ?DateTimeInterface $searchUntil = null,
    ?DateTimeInterface $cap = null,
) : DateTimeInterface`

Returns next open DateTime from the given DateTime ($dateTime or from now if this parameter is null or omitted).

If a DateTimeImmutable object is passed, a DateTimeImmutable object is returned.

Set $searchUntil to a date to throw an exception if no open time can be found before this moment.

Set $cap to a date so if no open time can be found before this moment, $cap is returned.

$openingHours->nextOpen(new DateTime('2016-12-24 11:00:00'));

`

OpeningHours::nextClose

OpeningHours::nextClose(
    ?DateTimeInterface $dateTime = null,
    ?DateTimeInterface $searchUntil = null,
    ?DateTimeInterface $cap = null,
) : DateTimeInterface`

Returns next close DateTime from the given DateTime ($dateTime or from now if this parameter is null or omitted).

If a DateTimeImmutable object is passed, a DateTimeImmutable object is returned.

Set $searchUntil to a date to throw an exception if no closed time can be found before this moment.

Set $cap to a date so if no closed time can be found before this moment, $cap is returned.

$openingHours->nextClose(new DateTime('2016-12-24 11:00:00'));

OpeningHours::previousOpen

OpeningHours::previousOpen(
    ?DateTimeInterface $dateTime = null,
    ?DateTimeInterface $searchUntil = null,
    ?DateTimeInterface $cap = null,
) : DateTimeInterface`

Returns previous open DateTime from the given DateTime ($dateTime or from now if this parameter is null or omitted).

If a DateTimeImmutable object is passed, a DateTimeImmutable object is returned.

Set $searchUntil to a date to throw an exception if no open time can be found after this moment.

Set $cap to a date so if no open time can be found after this moment, $cap is returned.

$openingHours->previousOpen(new DateTime('2016-12-24 11:00:00'));

OpeningHours::previousClose

OpeningHours::previousClose(
    ?DateTimeInterface $dateTime = null,
    ?DateTimeInterface $searchUntil = null,
    ?DateTimeInterface $cap = null,
) : DateTimeInterface`

Returns previous close DateTime from the given DateTime ($dateTime or from now if this parameter is null or omitted).

If a DateTimeImmutable object is passed, a DateTimeImmutable object is returned.

Set $searchUntil to a date to throw an exception if no closed time can be found after this moment.

Set $cap to a date so if no closed time can be found after this moment, $cap is returned.

$openingHours->nextClose(new DateTime('2016-12-24 11:00:00'));

OpeningHours::diffInOpenHours(DateTimeInterface $startDate, DateTimeInterface $endDate) : float

Return the amount of open time (number of hours as a floating number) between 2 dates/times.

$openingHours->diffInOpenHours(new DateTime('2016-12-24 11:00:00'), new DateTime('2016-12-24 16:34:25'));

OpeningHours::diffInOpenMinutes(DateTimeInterface $startDate, DateTimeInterface $endDate) : float

Return the amount of open time (number of minutes as a floating number) between 2 dates/times.

OpeningHours::diffInOpenSeconds(DateTimeInterface $startDate, DateTimeInterface $endDate) : float

Return the amount of open time (number of seconds as a floating number) between 2 dates/times.

OpeningHours::diffInClosedHours(DateTimeInterface $startDate, DateTimeInterface $endDate) : float

Return the amount of closed time (number of hours as a floating number) between 2 dates/times.

$openingHours->diffInClosedHours(new DateTime('2016-12-24 11:00:00'), new DateTime('2016-12-24 16:34:25'));

OpeningHours::diffInClosedMinutes(DateTimeInterface $startDate, DateTimeInterface $endDate) : float

Return the amount of closed time (number of minutes as a floating number) between 2 dates/times.

OpeningHours::diffInClosedSeconds(DateTimeInterface $startDate, DateTimeInterface $endDate) : float

Return the amount of closed time (number of seconds as a floating number) between 2 dates/times.

OpeningHours::currentOpenRange(DateTimeInterface $dateTime) : false | TimeRange

Returns a Spatie\OpeningHours\TimeRange instance of the current open range if the business is open, false if the business is closed.

$range = $openingHours->currentOpenRange(new DateTime('2016-12-24 11:00:00'));

if ($range) {
    echo "It's open since ".$range->start()."\n";
    echo "It will close at ".$range->end()."\n";
} else {
    echo "It's closed";
}

start() and end() methods return Spatie\OpeningHours\Time instances. Time instances created from a date can be formatted with date information. This is useful for ranges overflowing midnight:

$period = $openingHours->currentOpenRange(new DateTime('2016-12-24 11:00:00'));

if ($period) {
    echo "It's open since ".$period->start()->format('D G\h')."\n";
    echo "It will close at ".$period->end()->format('D G\h')."\n";
} else {
    echo "It's closed";
}

OpeningHours::currentOpenRangeStart(DateTimeInterface $dateTime) : false | DateTime

Returns a DateTime instance of the date and time since when the business is open if the business is open, false if the business is closed.

Note: date can be the previous day if you use night ranges.

$date = $openingHours->currentOpenRangeStart(new DateTime('2016-12-24 11:00:00'));

if ($date) {
    echo "It's open since ".$date->format('H:i');
} else {
    echo "It's closed";
}

OpeningHours::currentOpenRangeEnd(DateTimeInterface $dateTime) : false | DateTime

Returns a DateTime instance of the date and time until when the business will be open if the business is open, false if the business is closed.

Note: date can be the next day if you use night ranges.

$date = $openingHours->currentOpenRangeEnd(new DateTime('2016-12-24 11:00:00'));

if ($date) {
    echo "It will close at ".$date->format('H:i');
} else {
    echo "It's closed";
}

OpeningHours::createFromStructuredData(array|string $data, $timezone = null, $outputTimezone = null): Spatie\OpeningHours\OpeningHours

Static factory method to fill the set with a https://schema.org/OpeningHoursSpecification array or JSON string.

dayOfWeek supports array of day names (Google-flavored) or array of day URLs (official schema.org specification).

$openingHours = OpeningHours::createFromStructuredData('[
    {
        "@type": "OpeningHoursSpecification",
        "opens": "08:00",
        "closes": "12:00",
        "dayOfWeek": [
            "https://schema.org/Monday",
            "https://schema.org/Tuesday",
            "https://schema.org/Wednesday",
            "https://schema.org/Thursday",
            "https://schema.org/Friday"
        ]
    },
    {
        "@type": "OpeningHoursSpecification",
        "opens": "14:00",
        "closes": "18:00",
        "dayOfWeek": [
            "Monday",
            "Tuesday",
            "Wednesday",
            "Thursday",
            "Friday"
        ]
    },
    {
        "@type": "OpeningHoursSpecification",
        "opens": "00:00",
        "closes": "00:00",
        "validFrom": "2023-12-25",
        "validThrough": "2023-12-25"
    }
]');

OpeningHours::asStructuredData(strinf $format = 'H:i', string|DateTimeZone $timezone) : array

Returns a OpeningHoursSpecification as an array.

$openingHours->asStructuredData();
$openingHours->asStructuredData('H:i:s'); // Customize time format, could be 'h:i a', 'G:i', etc.
$openingHours->asStructuredData('H:iP', '-05:00'); // Add a timezone
// Timezone can be numeric or string like "America/Toronto" or a DateTimeZone instance
// But be careful, the time is arbitrary applied on 1970-01-01, so it does not handle daylight
// saving time, meaning Europe/Paris is always +01:00 even in summer time.

Spatie\OpeningHours\OpeningHoursForDay

This class is meant as read-only. It implements ArrayAccess, Countable and IteratorAggregate so you can process the list of TimeRanges in an array-like way.

Spatie\OpeningHours\TimeRange

Value object describing a period with a start and an end time. Can be cast to a string in a H:i-H:i format.

Spatie\OpeningHours\Time

Value object describing a single time. Can be cast to a string in a H:i format.

Adapters

OpenStreetMap

You can convert OpenStreetMap format to OpeningHours object using osm-opening-hours (thanks to mgrundkoetter)

Changelog

Please see CHANGELOG for more information about what has changed recently.

Testing

composer test

Contributing

Please see CONTRIBUTING for details.

Security

If you've found a bug regarding security please mail [email protected] instead of using the issue tracker.

Postcardware

You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.

Our address is: Spatie, Kruikstraat 22, 2018 Antwerp, Belgium.

We publish all received postcards on our company website.

Credits

License

The MIT License (MIT). Please see License File for more information.

opening-hours's People

Contributors

adrianmrn avatar akoepcke avatar ambroisemaupate avatar ashpowell avatar benmorel avatar bobbybouwmann avatar carusogabriel avatar djuki avatar dkozickis avatar freekmurze avatar hulkur avatar jerome2710 avatar jeromegamez avatar joshuadegier avatar kylekatarnls avatar miclf avatar mikemand avatar mitchellvanw avatar mkopinsky avatar n7olkachev avatar peter279k avatar rlweb avatar sebastiandedeyne avatar sti3bas avatar stylecibot avatar swapnilsarwe avatar thebnl avatar tjoosten avatar tmus avatar zlodes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

opening-hours's Issues

Missing time zone information when exporting asStructuredData

Hey guys, great work with this package!

I'm exporting structured opening hours data to a Place and I need the information of the timezone to be encoded. I'm creating the object using the OpeningHours::create static method with a time zone passed, but it appears that the timezone is never referenced elsewhere in the code.

Could we tack on the offset according to the schema.org Time spec if there's a timezone set?

Import structured data

As noted, the method asStructuredData() converts a JSON-like data for plain text representation. It would be nice to import these rules which acts as a counterpart to create(), e.g.

$openingHours = OpeningHours::fromStructuredData($jsonString);
assert($jsonString === $openingHours->asStructuredData());

Merge overlapping hours

Would be nice to have an option to merge hours if they overlap. If I find some free time, I might make a PR if this is something you're willing to have in the package.

What I mean if for 1 day someone enters 08:00-12:00, and 10:00-13:00, it would be merged to 1 entry of 08:00-13:00.

Persist OpeningHours in DB

Hi
Thanks for such a great package!
I was wondering how do you guy persist the OpeningHours in the DB ? I cannot find any models/query to do that.
Cordially

Bug with next open when opening times are set to 00:00

When setting the following hours:

$openingHours = OpeningHours::create([
            'monday' => ['00:00-16:00'],
            'tuesday' => ['00:00-16:00'],
            'wednesday' => ['00:00-16:00'],
            'thursday' => ['00:00-16:00'],
            'friday' => ['00:00-16:00'],
            'saturday' => [],
            'sunday' => []
            // ...
        ]);

And executing:

$openingHours->nextOpen(Carbon::now())

The script gets stuck in an infinite loop.

Any opening time other that 00:00 and it seems to be fine.

Suggestion: Adding remarks to exceptions

Great package!

Idea: It would come handy to be able to use remarks on the exceptions:

$openingHours = OpeningHours::create([
    'monday' => ['09:00-12:00', '13:00-18:00'],
    'tuesday' => ['09:00-12:00', '13:00-18:00'],
    'wednesday' => ['09:00-12:00'],
    'thursday' => ['09:00-12:00', '13:00-18:00'],
    'friday' => ['09:00-12:00', '13:00-20:00'],
    'saturday' => ['09:00-12:00', '13:00-16:00'],
    'sunday' => [],
    'exceptions' => [
        '2016-12-25' => [
            'hours' => [],
            'remarks' => 'First christmas day closed'   
        ],
        '2016-12-26' => [
            'hours' => [],
            'remarks' => 'First christmas day closed'   
        ],
        '2011-01-01' => [
            'hours' => ['13:00-18:00'],
            'remarks' => 'Newyearsday opening times'   
        ]
    ],
]);

Can this library be used to match 2 groups of opening-hours?

Let's say we have teacher and student.

Teacher has some timetable (opening-hours) when he can teach,
and student has some timetable (opening-hours) when he can go to lectures.

Does this library support matching these two?

So I will see result: "teacher 1 (9-11 AM) can teach student 2 (9-10 AM) and student 3 (10-11 AM)"

Discussion of asStructuredData() for 00:00-24:00

With the latest 2.3.2 version asStructuredData() outputs

<?php
use \Spatie\OpeningHours\OpeningHours;

require __DIR__ . '/vendor/autoload.php';

$data = [
    'monday' => [
        'hours' =>  [
            '09:00-17:00',
        ]
    ],
    'exceptions' => [
        '2019-06-12' => [
            'hours' => [
                '00:00-24:00',
            ]
        ],
    ]
];

$opening_hours = OpeningHours::create(array_merge($data,['overflow' => true]));
var_dump($opening_hours->asStructuredData());

output

...
array(5) {
    ["@type"]=>
    string(25) "OpeningHoursSpecification"
    ["opens"]=>
    string(5) "00:00"
    ["closes"]=>
    string(5) "24:00"
    ["validFrom"]=>
    string(10) "2019-06-12"
    ["validThrough"]=>
    string(10) "2019-06-12"
  }

Points to consider

Non-composer installation?

Thanks in advance for any help. Would it be possible to explain or add directions for installing this on a PHP site without using composer?

isOpenAt Now

how can we test is open at now
$openingHours->isOpenAt(new DateTime($time_now);
not working
i want to check with current time

memory size

How can I optimize my code ?

Allowed memory size of 134217728 bytes exhausted (tried to allocate 16777224 bytes) 

            if ($openingHours->isOpenOn(Carbon::tomorrow()->format('l'))) {
                $vt = new Verta(Carbon::tomorrow('Asia/Tehran'));
                $text = 'tommotow- '.$vt->format('%d %B');
                $daylist[] = ['id'=>2,"text"=>$text];
                foreach ($openingHours->forDay(strtolower(Carbon::tomorrow()->format('l'))) as $time) {
                    $j = $time->start();
                    while (strtotime($j) <= strtotime($time->end())) {
                        $hourlist[] = ['day_id'=>2,"text"=>"from $j to ".date('H:i', strtotime('+30 minutes', strtotime($j))),"timestamp"=>strtotime($j)];
                        $j = date('H:i', strtotime('+30 minutes', strtotime($j)));
                    }
                }
            }

Unexpected behavior and infinite loops when using 24:00

I'm experiencing some strange problems with 2.3.0 when adding an hour range of ['00:00-24:00'] on a day.

Problem 1: When using nextOpen() the result will show the next week instead of the next day
Problem 2: When creating the object with an array instead of static statement, there is an infinite loop somewhere which causes a hang.

This seems to be quite similar to issue #44 - Bug with next open when opening times are set to 00:00, except that it affects the 24:00.

Here is some example code:

<?php
  
use Spatie\OpeningHours\OpeningHours;

require_once __DIR__.DIRECTORY_SEPARATOR.'vendor/autoload.php';


$dayHours = [];
foreach (array('monday','tuesday','wednesday','thursday','friday','saturday','sunday') as $day) {
        $dayHours[$day] = ['00:00-24:00'];
}

$scheduleHangs = OpeningHours::create($dayHours);

$scheduleMidnight = OpeningHours::create([
    'monday'    => ['00:00-24:00'],
    'tuesday'   => ['00:00-24:00'],
    'wednesday' => ['00:00-24:00'],
    'thursday'  => ['00:00-24:00'],
    'friday'    => ['00:00-24:00']
]
);

$scheduleAlmostMidnight = OpeningHours::create([
    'monday'    => ['00:00-23:59'],
    'tuesday'   => ['00:00-23:59'],
    'wednesday' => ['00:00-23:59'],
    'thursday'  => ['00:00-23:59'],
    'friday'    => ['00:00-23:59']
]
);

// Thursday 06 June 2019 19:02:00
var_dump($scheduleMidnight->nextOpen(new DateTime("2019-06-06 19:02:00")));
var_dump($scheduleHangs->nextOpen(new DateTime("2019-06-06 19:02:00")));
var_dump($scheduleAlmostMidnight->nextOpen(new DateTime("2019-06-06 19:02:00")));


?>

The result of this is an incorrect value on the first var_dump, and a hang on the second:

object(DateTime)#63 (3) {
  ["date"]=>
  string(26) "2019-06-10 00:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(19) "Africa/Johannesburg"
}
^C

Changing the "00:00" to "00:01" has no effect, but changing "24:00" to "23;59" resolves the issue with all three of the var_dump() statements returning the correct value of "2019-06-07 00:00:00".

object(DateTime)#63 (3) {
  ["date"]=>
  string(26) "2019-06-07 00:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(19) "Africa/Johannesburg"
}
object(DateTime)#60 (3) {
  ["date"]=>
  string(26) "2019-06-07 00:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(19) "Africa/Johannesburg"
}
object(DateTime)#63 (3) {
  ["date"]=>
  string(26) "2019-06-07 00:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(19) "Africa/Johannesburg"
}

Use Carbon and use it instead of DateTime (or make it optional)

I have code like
Carbon\Carbon::instance($openingHours->nextOpen(new DateTime()))->diffForHumans()

I would love to set a variable in the openingHours.php config file like 'library' => 'Carbon', or 'library' => 'DateTime',

This could result in all calls that return dates or times to return Carbon objects that could then be formatted with the existing Carbon fluent commands.

$openingHours->nextOpen()->diffForHumans() could return the string 6 hours from now and $openingHours->nextOpen()->format('l@h:i A') could return Thursday@09:00 AM

[help wanted] Voyager BREAD options

Try to make a custom browse and edit-add.blade.php
For now i made a pretty simple BREAD details for the column opening_hours:
{ "monday": { "open": "00:00", "close": "00:00" }, "tuesday": { "open": "00:00", "close": "00:00" }, "wednesday": { "open": "00:00", "close": "00:00" }, "thursday": { "open": "00:00", "close": "00:00" }, "friday": { "open": "00:00", "close": "00:00" }, "saturday": { "open": "00:00", "close": "00:00" }, "sunday": { "open": "00:00", "close": "00:00" } }

The idea here is to generate a form withing voyager what can be filled, and save the result to the column.
[could be a nice hook btw]

Show the openingHours in Blade

I am feeling a bit stupid here...

Within composer

$openingHours = OpeningHours::create([
            'monday'    => ['07:00-12:00', '13:00-17:00'],
            'tuesday'   => ['10:00-18:00'],
            'wednesday' => ['10:00-18:00'],
            'thursday'  => ['10:00-18:00'],
            'friday'    => ['10:00-21:00'],
            'saturday'  => ['10:00-17:00'],
            'sunday'    => [],
            'exceptions' => [
                '2016-11-11' => ['09:00-12:00'],
                '2016-12-25' => [],
            ]);
$view->with('openingHours', $openingHours);

Next I want to iterate over the data to show a list of hours for the week:

<table>
    @foreach($openingHours->forWeek() as $day => $hours)
        @if($openingHours->isOpenOn($day))
            <tr>
                <td>{{ $day }}</td>
                <td>
                    @foreach($openingHours->forDay($day) as $time)
                        {{ var_dump($time->start()) }} // This shows the object with the times (*)
                    @endforeach
                </td>
            </tr>
        @else
            <tr>
                <td>{{ $day }}</td>
                <td>Closed</td>
            </tr>
        @endif
    @endforeach
</table>

(*) Part of the array as example:

/var/www/domain.nl/storage/framework/views/bcadb96489e675e93945f2c5cf28057afa29cb94.php:13:
object(Spatie\OpeningHours\Time)[370]
  protected 'hours' => int 10
  protected 'minutes' => int 0

How do I access the times? When I do {{ var_dump($time->start()->hours) }} they are protected.

Unexpected result for ->overlaps

When running the following code:

use Spatie\OpeningHours\TimeRange;

var_dump(TimeRange::fromString('10:00-13:00')->overlaps(TimeRange::fromString('11:00-12:00')));
var_dump(TimeRange::fromString('11:00-12:00')->overlaps(TimeRange::fromString('10:00-13:00')));

I get:

bool(true)
bool(false)

When I would expect:

bool(true)
bool(true)

@freekmurze Is it the expected behavior for some reason ? Else I think we should fix this for v2.

isOpenAt is not able to check if still open from the previous day

Hello,

Currently this test doesn't pass :

public function testItHandlesWhenClosingHourIsTheDayAfter()
{
    $openingHours = OpeningHours::create(['monday' => ['19:00-04:00'], 'tuesday' => []]);
    $this->assertTrue($openingHours->isOpenAt(new \DateTime('next tuesday 02:00:00')));
}

Do you plan to handle this case?
Have you already considered the changes it would induce on the other methods ?

Thank you :)

Allow exceptions to include annual closures

It would be useful to allow marking annual closures as exceptions - Something along the lines of this -

$exceptions = [
   '0000-12-25' => []  // Closed Christmas day each year
   '0000-01-01' => []  // Closed New Years day each year
]

Object of class DateTime could not be converted to string

Hi!

I can't use the nextOpen() nor nextClose() functions because PHP complains about new DateTime() not being a string:

Recoverable fatal error: Object of class DateTime could not be converted to string in ...

$nextOpen = $openingHours->nextOpen(new DateTime('2019-01-07'));
echo $nextOpen;

Please fix!

Looking for maintainer

We're looking for a maintainer that can help us answer to issues, review PRs and tag new releases.

If you have experience with open source in general and/or this package in particular, let me know: [email protected]

isOpenAt returns false (and isClosedAt returns true) for the last second of the day

I believe from a practical sense, the last second of the working day should be considered working time.

When connected to automated systems, if something is scheduled every hour, it should be accepted at the last hour too.

Code snippet to illustrate

$openingHours = OpeningHours::create([
    'monday' => ['09:00-18:00'],
    'tuesday' => ['09:00-18:00'],
    'wednesday' => ['09:00-18:00'],
    'thursday' => ['09:00-18:00'],
    'friday' => ['09:00-18:00'],
    'saturday' => ['09:00-18:00'],
    'sunday' => ['09:00-18:00']
]);

// Expected to return true, returns false
$openingHours->isOpenAt(new DateTime('2016-09-26 18:00:00'));

Opening hours across Midnight

Is there a way to define Opening Hours accross Midnight??

For example:

$openingHours = OpeningHours::create([
    'tuesday'     => ['09:00-00:00'],
    'tuesday'     => ['09:00-03:00'],
    'exceptions' => [
        '2019-02-05' => ['09:00-04:00'],
    ],
]);

If we split into two slots 09:00-23-59 and 00:00-03:00, then at 16:00 the nextClose would be 23:59 instead of 03:00. Has someone a possible solution for that problem?

nextOpen doesn't check exceptions ?

Good day. I have some problem with nextOpen function. Does it count for exception?
I have 2017-03-27 in exception from 10 to 12 hours. But when i'm trying to get nextOpen hour it returns me this:

DateTime {#319
  +"date": "2017-03-27 10:00:00.000000"
  +"timezone_type": 3
  +"timezone": "Europe/Kiev"
}

I think it's not right, because 10:00 on 2017-03-27 is in exception. Am i right or missing something?

#exceptions: array:10 [
    "2017-03-27" => OpeningHoursForDay {#347
      #openingHours: array:1 [
        0 => TimeRange {#349
          #start: Time {#350
            #hours: 10
            #minutes: 0
          }
          #end: Time {#351
            #hours: 12
            #minutes: 0
          }
        }
      ]
    }

Installation Woes

I have tried installing on 3 different environments. localhost, Heroku, and DigitalOcean. All servers are running latest PHP 7. No errors with composer and installation but when I try to use the class I get class not found error.

Fatal error: Uncaught Error: Class 'OpeningHours' not found in /app/index.php:9 Stack trace: #0 {main} thrown in /app/index.php on line 9

The test code is simple:

require __DIR__ . '/vendor/autoload.php';

$openingHours = OpeningHours::create([
    'monday'     => ['09:00-12:00', '13:00-18:00'],
    'tuesday'    => ['09:00-12:00', '13:00-18:00'],
    'wednesday'  => ['09:00-12:00'],
    'thursday'   => ['09:00-12:00', '13:00-18:00'],
    'friday'     => ['09:00-12:00', '13:00-20:00'],
    'saturday'   => ['09:00-12:00', '13:00-16:00'],
    'sunday'     => [],
    'exceptions' => [
        '2016-11-11' => ['09:00-12:00'],
        '2016-12-25' => [],
        '01-01'      => [],                // Recurring on each 1st of January
        '12-25'      => ['09:00-12:00'],   // Recurring on each 25th of December
    ],
]);

$week = $openingHours->forWeek();

echo "<pre>";
print_r($week);
echo "</pre>";

Any ideas?

Feature request: Recurring exceptions

Some way to include recurring exceptions without having to add every occurrence to the exceptions array would be great. That way every Christmas (for example), past and present, could be marked as closed without ending up with entries every year.

Another option would be to allow exceptions to only have a month and day part, including or excluding the year as needed.

Time allows 60 mins.

Time::fromString('aa:bb'); // throw

Time::fromString('24:10'); // not thow
Time::fromString('16:60');

Are you both allowed on purpose?

Set closed time in exception

On an open day, I have like a 'busy time' of half-an-hour which can change dynamically.

So I first define the default opening time

$openingHours = OpeningHours::create([
    'monday' => ['09:00-18:00']
]}

Now there is a chance there is like half an hour of closed time on certain days, so ideally I would do:

    'exceptions' => [
        '2016-11-11' => 'closed' => ['12:30-13:00'],
    ]

However, I need to do:

 'exceptions' => [
        '2016-11-11' => ['09:00-12:30','12:30-18:00'],
    ]

The thing is, I only have the value of when there is half an hour of closed time. How would I calculate the opening time with the correct "gaps" in it? (['09:00-12:30','12:30-18:00'],) using the current opening time and the piece of closed time?

Any help is appreciated.

Edit: ideally a function like

$openingHours->addClosedTime('2016-11-11' , ['12:30-13:00']);

would be perfect 🙂

Can't use the data string because it's not set as a time?

Hi!

I ran up on a bug in your function. When I try the example below, I'll get this error message:

Fatal error: Uncaught Spatie\OpeningHours\Exceptions\InvalidTimeRangeString: The string `Extra on Tuesday evening` isn't a valid time range string. A time string must be a formatted as `H:i-H:i`, e.g. `09:00-18:00`. in F:\Websites\www\erik-edgren-blog\vendor\spatie\opening-hours\src\Exceptions\InvalidTimeRangeString.php:9 Stack trace: #0 F:\Websites\www\erik-edgren-blog\vendor\spatie\opening-hours\src\TimeRange.php(31): Spatie\OpeningHours\Exceptions\InvalidTimeRangeString::forString('Extra on Tuesda...') #1 F:\Websites\www\erik-edgren-blog\vendor\spatie\opening-hours\src\TimeRange.php(58): Spatie\OpeningHours\TimeRange::fromString('Extra on Tuesda...') #2 F:\Websites\www\erik-edgren-blog\vendor\spatie\opening-hours\src\TimeRange.php(63): Spatie\OpeningHours\TimeRange::fromArray(Array) #3 F:\Websites\www\erik-edgren-blog\vendor\spatie\opening-hours\src\OpeningHoursForDay.php(35): Spatie\OpeningHours\TimeRange::fromDefinition(Array) #4 [internal function]: Spatie\OpeningHours\OpeningHoursForDay::Spatie\OpeningHours\{closur in F:\Websites\www\erik-edgren-blog\vendor\spatie\opening-hours\src\Exceptions\InvalidTimeRangeString.php on line 9

	$openingHours = Spatie\OpeningHours\OpeningHours::create([
		'monday' => [
			'data' => 'Typical Monday',
			'09:00-12:00',
			'13:00-18:00',
		],
		'tuesday' => [
			'09:00-12:00',
			'13:00-18:00',
			[
				'19:00-21:00',
				'data' => 'Extra on Tuesday evening',
			],
		],
		'exceptions' => [
			'2016-12-25' => [
				'data' => 'Closed for Christmas',
			],
		],
	]);

Please fix!

Quick Check?

I already love this package - thank you for publishing it. I've a project that can use it launching this week. This is all 100% working for me and I appreciate you probably don't have time to check over other people's code, but I wanted to quickly check with you if I'm using it right (ahead of you publishing the introduction blog post).

This is a small helper function I've written (laravel).

This shop only has one set of opening hours per day - hence the ugly $today[0]:

public static function renderNextTenDaysOfTradingHoursHTML(){

        $openingHours = OpeningHours::create(config('site.tradingHours'));

        return collect()->range(0,10)->transform(function($day) use ($openingHours) {

            $today = $openingHours->forDate(Carbon::now()->addDay($day));

            if (count($today)) { return $today[0]->start()." - ".$today[0]->end(); }

            return "Closed";

        })->implode('<br>');

    }

I was little unsure how to check for the store being open/closed on a specific date using the forDate objects so I just ended up using a count().

createAndMergeOverlappingRanges() should support 00:00-24:00 in exceptions

I was trying to pass a exception for 24 hour open
Following examples also apply to daily schedule

Using createAndMergeOverlappingRanges the following examples do not work

$opening_hours_data['exceptions']['2019-06-11']['hours'][] = '00:00-24:00';
$opening_hours_data['exceptions']['2019-06-11']['hours'][] = '00:00-00:01';
$opening_hours_data['exceptions']['2019-06-11']['hours'][] = '00:01-00:00';

The following works but has a minute gap

$opening_hours_data['exceptions']['2019-06-11']['hours'][] = '00:00-23:59';
...
$opening_hours->isOpenAt(new \DateTime('2019-06-11 23:58:10')) is TRUE
$opening_hours->isOpenAt(new \DateTime('2019-06-11 23:59:10')) is FALSE

Is there a way to add a rule for 24 hours open without the minute gap?
I expected the '00:00-00:01' & '00:01-00:00' rule to work but it discards the second rule

Thanks in advance

overflow does not behave as expected

I create opening hours for MONDAY and TUESDAY that are identical.

$open_with_overflow and $open_without_overflow

I create $the_time that is set to Tuesday the 4th of June 2019.

//Tuesday 4th of June 2019, 11.35 am
$the_time = new DateTime('2019-06-04 11:35:00');

$open_with_overflow = OpeningHours::create([
    'overflow' => true,
    'monday'    => ['11:00-18:00'],
    'tuesday'    => ['13:37-15:37'],
]
);

$open_without_overflow = OpeningHours::create([
    'overflow' => false,
    'monday'    => ['11:00-18:00'],
    'tuesday'    => ['13:37-15:37'],
   
]
);

var_dump(
    $open_with_overflow->isOpenAt($the_time),
    $open_without_overflow->isOpenAt($the_time)
);

These two isOpen calls should both result in the same anser, but the output from the above is:

bool(true)
bool(false)

If i change the closing hours on monday to 11.34 they both return false.

It seems like the overflow automatically assumes that the previous days closing time applies to the current date.

This should only be true if the previous days closing time happens BEFORE the current days closing time.

What is the definition for non-stop?

I work in an environment where I have to take into account that the period between 23:59:01-23:59:59 is also valid

dd(
    OpeningHours::create(['tuesday' => ['00:00-23:59']])
        ->isOpenAt(
            Carbon::createFromFormat('Y-m-d H:i:s', '2016-10-25 23:59:01')
        )
    );

false

dd(
    OpeningHours::create(['tuesday' => ['00:00-23:59']])
        ->isOpenAt(
            Carbon::createFromFormat('Y-m-d H:i:s', '2016-10-25 23:58:59')
        )
    ); 

true

['tuesday' => ['00:00-00:00']] returns false everytime

TimeRange array definition must at least contains an "hours" property.

Hi Guys,
i'm getting the message error "TimeRange array definition must at least contains an "hours" property.". It happend when I tried to execute the create method.on array below.
$openingHours = OpeningHours::create($array_hours);

I realized that error i raised on "sunday" dayweek in the code snippet in vendor\spatie\opening-hours\src\Exceptions\InvalidTimeRangeArray.php at line 9

Rules
On sundays i don't have any opening hours, in other words, there is no work on that day. So think that the "hours" property value must be blank. Look below.
Is not this right way to represent this rule ?

array:8 [▼
"exceptions" => array:1 [▶]
"friday" => array:2 [▶]
"monday" => array:2 [▶]
"saturday" => array:3 [▶]
"sunday" => array:1 [▼
0 => array:2 [▼
"hours" => ""
"data" => ""
]
]
"thursday" => array:2 [▼
0 => array:2 [▼
"hours" => "09:00-12:00"
"data" => ""
]
1 => array:2 [▶]
]
"tuesday" => array:2 [▶]
"wednesday" => array:1 [▶]
]
Thanks any suggestion.

Replace DateTime by DateTimeInterface

Standalone code to reproduce the problem

<?php

use Spatie\OpeningHours\Time;

$time = Time::fromDateTime(date_create_immutable('2012-11-06 13:25:59.123456'));

Expected

13:25

Actual Result

PHP Fatal error:  Uncaught TypeError: Argument 1 passed to Spatie\OpeningHours\Time::fromDateTime() must be an instance of DateTime, instance of DateTimeImmutable given, called in xxx

nextOpen() and nextClose() don't return the correct values when called simultaneously

Hello,

The following test does not pass :

public function testItReturnsNextOpenAndNextClose()
{
    $mondayAfternoon = new \DateTime('2018-10-15 15:00'); // monday 15:00

    $openingHours = OpeningHours::create(['monday' => ['14:00-18:00'], 'tuesday' => ['09:00-12:00']]);

    $this->assertEquals(new \DateTime('2018-10-16 09:00'), $openingHours->nextOpen($mondayAfternoon));
    $this->assertEquals(new \DateTime('2018-10-15 18:00'), $openingHours->nextClose($mondayAfternoon));
}

DateTime $mondayAfternoon is modified when passed to nextOpen() and nextClose() methods. After the first assertion, $mondayAfternoon value is the same as nextOpen() return.

The problem persists even when I replace $mondayAfternoon param by a clone.

$mondayAfternoon is not modified anymore, but nextClose() will return a result based on a previous nextXXX() call.

The following test

public function testItReturnsNextCloseTwiceInARow()
{
    $mondayAfternoon = new \DateTime('2018-10-15 15:00'); // monday 15:00

    $openingHours = OpeningHours::create(['monday' => ['14:00-18:00'], 'tuesday' => ['09:00-12:00']]);

    $this->assertEquals(new \DateTime('2018-10-15 18:00'), $openingHours->nextClose(clone $mondayAfternoon)); // OK
    $this->assertEquals(new \DateTime('2018-10-15 18:00'), $openingHours->nextClose(clone $mondayAfternoon)); // NOT OK
}

fails with the following message

Failed asserting that two DateTime objects are equal.
--- Expected
+++ Actual
@@ @@
-2018-10-15T18:00:00.000000+0000
+2018-10-16T12:00:00.000000+0000

Finally I cannot pass a new DateTimeImmutable object to nextOpen() and nextClose() methods (even their signatures accept DateTimeInterface) because of the return type hint (DateTime) but it may be another ticket.

odd/even days

Good day!

  1. How to implement this package if shop work on odd/even days of month ?
    For example, shop opened on even days, but no matter what day it is (odd/even) the shop has Sunday off.
    It's regular schedule for people like barber, masseur. They work 1 day and have 1 day off.
  2. How to create this schedule and attach to shop to make queries ?
    This will create object, but how to attach it to my shop instance ?
    $openingHours = OpeningHours::create([
    'monday' => ['09:00-12:00', '13:00-18:00'],
    'tuesday' => ['09:00-12:00', '13:00-18:00'],
    'wednesday' => ['09:00-12:00'],
    'thursday' => ['09:00-12:00', '13:00-18:00'],
    'friday' => ['09:00-12:00', '13:00-20:00'],
    'saturday' => ['09:00-12:00', '13:00-16:00'],
    'sunday' => [],
    'exceptions' => [
    '2016-11-11' => ['09:00-12:00'],
    '2016-12-25' => [],
    ],
    ]);

nextClose() just like nextOpen()

This would be very clean and useful...

if($openingHours->isOpen()) {
  $closesAt = $openingHours->nextClose(new DateTime());
} else {
  $opensAt = $openingHours->nextOpen(new DateTime());
}

$openingHours->nextOpen(new DateTime()) is a great way to output the next time a business opens, but there is not a correlary nextClose() command!

It would be very useful to calculate how long a business is going to be open for until it closes.

Issue merging windows when the last window ends at 24:00

If I create a set of ranges with the last one ending at 23:59, it merges OK:

>>> $hours = OpeningHours::createAndMergeOverlappingRanges([
    'monday' => ['00:00-08:00', '08:00-18:00', '18:00-23:59']
  ])
=> Spatie\OpeningHours\OpeningHours {#3093}
>>> (string) $hours->forDay('monday')
=> "00:00-23:59"

But if the last range ends at 24:00, it somehow gets left out of the merge process.

>>> $hours = OpeningHours::createAndMergeOverlappingRanges([
    'monday' => ['00:00-08:00', '08:00-18:00', '18:00-24:00']
  ])
=> Spatie\OpeningHours\OpeningHours {#3081}
>>> (string) $hours->forDay('monday')
=> "00:00-18:00"

PR incoming momentarily.

time first open

would be nice to have a function to get the timestamp of first next open time.
$openingHours->nextOpen(new DateTime('2016-09-26 19:00:00')); // returns dateobject of 2016-09-27 09:00:99

Return a Period of days, based on a start DateTime

It would help a lot if this library would add a simple method to return a Period of n-days, based on a start DateTime.
For example to return the 14 days from monday this week.

Right now I'm using a little snippet to do this:

$openingTimes = OpeningHours::create([...]);
$first = new \DateTimeImmutable('monday this week');
$range = new \DatePeriod($first, new \DateInterval('P1D'), $first->add(new \DateInterval('P14D')));

foreach ($range as $date) {
   $openingDay = $openingTimes->forDate($date);
   $keyedIndex = Day::onDateTime($date);
   ...
}

Example Table Schema

Hi,

do you have any example table schema to implement opening hours based on your script?

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.