Complete set of PHP Date classes extensions:
class | extends | comment |
---|---|---|
Carbon | DateTime | |
CarbonImmutable | DateTimeImmutable | new |
CarbonInterface | DateTimeInterface | new |
CarbonTimeZone | DateTimeZone | new |
CarbonInterval | DateInterval | Read-only days property |
CarbonPeriod | DatePeriod | Blocked until php/php-src#3121 get merged |
Setter methods of Carbon become cloners in CarbonImmutable:
$m = new Carbon;
$c1 = $m->setDay(1);
// $m->day === $c1->day as $c1 === $m
$i = new CarbonImmutable;
$c2 = $i->setDay(1);
// $m->day !== $c2->day as $c2 is a new object
$c1->day = 9; // $m->day === 9
$c2->day = 9; // error, immutable
To reduce code amount, we could rather have a magic __call
method parsing methods matching actionUnit
with action in a given list (isSame
, isCurrent
, add
, sub
, get
, set
, floor
, ceil
, round
, etc.) and unit as valid unit (second
, minute
, hour
, day
, etc.) to route to generic method (action) with a unit argument.
This will enforce some methods consistency (easy way to handle singular/plural, same arguments for a given action). And a easier way to add actions. This would encourage to drop boolean arguments (which give multiple responsability to a single method) in favor of adding methods.
A automated script could update the PHPDoc comment to add @method
annotations so even if the methods does not really exist, IDE autocompletion will propose magic methods and we would not have to add them all manually.
Having a methods set compatible with https://momentjs.com could help to create a full-stack standard.
Critical methods should be final (macro methods, magic methods) so it would help us to prevent breaking changes in sub-classes.
Methods should be grouped in traits and each trait should remain stand-alone (could be taken alone).
Instead of extends the Carbon classes, users would be able to compose their own date classes picking traits.
Documentation could be interactive (see https://pug-demo.herokuapp.com and https://phug-lang.com/, it provides code examples the user can edit in live to see the PHP result).
Carbon become big, the single-page and one-level links menu will become a bit too limited, some search/expand/collapse tools would help to browse it.
Each method must be documented with minimal informations:
- history, example: added in 2.1.0, argument $foo added in 2.2.0, $foo can be an array since 2.2.1
- trait, example: set() belongs to Setter trait
- arguments, return and exceptions thrown
- code examples
To provide reliable solid library, Carbon should rely on a full microseconds support, that means at least PHP 7.1.8.
Carbon is widely used. We should not abandon v1 users.
- V1 documentation must remain available
- An issue templating and 1.x/2.x tagging will help to handle issues
- A migration guide could help user to upgrade
- Dropped methods could remain in the documentation (tagged as obsolete with some gray-styling and collapsed description)
Compatibility and bugs
After some diving in the depths of PHP I bring a good news regarding CarbonPeriod. Work has been done in php/php-src#3121 to allow modifying DatePeriod's properties. If it gets merged it'll be possible to rewrite CarbonPeriod so that it extends the DatePeriod!
I was thinking about fixing the daylight saving time transitions and other bugs directly in the source. Interestingly RFC regarding DST stuff was proposed and accepted (!) already in 2011. However it looks like nothing was done about it ever after. Discussions in internals here and here. On top of that, it looks like tests for the transitions have been added, but as of now they're marked as "expected to fail" - see http://gcov.php.net/viewer.php?version=PHP_7_2&func=expected_tests.
I bet it wouldn't be difficult to push the fixes through. Unfortunately the fixing itself seems to be beyond my possibilities, both in terms of skills and free time. However best-case scenario here could still be 7.3 release. I suppose we may settle on patching what we can in Carbon, at least we can act faster. Is there anything we can do about DST transitions? Would some variant of the hack used in CarbonPeriod work in Carbon?
Generic methods
I agree that standardization of methods should be done. However I'm afraid that proxying everything through
__call
will quickly get complicated, particularly as new methods are added. We can automate generating class docblocks to provide auto-completion in IDEs, but we still make Carbon less transparent for someone lurking the code. Also code coverage would become less informative.As an alternative we could generate a trait with all the repetitive methods based on a template. Drawback here is that contributors could feel tempted to modify or add methods to that auto-generated file.
Last option would be to leave the definitions as they currently are, just do some manual standardization. Would that be so bad after all? These methods don't change very often. Once written will probably stay just the same ever after. Cluttering the code shouldn't be a problem as now the methods can be moved into a dedicated trait.
Traits
In my opinion splitting into traits just for better organization of the codebase. I don't think composing your own Carbon is any useful for normal users, so it's not something I'd encourage or mention in the docs.
Stricter extends
I don't like the idea of making methods final/private. Too many times I was coming up with some weird workarounds to make a simple change, because making it directly wasn't possible. Is it because of backwards compatibility? I think we can adhere to the convention of guarantying only the public methods
On the other hand I don't have much experience with maintaining such projects so I may be missing something.
Immutability
Browsing https://github.com/cakephp/chronos made me realize that immutability comes with it's own set of PHP quirks. Splitting into two variants (Carbon and CarbonImmutable) will also increase number of issues. Just something we should be aware of.
Translations
Translated formats like in https://github.com/jenssegers/date would be a great addition to the
toString
anddiff
translations. Not sure how useful is the reverse translation forcreateFromFormat
method, but that's probably something that could also be implemented.I think it'd be nice if by example of Chronos we could get rid of dependencies. I realize that it would require writing and maintaining our own Translator. I didn't investigate how much effort that would require, thus I'll just leave it as that.
Moment.js compatibility
It'd be great if we could achieve compliance with moment not only in terms of API, but also formatting - so that Carbon's
diffForHumans
and moment'sfromNow
would produce the same strings for the same ranges and both could be used interchangeably.