/**
  * Set the timezone.
  *
  * @param DateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime|null $timezone A DateTimeZone or PHPDateTimeZone
  *     instance or the timezone ID as a string. A DateTime or PHPDateTime instance to use it's timezone. Null to use the default timezone.
  *
  * @return static A DateTime instance on success for method chaining.
  *
  * @throws InvalidDateTimeZoneException Throws InvalidDateTimeZoneException if the timezone is invalid.
  */
 public function setTimezone($timezone = null)
 {
     // Parse the timezone, return null on failure
     if (($timezone = DateTimeZone::parse($timezone)) === null) {
         throw new InvalidDateTimeZoneException('The given timezone is invalid');
     }
     // Set the timezone, and return the current instance for method chaining
     parent::setTimezone($timezone);
     return $this;
 }
 /**
  * Parse a timezone. A new instance will be created if required.
  *
  * @param DateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime|null $timezone The timezone to parse as
  *     DateTimeZone or PHPDateTimeZone instance or as a string. A DateTime or PHPDateTime instance to use it's
  *     timezone. Null to parse as the default timezone.
  * @param mixed|null $default [optional] The default value returned on failure.
  *
  * @return DateTimeZone|mixed The parsed timezone as DateTimeZone instance, or the default value on failure.
  */
 public static function parse($timezone = null, $default = null)
 {
     return ($result = DateTimeZone::parse($timezone)) !== null ? $result : $default;
 }
 /**
  * Check whether the result for the given sets is true based on the specified closure filter.
  * By default true will be returned if any object set has the result true, if <var>$all</var> is set to true all
  * object sets must have a result of true. False is returned otherwise.
  *
  * The number of objects in all sets must be equal, some sets may equal one.
  * If the number of objects in all sets equals, all objects will be compared to the object in the other set at the
  * same set index. If a set contains one object, this one object will be compared to the relevant objects of the
  * other sets.
  *
  * A non-array object may be given which is then converted into an array.
  *
  * The closure filter one argument, which is an array of all the relevant DateTimeZone objects for that iteration.
  * The number of objects in this array equals the number of available sets. The filter must return true if the
  * result is correct, or false if not. Null may be returned on failure.
  *
  * @param Array|DateTimeZone|PHPDateTimeZone|string|DateTime|PHPDateTime|null $sets [optional] An array containing
  *     all sets as arrays, with values accepted by DateTimeArrayUtils::parse().
  * @param Closure $callback The filter to run for each object set.
  * @param bool $all [optional] True to require all object sets to return true, false if not.
  * @param mixed|null $default [optional] The default value returned on failure.
  *
  * @return bool|mixed True if any of the iterations gives a result of true, if <var>$all</var> is set to true all
  *     iterations must give a result of true to return true, false will be returned otherwise. The default value
  *     will be returned on failure.
  */
 public static function compareSets($sets, Closure $callback, $all = true, $default = null)
 {
     // Make sure the sets parameter is an array
     if (!is_array($sets)) {
         return $default;
     }
     // Make sure all sets are arrays
     foreach ($sets as &$value) {
         if (!is_array($value)) {
             $value = array($value);
         }
     }
     // Unset the value reference to prevent accidental modifications
     unset($value);
     // Make sure the closure is valid
     if (!Utils::isValidClosure($callback)) {
         return $default;
     }
     // Count the values of all sets
     $setCounts = array();
     foreach ($sets as $value) {
         $setCounts[] = count($value);
     }
     $countMax = max($setCounts);
     $setCount = count($sets);
     // Make sure all counts equal the maximum count or one
     foreach ($setCounts as $count) {
         if ($count != 1 && $count != $countMax) {
             return $default;
         }
     }
     // Define the variables for all set values
     $setValues = array_fill(0, $setCount, null);
     // Predefine and parse sets with a single element for better performance
     for ($valueIndex = 0; $valueIndex < $setCount; $valueIndex++) {
         if ($setCounts[$valueIndex] == 1) {
             if (($setValues[$valueIndex] = DateTimeZone::parse($sets[$valueIndex])) === null) {
                 return $default;
             }
         }
     }
     // Loop through the objects
     for ($valueIndex = 0; $valueIndex < $countMax; $valueIndex++) {
         // Update all values for the sets that have more than one item
         foreach ($sets as $setIndex => $value) {
             if ($setCounts[$setIndex] > 1) {
                 $setValues[$setIndex] = $value[$valueIndex];
             }
         }
         // Check whether any of the set values is an array
         $isArray = false;
         foreach ($setValues as $value) {
             if (is_array($value)) {
                 $isArray = true;
                 break;
             }
         }
         // Handle recursive arrays
         if ($isArray) {
             // Get the result from the recursive array
             $result = static::compareSets($setValues, $callback, $all, null);
             // Return the default value if the result is invalid
             if ($result === null) {
                 return $default;
             }
         } else {
             // Parse all set values that have sets with more than one item, return the default value on failure
             foreach ($setValues as $setIndex2 => &$setValue2) {
                 if ($setCounts[$setIndex2] > 1) {
                     if (($setValue2 = DateTimeZone::parse($setValue2)) === null) {
                         return $default;
                     }
                 }
             }
             // Call the closure to get the result
             $result = call_user_func($callback, $setValues);
             // Make sure the value is boolean, or return the default value
             if (!is_bool($result)) {
                 return $default;
             }
         }
         // Return false if the result is false while everything must be true
         if (!$result && $all) {
             return false;
         }
         // Return true if the result is true while anything must be true
         if ($result && !$all) {
             return true;
         }
     }
     // Return the result
     return (bool) $all;
 }