1<?php
2
3/**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7
8declare(strict_types=1);
9
10namespace Nette\Utils;
11
12use Nette;
13
14
15/**
16 * DateTime.
17 */
18class DateTime extends \DateTime implements \JsonSerializable
19{
20	use Nette\SmartObject;
21
22	/** minute in seconds */
23	public const MINUTE = 60;
24
25	/** hour in seconds */
26	public const HOUR = 60 * self::MINUTE;
27
28	/** day in seconds */
29	public const DAY = 24 * self::HOUR;
30
31	/** week in seconds */
32	public const WEEK = 7 * self::DAY;
33
34	/** average month in seconds */
35	public const MONTH = 2_629_800;
36
37	/** average year in seconds */
38	public const YEAR = 31_557_600;
39
40
41	/**
42	 * Creates a DateTime object from a string, UNIX timestamp, or other DateTimeInterface object.
43	 * @throws \Exception if the date and time are not valid.
44	 */
45	public static function from(string|int|\DateTimeInterface|null $time): static
46	{
47		if ($time instanceof \DateTimeInterface) {
48			return new static($time->format('Y-m-d H:i:s.u'), $time->getTimezone());
49
50		} elseif (is_numeric($time)) {
51			if ($time <= self::YEAR) {
52				$time += time();
53			}
54
55			return (new static('@' . $time))->setTimezone(new \DateTimeZone(date_default_timezone_get()));
56
57		} else { // textual or null
58			return new static((string) $time);
59		}
60	}
61
62
63	/**
64	 * Creates DateTime object.
65	 * @throws Nette\InvalidArgumentException if the date and time are not valid.
66	 */
67	public static function fromParts(
68		int $year,
69		int $month,
70		int $day,
71		int $hour = 0,
72		int $minute = 0,
73		float $second = 0.0,
74	): static
75	{
76		$s = sprintf('%04d-%02d-%02d %02d:%02d:%02.5F', $year, $month, $day, $hour, $minute, $second);
77		if (
78			!checkdate($month, $day, $year)
79			|| $hour < 0
80			|| $hour > 23
81			|| $minute < 0
82			|| $minute > 59
83			|| $second < 0
84			|| $second >= 60
85		) {
86			throw new Nette\InvalidArgumentException("Invalid date '$s'");
87		}
88
89		return new static($s);
90	}
91
92
93	/**
94	 * Returns new DateTime object formatted according to the specified format.
95	 */
96	public static function createFromFormat(
97		string $format,
98		string $time,
99		string|\DateTimeZone|null $timezone = null,
100	): static|false
101	{
102		if ($timezone === null) {
103			$timezone = new \DateTimeZone(date_default_timezone_get());
104
105		} elseif (is_string($timezone)) {
106			$timezone = new \DateTimeZone($timezone);
107		}
108
109		$date = parent::createFromFormat($format, $time, $timezone);
110		return $date ? static::from($date) : false;
111	}
112
113
114	/**
115	 * Returns JSON representation in ISO 8601 (used by JavaScript).
116	 */
117	public function jsonSerialize(): string
118	{
119		return $this->format('c');
120	}
121
122
123	/**
124	 * Returns the date and time in the format 'Y-m-d H:i:s'.
125	 */
126	public function __toString(): string
127	{
128		return $this->format('Y-m-d H:i:s');
129	}
130
131
132	/**
133	 * Creates a copy with a modified time.
134	 */
135	public function modifyClone(string $modify = ''): static
136	{
137		$dolly = clone $this;
138		return $modify ? $dolly->modify($modify) : $dolly;
139	}
140}
141