# RFC: More Appropriate Date/Time Exceptions

- Status: Implemented (PHP 8.3)
- Authors: Derick Rethans derick@php.net
- Date: 2022-12-15 (First created: 2022-11-29)
- Version: 0.9.1
- Original: https://wiki.php.net/rfc/datetime-exceptions
- Page: https://externals.io/rfc/datetime-exceptions
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

## Votes

### More Appropriate Date/Time Exceptions (closed)

Yes: 25 · No: 1

Voters: alcaeus (Yes), asgrim (Yes), ashnazg (Yes), cmb (Yes), crell (Yes), derick (Yes), galvao (Yes), girgias (Yes), heiglandreas (Yes), ilutov (Yes), kalle (Yes), mauricio (Yes), mbeccati (Yes), nicolasgrekas (Yes), ocramius (Yes), reywob (Yes), sebastian (Yes), sergey (Yes), svpernova09 (Yes), thekid (Yes), theodorejb (No), timwolla (Yes), trowski (Yes), tstarling (Yes), villfa (Yes), wjx (Yes)

## Discussions on the mailing list

- [\[RFC\] More Appropriate Date/Time Exceptions](https://externals.io/message/119050.md) — started on 2022-11-29, 10 messages
- [\[Vote\] More Appropriate Date/Time Exceptions](https://externals.io/message/119147.md) — started on 2022-12-15, 7 messages
- [\[RFC\] Voting Result for More Appropriate Date/Time Exceptions](https://externals.io/message/119230.md) — started on 2023-01-05, 1 messages
- [Follow up of DateTimeException RFC](https://externals.io/message/123592.md) — started on 2024-06-13, 1 messages

---

## Introduction

This RFC proposes to introduce Date/Time extension specific exceptions and errors where this makes sense.

Right now, they are either warnings/errors, or plain “Exception” or “Error”. This does not allow for catching Date/Time exceptions as they are not specific enough.

Out of scope of this RFC is changing and improving warning, Error, and Exception messages.

## Proposal

The proposal is to reclassify warnings and errors as Exceptions, and introduce Date extension specific exceptions and errors.

The rationale behind all of these is the following:

- Common errors (such as modifying readonly properties and invalid serialisation data) will throw an `Error`, as that is what PHP does for user land code that makes these errors.
- Programming errors that do not come forth from user input, or free form text, become a `ValueError`.
- Corrupted data (such as an invalid Timezone Database) becomes a `DateError`.
- Uninitialised objects (due to a child not calling `parent::construct`) becomes a `DateObjectError`
- Issues that come to light due to user input, or free form text arguments that need to be parsed, become specific `Exceptions` under the `DateException` hierarchy: 
    - Errors with timezone creation cause an `DateInvalidTimeZoneException`.
    - Errors that come forth out of operations that can not be executed (such as using a non-special relative time specification with `DateTime::sub()`) become a `DateInvalidOperationException`
    - Errors with parsing the free-form text to create a `DateTime` and/or `DateTimeImmutable` object become a `DateMalformedStringException`.
    - Errors with parsing the free-form text to create a `DateInterval` object become a `DateMalformedIntervalStringException`.
    - Errors with parsing the free-form text to create a `DatePeriod` object become a `DateMalformedPeriodStringException`.

The hierarchy will look like:

- Error 
    - `“Cannot modify readonly property DatePeriod::$%s”`
    - `“Invalid serialization data for DateTime object”` (used with `__set_state` and `__wakeup`)
    - `“Invalid serialization data for DateTimeImmutable object”` (used with `__set_state` and `__wakeup`)
    - `“Invalid serialization data for DateTimeZone object”` (used with `__set_state` and `__wakeup`)
    - `“Invalid serialization data for DatePeriod object”` (used with `__set_state` and `__wakeup`)
    - `“Unknown or bad format (%s) at position %d (%c) while unserializing: %s”` (currently a warning)
    - `“Trying to compare uninitialized DateTimeZone objects”`
    - `“An iterator cannot be used with foreach by reference”`
    - ValueError (all, already like this) 
        - `“must be a two-letter ISO 3166-1 compatible country code when argument #1 ($timezoneGroup) is DateTimeZone::PER_COUNTRY”`
        - `“must be one of SUNFUNCS_RET_TIMESTAMP, SUNFUNCS_RET_STRING, or SUNFUNCS_RET_DOUBLE”`
    - TypeError 
        - `“DatePeriod::construct() accepts (DateTimeInterface, DateInterval, int [, int]), or (DateTimeInterface, DateInterval, DateTime [, int]), or (string [, int]) as arguments”` (already like this)
    - DateError 
        - `“Timezone database is corrupt. Please file a bug report as this should never happen”`
        - `“Timezone initialization failed”`
        - DateObjectError 
            - `“The ” #class_name “ object has not been correctly initialized by its constructor”` (DATE\_CHECK\_INITIALIZED)
            - `“DatePeriod has not been initialized correctly”`
            - `“Trying to compare uninitialized DateTimeZone objects”`
            - `“The DateTime object has not been correctly initialized by its constructor”`
        - DateRangeError (is currently a ValueError) 
            - `“Epoch doesn't fit in a PHP integer”`
- Exception 
    - DateException 
        - DateInvalidTimeZoneException (these are not easy to just change/add) 
            - `“Timezone must not contain null bytes”`
            - `“Timezone offset is out of range (%s)”`
            - `“Unknown or bad timezone (%s)”`
        - DateInvalidOperationException 
            - `“Only non-special relative time specifications are supported for subtraction”` (currently a WARNING for both procedural and OO variants)
        - DateMalformedStringException 
            - `Failed to parse time string (%s) at position %d (%c): %s“`
        - DateMalformedIntervalStringException 
            - `“Unknown or bad format (%s)”` (date\_interval\_initialize)
            - `“Failed to parse interval (%s)”` (date\_interval\_initialize)
            - `“Unknown or bad format (%s) at position %d (%c): %s”` (date\_interval\_create\_from\_date\_string/OO variant; now: either a warning or exception)
            - `“String '%s' contains non-relative elements”` (date\_interval\_create\_from\_date\_string/OO variant; now: either a warning or exception)
        - DateMalformedPeriodStringException (all DatePeriod's constructor) 
            - `“Unknown or bad format (%s)” (date_period_initialize)`
            - `”%s(): ISO interval must contain a start date, \“%s\” given“`
            - `”%s(): ISO interval must contain an interval, \“%s\” given“`
            - `”%s(): ISO interval must contain an end date or a recurrence count, \“%s\” given“`
            - `”%s(): Recurrence count must be greater than 0“`

Procedural style use of date/time functions is not affected, and will continue to use warnings and errors as it currently does.

## Backward Incompatible Changes

1. The `“Epoch doesn't fit in a PHP integer”` now returns a new `DateRangeError` instead of a generic `ValueError`, which it does not subclass. This is only an issue for 32-bit platforms.
2. The `“Only non-special relative time specifications are supported for subtraction”` warning with `DateTime::sub()` and `date_sub()` becomes a new `DateInvalidOperationException`. Leaving this with a warning and a NULL return is not useful behaviour.
3. The `“Unknown or bad format (%s) at position %d (%c): %s”` and `“String '%s' contains non-relative elements”` warnings that are created while parsing wrong/broken `DateInterval` strings will now throw a new `DateMalformedIntervalStringException` when used with the OO interface, instead of showing a warning and returning false.

## Proposed PHP Version(s)

Next PHP 8.x.

## RFC Impact

No impact to any other extensions or opcode.

## Open Issues

There are currently no open issues.

## Unaffected PHP Functionality

Warnings and errors that are currently generated by the procedural versions of the Date/Time functionality are not impacted. Only the Object Orientated interface is.

## Voting

To accept this RFC, and get more appropriate exceptions:

**Vote: More Appropriate Date/Time Exceptions** — Yes: 25 · No: 1 (closed)

Vote started December 15th, and runs until December 31st, 24:00 UTC.

## Patches and Tests

There is no patch yet.

## Implementation

\- Version: PHP 8.3 - Commit: <https://github.com/php/php-src/commit/66a1a911f1d6cd4b89c0bb5577fa77f1d6a2cb96>

## Rejected Features

None yet.

## ChangeLog

0.9.1

- Clarified that changing/improving messages is out of scope
- Clarified that the “Invalid serialization data for \* object” are used for both PHP's unserialize, as well as `__set_state`.
