Make DateTime::createFromFormat() return child class instead of parent
Asked Answered
S

4

11

I'm extending DateTime do add some useful methods and constants.

When using new to create a new object everything is fine but when using the static method createFromFormat it always returns the original DateTime object and of course none of the child methods are available.

I am using the following code to circumvent this issue. Is this the best approach?

namespace NoiseLabs\DateTime;

class DateTime extends \DateTime
{
    static public function createFromFormat($format, $time)
    {
        $ext_dt = new self();

        $ext_dt->setTimestamp(parent::createFromFormat($format, time)->getTimestamp());

        return $ext_dt;
    }
}
Simplehearted answered 27/3, 2011 at 15:28 Comment(1)
OK. Thanks for reviewing this, @lonesomeday.Simplehearted
A
14

This is the way to go. However, since what seems you want to do is to render the DateTime class extensible, I'd suggest you use static instead of self:

namespace NoiseLabs\DateTime;

class DateTime extends \DateTime
{
    static public function createFromFormat($format, $time)
    {
        $ext_dt = new static();
        $parent_dt = parent::createFromFormat($format, $time);

        if (!$parent_dt) {
            return false;
        }

        $ext_dt->setTimestamp($parent_dt->getTimestamp());
        return $ext_dt;
    }
}

It's not necessary if you don't plan on extending the class, but if someone ever does, it will prevent him from having to do the same workaround again.

Alike answered 27/3, 2011 at 16:32 Comment(1)
True, this is good practice, thanks for bringing static into the game. I was hoping to mimic createFromFormat and replace self with static but since DateTime is a C implementation I guess there is nothing else I can do, right?Simplehearted
M
1

I think your solution is fine. An alternative way (just refactored a bit) is this:

public static function fromDateTime(DateTime $foo)
{
  return new static($foo->format('Y-m-d H:i:s e')); 
}

public static function createFromFormat($f, $t, $tz)
{
  return static::fromDateTime(parent::createFromFormat($f, $t, $tz));
}

I'm not sure what the best way to implement the fromDateTime is. You could even take what you've got and put it in there. Just make sure not to lose the timezone.

Note that you could even implement __callStatic and use a bit of reflection to make it future proof.

Mize answered 27/3, 2011 at 17:46 Comment(0)
T
1

Previous solutions neglect time zones and microseconds, so my little improve is here. I prefer variant 1, but in terms of performance 2 can be little faster on old PHPs.

class NDateTimeImmutable extends \DateTimeImmutable
{
    public static function createFromFormat1($format, $time)
    {
        $parent = parent::createFromFormat($format, $time);
        if (!$parent) {
            return false;
        }
        //Seting timezone like this and not by format to preserve timezone format
        $static = new static($parent->format('Y-m-d\TH:i:s.u'), $parent->getTimezone());
        return $static;
    }

    public static function createFromFormat2($format, $time)
    {
        $parent = parent::createFromFormat($format, $time);
         if (!$parent) {
            return false;
        }
        $serialized = serialize($parent);
        // numbers can be computed by strlen() 
        // eg. strlen(parent::class) = 17 but it is slow
        $serialized = strtr($serialized, ['17:"'.parent::class => '18:"'.static::class]);
        return unserialize($serialized);
    }
}

Thun answered 26/6, 2019 at 15:53 Comment(0)
T
0
class DateTimeEx extends DateTime
{
    static public function createFromFormat($format, $time, $object = null)
    {
        return new static(DateTime::createFromFormat($format, $time, $object)->format(DateTime::ATOM));
    }
}
Thyratron answered 22/4, 2016 at 9:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.