Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a ZonedDateTimeRange #45

Open
tigitz opened this issue Apr 1, 2022 · 10 comments
Open

Introduce a ZonedDateTimeRange #45

tigitz opened this issue Apr 1, 2022 · 10 comments

Comments

@tigitz
Copy link
Contributor

tigitz commented Apr 1, 2022

Hello,

Interval is a range of two instants and LocalDateRange is not Zoned nor include time.

However for cases where you need to schedule an event in the future (e.g. from 21/02/2022 10h00 Europe/Paris to 21/02/2022 12h00 Europe/Paris) you want to keep the timezone to prevent timezone shifts.

WDYT ?

@solodkiy
Copy link
Contributor

Should the end of this range be inclusive like in LoccalDateRange, or exclusive like in Interval?

@solodkiy
Copy link
Contributor

Related to #34

@tigitz
Copy link
Contributor Author

tigitz commented Apr 24, 2022

Should the end of this range be inclusive like in LoccalDateRange, or exclusive like in Interval?

I don't have a strong opinion about this. Ideally it would stay consistent with other range concepts in the lib. Maybe @BenMorel could explain its reasoning on why it's different in the first place.

@solodkiy
Copy link
Contributor

I think the reason is that concept of inclusive duration is quite complicated when units become small.

$a = ZonedDateTime::parse('2020-01-01T15:16:20');
$range = crateRange($a, $a);
$duration = $range->getDuration();
var_dump($duration);

How long $duration should be in this case?

@tigitz
Copy link
Contributor Author

tigitz commented Apr 24, 2022

I think the reason is that concept of inclusive duration is quite complicated when units become small.

$a = ZonedDateTime::parse('2020-01-01T15:16:20');
$range = crateRange($a, $a);
$duration = $range->getDuration();
var_dump($duration);

How long $duration should be in this case?

Any reason it shouldn't be 0 ?

@solodkiy
Copy link
Contributor

Any reason it shouldn't be 0 ?

Inclusion :)
For example same code but with LocalDateRange give you duration of one day.
In this case difference between inclusive and exclusive is in inclusive version 2020-01-01T15:16:20 is part of the range (and duration cannot be zero) and in exclusive 2020-01-01T15:16:20 is not in range (duration is zero).

@solodkiy
Copy link
Contributor

solodkiy commented Apr 25, 2022

After some thinking I consider that duration in case when start = end should be 1 nanosec.

$r = crateRange(
    '2020-01-01T00:00:00.000000000Z', // day min
    '2020-01-01T23:59:59.999999999Z' // day max
); 
$r->getDuration(); // = 86400.0 sec (86399.999999999 + 0.000000001)

But working in this type of scale is quite tricky when you put this dates in query like this: WHERE date >= :from AND date <= :to because of rounding in most databases starts after 6 sign.

Screen Shot 2022-04-25 at 09 59 46

Use exclusion type like Interval leads to different query WHERE date >= :from AND date < :to wich looks more safe in my opinion.

@tigitz
Copy link
Contributor Author

tigitz commented Apr 25, 2022

I don't understand your point sorry, ZonedDateTime::parse('2020-01-01T15:16:20') is not even parsable. There's no inclusiveness or exclusiveness to think about actually, you just subtract both lowest unit which are nanos in this case and you get a Duration in nanos.

Since ZonedDateTime are basically Instant it would just rely on the Interval class:

class ZonedDateTimeRange {
    private function __construct(private ZonedDateTime $start, private ZonedDateTime $end)
    {
        
    }

    public static function of(ZonedDateTime $start, ZonedDateTime $end)
    {
        return new self($start, $end);
    }

    public function getDuration(): Duration
    {
        return (new Interval($this->start->getInstant(), $this->end->getInstant()))->getDuration();
    }
}
$zd = ZonedDateTime::parse('2020-01-01T15:16:20+01:00');
$zd2 = ZonedDateTime::parse('2020-01-01T15:16:20+01:00');

$duration = ZonedDateTimeRange::of($zd , $zd2);
echo $duration->getDuration()->getTotalNanos(); // 0

@gnutix
Copy link
Contributor

gnutix commented Jul 3, 2022

We've developed a package on top of brick/date-time which includes a LocalDateTimeInterval object using an exclusive end. We use it quite extensively in our time-management project, and it works like a charm. We have not developed the Zoned* version, as we currently only deal with local dates/times. Maybe some implementation details can be of interest ?

Here's the latest version we have in our project, which has not yet been made available publicly (not yet had the time to update the public package...) : https://gist.github.com/gnutix/d730da3f5e3a9beedd62571418f62194 (class and tests, updated on 2024-03-23).

And here's the (outdated) version available in the public repository :
Class: https://github.com/gammadia/date-time-extra/blob/develop/src/LocalDateTimeInterval.php
Tests: https://github.com/gammadia/date-time-extra/blob/develop/tests/LocalDateTimeIntervalTest.php

Some methods worth having a look at (in the latest version) are contains, sees and intersects, which offer different ways of dealing with empty intervals (2022-07-03T15:50/2022-07-03T15:50). It also includes ALLEN-relationship methods and various helpers (like cast, containerOf, collapse, expand, ...).

@gnutix
Copy link
Contributor

gnutix commented Mar 23, 2024

For those interested, I've updated the gist with the latest version of our code. A proper package will come, someday...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants