Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Clock] Fix sleep() in MockClock #47615

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

Merged
merged 1 commit into from
Oct 17, 2022
Merged

Conversation

upyx
Copy link
Contributor

@upyx upyx commented Sep 18, 2022

Q A
Branch? 6.2
Bug fix? yes
New feature? no
Deprecations? no
Tickets none
License MIT
Doc PR none

It fixes losing precision in $us = $seconds - $s that occurs because the $seconds is float.

Anyway, why so many calculation? Double precision is enough to represent any valid date and time:

    public function sleep(float|int $seconds): void
    {
        $datetime = '@'. ((float)$this->now->format('U.u') + $seconds);
        $timezone = $this->now->getTimezone();

        $this->now = (new \DateTimeImmutable($datetime))->setTimezone($timezone);
    }

Why is cloning needed for immutable? The beauty of immutables is that they don't need to be copied.

    public function now(): \DateTimeImmutable
    {
        return clone $this->now;
    }

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.
To answer your questions, float is not enough precision on 32bit CPU. And clone is used to not leak the identify of the internal object.

@carsonbot carsonbot changed the title Fix sleep() in MockClock [Clock] Fix sleep() in MockClock Sep 18, 2022
@upyx
Copy link
Contributor Author

upyx commented Sep 19, 2022

float is not enough precision on 32bit CPU

If it is true, some of my code leads to bugs. Could you guide me on what platforms PHP uses single precision? Maybe ARM32?

The https://github.com/php/php-src/blob/master/Zend/zend_float.h tries hard to force double precisioin on any known platform. It's hardly possible to compile modern PHP with single precision floats. Or have I missed something?

@upyx upyx force-pushed the fix-mock-clock branch 2 times, most recently from 4371ba8 to 0f73cd8 Compare September 19, 2022 05:20
@nicolas-grekas
Copy link
Member

zend_float.h tries hard to force double precision on any known platform

Oh, that's something I have missed, thanks for pointing at this! We can then simplify things a bit you're right. We just need to be sure to never use any intermediary int in the process and all should be good. Can you do it in this PR?

@upyx
Copy link
Contributor Author

upyx commented Sep 19, 2022

Can you do it in this PR?

OK

@upyx
Copy link
Contributor Author

upyx commented Sep 19, 2022

Well, the default "precision" ini setting is 14. So it's a bit tricky convertion:

$m = 2.0**53/1e6;
printf('format: %.6F, auto: '. $m, $m);
// format: 9007199254.740992, auto: 9007199254.741

The sprintf is slow, but it is the only way to bypass the "precision" setting.

And the maximum date without losing any presition will be "2112-09-17 23:53:47.370496":

php > var_dump(new DateTime(sprintf('@%.6F', 2.0**52/1e6)));
object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2112-09-17 23:53:47.370496"
  ["timezone_type"]=>
  int(1)
  ["timezone"]=>
  string(6) "+00:00"
}

It's possible to increase it up to "2255-06-05 23:47:34.740992" (plus 1 bit), but code will be less clear:

    public function sleep(float|int $seconds): void
    {
        $microseconds = (float) $this->now->format('Uu') + $seconds * 1e6;
        $datetime = substr_replace(sprintf('@%07.0F', $microseconds), '.', -6, 0);
        $timezone = $this->now->getTimezone();

        $this->now = (new \DateTimeImmutable($datetime, $timezone))->setTimezone($timezone);
    }

@upyx upyx force-pushed the fix-mock-clock branch 2 times, most recently from 5f9ce0f to 37f4f7d Compare September 19, 2022 08:34
@nicolas-grekas
Copy link
Member

nicolas-grekas commented Sep 19, 2022

Shouldn't we keep the current code then if it doesn't have these issues?

@upyx
Copy link
Contributor Author

upyx commented Sep 19, 2022

Shouldn't we keep the current code then if it doesn't have these issues?

🤷‍♂️

Dates after 2100 year don't look practical. Anyway, it's a mock for testing only.

@upyx upyx force-pushed the fix-mock-clock branch 2 times, most recently from f1a80b0 to 8a5a387 Compare September 19, 2022 09:03
@nicolas-grekas
Copy link
Member

Thank you @upyx.

@nicolas-grekas nicolas-grekas merged commit a0b82ee into symfony:6.2 Oct 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants