PHPackages                             stubbles/date - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. stubbles/date

ActiveLibrary

stubbles/date
=============

Simplified handling of dates and datespans.

v10.0.0(5mo ago)25.6k[2 PRs](https://github.com/stubbles/stubbles-date/pulls)2BSD-3-ClausePHPPHP ^8.3CI passing

Since Jul 31Pushed 4w ago2 watchersCompare

[ Source](https://github.com/stubbles/stubbles-date)[ Packagist](https://packagist.org/packages/stubbles/date)[ Docs](http://stubbles.net/)[ RSS](/packages/stubbles-date/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (10)Dependencies (3)Versions (18)Used By (2)

stubbles/date
=============

[](#stubblesdate)

Handling dates and date spans in a immutable way, using a nice API.

Build status
------------

[](#build-status)

[![Tests](https://github.com/stubbles/stubbles-date/workflows/Tests/badge.svg)](https://github.com/stubbles/stubbles-date/workflows/Tests/badge.svg)

[![Latest Stable Version](https://camo.githubusercontent.com/9a011e25995a146930fa8806be92711aec98c937f1b8bddafce1a3737e649442/68747470733a2f2f706f7365722e707567782e6f72672f73747562626c65732f646174652f76657273696f6e2e706e67)](https://packagist.org/packages/stubbles/date) [![Latest Unstable Version](https://camo.githubusercontent.com/cfddbc14f1addf97f62aea49776ee08480c171af5ca812b87ce7173d866f8e27/68747470733a2f2f706f7365722e707567782e6f72672f73747562626c65732f646174652f762f756e737461626c652e706e67)](//packagist.org/packages/stubbles/date)

Installation
------------

[](#installation)

*stubbles/date* is distributed as [Composer](https://getcomposer.org/)package. To install it as a dependency of your package use the following command:

```
composer require "stubbles/date": "^10.0"

```

Requirements
------------

[](#requirements)

*stubbles/date* requires at least PHP 8.3.

Introduction
------------

[](#introduction)

The origin of the classes in this library are from the days when PHP's object- oriented date/time handling classes were not immutable. In the meantime immutable versions of them have been added to PHP, but we like the API of the classes here more, as they lead to a better grammar in the code in which they are used.

The underlaying implementation makes use of PHP's object-oriented date/time handling classes, but abstracts them such that the *stubbles/date* instances are immutable. Every change to a date will result in a new instance. Although the date class exposes the underlaying handle, the user will only receive a clone of that, so modifying such a handle will not mutate the date instance from which the handle was obtained.

`stubbles\date\Date`
--------------------

[](#stubblesdatedate)

Please note that this class not only covers a date, but an exact point in time. Each instance also has a time part. If you are interested in a concrete day only please use `stubbles\date\span\Day` (see below).

### Create a new instance

[](#create-a-new-instance)

When creating a new date instance there are different ways to set the actual date:

- integer, interpreted as timestamp: `new Date(1187872547)`
- string, parsed into a date: `new Date('2007-01-01 01:00:00 Europe/Berlin')`
- `\DateTime` object, will be used as is: `new Date(new \DateTime())`
- no argument,creates a date representing the current time: `new Date()`, equivalent to `Date::now()`

The second argument to the constructor is an optional timezone. Timezone assignment works through these rules:

- If the time is given as string and contains a parseable timezone identifier that one is used.
- If no timezone could be determined, the timezone given by the second constructor parameter is used.
- If no timezone has been given as second parameter, the default timezone of the system is used.

### Liberal type hinting

[](#liberal-type-hinting)

If you have a method which accepts an instance of `stubbles\date\Date` you can choose to be liberal in what you accept. Instead of type hinting against the concrete type your method can cast the provided value into an instance:

```
function doSomething(int|string|\stubbles\date\Date $date)
{
    $date = Date::castFrom($date);
    // now do something with $date, which is an instance of stubbles\date\Date
}
```

The `Date::castFrom();` accepts four different value types:

- integer, interpreted as unix timestamp: `Date:castFrom(1187872547)`
- string, parsed into a date: `Date:castFrom('2007-01-01 01:00:00 Europe/Berlin')`
- `\DateTime` object: `Date:castFrom(new \DateTime())`
- instances of `stubbles\date\Date` itself

An instance of `stubbles\date\Date` will always be returned as is, the other allowed values will result in the creation of a `stubbles\date\Date` instance. Passing any other value will result in a `\InvalidArgumentException`.

### Change a date

[](#change-a-date)

To change a date the `Date::change()` method can be called. This will return an instance of `stubbles\date\DateModifier` which provides several different methods to change the date and/or time. All changes will result in a new `stubbles\date\Date`instance, the instance on which `Date::change()` is originally called remains unchanged:

```
$currentDate = Date::now();
// create new date with current time but 48 hours ago, this will not change $currentDate
$newDate = $currentDate->change()->byHours(-48);
```

Here's a list of methods offered by `stubbles\date\DateModifier`:

- `to($target)`: change date by relative format accepted by [strtotime()](http://php.net/strtotime)
- `timeTo($time)`: keep date, i.e. day, month and year, but change time to given value, must be in format *HH:mm:ss*
- `createDateWithNewTime($hour, $minute, $second)`: same as above, but with separate parameters for all values
- `timeToStartOfDay()`: alias for `timeTo('00:00:00')`
- `timeToEndOfDay()`: alias for `timeTo('23:59:59')`
- `hourTo($hour)`; keep day, month, year, minutes and seconds, but change hour to given value
- `byHours($hours)`: add given amount of hours to current date and time. A negative value will subtract the hours
- `minuteTo($minute)`: keep day, month, year, hours and seconds, but change minutes to given value
- `byMinutes($minutes)`: add given amount of minutes to current date and time. A negative value will subtract the minutes.
- `secondTo($second)`: keep day, month, year, hours and minutes, but change seconds to given value
- `bySeconds($seconds)`: add given amount of seconds to current date and time. A negative value will subtract the seconds.
- `dateTo($date)`: change date but keep time, given date must be in format *YYYY-MM-DD*
- `yearTo($year)`: keep day, month, hours, minutes and seconds, but change year to given value
- `byYears($years)`: add given amount of years to current date and time. A negative value will subtract the years.
- `monthTo($month)`: keep day, year, hours, minutes and seconds, but change month to given value
- `byMonths($months)`: add given amount of months to current date and time. A negative value will subtract the months.
- `dayTo($day)`: keep month, year, hours, minutes and seconds, but change day to given value
- `byDays($days)`: add given amount of days to current date and time. A negative value will subtract the days.

### Comparing dates

[](#comparing-dates)

Instances of `stubbles\date\Date` can be compared with each other:

```
$date = Date::now();
if ($date->isBefore('2017-01-01 00:00:00')) {
    // execute when current date is before 2017
}
```

```
$date = Date::now();
if ($date->isAfter('2017-01-01 00:00:00')) {
    // execute when current date is after beginning of 2017
}
```

Comparison is done based on the unix timestamp.

Both `isBefore()` and `isAfter()` accept all values that are accepted by `Date::castFrom()`, see above.

### Date formatting

[](#date-formatting)

The date can be displayed as a string by formatting:

```
echo 'Current date and time in system timezone: ' . Date::now()->format('Y-m-d H:i:s') . PHP_EOL;
echo 'Current date and time in timezone Europe/Berlin: ' . Date::now()->format('Y-m-d H:i:s', new TimeZone('Europe/Berlin')) . PHP_EOL;
```

When an instances is casted to a string, the output format will be *Y-m-d H:i:sO*.

Date spans
----------

[](#date-spans)

Sometimes it is necessary to not cover a specific date only, but a span between two points in time. Most notably these are things like a single day, months, weeks or even a year. As it is impractical to always carry the starting and ending point of such a span, *stubbles/date* provides the `stubbles\date\span\Datespan`interface and various implementations.

### Default methods of each date span

[](#default-methods-of-each-date-span)

- `start()`: returns the exact starting point of the span
- `startsBefore($date)`: checks if the date span starts before the given point
- `startsAfter($date)`: checks if the date span starts after the given point
- `end()`: returns exact ending point of the span
- `endsBefore($date)`: checks if date span ends before the given point
- `endsAfter($date)`: checks if date span ends after the given point
- `formatStart($format, TimeZone $timeZone = null)`: format start point in given format
- `formatEnd($format, TimeZone $timeZone = null)`: format end point in given format
- `amountOfDays()`: returns amount of days that are covered by the date span
- `days()`: returns an iterator which allows to iterate over each single day within this date span
- `isInFuture()`: checks whether date span is completely in the future based on current date and time
- `containsDate($date)`: checks if given date and time are contained in the date span

All methods which have a `$date` parameter accept all values that are accepted by `Date::castFrom()`, see above.

### List of provided date span implementations

[](#list-of-provided-date-span-implementations)

#### `stubbles\dates\span\Day`

[](#stubblesdatesspanday)

Covers a whole day, starting at *00:00:00* and ending at *23:59:59*.

```
// create without argument always points to current day
$today = new Day();

// create with given date
$another = new Day('2016-06-27');

// create day from given stubbles\date\Date instance
$oneMore = new Day(new Date('2013-05-28'));

// creates a new instance representing tomorrow
$tomorrow = Day::tomorrow();

// creates a new instance representing yesterday
$yesterday = Day::yesterday();
```

Additional methods:

- `next()`: creates a new instance with the day after the represented day
- `before()`: create a new instance with the day before the represented day
- `isToday()`: checks if the day represents the current day

#### `stubbles\dates\span\Week`

[](#stubblesdatesspanweek)

Covers a whole week, starting on the given date at *00:00:00* and ending at seven days later at *23:59:59*.

```
// create a week starting today
$week1 = new Week(Date::now());

// create a week which starts tomorrow
$week2 = new Week('tomorrow');

// create a week which represents the 5th calender week of 2016
$week3 = Week::fromString('2016-W05')
```

Additional methods:

- `number()`: returns the week number

#### `stubbles\dates\span\Month`

[](#stubblesdatesspanmonth)

Covers a month, starting at the first of the month at *00:00:00* and ending at the last day of the month at *23:59:59*.

```
// creates instance representing the current month
$currentMonth = new Month();

// creates instance with current month but in the year 2014-05
$currentMonthIn2015 = new Month(2015);

// create instance representing June 2016
$exactMonth = new Month(2016, 6);

// create instance representing month given as string, format must be YYYY-MM
$otherMonth = Month::fromString('2016-07');

// creates instance representing the month before current month
$lastMonth = Month::last();

// creates instance for current month execpt when today is the first day of a
// month, the the instance represents the month before
// ideally suited when creating reports, as most often the report created on the
// first month of a day should be for the last month instead of for the current
// month
$reportingMonth = Month::currentOrLastWhenFirstDay()
```

Additional methods:

- `next()`: creates instance with month after the currently represented month
- `before()`: creates instance with month before the currently represented month
- `year()`: returns the year in which the month is
- `isCurrentMonth()`: checks whether month instance represents the current month

#### `stubbles\dates\span\Year`

[](#stubblesdatesspanyear)

Covers a year, starting at *January 01 00:00:00* and ending at *December 31 23:59:59*.

```
// create instance representing the current year
$currentYear = new Year();

// creates instance representing the year 2015
$year2015 = new Year(2015);
```

Additional methods:

- `months()`: returns an iterator which contains all instances of `stubbles\dates\span\Month`for the year
- `isLeapYear()`: checks whether year is a leap year
- `isCurrentYear()`: checks whether year represents the current year

#### `stubbles\dates\span\CustomDatespan`

[](#stubblesdatesspancustomdatespan)

Covers a custom date span starting at the given date at *00:00:00* and ending at the given date at 23:59:59.

```
// create a span from 2006-04-04 00:00:00 to 2006-04-20 23:59:59
$custom = new CustomDatespan('2006-04-04', '2006-04-20');
```

Constructor parameters accept all values that are accepted by `Date::castFrom()`, see above. Please note that the time for the start is always set to *00:00:00*and for the end is always set to *23:59:59*. It is not possible to change this to another time.

Integration with *bovigo/assert*
--------------------------------

[](#integration-with-bovigoassert)

In case you want to unit test your code and need to test for date equality you can use [*bovigo/assert*](https://github.com/mikey179/bovigo-assert)for the assertions. *stubbles/date* provides the predicate `stubbles\date\assert\equalsDate()`which can be used to check for equality of dates. It can take any argument that `stubbles\date\Date` accepts, and compares the unix timestamp with the actual value. In case they don't refer to the same point in time the error message will contain a diff with both dates in human readable form.

###  Health Score

57

—

FairBetter than 98% of packages

Maintenance84

Actively maintained with recent releases

Popularity25

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity87

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 53.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~318 days

Recently: every ~854 days

Total

14

Last Release

169d ago

Major Versions

v5.5.1 → v6.0.02016-06-27

v6.0.0 → v7.0.02016-07-23

v7.0.0 → v8.0.02019-10-30

v8.0.1 → v9.0.02023-12-31

v9.0.0 → v10.0.02025-11-30

PHP version history (6 changes)v4.0.0PHP &gt;=5.4.0

v6.0.0PHP &gt;=5.6.0

v7.0.0PHP ^7.0

v8.0.0PHP ^7.3

v9.0.0PHP ^8.2

v10.0.0PHP ^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/cde3f4e0a0e4454942288eb300c91644bd359cc3572d65b473e3484701fa4931?d=identicon)[mikey179](/maintainers/mikey179)

---

Top Contributors

[![mikey179](https://avatars.githubusercontent.com/u/190475?v=4)](https://github.com/mikey179 "mikey179 (48 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (41 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/stubbles-date/health.svg)

```
[![Health](https://phpackages.com/badges/stubbles-date/health.svg)](https://phpackages.com/packages/stubbles-date)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
