Overview

Namespaces

  • Apptus
    • ESales
      • Connector
        • Report
        • Time
    • Util
      • Cache
  • PHP
  • Overview
  • Namespace
  • Class
  • Tree
  1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 
<?php
namespace Apptus\ESales\Connector\Time;

/**
 * A duration on the form P[yY][mM][dD][T[hH][mM][sS]] or P[wW], that specifies
 * the length of a time interval.
 *
 * This class describes a duration as defined by ISO 8601.
 */
class Duration extends \DateInterval {
    /**
     * Parses a duration.
     *
     * @param string
     *            Duration in ISO 8601 format.
     * @return Duration
     *             A new instance representing the same duration as the input.
     */
    public static function parse($input) {
        return new Duration(self::parseDuration($input));
    }

    private static function parseDuration($input) {
        $matches = array();
        $m = preg_match_all('/\\d+|[PYMWDTHMS]/', $input, $matches);

        if ($m === false || $m === 0 || $matches[0][0] !== 'P') {
            throw new \InvalidArgumentException('Illegal format for duration. Has to start with P.');
        }

        $date = true;
        $amount = null;

        $elements = array();

        $matches = $matches[0];
        $num = count($matches);
        for ($i = 1; $i < $num; ++$i) {
            $token = $matches[$i];
            $unit = null;

            if ($date) {
                if ($token === 'Y') {
                    $unit = 'YEAR';
                } elseif ($token === 'M') {
                    $unit = 'MONTH';
                } elseif ($token === 'W') {
                    $unit = 'WEEK';
                } elseif ($token === 'D') {
                    $unit = 'DAY';
                } elseif ($token === 'T') {
                    if ($amount !== null) {
                        throw new \InvalidArgumentException('Illegal format for duration. Missing unit for amount before T.');
                    }
                    $date = false;
                    continue;
                }
            } else {
                if ($token === 'H') {
                    $unit = 'HOUR';
                } elseif ($token === 'M') {
                    $unit = 'MINUTE';
                } elseif ($token === 'S') {
                    $unit = 'SECOND';
                }
            }
            if ($unit !== null) {
                if (isset($elements[$unit])) {
                    throw new \InvalidArgumentException('Double specification of ' . $unit . '.');
                } elseif ($amount === null) {
                    throw new \InvalidArgumentException('Amount missing for unit ' . $unit . '.');
                }
                $elements[$unit] = $amount;
                $amount = null;
            } else { // a number
                if (!ctype_digit($token)) {
                    throw new \InvalidArgumentException('Unexpected token: ' . $token);
                }
                $amount = (int) $token;
            }
        }
        return $elements;
    }

    private $elements = null;

    /**
     * Create a duration from an amount and a unit.
     *
     * @param int
     * @param string
     *            One of the string constants in {@see Unit}.
     * @throws \InvalidArgumentException if the unit is invalid.
     * @return Duration
     */
    public static function create($amount, $unit) {
        Unit::symbol($unit); // Throws InvArgExc if unknown unit.
        return new Duration(array($unit => $amount));
    }

    /**
     * Construct a Duration.
     *
     * @internal
     * @param string|array A string with an ISO 8601 representaion of a duration or an associative array with units as keys and amounts as values.
     * @throws \InvalidArgumentException
     */
    public function __construct($elements) {
        if (gettype($elements) === 'string') {
            $elements = self::parseDuration($elements);
        } elseif (gettype($elements) !== 'array') {
            $type = gettype($elements) === 'object' ? get_class($elements) : gettype($elements);
            throw new \InvalidArgumentException('Expected string or array, got: ' . $type);
        }
        $this->elements = $elements;

        if (isset($elements[Unit::WEEK]) && count($elements) > 1) {
            throw new \InvalidArgumentException('Illegal format for duration. Week cannot be combined with other units.');
        } elseif (count($elements) === 0) {
            throw new \InvalidArgumentException('Illegal format for duration. No date or time element specified.');
        }
        parent::__construct($this->__toString());
    }

    /**
     * Returns the amount of a certain unit.
     *
     * This is not a total amount, but instead the amount specified for the specified field.
     *
     * @param string
     *          The unit. Must be one of the constants in the Unit class.
     * @return int
     */
    public function amount($unit) {
        Unit::symbol($unit); // Throws InvArgExc if unknown unit.
        if (!isset($this->elements[$unit])) {
            return 0;
        }
        return $this->elements[$unit];
    }

    /**
     * Create a new Duration that is the sum of this duration and the argument duration.
     *
     * @param Duration
     * @return Duration
     */
    public function add(Duration $d) {
        $total = $this->elements; // php assign arrays by copy

        foreach ($d->sortedUnits(false) as $u) {
            $total[$u] += $d->amount($u);
        }

        return new Duration($total);
    }

    /**
     * Return a DateTime moved forward in time by the length of this duration.
     *
     * @param \DateTime
     * @return \DateTime
     */
    public function forward(\DateTime $dt) {
        $sortedUnits = $this->sortedUnits(true);

        $result = clone $dt;
        foreach ($sortedUnits as $unit) {
            Unit::add($result, $this->elements[$unit], $unit);
        }
        return $result;
    }

    /**
     * Return a DateTime moved back in time by the length of this duration.
     *
     * @param \DateTime
     * @return \DateTime
     */
    public function back(\DateTime $dt) {
        $sortedUnits = $this->sortedUnits(false);

        $result = clone $dt;
        foreach ($sortedUnits as $unit) {
            Unit::add($result, -$this->elements[$unit], $unit);
        }
        return $result;
    }


    private function sortedUnits($desc = false) {
        $units = array_keys($this->elements);

        if ($desc) {
            usort($units, 'Apptus\ESales\Connector\Time\Unit::reverse_compare');
        } else {
            usort($units, 'Apptus\ESales\Connector\Time\Unit::compare');
        }

        return $units;
    }

    /**
     * The finest unit with a specified amount for this duration.
     *
     * For instance, the duration PT1M has minute precision, while PT1M0S has second
     * precision (even if the number of seconds specified is zero).
     *
     * @return string The smallest specified unit.
     */
    public function precision() {
        $units = $this->sortedUnits(false);
        return $units[0]; // cannot be empty
    }

    /**
     * True if the argument is a duration with the same or equivalent specification.
     *
     * Note that P1W equals P7D by definition, whereas e.g. P1M does not equal P30D
     * since months are not 30 days in general. Less obvious, P1D is not equal to
     * P24H in general.
     *
     * Years are normalized to months, weeks to days and hours and minutes to seconds.
     * After normalization, both durations are compared component by component. Iff
     * all components match, this method returns true.
     */
    public function equals($o) {
        if ($o instanceof Duration) {
            $that = $o;
            return $this->normalized()->elements === $that->normalized()->elements;
        }
        return false;
    }

    private function normalized() {
        $months = $this->amount(Unit::YEAR) * 12 + $this->amount(Unit::MONTH);
        $days = $this->amount(Unit::WEEK) * 7 + $this->amount(Unit::DAY);
        $seconds = ($this->amount(Unit::HOUR) * 60 + $this->amount(Unit::MINUTE) * 60) + $this->amount(Unit::SECOND);

        $elems = array ();

        if ($months > 0) {
            $elems[Unit::MONTH] = $months;
        }

        if ($days > 0) {
            $elems[Unit::DAY] = $days;
        }

        if ($seconds > 0) {
            if ($seconds > 60) {
                $minutes = (int) $seconds / 60;
                $seconds %= 60;

                if ($minutes > 60) {
                    $hours = (int) $minutes / 60;
                    $minutes %= 60;

                    $elems[Unit::HOUR] = $hours;
                }

                $elems[Unit::MINUTE] = $minutes;
            }

            $elems[Unit::SECOND] = $seconds;
        }

        return new Duration($elems);
    }

    /**
     * Returns a string with the ISO 8601 representation of this duration.
     *
     * @return string The ISO 8601 representaion of this duration.
     */
    public function __toString() {
        $result = 'P';
        $result .= $this->append(Unit::YEAR);
        $result .= $this->append(Unit::MONTH);
        $result .= $this->append(Unit::WEEK);
        $result .= $this->append(Unit::DAY);

        if (isset($this->elements[Unit::HOUR]) || isset($this->elements[Unit::MINUTE]) || isset($this->elements[Unit::SECOND])) {
            $result .= 'T';
            $result .= $this->append(Unit::HOUR);
            $result .= $this->append(Unit::MINUTE);
            $result .= $this->append(Unit::SECOND);
        }

        return $result;
    }

    private function append($unit) {
        if (isset($this->elements[$unit])) {
            return $this->elements[$unit] . substr($unit, 0, 1);
        }
        return '';
    }
}
Apptus ESales Connector PHP API documentation generated by ApiGen