<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Validator;
use DateInterval;
use DateTime;
use DateTimeZone;
use Traversable;
use Zend\Stdlib\ArrayUtils;
class DateStep extends Date
{
const NOT_STEP = 'dateStepNotStep';
/**
* @var array
*/
protected $messageTemplates = array(
self::NOT_STEP => "The input is not a valid step"
);
/**
* Optional base date value
*
* @var string|int|\DateTime
*/
protected $baseValue = '1970-01-01T00:00:00Z';
/**
* Date step interval (defaults to 1 day).
* Uses the DateInterval specification.
*
* @var DateInterval
*/
protected $step;
/**
* Format to use for parsing date strings
*
* @var string
*/
protected $format = DateTime::ISO8601;
/**
* Optional timezone to be used when the baseValue
* and validation values do not contain timezone info
*
* @var DateTimeZone
*/
protected $timezone;
/**
* Set default options for this instance
*
* @param array $options
*/
public function __construct($options = array())
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
} elseif (!is_array($options)) {
$options = func_get_args();
$temp['baseValue'] = array_shift($options);
if (!empty($options)) {
$temp['step'] = array_shift($options);
}
if (!empty($options)) {
$temp['format'] = array_shift($options);
}
if (!empty($options)) {
$temp['timezone'] = array_shift($options);
}
$options = $temp;
}
if (isset($options['baseValue'])) {
$this->setBaseValue($options['baseValue']);
}
if (isset($options['step'])) {
$this->setStep($options['step']);
} else {
$this->setStep(new DateInterval('P1D'));
}
if (array_key_exists('format', $options)) {
$this->setFormat($options['format']);
}
if (isset($options['timezone'])) {
$this->setTimezone($options['timezone']);
} else {
$this->setTimezone(new DateTimeZone(date_default_timezone_get()));
}
parent::__construct($options);
}
/**
* Sets the base value from which the step should be computed
*
* @param string|int|\DateTime $baseValue
* @return DateStep
*/
public function setBaseValue($baseValue)
{
$this->baseValue = $baseValue;
return $this;
}
/**
* Returns the base value from which the step should be computed
*
* @return string|int|\DateTime
*/
public function getBaseValue()
{
return $this->baseValue;
}
/**
* Sets the step date interval
*
* @param DateInterval $step
* @return DateStep
*/
public function setStep(DateInterval $step)
{
$this->step = $step;
return $this;
}
/**
* Returns the step date interval
*
* @return DateInterval
*/
public function getStep()
{
return $this->step;
}
/**
* Returns the timezone option
*
* @return DateTimeZone
*/
public function getTimezone()
{
return $this->timezone;
}
/**
* Sets the timezone option
*
* @param DateTimeZone $timezone
* @return DateStep
*/
public function setTimezone(DateTimeZone $timezone)
{
$this->timezone = $timezone;
return $this;
}
/**
* Converts an int or string to a DateTime object
*
* @param string|int|\DateTime $param
* @return \DateTime
* @throws Exception\InvalidArgumentException
*/
protected function convertToDateTime($param)
{
$dateObj = $param;
if (is_int($param)) {
// Convert from timestamp
$dateObj = date_create("@$param");
} elseif (is_string($param)) {
// Custom week format support
if (strpos($this->getFormat(), 'Y-\WW') === 0
&& preg_match('/^([0-9]{4})\-W([0-9]{2})/', $param, $matches)
) {
$dateObj = new DateTime();
$dateObj->setISODate($matches[1], $matches[2]);
} else {
$dateObj = DateTime::createFromFormat(
$this->getFormat(), $param, $this->getTimezone()
);
}
}
if (!($dateObj instanceof DateTime)) {
throw new Exception\InvalidArgumentException('Invalid date param given');
}
return $dateObj;
}
/**
* Returns true if a date is within a valid step
*
* @param string|int|\DateTime $value
* @return bool
* @throws Exception\InvalidArgumentException
*/
public function isValid($value)
{
parent::isValid($value);
$this->setValue($value);
$baseDate = $this->convertToDateTime($this->getBaseValue());
$step = $this->getStep();
// Parse the date
try {
$valueDate = $this->convertToDateTime($value);
} catch (Exception\InvalidArgumentException $ex) {
return false;
}
// Same date?
if ($valueDate == $baseDate) {
return true;
}
// Optimization for simple intervals.
// Handle intervals of just one date or time unit.
$intervalParts = explode('|', $step->format('%y|%m|%d|%h|%i|%s'));
$partCounts = array_count_values($intervalParts);
if (5 === $partCounts["0"]) {
// Find the unit with the non-zero interval
$unitKeys = array('years', 'months', 'days', 'hours', 'minutes', 'seconds');
$intervalParts = array_combine($unitKeys, $intervalParts);
$intervalUnit = null;
$stepValue = null;
foreach ($intervalParts as $key => $value) {
if (0 != $value) {
$intervalUnit = $key;
$stepValue = (int) $value;
break;
}
}
// Get absolute time difference
$timeDiff = $valueDate->diff($baseDate, true);
$diffParts = explode('|', $timeDiff->format('%y|%m|%d|%h|%i|%s'));
$diffParts = array_combine($unitKeys, $diffParts);
// Check date units
if (in_array($intervalUnit, array('years', 'months', 'days'))) {
switch ($intervalUnit) {
case 'years':
if ( 0 == $diffParts['months'] && 0 == $diffParts['days']
&& 0 == $diffParts['hours'] && 0 == $diffParts['minutes']
&& 0 == $diffParts['seconds']
) {
if (($diffParts['years'] % $stepValue) === 0) {
return true;
}
}
break;
case 'months':
if ( 0 == $diffParts['days'] && 0 == $diffParts['hours']
&& 0 == $diffParts['minutes'] && 0 == $diffParts['seconds']
) {
$months = ($diffParts['years'] * 12) + $diffParts['months'];
if (($months % $stepValue) === 0) {
return true;
}
}
break;
case 'days':
if ( 0 == $diffParts['hours'] && 0 == $diffParts['minutes']
&& 0 == $diffParts['seconds']
) {
$days = $timeDiff->format('%a'); // Total days
if (($days % $stepValue) === 0) {
return true;
}
}
break;
}
$this->error(self::NOT_STEP);
return false;
}
// Check time units
if (in_array($intervalUnit, array('hours', 'minutes', 'seconds'))) {
// Simple test if $stepValue is 1.
if (1 == $stepValue) {
if ('hours' === $intervalUnit
&& 0 == $diffParts['minutes'] && 0 == $diffParts['seconds']
) {
return true;
} elseif ('minutes' === $intervalUnit && 0 == $diffParts['seconds']) {
return true;
} elseif ('seconds' === $intervalUnit) {
return true;
}
}
// Simple test for same day, when using default baseDate
if ($baseDate->format('Y-m-d') == $valueDate->format('Y-m-d')
&& $baseDate->format('Y-m-d') == '1970-01-01'
) {
switch ($intervalUnit) {
case 'hours':
if (0 == $diffParts['minutes'] && 0 == $diffParts['seconds']) {
if (($diffParts['hours'] % $stepValue) === 0) {
return true;
}
}
break;
case 'minutes':
if (0 == $diffParts['seconds']) {
$minutes = ($diffParts['hours'] * 60) + $diffParts['minutes'];
if (($minutes % $stepValue) === 0) {
return true;
}
}
break;
case 'seconds':
$seconds = ($diffParts['hours'] * 60)
+ ($diffParts['minutes'] * 60)
+ $diffParts['seconds'];
if (($seconds % $stepValue) === 0) {
return true;
}
break;
}
$this->error(self::NOT_STEP);
return false;
}
}
}
// Fall back to slower (but accurate) method for complex intervals.
// Keep adding steps to the base date until a match is found
// or until the value is exceeded.
if ($baseDate < $valueDate) {
while ($baseDate < $valueDate) {
$baseDate->add($step);
if ($baseDate == $valueDate) {
return true;
}
}
} else {
while ($baseDate > $valueDate) {
$baseDate->sub($step);
if ($baseDate == $valueDate) {
return true;
}
}
}
$this->error(self::NOT_STEP);
return false;
}
}
# |
Change |
User |
Description |
Committed |
|
#1
|
18334 |
Liz Lam |
initial add of jambox |
|
|