public function testFebruary() { // Time zone with daylight savings time from the first Sunday in November // to the last Sunday in February. // Similar to the new rule for Brazil (Sao Paulo) in tzdata2006n. // // Note: In tzdata2007h, the rule had changed, so no actual zones uses // lastSun in Feb anymore. $tz1 = new AgaviSimpleTimeZone($this->tm, -3 * AgaviDateDefinitions::MILLIS_PER_HOUR, "nov-feb", AgaviDateDefinitions::NOVEMBER, 1, AgaviDateDefinitions::SUNDAY, 0, AgaviDateDefinitions::FEBRUARY, -1, AgaviDateDefinitions::SUNDAY, 0); // Now hardcode the same rules as for Brazil, so that we cover the intended code // even when in the future zoneinfo hardcodes these transition dates. $tz2 = new AgaviSimpleTimeZone($this->tm, -3 * AgaviDateDefinitions::MILLIS_PER_HOUR, "nov-feb2", AgaviDateDefinitions::NOVEMBER, 1, -AgaviDateDefinitions::SUNDAY, 0, AgaviDateDefinitions::FEBRUARY, -29, -AgaviDateDefinitions::SUNDAY, 0); // Gregorian calendar with the UTC time zone for getting sample test date/times. $gc = new AgaviGregorianCalendar(AgaviTimeZone::getGMT($this->tm)); $data = array(array('year' => 2006, 'month' => AgaviDateDefinitions::NOVEMBER, 'day' => 5, 'hour' => 02, 'minute' => 59, 'second' => 59, 'offsetHours' => -3), array('year' => 2006, 'month' => AgaviDateDefinitions::NOVEMBER, 'day' => 5, 'hour' => 03, 'minute' => 00, 'second' => 00, 'offsetHours' => -2), array('year' => 2007, 'month' => AgaviDateDefinitions::FEBRUARY, 'day' => 25, 'hour' => 01, 'minute' => 59, 'second' => 59, 'offsetHours' => -2), array('year' => 2007, 'month' => AgaviDateDefinitions::FEBRUARY, 'day' => 25, 'hour' => 02, 'minute' => 00, 'second' => 00, 'offsetHours' => -3), array('year' => 2007, 'month' => AgaviDateDefinitions::NOVEMBER, 'day' => 4, 'hour' => 02, 'minute' => 59, 'second' => 59, 'offsetHours' => -3), array('year' => 2007, 'month' => AgaviDateDefinitions::NOVEMBER, 'day' => 4, 'hour' => 03, 'minute' => 00, 'second' => 00, 'offsetHours' => -2), array('year' => 2008, 'month' => AgaviDateDefinitions::FEBRUARY, 'day' => 24, 'hour' => 01, 'minute' => 59, 'second' => 59, 'offsetHours' => -2), array('year' => 2008, 'month' => AgaviDateDefinitions::FEBRUARY, 'day' => 24, 'hour' => 02, 'minute' => 00, 'second' => 00, 'offsetHours' => -3), array('year' => 2008, 'month' => AgaviDateDefinitions::NOVEMBER, 'day' => 2, 'hour' => 02, 'minute' => 59, 'second' => 59, 'offsetHours' => -3), array('year' => 2008, 'month' => AgaviDateDefinitions::NOVEMBER, 'day' => 2, 'hour' => 03, 'minute' => 00, 'second' => 00, 'offsetHours' => -2), array('year' => 2009, 'month' => AgaviDateDefinitions::FEBRUARY, 'day' => 22, 'hour' => 01, 'minute' => 59, 'second' => 59, 'offsetHours' => -2), array('year' => 2009, 'month' => AgaviDateDefinitions::FEBRUARY, 'day' => 22, 'hour' => 02, 'minute' => 00, 'second' => 00, 'offsetHours' => -3), array('year' => 2009, 'month' => AgaviDateDefinitions::NOVEMBER, 'day' => 1, 'hour' => 02, 'minute' => 59, 'second' => 59, 'offsetHours' => -3), array('year' => 2009, 'month' => AgaviDateDefinitions::NOVEMBER, 'day' => 1, 'hour' => 03, 'minute' => 00, 'second' => 00, 'offsetHours' => -2), array('year' => 2010, 'month' => AgaviDateDefinitions::FEBRUARY, 'day' => 28, 'hour' => 01, 'minute' => 59, 'second' => 59, 'offsetHours' => -2), array('year' => 2010, 'month' => AgaviDateDefinitions::FEBRUARY, 'day' => 28, 'hour' => 02, 'minute' => 00, 'second' => 00, 'offsetHours' => -3)); $timezones = array($tz1, $tz2); # TimeZone *tz; # UDate dt; # int32_t t, i, raw, dst; $t = $i = $raw = $dst = null; for ($t = 0; $t < count($timezones); ++$t) { $tz = clone $timezones[$t]; for ($i = 0; $i < count($data); ++$i) { $gc->set($data[$i]['year'], $data[$i]['month'], $data[$i]['day'], $data[$i]['hour'], $data[$i]['minute'], $data[$i]['second']); $dt = $gc->getTime(); $tz->getOffsetRef($dt, false, $raw, $dst); if ($raw + $dst != $data[$i]['offsetHours'] * AgaviDateDefinitions::MILLIS_PER_HOUR) { $this->fail(sprintf("test case %d.%d: tz.getOffset(%04d-%02d-%02d %02d:%02d:%02d) returns %d+%d != %d", $t, $i, $data[$i]['year'], $data[$i]['month'] + 1, $data[$i]['day'], $data[$i]['hour'], $data[$i]['minute'], $data[$i]['second'], $raw, $dst, $data[$i]['offsetHours'] * AgaviDateDefinitions::MILLIS_PER_HOUR)); } } } }
/** * Execute adding and rolling in GregorianCalendar extensively, */ public function testAddRollExtensive() { $maxlimit = 40; $y = 1997; $m = AgaviDateDefinitions::FEBRUARY; $d = 1; $hr = 1; $min = 1; $sec = 0; $ms = 0; $temp = new AgaviGregorianCalendar($this->tm, $y, $m, $d); $temp->set(AgaviDateDefinitions::HOUR, $hr); $temp->set(AgaviDateDefinitions::MINUTE, $min); $temp->set(AgaviDateDefinitions::SECOND, $sec); $temp->set(AgaviDateDefinitions::MILLISECOND, $ms); $e = AgaviDateDefinitions::YEAR; while ($e < AgaviDateDefinitions::FIELD_COUNT) { $limit = $maxlimit; for ($i = 0; $i < $limit; ++$i) { try { $temp->add($e, 1); // TODO: specify exact exception here } catch (Exception $ex) { $limit = $i; } } for ($i = 0; $i < $limit; ++$i) { $temp->add($e, -1); } $this->check520($temp, $y, $m, $d, $hr, $min, $sec, $ms, $e); ++$e; } $e = AgaviDateDefinitions::YEAR; while ($e < AgaviDateDefinitions::FIELD_COUNT) { $limit = $maxlimit; for ($i = 0; $i < $limit; ++$i) { try { $temp->roll($e, 1); // TODO: specify exact exception here } catch (Exception $ex) { $limit = $i; } } for ($i = 0; $i < $limit; ++$i) { $temp->roll($e, -1); } $this->check520($temp, $y, $m, $d, $hr, $min, $sec, $ms, $e); ++$e; } }
/** * Creates a new calendar instance with the current time set. * * @param mixed This can be either an AgaviLocale, an AgaviTimeZone or * a string specifying the calendar type. * * @return AgaviCalendar The current timezone instance. * * @author Dominik del Bondio <*****@*****.**> * @since 0.11.0 */ public function createCalendar($type = null) { $locale = $this->getCurrentLocale(); $calendarType = null; $zone = null; $time = null; if ($type instanceof AgaviLocale) { $locale = $type; } elseif ($type instanceof AgaviTimeZone) { $zone = $type; } elseif ($type instanceof DateTime) { $time = $type; } elseif (is_int($type)) { $time = $type * AgaviDateDefinitions::MILLIS_PER_SECOND; } elseif ($type !== null) { $calendarType = $type; } if ($time === null) { $time = AgaviCalendar::getNow(); } if (!$zone) { if ($locale->getLocaleTimeZone()) { $zone = $this->createTimeZone($locale->getLocaleTimeZone()); } } if (!$calendarType) { $calendarType = $locale->getLocaleCalendar(); if (!$calendarType) { $calendarType = AgaviCalendar::GREGORIAN; } } switch ($calendarType) { case AgaviCalendar::GREGORIAN: $c = new AgaviGregorianCalendar($this); break; default: throw new AgaviException('Calendar type ' . $calendarType . ' not supported'); } // Now, reset calendar to default state: if ($zone) { $c->setTimeZone($zone); } if ($time instanceof DateTime) { // FIXME: we can't use $time->getTimezone()->getName() here since that triggers // https://github.com/facebook/hhvm/issues/1777 but luckily using format('e') // works for both php and hhvm $tzName = $time->format('e'); if (preg_match('/^[+-0-9]/', $tzName)) { $tzName = 'GMT' . $tzName; } $c->setTimeZone($this->createTimeZone($tzName)); $dateStr = $time->format('Y z G i s'); list($year, $doy, $hour, $minute, $second) = explode(' ', $dateStr); $c->set(AgaviDateDefinitions::YEAR, $year); $c->set(AgaviDateDefinitions::DAY_OF_YEAR, $doy + 1); $c->set(AgaviDateDefinitions::HOUR_OF_DAY, $hour); $c->set(AgaviDateDefinitions::MINUTE, $minute); $c->set(AgaviDateDefinitions::SECOND, $second); // complete the calendar $c->getAll(); } else { $c->setTime($time); // let the new calendar have the current time. } return $c; }
/** * Creates a new calendar instance with the current time set. * * @param mixed This can be either an AgaviLocale, an AgaviTimeZone or * a string specifying the calendar type. * * @return AgaviCalendar The current timezone instance. * * @author Dominik del Bondio <*****@*****.**> * @since 0.11.0 */ public function createCalendar($type = null) { $locale = $this->getCurrentLocale(); $calendarType = null; $zone = null; $time = null; if ($type instanceof AgaviLocale) { $locale = $type; } elseif ($type instanceof AgaviTimeZone) { $zone = $type; } elseif ($type instanceof DateTime) { $time = $type; } elseif (is_int($type)) { $time = $type * AgaviDateDefinitions::MILLIS_PER_SECOND; } elseif ($type !== null) { $calendarType = $type; } if ($time === null) { $time = AgaviCalendar::getNow(); } if (!$zone) { if ($locale->getLocaleTimeZone()) { $zone = $this->createTimeZone($locale->getLocaleTimeZone()); } } if (!$calendarType) { $calendarType = $locale->getLocaleCalendar(); if (!$calendarType) { $calendarType = AgaviCalendar::GREGORIAN; } } switch ($calendarType) { case AgaviCalendar::GREGORIAN: $c = new AgaviGregorianCalendar($this); break; default: throw new AgaviException('Calendar type ' . $calendarType . ' not supported'); } // Now, reset calendar to default state: if ($zone) { $c->setTimeZone($zone); } if ($time instanceof DateTime) { $tzName = $time->getTimezone()->getName(); if (version_compare(PHP_VERSION, '5.3', '<')) { // when a datetime object is created with an timezone offset like in '2005-02-21 00:00:00+01:00' // php falsely returns the name of the current default timezone as the name of the datetimes timezone // but luckily timezone abbreviation (T) is GMT name (GMT-0200) of the timezone // to not accidentally report dates which are really in the default timezone the name is explicitly checked if ($tzName == date_default_timezone_get()) { $abbr = $time->format('T'); if (preg_match('/^GMT[+-]\\d{4}$/', $abbr)) { $tzName = $abbr; } } } if (preg_match('/^[+-0-9]/', $tzName)) { $tzName = 'GMT' . $tzName; } $c->setTimeZone($this->createTimeZone($tzName)); $dateStr = $time->format('Y z G i s'); list($year, $doy, $hour, $minute, $second) = explode(' ', $dateStr); $c->set(AgaviDateDefinitions::YEAR, $year); $c->set(AgaviDateDefinitions::DAY_OF_YEAR, $doy + 1); $c->set(AgaviDateDefinitions::HOUR_OF_DAY, $hour); $c->set(AgaviDateDefinitions::MINUTE, $minute); $c->set(AgaviDateDefinitions::SECOND, $second); // complete the calendar $c->getAll(); } else { $c->setTime($time); // let the new calendar have the current time. } return $c; }