Passed
Branch php-cs-fixer (b9836a)
by Fabio
15:02
created

TSimpleDateFormatter::setPattern()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * TSimpleDateFormatter class file
4
 *
5
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
6
 * @link https://github.com/pradosoft/prado
7
 * @copyright Copyright &copy; 2005-2016 The PRADO Group
8
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
9
 * @package Prado\Util
10
 */
11
12
namespace Prado\Util;
13
14
use Prado\Prado;
15
use Prado\Exceptions\TInvalidDataValueException;
16
17
/**
18
 * TSimpleDateFormatter class.
19
 *
20
 * Formats and parses dates using the SimpleDateFormat pattern.
21
 * This pattern is compatible with the I18N and java's SimpleDateFormatter.
22
 * <code>
23
 * Pattern |      Description
24
 * ----------------------------------------------------
25
 * d       | Day of month 1 to 31, no padding
26
 * dd      | Day of monath 01 to 31, zero leading
27
 * M       | Month digit 1 to 12, no padding
28
 * MM      | Month digit 01 to 12, zero leading
29
 * yy      | 2 year digit, e.g., 96, 05
30
 * yyyy    | 4 year digit, e.g., 2005
31
 * ----------------------------------------------------
32
 * </code>
33
 *
34
 * Usage example, to format a date
35
 * <code>
36
 * $formatter = new TSimpleDateFormatter("dd/MM/yyy");
37
 * echo $formatter->format(time());
38
 * </code>
39
 *
40
 * To parse the date string into a date timestamp.
41
 * <code>
42
 * $formatter = new TSimpleDateFormatter("d-M-yyy");
43
 * echo $formatter->parse("24-6-2005");
44
 * </code>
45
 *
46
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
47
 * @package Prado\Util
48
 * @since 3.0
49
 */
50
class TSimpleDateFormatter
51
{
52
	/**
53
	 * Formatting pattern.
54
	 * @var string
55
	 */
56
	private $pattern;
57
58
	/**
59
	 * Charset, default is 'UTF-8'
60
	 * @var string
61
	 */
62
	private $charset = 'UTF-8';
63
64
	/**
65
	 * Constructor, create a new date time formatter.
66
	 * @param string formatting pattern.
0 ignored issues
show
Bug introduced by
The type Prado\Util\pattern was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
67
	 * @param string pattern and value charset
68
	 */
69
	public function __construct($pattern, $charset = 'UTF-8')
70
	{
71
		$this->setPattern($pattern);
72
		$this->setCharset($charset);
73
	}
74
75
	/**
76
	 * @return string formatting pattern.
77
	 */
78
	public function getPattern()
79
	{
80
		return $this->pattern;
81
	}
82
83
	/**
84
	 * @param string formatting pattern.
0 ignored issues
show
Bug introduced by
The type Prado\Util\formatting was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
85
	 */
86
	public function setPattern($pattern)
87
	{
88
		$this->pattern = $pattern;
89
	}
90
91
	/**
92
	 * @return string formatting charset.
93
	 */
94
	public function getCharset()
95
	{
96
		return $this->charset;
97
	}
98
99
	/**
100
	 * @param string formatting charset.
101
	 */
102
	public function setCharset($charset)
103
	{
104
		$this->charset = $charset;
105
	}
106
107
	/**
108
	 * Format the date according to the pattern.
0 ignored issues
show
Bug introduced by
The type Prado\Util\the was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
109
	 * @param string|int the date to format, either integer or a string readable by strtotime.
110
	 * @return string formatted date.
111
	 */
112
	public function format($value)
113
	{
114
		$dt = $this->getDate($value);
115
		$bits['yyyy'] = $dt->format('Y');
0 ignored issues
show
Comprehensibility Best Practice introduced by
$bits was never initialized. Although not strictly required by PHP, it is generally a good practice to add $bits = array(); before regardless.
Loading history...
116
		$bits['yy'] = $dt->format('y');
117
118
		$bits['MM'] = $dt->format('m');
119
		$bits['M'] = $dt->format('n');
120
121
		$bits['dd'] = $dt->format('d');
122
		$bits['d'] = $dt->format('j');
123
124
		$pattern = preg_replace('/M{3,4}/', 'MM', $this->pattern);
125
		return str_replace(array_keys($bits), $bits, $pattern);
126
	}
127
128
	public function getMonthPattern()
129
	{
130
		if(is_int(strpos($this->pattern, 'MMMM')))
0 ignored issues
show
introduced by
The condition is_int(strpos($this->pattern, 'MMMM')) can never be false.
Loading history...
131
			return 'MMMM';
132
		if(is_int(strpos($this->pattern, 'MMM')))
133
			return 'MMM';
134
		if(is_int(strpos($this->pattern, 'MM')))
135
			return 'MM';
136
		if(is_int(strpos($this->pattern, 'M')))
137
			return 'M';
138
		return false;
139
	}
140
141
	public function getDayPattern()
142
	{
143
		if(is_int(strpos($this->pattern, 'dd')))
0 ignored issues
show
introduced by
The condition is_int(strpos($this->pattern, 'dd')) can never be false.
Loading history...
144
			return 'dd';
145
		if(is_int(strpos($this->pattern, 'd')))
146
			return 'd';
147
		return false;
148
	}
149
150
	public function getYearPattern()
151
	{
152
		if(is_int(strpos($this->pattern, 'yyyy')))
0 ignored issues
show
introduced by
The condition is_int(strpos($this->pattern, 'yyyy')) can never be false.
Loading history...
153
			return 'yyyy';
154
		if(is_int(strpos($this->pattern, 'yy')))
155
			return 'yy';
156
		return false;
157
	}
158
159
	public function getDayMonthYearOrdering()
160
	{
161
		$ordering = [];
162
		if(is_int($day = strpos($this->pattern, 'd')))
0 ignored issues
show
introduced by
The condition is_int($day = strpos($this->pattern, 'd')) can never be false.
Loading history...
163
			$ordering['day'] = $day;
164
		if(is_int($month = strpos($this->pattern, 'M')))
0 ignored issues
show
introduced by
The condition is_int($month = strpos($this->pattern, 'M')) can never be false.
Loading history...
165
			$ordering['month'] = $month;
166
		if(is_int($year = strpos($this->pattern, 'yy')))
0 ignored issues
show
introduced by
The condition is_int($year = strpos($this->pattern, 'yy')) can never be false.
Loading history...
167
			$ordering['year'] = $year;
168
		asort($ordering);
169
		return array_keys($ordering);
170
	}
171
172
	/**
173
	 * Gets the time stamp from string or integer.
174
	 * @param string|int date to parse
0 ignored issues
show
Bug introduced by
The type Prado\Util\date was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
175
	 * @return array date info array
176
	 */
177
	private function getDate($value)
178
	{
179
		if(is_numeric($value))
180
		{
181
			$date = new \DateTime;
182
			$date->setTimeStamp($value);
183
		} else {
184
			$date = new \DateTime($value);
185
		}
186
		return $date;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $date returns the type DateTime which is incompatible with the documented return type array.
Loading history...
187
	}
188
189
	/**
190
	 * @return boolean true if the given value matches with the date pattern.
191
	 */
192
	public function isValidDate($value)
193
	{
194
		if($value === null) {
195
			return false;
196
		} else {
197
			return $this->parse($value, false) !== null;
198
		}
199
	}
200
201
	/**
202
	 * Parse the string according to the pattern.
203
	 * @param string|int date string or integer to parse
204
	 * @return int date time stamp
205
	 * @throws TInvalidDataValueException if date string is malformed.
206
	 */
207
	public function parse($value, $defaultToCurrentTime = true)
208
	{
209
		if(is_int($value) || is_float($value))
210
			return $value;
211
		elseif(!is_string($value))
212
			throw new TInvalidDataValueException('date_to_parse_must_be_string', $value);
213
214
		if(empty($this->pattern)) return time();
215
216
		$date = time();
217
218
		if($this->length(trim($value)) < 1)
219
			return $defaultToCurrentTime ? $date : null;
220
221
		$pattern = $this->pattern;
222
223
		$i_val = 0;
224
		$i_format = 0;
225
		$pattern_length = $this->length($pattern);
226
		$c = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $c is dead and can be removed.
Loading history...
227
		$token = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $token is dead and can be removed.
Loading history...
228
		$x = null; $y = null;
229
230
231
		if($defaultToCurrentTime)
232
		{
233
			$year = "{$date['year']}";
234
			$month = $date['mon'];
235
			$day = $date['mday'];
236
		}
237
		else
238
		{
239
			$year = null;
240
			$month = null;
241
			$day = null;
242
		}
243
244
		while ($i_format < $pattern_length)
245
		{
246
			$c = $this->charAt($pattern, $i_format);
247
			$token = '';
248
			while ($this->charEqual($pattern, $i_format, $c)
249
						&& ($i_format < $pattern_length))
250
			{
251
				$token .= $this->charAt($pattern, $i_format++);
252
			}
253
254
			if ($token == 'yyyy' || $token == 'yy' || $token == 'y')
255
			{
256
				if ($token == 'yyyy') { $x = 4;$y = 4; }
257
				if ($token == 'yy')   { $x = 2;$y = 2; }
258
				if ($token == 'y')    { $x = 2;$y = 4; }
259
				$year = $this->getInteger($value, $i_val, $x, $y);
260
				if($year === null)
261
					return null;
262
					//throw new TInvalidDataValueException('Invalid year', $value);
263
				$i_val += strlen($year);
264
				if(strlen($year) == 2)
265
				{
266
					$iYear = (int)$year;
267
					if($iYear > 70)
268
						$year = $iYear + 1900;
269
					else
270
						$year = $iYear + 2000;
271
				}
272
				$year = (int)$year;
273
			}
274
			elseif($token == 'MM' || $token == 'M')
275
			{
276
				$month = $this->getInteger($value,$i_val,
277
									$this->length($token), 2);
278
				$iMonth = (int)$month;
279
				if($month === null || $iMonth < 1 || $iMonth > 12)
280
					return null;
281
					//throw new TInvalidDataValueException('Invalid month', $value);
282
				$i_val += strlen($month);
283
				$month = $iMonth;
284
			}
285
			elseif ($token == 'dd' || $token == 'd')
286
			{
287
				$day = $this->getInteger($value,$i_val,
288
									$this->length($token), 2);
289
				$iDay = (int)$day;
290
				if($day === null || $iDay < 1 || $iDay > 31)
291
					return null;
292
					//throw new TInvalidDataValueException('Invalid day', $value);
293
				$i_val += strlen($day);
294
				$day = $iDay;
295
			}
296
			else
297
			{
298
				if($this->substring($value, $i_val, $this->length($token)) != $token)
299
					return null;
300
					//throw new TInvalidDataValueException("Subpattern '{$this->pattern}' mismatch", $value);
301
				else
302
					$i_val += $this->length($token);
303
			}
304
		}
305
		if ($i_val != $this->length($value))
306
			return null;
307
			//throw new TInvalidDataValueException("Pattern '{$this->pattern}' mismatch", $value);
308
		if(!$defaultToCurrentTime && ($month === null || $day === null || $year === null))
309
			return null;
310
		else
311
		{
312
			if(empty($year)) {
0 ignored issues
show
introduced by
The condition empty($year) can never be true.
Loading history...
313
				$year = date('Y');
314
			}
315
			$day = (int)$day <= 0 ? 1 : (int)$day;
316
			$month = (int)$month <= 0 ? 1 : (int)$month;
317
318
			$s = new \DateTime;
319
			$s->setDate($year, $month, $day);
0 ignored issues
show
Bug introduced by
It seems like $year can also be of type string; however, parameter $year of DateTime::setDate() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

319
			$s->setDate(/** @scrutinizer ignore-type */ $year, $month, $day);
Loading history...
320
			$s->setTime(0, 0, 0);
321
			return $s->getTimeStamp();
322
		}
323
	}
324
325
	/**
326
	 * Calculate the length of a string, may be consider iconv_strlen?
327
	 */
328
	private function length($string)
329
	{
330
		//use iconv_strlen or just strlen?
331
		return strlen($string);
332
	}
333
334
	/**
335
	 * Get the char at a position.
336
	 */
337
	private function charAt($string, $pos)
338
	{
339
		return $this->substring($string, $pos, 1);
340
	}
341
342
	/**
343
	 * Gets a portion of a string, uses iconv_substr.
344
	 */
345
	private function substring($string, $start, $length)
346
	{
347
		return iconv_substr($string, $start, $length);
348
	}
349
350
	/**
351
	 * Returns true if char at position equals a particular char.
352
	 */
353
	private function charEqual($string, $pos, $char)
354
	{
355
		return $this->charAt($string, $pos) == $char;
356
	}
357
358
	/**
359
	 * Gets integer from part of a string, allows integers of any length.
360
	 * @param string string to retrieve the integer from.
361
	 * @param int starting position
362
	 * @param int minimum integer length
363
	 * @param int maximum integer length
0 ignored issues
show
Bug introduced by
The type Prado\Util\maximum was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
364
	 * @return string integer portion of the string, null otherwise
365
	 */
366
	private function getInteger($str, $i, $minlength, $maxlength)
367
	{
368
		//match for digits backwards
369
		for ($x = $maxlength; $x >= $minlength; $x--)
370
		{
371
			$token = $this->substring($str, $i, $x);
372
			if ($this->length($token) < $minlength)
373
				return null;
374
			if (preg_match('/^\d+$/', $token))
375
				return $token;
376
		}
377
		return null;
378
	}
379
}
380
381