blob: abd656c8ddf098ab3fd410096f04c56486429080 [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
3namespace LdapRecord\Models\Attributes;
4
5use Carbon\Carbon;
6use Carbon\CarbonInterface;
7use DateTime;
8use LdapRecord\LdapRecordException;
9use LdapRecord\Utilities;
10
11class Timestamp
12{
13 /**
14 * The current timestamp type.
15 *
16 * @var string
17 */
18 protected $type;
19
20 /**
21 * The available timestamp types.
22 *
23 * @var array
24 */
25 protected $types = [
26 'ldap',
27 'windows',
28 'windows-int',
29 ];
30
31 /**
32 * Constructor.
33 *
34 * @param string $type
35 *
36 * @throws LdapRecordException
37 */
38 public function __construct($type)
39 {
40 $this->setType($type);
41 }
42
43 /**
44 * Set the type of timestamp to convert from / to.
45 *
46 * @param string $type
47 *
48 * @throws LdapRecordException
49 */
50 public function setType($type)
51 {
52 if (! in_array($type, $this->types)) {
53 throw new LdapRecordException("Unrecognized LDAP date type [$type]");
54 }
55
56 $this->type = $type;
57 }
58
59 /**
60 * Converts the value to an LDAP date string.
61 *
62 * @param mixed $value
63 *
64 * @throws LdapRecordException
65 *
66 * @return float|string
67 */
68 public function fromDateTime($value)
69 {
70 $value = is_array($value) ? reset($value) : $value;
71
72 // If the value is being converted to a windows integer format but it
73 // is already in that format, we will simply return the value back.
74 if ($this->type == 'windows-int' && $this->valueIsWindowsIntegerType($value)) {
75 return $value;
76 }
77 // If the value is numeric, we will assume it's a UNIX timestamp.
78 elseif (is_numeric($value)) {
79 $value = Carbon::createFromTimestamp($value);
80 }
81 // If a string is given, we will pass it into a new carbon instance.
82 elseif (is_string($value)) {
83 $value = Carbon::parse($value);
84 }
85 // If a date object is given, we will convert it to a carbon instance.
86 elseif ($value instanceof DateTime) {
87 $value = Carbon::instance($value);
88 }
89
90 switch ($this->type) {
91 case 'ldap':
92 $value = $this->convertDateTimeToLdapTime($value);
93 break;
94 case 'windows':
95 $value = $this->convertDateTimeToWindows($value);
96 break;
97 case 'windows-int':
98 $value = $this->convertDateTimeToWindowsInteger($value);
99 break;
100 default:
101 throw new LdapRecordException("Unrecognized date type [{$this->type}]");
102 }
103
104 return $value;
105 }
106
107 /**
108 * Determine if the value given is in Windows Integer (NTFS Filetime) format.
109 *
110 * @param int|string $value
111 *
112 * @return bool
113 */
114 protected function valueIsWindowsIntegerType($value)
115 {
116 return is_numeric($value) && strlen((string) $value) === 18;
117 }
118
119 /**
120 * Converts the LDAP timestamp value to a Carbon instance.
121 *
122 * @param mixed $value
123 *
124 * @throws LdapRecordException
125 *
126 * @return Carbon|false
127 */
128 public function toDateTime($value)
129 {
130 $value = is_array($value) ? reset($value) : $value;
131
132 if ($value instanceof CarbonInterface || $value instanceof DateTime) {
133 return Carbon::instance($value);
134 }
135
136 switch ($this->type) {
137 case 'ldap':
138 $value = $this->convertLdapTimeToDateTime($value);
139 break;
140 case 'windows':
141 $value = $this->convertWindowsTimeToDateTime($value);
142 break;
143 case 'windows-int':
144 $value = $this->convertWindowsIntegerTimeToDateTime($value);
145 break;
146 default:
147 throw new LdapRecordException("Unrecognized date type [{$this->type}]");
148 }
149
150 return $value instanceof DateTime ? Carbon::instance($value) : $value;
151 }
152
153 /**
154 * Converts standard LDAP timestamps to a date time object.
155 *
156 * @param string $value
157 *
158 * @return DateTime|bool
159 */
160 protected function convertLdapTimeToDateTime($value)
161 {
162 return DateTime::createFromFormat(
163 strpos($value, 'Z') !== false ? 'YmdHis\Z' : 'YmdHisT',
164 $value
165 );
166 }
167
168 /**
169 * Converts date objects to a standard LDAP timestamp.
170 *
171 * @param DateTime $date
172 *
173 * @return string
174 */
175 protected function convertDateTimeToLdapTime(DateTime $date)
176 {
177 return $date->format(
178 $date->getOffset() == 0 ? 'YmdHis\Z' : 'YmdHisO'
179 );
180 }
181
182 /**
183 * Converts standard windows timestamps to a date time object.
184 *
185 * @param string $value
186 *
187 * @return DateTime|bool
188 */
189 protected function convertWindowsTimeToDateTime($value)
190 {
191 return DateTime::createFromFormat(
192 strpos($value, '0Z') !== false ? 'YmdHis.0\Z' : 'YmdHis.0T',
193 $value
194 );
195 }
196
197 /**
198 * Converts date objects to a windows timestamp.
199 *
200 * @param DateTime $date
201 *
202 * @return string
203 */
204 protected function convertDateTimeToWindows(DateTime $date)
205 {
206 return $date->format(
207 $date->getOffset() == 0 ? 'YmdHis.0\Z' : 'YmdHis.0O'
208 );
209 }
210
211 /**
212 * Converts standard windows integer dates to a date time object.
213 *
214 * @param int $value
215 *
216 * @throws \Exception
217 *
218 * @return DateTime|bool
219 */
220 protected function convertWindowsIntegerTimeToDateTime($value)
221 {
222 // ActiveDirectory dates that contain integers may return
223 // "0" when they are not set. We will validate that here.
224 if (! $value) {
225 return false;
226 }
227
228 return (new DateTime())->setTimestamp(
229 Utilities::convertWindowsTimeToUnixTime($value)
230 );
231 }
232
233 /**
234 * Converts date objects to a windows integer timestamp.
235 *
236 * @param DateTime $date
237 *
238 * @return float
239 */
240 protected function convertDateTimeToWindowsInteger(DateTime $date)
241 {
242 return Utilities::convertUnixTimeToWindowsTime($date->getTimestamp());
243 }
244}