1: <?php
2: namespace NGS;
3:
4: require_once(__DIR__.'/Utils.php');
5: require_once(__DIR__.'/LocalDate.php');
6:
7: use NGS\Utils;
8: use \InvalidArgumentException;
9: use \DateTimeZone;
10:
11: 12: 13:
14: class Timestamp
15: {
16:
17: const DEFAULT_TIMEZONE = 'UTC';
18: const STRING_FORMAT = 'Y-m-d\\TH:i:s.uP';
19:
20: const FALLBACK_FORMAT = 'Y-m-d\\TH:i:sP';
21:
22: private static $defaultTimezone;
23:
24: 25: 26:
27: protected $datetime;
28:
29: 30: 31: 32: 33: 34: 35: 36:
37: private static function fromNumeric($utime, $timezone)
38: {
39: $strtime = sprintf('%.6f', $utime);
40:
41: $dt = \DateTime::createFromFormat('U.u', $strtime, new DateTimeZone($timezone));
42: if($dt === false) {
43: throw new \InvalidArgumentException('Cannot initialize "NGS\\Timestamp". Input number was in invalid format: "'.$utime.'"');
44: }
45:
46: $dt->setTimezone(new DateTimeZone($timezone));
47: return $dt;
48: }
49:
50: 51: 52: 53: 54: 55: 56: 57: 58:
59: private static function fromString($strtime, $timezone, $pattern)
60: {
61: try {
62: $dtZone = new DateTimeZone($timezone);
63: $dt = \DateTime::createFromFormat($pattern, $strtime, $dtZone);
64:
65: if($dt === false) {
66:
67: $dt = \DateTime::createFromFormat(self::FALLBACK_FORMAT, $strtime, $dtZone);
68:
69: if($dt === false) {
70:
71: $dt = new \DateTime($strtime, $dtZone);
72: if(!$dt instanceof \DateTime) {
73: throw new \InvalidArgumentException('Cannot initialize "NGS\\Timestamp". Input string was in invalid format: "'.$strtime.'"');
74: }
75: }
76: }
77: }
78: catch(\Exception $ex) {
79:
80: if ($ex instanceof InvalidArgumentException) {
81: throw $ex;
82: }
83:
84:
85: throw new \InvalidArgumentException('Cannot initialize "NGS\\LocalDate". Input string was in invalid format: "'.$strtime.'"',
86: null,
87: $ex);
88: }
89:
90:
91:
92:
93:
94:
95:
96: return $dt;
97: }
98:
99: 100: 101: 102: 103: 104: 105:
106: public static function setDefaultTimezone($timezone)
107: {
108: if (is_string($timezone)) {
109: self::$defaultTimezone = new DateTimeZone($timezone);
110: } elseif ($timezone instanceof \DateTimeZone) {
111: self::$defaultTimezone = $timezone;
112: } else {
113: throw new InvalidArgumentException('Timezone was not a string or an instance of \\DateTimeZone');
114: }
115: return self::$defaultTimezone;
116: }
117:
118: 119: 120: 121: 122:
123: public static function getDefaultTimezone()
124: {
125: if (!isset(self::$defaultTimezone)) {
126: $timezoneString = date_default_timezone_get();
127: self::$defaultTimezone = new DateTimeZone($timezoneString);
128: }
129: return self::$defaultTimezone;
130: }
131:
132: 133: 134: 135: 136: 137: 138: 139:
140: public function __construct($value = 'now', $pattern = self::STRING_FORMAT, $timezone = self::DEFAULT_TIMEZONE)
141: {
142:
143: if($value === 'now') {
144: $value = microtime(true);
145: }
146:
147: if($pattern === null) {
148: $pattern = self::STRING_FORMAT;
149: }
150:
151: if($value instanceof \DateTime) {
152: $this->datetime = clone $value;
153: }
154: elseif($value instanceof \NGS\Timestamp) {
155: $this->datetime = $value->toDateTime();
156: }
157: elseif($value instanceof \NGS\LocalDate) {
158: $this->datetime = $value->toDateTime();
159: }
160: else if(is_int($value) || is_float($value)) {
161: $this->datetime = self::fromNumeric($value, $timezone);
162: }
163: elseif(is_string($value)) {
164: $this->datetime = self::fromString($value, $timezone, $pattern);
165: }
166: else {
167: throw new \InvalidArgumentException('Timestamp cannot be constructed from type "'.Utils::getType($value).'", valid types are \NGS\Timestamp, \DateTime, string, int, float or null (for current date/time).');
168: }
169:
170: if($this->datetime === null) {
171: throw new \InvalidArgumentException('Timestamp could not be constructed from type "'.Utils::getType($value).'" with value: "'.$value.'"');
172: }
173:
174: $this->datetime->setTimezone(self::getDefaultTimezone());
175: }
176:
177: 178: 179: 180: 181: 182: 183:
184: public static function toArray(array $items, $allowNullValues=false)
185: {
186: $results = array();
187: try {
188: foreach ($items as $key => $val) {
189: if ($allowNullValues && $val===null) {
190: $results[] = null;
191: } elseif ($val === null) {
192: throw new \InvalidArgumentException('Null value found in provided array');
193: } elseif (!$val instanceof \NGS\Timestamp) {
194: $results[] = new \NGS\Timestamp($val);
195: } else {
196: $results[] = $val;
197: }
198: }
199: }
200: catch(\Exception $e) {
201: throw new \InvalidArgumentException('Element at index '.$key.' could not be converted to Timestamp!', 42, $e);
202: }
203: return $results;
204: }
205:
206: 207: 208: 209: 210:
211: public function __toString()
212: {
213: return $this->format(self::STRING_FORMAT);
214: }
215:
216: 217: 218: 219: 220:
221: public function format($pattern)
222: {
223: return $this->datetime->format($pattern);
224: }
225:
226: 227: 228: 229: 230:
231: public function equals(\NGS\Timestamp $other)
232: {
233: return $this->datetime == $other->toDateTime();
234: }
235:
236: 237: 238: 239: 240:
241: public function toInt()
242: {
243: return $this->datetime->getTimestamp();
244: }
245:
246: 247: 248: 249: 250:
251: public function toFloat()
252: {
253: return (float) $this->datetime->format('U.u');
254: }
255:
256: 257: 258: 259: 260:
261: public function toDateTime()
262: {
263: return clone $this->datetime;
264: }
265:
266: 267: 268: 269: 270:
271: public function toLocalDate()
272: {
273: return new \NGS\LocalDate($this->datetime);
274: }
275: }
276: