1

Faced the following error in a function that returns the current time with microseconds:

Fatal error: Call to a member function setTimeZone() on boolean

The code this function is:

public static function getCurrentTime() {
    $microtime = microtime(true);
    $time = DateTime::createFromFormat('U.u', (int) $microtime != $microtime ? $microtime : $microtime + 0.001);
    $time->setTimeZone(new DateTimeZone(date_default_timezone_get()));
    return $time->format('Y-m-d H:i:s.u');
}

This error occurs with a small proportion of users (<1%). I know that setTimeZone() can return false if a problem occurs, but how can I avoid this problem in my code? Why does setTimeZone() return false?

I am using PHP Version 7.0.9.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Vergiliy
  • 1,248
  • 1
  • 13
  • 22
  • 2
    Possible duplicate of [Reference - What does this error mean in PHP?](https://stackoverflow.com/questions/12769982/reference-what-does-this-error-mean-in-php) – Mike Doe Dec 12 '18 at 08:20
  • @emix. Not really, I understand the error, but I can not find a solution for my code – Vergiliy Dec 12 '18 at 08:23
  • It's not `setTimeZone()` returning false you need to be wary of here, it's `DateTime::createFromFormat()` - [the docs](http://php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-returnvalues) say "Returns a new DateTime instance or FALSE on failure." under return values, but unfortunately I can't see anything that illustrates what would cause it to fail. – Scoots Dec 12 '18 at 08:23
  • Not sure if you understand the error. The error happens because you invoke a method upon a null value. This means the `$time` object (`new DateTime`) was not created. If you read the [DateTime's](http://php.net/manual/en/datetime.construct.php#refsect1-datetime.construct-returnvalues) documentation you will find out why. Please always refer to the documentation and read what PHP throws at you. – Mike Doe Dec 12 '18 at 08:24
  • @Scoots. Yes, I thought about that too. Then the only possible reason is `date_default_timezone_get`, but judging by manual, it should not fai: http://php.net/manual/ru/function.date-default-timezone-get.php – Vergiliy Dec 12 '18 at 08:26
  • You are wrong. The error is in the `$time = DateTime::createFromFormat('U.u', (int) $microtime != $microtime ? $microtime : $microtime + 0.001);` line. – Mike Doe Dec 12 '18 at 08:27
  • As Emix says, this is not the case. [The error you are seeing is a generic error PHP returns when you try to treat a non-object like an object](https://tio.run/##K8go@P/fxr4go4CLSyVRwVYhLTGnONUayNa1y62Mz00tychP0dC0/v8fAA). – Scoots Dec 12 '18 at 08:29
  • Thank you. Understood, it means the problem in the line with the initialization of $time – Vergiliy Dec 12 '18 at 08:41
  • I've rolled-back your edit adding an answer in your question. I'm glad you found a solution to your problem. However, an actual answer/solution should **not** be edited into your Question. In general, you should [edit] the Question to *clarify the Question*, but not to include an Answer within the Question. You should create your own Answer with the code you used to solve your problem, then accept it (the system may require a 48 hour delay prior to accepting your own answer). When you've solved the problem yourself, [answering your own question is encouraged](/help/self-answer). – Dharman Jan 23 '20 at 12:21

3 Answers3

3

The problem turned out to be that the microtime(true) function sometimes returns a number without a dot and the U.u format does not apply to it. As a result, two solutions were found to solve the problem:

Solution 1: Replace (int) $microtime != $microtime with stripos($microtime, '.')

Solution 2: Wrap the code in a try-catch construct, like this:

try {
    getCurrentTime();
} catch (Exception $e) {
    // Retry a maximum of 3 times
}

Posted on behalf of OP

Dharman
  • 30,962
  • 25
  • 85
  • 135
1

The casting to (int) on your ternary statement might be the culprit.

What you are effectively doing is:

$microtime_int != $microtime_float

Which will almost always give you a false. But when it returns TRUE, when you hit the magic sweet spot of $microtime having zeroes after the decimal point, your format U.u will not work.

Since you must have microseconds, then I will suggest catching an error and retrying until you are fine.

This pseudocode should fix your issue:

try{
    getCurrentTime();
}catch(e Exception){
    retry max 3 times
}

Please see the proof:

Ouput:

1544603355.9997
1544603355.9998
1544603355.9998
1544603355.9999
1544603355.9999
1544603355.9999
1544603356
PHP Fatal error:  Uncaught Error: Call to a member function setTimeZone() on boolean in a.php:6
Stack trace:
#0 {main}
  thrown in a.php on line 6

script:

<?php
for ($i = 0; $i <= 999999; $i++){
$microtime = microtime(true);                                                                                                                      
echo $microtime . "\n";
$time = DateTime::createFromFormat('U.u', (int) $microtime != $microtime ? $microtime : $microtime + 0.001);
$time->setTimeZone(new DateTimeZone(date_default_timezone_get()));
}
Unamata Sanatarai
  • 6,475
  • 3
  • 29
  • 51
  • The problem is that I need microseconds. And this code `(int) $microtime != $microtime ? $microtime : $microtime + 0.001` allows me to avoid problems when the current time is a multiple of a second - then the format does not work correctly, becouse 1234567 != 1234567.00 (U != U.u) – Vergiliy Dec 12 '18 at 08:46
  • FairEnough. However, as I've shown above, the problem exists when there's no `.DDDD` – Unamata Sanatarai Dec 12 '18 at 08:48
  • With the help of your code, I catch an error, but I can’t understand exactly how to avoid it and at the same time save microseconds. The code `$microtime = (int) microtime(true);` robs me of microseconds. – Vergiliy Dec 12 '18 at 08:53
  • Thanks for your help. The solution, of course, is not very elegant, but it works. Tried to work with types - no effect. The only solution I found was to replace `(int) $microtime != $microtime` with `stripos($microtime, '.')`. Also not a great version, but it looks more understandable. – Vergiliy Dec 12 '18 at 09:35
0

Above fixes, still in very small amount of cases, give me an error. So I fixed it, in rather very creapy way, but if it works, it works:

$microtime = number_format(microtime(true),4,'.','');
zomers
  • 31
  • 1
  • 7