1

How do I combine a Date & Time in PHP using a mysql 'time' column format

$date = New DateTime();
$time = $record->time //14:00:00

I can do this

$date = New DateTime();
$date->setTime(explode(':',$record->time)[0],explode(':',$record->time)[1],explode(':',$record->time)[2]);

Is there a 'better' way

user3459394
  • 107
  • 1
  • 11
  • 2
    The MySQL time type stores more than just the time of day, and can in fact store up to 838 in the "hours" position, so you might want to account for that, too. Generally, unless you are storing elapsed time (such as hours of work), I'd recommend avoiding this and instead use a true DateTime format if possible. However, there is also "My office hours are from 8:00 to 5:00", and as long as you manually constrain this in PHP, I guess this would be a valid use-case, too. – Chris Haas Jul 31 '23 at 16:19
  • You'll need to define "better" before we can really help. What do you see as being the issue with your current code? – ADyson Jul 31 '23 at 16:41
  • Related, but not a good dupe target; [How to correctly add a date and a time (string) in PHP?](https://stackoverflow.com/q/53451000/2943403) and [Add time to datetime object with PHP](https://stackoverflow.com/q/34031577/2943403) and [PHP add time to date](https://stackoverflow.com/q/39069519/2943403) – mickmackusa Aug 06 '23 at 06:52

1 Answers1

1

I personally like working with higher level objects instead of just arrays or strings, so here's how I would tackle this. For some people, this is majorly overkill, and I totally get that, however the core method can easily be just rewritten as a global function, too, so hopefully that can be taken into account.

I think it is overall pretty straight-forward, and almost identical to your code except after exploding we convert to int and then validate that the hours are in an expected range.

readonly class MySqlTime
{
    public function __construct(
        public int $hours,
        public int $minutes,
        public int $seconds,
    ) {
    }

    public static function fromMySqlTimeAsDurationInDay(string $time): self
    {
        [$hours, $minutes, $seconds] = array_map('intval', explode(':', $time));

        // Ensure only positive values on a single day.
        // MySql should already be throwing errors about minutes or seconds being out of range, so we don't
        // need to check those.
        // The seconds could include decimals, however for this purpose we are ignoring them.
        if ($hours > 23 || $hours < 0) {
            throw new InvalidArgumentException('Hours must be between 0 and 23');
        }

        return new self($hours, $minutes, $seconds);
    }

    public function toDateTime(DateTimeInterface $dateTime = null) : DateTimeInterface
    {
        $dateTime ??= new DateTimeImmutable();

        return $dateTime->setTime($this->hours, $this->minutes, $this->seconds);
    }
}

Usage:

var_dump(MySqlTime::fromMySqlTimeAsDurationInDay('12:34:56')->toDateTime());

Output:

object(DateTimeImmutable)#3 (3) {
  ["date"]=>
  string(26) "2023-07-31 12:34:56.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(16) "Europe/Amsterdam"
}

Demo: https://3v4l.org/B6mr4#v8.2.7

Chris Haas
  • 53,986
  • 12
  • 141
  • 274
  • No extra loop for casting integers with `sscanf($time, '%d:%d:%d', $hours, $minutes, $seconds);` related approach [here](https://stackoverflow.com/a/68931266/2943403) – mickmackusa Aug 06 '23 at 06:47