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

DateTime: createFromFormat() returns Nette\Utils\DateTime instance #6

Merged
merged 1 commit into from
Apr 29, 2014

Conversation

redhead
Copy link
Contributor

@redhead redhead commented Apr 10, 2014

Static factory method createFromFormat() now returns Nette\Utils\DateTime instance instead of PHP's \DateTime.

This fixes somewhat unexpected behaviour when calling factory method on a Nette\Utils\DateTime class and receiving an instance of different class.

Closes nette/nette#1258.

Note: I used call_user_func_array() because the third parameter is optional and cannot be NULL for the parent method. I didn't want to use an if condition just for this (but if it is found ugly I will update it).

if (func_num_args() === 3 && !$timezone instanceof \DateTimeZone) {
throw new Nette\InvalidArgumentException('Nette\Utils\DateTime::createFromFormat() expects parameter 3 to be DateTimeZone, ' . gettype($timezone) . ' given');
}
$datetime = call_user_func_array('parent::createFromFormat', func_get_args());
Copy link
Contributor

Choose a reason for hiding this comment

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

this cannot be done without call_user_func_array ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It can, see the note in the PR description, I will change it to if then, I suppose.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, it doesn't work when you pass NULL in variable? It has to be called without the third parameter?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Exactly. The 3rd parameter has to be a DateTimeZone instance or it must not be passed at all. (I hate PHP so much in these regards)

The other variant has a condition if ($timezone !== NULL) then call parent method with 3 params, otherwise call with it with 2 (both filling $datetime variable, the last line remains the same).

Which is better?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this one is fine. But it didn't work in some versions of PHP (I guess the old ones). But travis passes, so fuck it :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Very well.

If you mean the "parent::foo", I searched for similar uses in Nette, and it is used e.g. in Nette\Database, so I figured it shouldn't be a problem.

Choose a reason for hiding this comment

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

Just use 3v4l.org if you are not sure which version does what.

@milo
Copy link
Member

milo commented Apr 10, 2014

👍 I hit this problem too. I personally prefer the 3rd argument can be NULL so:

$datetime = $zone === NULL
    ? parent::createFromFormat($format, $time)
    : parent::createFromFormat($format, $time, $zone);

return static::from($datetime);

What others?

And I think there can be annotations there it has meening.

@redhead
Copy link
Contributor Author

redhead commented Apr 10, 2014

@milo Me too. It is pretty annoying how PHP handles this, but I didn't want to change the behaviour of the parent method. So, they act the same just the new one returns the correct instance.

What annotations do you mean?

@fprochazka
Copy link
Contributor

Just a thought: you could add the timezone created using date_default_timezone_get() if it's null

@xificurk
Copy link
Contributor

@fprochazka Or even better: allow passing string as timezone and automatically create the DateTimeZone object.

@redhead
Copy link
Contributor Author

redhead commented Apr 10, 2014

@fprochazka I thought of that too.

@xificurk It changes the parent's default behaviour a lot but I like it.

@@ -86,4 +86,14 @@ public function getTimestamp()
return is_float($tmp = $ts * 1) ? $ts : $tmp;
}


public static function createFromFormat($format, $time, $timezone = NULL)
Copy link
Contributor

Choose a reason for hiding this comment

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

3rd parameter should be typehinted as DateTimeZone, shouldn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would be nice, but I cannot change the signature - it has to be compatible with the parent method.

@redhead
Copy link
Contributor Author

redhead commented Apr 11, 2014

What about this? I find it pretty good now, including the phpdocs.


/**
* Returns new DateTime object formatted according to the specified format.
* @param string The format of the passed in $time parameter should be in
Copy link
Contributor

Choose a reason for hiding this comment

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

The format of the passed in $time parameter should be in

@milo
Copy link
Member

milo commented Apr 11, 2014

What would you expect from these calls?

createFromFormat('Y-m-d H:i:s', '2014-04-11 12:00:00', 'Pacific/Fiji')
createFromFormat('Y-m-d H:i:sP', '2014-04-11 12:00:00+02:00', 'Pacific/Fiji')
createFromFormat('Y-m-d H:i:sP', '2014-04-11 12:00:00+02:00')
createFromFormat('U', '1397240808')
createFromFormat('U', '1397240808', 'Europe/Prague')

@redhead
Copy link
Contributor Author

redhead commented Apr 11, 2014

I think it should do the same as what the docs say about it:
http://www.php.net/manual/en/datetime.createfromformat.php (see $timezone param description and note below)

@rostenkowski
Copy link
Contributor

If timezone is omitted and time contains no timezone, the current timezone will be used.

@redhead Good point

@redhead
Copy link
Contributor Author

redhead commented Apr 27, 2014

Is it good to go? (as it is a school assignment I would like it to be merged by the end of the semester.. :-/ if possible )

@dg
Copy link
Member

dg commented Apr 27, 2014

I am not sure that changing method signature (3rd parameter) is good idea. In fact, I am suprised that PHP allowes this.

@redhead
Copy link
Contributor Author

redhead commented Apr 27, 2014

There is no type hint, so there is no change in the signature. It is just possible to pass DateTimeZone as well as a string (which is then made into a DateTimeZone instance).

Would you like it to be strictly compatible with the parent method (in behaviour)?

@dg
Copy link
Member

dg commented Apr 27, 2014

According to documentation of DateTime, 3rd argument has type hint, so it is change. Or isn't?

@milo
Copy link
Member

milo commented Apr 27, 2014

@dg It is somehow wrong PHP implementation/documentation. bug 60302, bug 55407

When you add in descendant

public static function createFromFormat($format, $time, \DateTimeZone $tz = NULL)

PHP Strict Standards:  Declaration of Milo\Dt::createFromFormat() should be compatible
with DateTime::createFromFormat($format, $time, $object = NULL)

And when you call

\DateTime::createFromFormat('Y-m-d', '2014-04-27', NULL);

PHP Warning:  DateTime::createFromFormat() expects parameter 3 to be DateTimeZone, null given

Imho It is buggy as much it can be.

If we extends createFromFormat() we are risking future fixed PHP incompatibiliy. I don't know what to think about it. Maybe add 2nd argument to Nette\Datetime::from($time, $format = NULL)?

@redhead
Copy link
Contributor Author

redhead commented Apr 28, 2014

Yes, it's quite broken. Adding parameter $format to method from() could be a solution, but it's not that nice since you can pass \DateTime or int value in parameter $time where it doesn't make much sense. A special method for formatted date string is better imho.

@mishak87
Copy link

So far I feel like these problems can be solved using proxy pattern. (Mask returned objects with proxy and include per PHP version logic that preserves expected behaviour of createformat.)

@milo Nette\Datetime is already not future compatible, definitely not binary compatible and breaking single responsibility rule (it also serves as and unix timestamp enum). This is just mine opinion, no need to discuss it. That bridge will be crossed when PHP gets there (5.7+).

I just hope that any solution won't become like Nette\Callback. (replacing native functionality, and being removed in next version)

$timezone = new \DateTimeZone($timezone);
}

if (!$timezone instanceof \DateTimeZone) {
Copy link
Member

Choose a reason for hiding this comment

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

Here should be elseif

@redhead
Copy link
Contributor Author

redhead commented Apr 28, 2014

Updated according to David's notes.

@dg
Copy link
Member

dg commented Apr 28, 2014

Can you squash it to simple commit?

@redhead
Copy link
Contributor Author

redhead commented Apr 28, 2014

Done.

dg added a commit that referenced this pull request Apr 29, 2014
DateTime: createFromFormat() returns Nette\Utils\DateTime instance
@dg dg merged commit 3c9cf0d into nette:master Apr 29, 2014
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

Successfully merging this pull request may close these issues.

\Nette\DateTime: inherited createFromFormat returns \DateTime
8 participants