mirror of
https://github.com/rlanvin/php-rrule.git
synced 2025-03-14 06:29:16 +01:00
Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
343c015118 | ||
|
fd213620c2 | ||
|
c68668b195 | ||
|
bef9568de9 | ||
|
cb5c6f44f2 | ||
|
a2dd785693 | ||
|
4b19d8ef60 | ||
|
747bc473d9 | ||
|
0f48c3f93b | ||
|
75b76c85c9 | ||
|
9fd062ae06 | ||
|
44d5b4c3f0 | ||
|
963a466e48 | ||
|
8bbb753c5a | ||
|
949addfb2d | ||
|
7af14b7dac | ||
|
9cd53d7bcc | ||
|
2cad19b61d | ||
|
bef5c05e43 | ||
|
7ddef3d49b | ||
|
ccbc749f65 | ||
|
38ea18eb55 | ||
|
f7bcad3538 | ||
|
2acd9950e8 | ||
|
fa13bf3f6b | ||
|
15646f89da | ||
|
8ad92b376d | ||
|
bfb494a64d | ||
|
dbeabe3e2f | ||
|
601e2cccfb | ||
|
32bab843e4 | ||
|
d29a068795 | ||
|
2221e8cdac | ||
|
5ef9eedb5d |
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/.github/ export-ignore
|
||||
/tests/ export-ignore
|
||||
/.gitattributes export-ignore
|
||||
/.gitignore export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@ -7,7 +7,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
php: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1']
|
||||
php: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
|
||||
name: PHP ${{ matrix.php }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,4 +2,5 @@
|
||||
vendor
|
||||
test.php
|
||||
composer.lock
|
||||
.idea
|
||||
.idea
|
||||
.phpunit.result.cache
|
79
CHANGELOG.md
79
CHANGELOG.md
@ -1,5 +1,70 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2.5.2] - 2025-02-21
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix "november" typo in french translation [#155](https://github.com/rlanvin/php-rrule/issues/155)
|
||||
- Fix incorrect calculation from partially filled cache [#160](https://github.com/rlanvin/php-rrule/issues/160)
|
||||
|
||||
## [2.5.1] - 2024-06-23
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix insufficient type detection for FREQ and WKST leading to deprecation warning in tests with PHP 8.3 [#149](https://github.com/rlanvin/php-rrule/pull/149)
|
||||
- Fix failing tests with ICU 72.1 because of NNBSP
|
||||
- Fix C.UTF8 locale support when intl isn't installed
|
||||
|
||||
## [2.5.0] - 2024-06-08
|
||||
|
||||
### Fixed
|
||||
|
||||
- Swedish: Corrects the spelling of monday and the grammar of enumeration partials. [#134](https://github.com/rlanvin/php-rrule/pull/134)
|
||||
- Spanish: Improve clarity in daily and weekly recurrence translation [#147](https://github.com/rlanvin/php-rrule/pull/147)
|
||||
- Dutch: weekdays and months are written in lowercase [#136](https://github.com/rlanvin/php-rrule/pull/136)
|
||||
- Better handle TZ with Exchange / M365 generated iCal files [#143](https://github.com/rlanvin/php-rrule/pull/143)
|
||||
|
||||
### Added
|
||||
|
||||
- Human readable time of day option [#124](https://github.com/rlanvin/php-rrule/pull/124)
|
||||
- Japanese translation [#139](https://github.com/rlanvin/php-rrule/pull/139)
|
||||
- Czech translation [#137](https://github.com/rlanvin/php-rrule/pull/137)
|
||||
|
||||
## [2.4.1] - 2023-06-07
|
||||
|
||||
### Fixed
|
||||
|
||||
- Correctly parse `DateTimeImmutable` [#132](https://github.com/rlanvin/php-rrule/pull/132)
|
||||
- Fix namespace on return type [#130](https://github.com/rlanvin/php-rrule/pull/130)
|
||||
- Humanreadable gets monthly wrong [#129](https://github.com/rlanvin/php-rrule/pull/129)
|
||||
|
||||
## [2.4.0] - 2023-01-06
|
||||
|
||||
### Fixed
|
||||
|
||||
- Exclude files from dist packages [#110](https://github.com/rlanvin/php-rrule/pull/110)
|
||||
- Improve German translation [#112](https://github.com/rlanvin/php-rrule/issues/112)
|
||||
- Daylight Saving Time issue with PHP 8.1 [#120](https://github.com/rlanvin/php-rrule/issues/120)
|
||||
|
||||
### Added
|
||||
|
||||
- Added Portuguese translation [#108](https://github.com/rlanvin/php-rrule/pull/108)
|
||||
- Added Polish translation [#106](https://github.com/rlanvin/php-rrule/pull/106)
|
||||
|
||||
## [2.3.2] - 2022-05-03
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix timezone (and the entire rule) changed to uppercase if rule was created using `createdFromRfcString` [#103](https://github.com/rlanvin/php-rrule/issues/103)
|
||||
|
||||
## [2.3.1] - 2022-04-22
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix microseconds not always removed from dtstart, causing date comparison issues with specific date input [#104](https://github.com/rlanvin/php-rrule/issues/104)
|
||||
|
||||
## [2.3.0] - 2021-10-25
|
||||
|
||||
### Added
|
||||
@ -56,7 +121,7 @@
|
||||
|
||||
## [2.0.0-rc1] - 2019-01-13
|
||||
|
||||
- Rewrite the core algorithm to use a native PHP generator, drop compability with PHP < 5.6 [#43](https://github.com/rlanvin/php-rrule/issues/43)
|
||||
- Rewrite the core algorithm to use a native PHP generator, drop compatibility with PHP < 5.6 [#43](https://github.com/rlanvin/php-rrule/issues/43)
|
||||
|
||||
### Added
|
||||
|
||||
@ -170,7 +235,7 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
- `RRule::parseRfcString()` is strictier and will not accept invalid `DTSTART` and `UNTIL` formats (use the array syntax in the constructor with `DateTime` objects if you need to create rules with complex combinations of timezones). [#13](https://github.com/rlanvin/php-rrule/issues/13)
|
||||
- `RRule::parseRfcString()` is stricter and will not accept invalid `DTSTART` and `UNTIL` formats (use the array syntax in the constructor with `DateTime` objects if you need to create rules with complex combinations of timezones). [#13](https://github.com/rlanvin/php-rrule/issues/13)
|
||||
|
||||
## [1.2.0] - 2016-04-09
|
||||
|
||||
@ -215,7 +280,15 @@
|
||||
|
||||
- First release, everything before that was unversioned (`dev-master` was used).
|
||||
|
||||
[Unreleased]: https://github.com/rlanvin/php-rrule/compare/v2.2.2...HEAD
|
||||
[Unreleased]: https://github.com/rlanvin/php-rrule/compare/v2.5.2...HEAD
|
||||
[2.5.2]: https://github.com/rlanvin/php-rrule/compare/v2.5.1...v2.5.2
|
||||
[2.5.1]: https://github.com/rlanvin/php-rrule/compare/v2.5.0...v2.5.1
|
||||
[2.5.0]: https://github.com/rlanvin/php-rrule/compare/v2.4.1...v2.5.0
|
||||
[2.4.1]: https://github.com/rlanvin/php-rrule/compare/v2.4.0...v2.4.1
|
||||
[2.4.0]: https://github.com/rlanvin/php-rrule/compare/v2.3.2...v2.4.0
|
||||
[2.3.2]: https://github.com/rlanvin/php-rrule/compare/v2.3.1...v2.3.2
|
||||
[2.3.1]: https://github.com/rlanvin/php-rrule/compare/v2.3.0...v2.3.1
|
||||
[2.3.0]: https://github.com/rlanvin/php-rrule/compare/v2.2.2...v2.3.0
|
||||
[2.2.2]: https://github.com/rlanvin/php-rrule/compare/v2.2.1...v2.2.2
|
||||
[2.2.1]: https://github.com/rlanvin/php-rrule/compare/v2.2.0...v2.2.1
|
||||
[2.2.0]: https://github.com/rlanvin/php-rrule/compare/v2.1.0...v2.2.0
|
||||
|
@ -76,7 +76,7 @@ you `lno1wkst`). I tried to comment and explain as much of the algorithm as poss
|
||||
in this PHP port, so feel free to check the code if you're interested.
|
||||
|
||||
The lib differs from the python version in various aspects, notably in the
|
||||
respect of the RFC. This version is a bit strictier and will not accept many
|
||||
respect of the RFC. This version is a bit stricter and will not accept many
|
||||
non-compliant combinations of rule parts, that the python version otherwise accepts.
|
||||
There are also some additional features in this version.
|
||||
|
||||
|
119
src/RRule.php
119
src/RRule.php
@ -179,7 +179,7 @@ class RRule implements RRuleInterface
|
||||
|
||||
/**
|
||||
* The constructor needs the entire rule at once.
|
||||
* There is no setter after the class has been instanciated,
|
||||
* There is no setter after the class has been instantiated,
|
||||
* because in order to validate some BYXXX parts, we need to know
|
||||
* the value of some other parts (FREQ or other BXXX parts).
|
||||
*
|
||||
@ -219,34 +219,38 @@ class RRule implements RRuleInterface
|
||||
$this->rule = $parts; // save original rule
|
||||
|
||||
// WKST
|
||||
$parts['WKST'] = strtoupper($parts['WKST']);
|
||||
if (! array_key_exists($parts['WKST'], self::WEEKDAYS)) {
|
||||
if (is_string($parts['WKST'])) {
|
||||
$parts['WKST'] = strtoupper($parts['WKST']);
|
||||
if (array_key_exists($parts['WKST'], self::WEEKDAYS)) {
|
||||
$this->wkst = self::WEEKDAYS[$parts['WKST']];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->wkst) {
|
||||
throw new \InvalidArgumentException(
|
||||
'The WKST rule part must be one of the following: '
|
||||
.implode(', ',array_keys(self::WEEKDAYS))
|
||||
);
|
||||
}
|
||||
$this->wkst = self::WEEKDAYS[$parts['WKST']];
|
||||
|
||||
// FREQ
|
||||
if (is_integer($parts['FREQ'])) {
|
||||
if ($parts['FREQ'] > self::SECONDLY || $parts['FREQ'] < self::YEARLY) {
|
||||
throw new \InvalidArgumentException(
|
||||
'The FREQ rule part must be one of the following: '
|
||||
.implode(', ',array_keys(self::FREQUENCIES))
|
||||
);
|
||||
if ($parts['FREQ'] <= self::SECONDLY && $parts['FREQ'] >= self::YEARLY) {
|
||||
$this->freq = $parts['FREQ'];
|
||||
}
|
||||
$this->freq = $parts['FREQ'];
|
||||
}
|
||||
else { // string
|
||||
elseif (is_string($parts['FREQ'])) {
|
||||
$parts['FREQ'] = strtoupper($parts['FREQ']);
|
||||
if (! array_key_exists($parts['FREQ'], self::FREQUENCIES)) {
|
||||
throw new \InvalidArgumentException(
|
||||
'The FREQ rule part must be one of the following: '
|
||||
.implode(', ',array_keys(self::FREQUENCIES))
|
||||
);
|
||||
if (array_key_exists($parts['FREQ'], self::FREQUENCIES)) {
|
||||
$this->freq = self::FREQUENCIES[$parts['FREQ']];
|
||||
}
|
||||
$this->freq = self::FREQUENCIES[$parts['FREQ']];
|
||||
}
|
||||
|
||||
if (!$this->freq) {
|
||||
throw new \InvalidArgumentException(
|
||||
'The FREQ rule part must be one of the following: '
|
||||
.implode(', ',array_keys(self::FREQUENCIES))
|
||||
);
|
||||
}
|
||||
|
||||
// INTERVAL
|
||||
@ -558,7 +562,7 @@ class RRule implements RRuleInterface
|
||||
/**
|
||||
* Format a rule according to RFC 5545
|
||||
*
|
||||
* @param bool $include_timezone Wether to generate a rule with timezone identifier on DTSTART (and UNTIL) or not.
|
||||
* @param bool $include_timezone Whether to generate a rule with timezone identifier on DTSTART (and UNTIL) or not.
|
||||
* @return string
|
||||
*/
|
||||
public function rfcString($include_timezone = true)
|
||||
@ -670,8 +674,8 @@ class RRule implements RRuleInterface
|
||||
|
||||
if (! $force_rset) {
|
||||
// try to detect if we have a RRULE or a set
|
||||
$string = strtoupper($string);
|
||||
$nb_rrule = substr_count($string, 'RRULE');
|
||||
$upper_string = strtoupper($string);
|
||||
$nb_rrule = substr_count($upper_string, 'RRULE');
|
||||
if ($nb_rrule == 0) {
|
||||
$class = '\RRule\RRule';
|
||||
}
|
||||
@ -680,7 +684,7 @@ class RRule implements RRuleInterface
|
||||
}
|
||||
else {
|
||||
$class = '\RRule\RRule';
|
||||
if (strpos($string, 'EXDATE') !== false || strpos($string, 'RDATE') !== false || strpos($string, 'EXRULE') !== false) {
|
||||
if (strpos($upper_string, 'EXDATE') !== false || strpos($upper_string, 'RDATE') !== false || strpos($upper_string, 'EXRULE') !== false) {
|
||||
$class = '\RRule\RSet';
|
||||
}
|
||||
}
|
||||
@ -717,7 +721,7 @@ class RRule implements RRuleInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the rrule has no end condition (infite)
|
||||
* Return true if the rrule has no end condition (infinite)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@ -822,7 +826,7 @@ class RRule implements RRuleInterface
|
||||
}
|
||||
}
|
||||
|
||||
// so now we have exhausted all the BYXXX rules (exept bysetpos),
|
||||
// so now we have exhausted all the BYXXX rules (except bysetpos),
|
||||
// we still need to consider frequency and interval
|
||||
list($start_year, $start_month) = explode('-',$this->dtstart->format('Y-m'));
|
||||
switch ($this->freq) {
|
||||
@ -916,6 +920,7 @@ class RRule implements RRuleInterface
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($offset)
|
||||
@ -925,6 +930,7 @@ class RRule implements RRuleInterface
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
@ -958,6 +964,7 @@ class RRule implements RRuleInterface
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($offset, $value)
|
||||
@ -967,6 +974,7 @@ class RRule implements RRuleInterface
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($offset)
|
||||
@ -980,7 +988,7 @@ class RRule implements RRuleInterface
|
||||
/**
|
||||
* Returns the number of occurrences in this rule. It will have go
|
||||
* through the whole recurrence, if this hasn't been done before, which
|
||||
* introduces a performance penality.
|
||||
* introduces a performance penalty.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@ -1354,16 +1362,8 @@ class RRule implements RRuleInterface
|
||||
|
||||
if ($occurrence) {
|
||||
$dtstart = clone $occurrence; // since DateTime is not immutable, clone to avoid any problem
|
||||
// so we skip the last occurrence of the cache
|
||||
if ($this->freq === self::SECONDLY) {
|
||||
$dtstart = $dtstart->modify('+'.$this->interval.'second');
|
||||
}
|
||||
else {
|
||||
$dtstart = $dtstart->modify('+1second');
|
||||
}
|
||||
}
|
||||
|
||||
if ($dtstart === null) {
|
||||
elseif ($dtstart === null) {
|
||||
$dtstart = clone $this->dtstart;
|
||||
}
|
||||
|
||||
@ -1391,7 +1391,7 @@ class RRule implements RRuleInterface
|
||||
$timeset = $this->timeset;
|
||||
}
|
||||
else {
|
||||
// initialize empty if it's not going to occurs on the first iteration
|
||||
// initialize empty if it's not going to occur on the first iteration
|
||||
if (
|
||||
($this->freq >= self::HOURLY && $this->byhour && ! in_array($hour, $this->byhour))
|
||||
|| ($this->freq >= self::MINUTELY && $this->byminute && ! in_array($minute, $this->byminute))
|
||||
@ -1404,8 +1404,21 @@ class RRule implements RRuleInterface
|
||||
}
|
||||
}
|
||||
|
||||
// if we restarted the calculation from cache, we know that dtstart has already been yielded
|
||||
// so we can skip ahead to the next second to avoid the same date to be yielded again
|
||||
// we need to do that after the correct frame as been set (see https://github.com/rlanvin/php-rrule/issues/160)
|
||||
if ($occurrence) {
|
||||
if ($this->freq === self::SECONDLY) {
|
||||
$dtstart = $dtstart->modify('+'.$this->interval.'second');
|
||||
}
|
||||
else {
|
||||
$dtstart = $dtstart->modify('+1second');
|
||||
}
|
||||
}
|
||||
|
||||
$max_cycles = self::MAX_CYCLES[$this->freq <= self::DAILY ? $this->freq : self::DAILY];
|
||||
for ($i = 0; $i < $max_cycles; $i++) {
|
||||
|
||||
// 1. get an array of all days in the next interval (day, month, week, etc.)
|
||||
// we filter out from this array all days that do not match the BYXXX conditions
|
||||
// to speed things up, we use days of the year (day numbers) instead of date
|
||||
@ -1506,8 +1519,8 @@ class RRule implements RRuleInterface
|
||||
$tmp = $year.':'.$yearday.':'.$time[0].':'.$time[1].':'.$time[2];
|
||||
if (! isset($filtered_set[$tmp])) {
|
||||
$occurrence = \DateTime::createFromFormat(
|
||||
'Y z',
|
||||
"$year $yearday",
|
||||
'Y z H:i:s',
|
||||
"$year $yearday 00:00:00",
|
||||
$this->dtstart->getTimezone()
|
||||
);
|
||||
$occurrence->setTime($time[0], $time[1], $time[2]);
|
||||
@ -1538,6 +1551,7 @@ class RRule implements RRuleInterface
|
||||
$this->total = $total;
|
||||
return;
|
||||
}
|
||||
|
||||
$total += 1;
|
||||
$this->cache[] = clone $occurrence;
|
||||
yield clone $occurrence; // yield
|
||||
@ -1549,8 +1563,8 @@ class RRule implements RRuleInterface
|
||||
// normal loop, without BYSETPOS
|
||||
foreach ($dayset as $yearday) {
|
||||
$occurrence = \DateTime::createFromFormat(
|
||||
'Y z',
|
||||
"$year $yearday",
|
||||
'Y z H:i:s',
|
||||
"$year $yearday 00:00:00",
|
||||
$this->dtstart->getTimezone()
|
||||
);
|
||||
|
||||
@ -1563,12 +1577,12 @@ class RRule implements RRuleInterface
|
||||
return;
|
||||
}
|
||||
|
||||
// next($timeset);
|
||||
if ($occurrence >= $dtstart) { // ignore occurrences before DTSTART
|
||||
if ($this->count && $total >= $this->count) {
|
||||
$this->total = $total;
|
||||
return;
|
||||
}
|
||||
|
||||
$total += 1;
|
||||
$this->cache[] = clone $occurrence;
|
||||
yield clone $occurrence; // yield
|
||||
@ -2065,9 +2079,10 @@ class RRule implements RRuleInterface
|
||||
* | `locale` | string | The locale to use (autodetect)
|
||||
* | `fallback` | string | Fallback locale if main locale is not found (default en)
|
||||
* | `date_formatter` | callable| Function used to format the date (takes date, returns formatted)
|
||||
* | `explicit_inifite`| bool | Mention "forever" if the rule is infinite (true)
|
||||
* | `explicit_infinite`| bool | Mention "forever" if the rule is infinite (true)
|
||||
* | `dtstart` | bool | Mention the start date (true)
|
||||
* | `include_start` | bool |
|
||||
* | `start_time_only` | bool | Mention the time of day only, without the date
|
||||
* | `include_until` | bool |
|
||||
* | `custom_path` | string |
|
||||
*
|
||||
@ -2088,6 +2103,7 @@ class RRule implements RRuleInterface
|
||||
'fallback' => 'en',
|
||||
'explicit_infinite' => true,
|
||||
'include_start' => true,
|
||||
'start_time_only' => false,
|
||||
'include_until' => true,
|
||||
'custom_path' => null
|
||||
);
|
||||
@ -2097,13 +2113,13 @@ class RRule implements RRuleInterface
|
||||
$default_opt['locale'] = \Locale::getDefault();
|
||||
} else {
|
||||
$default_opt['locale'] = setlocale(LC_CTYPE, 0);
|
||||
if ($default_opt['locale'] == 'C') {
|
||||
if (!$default_opt['locale'] || $default_opt['locale'][0] == 'C') {
|
||||
$default_opt['locale'] = 'en';
|
||||
}
|
||||
}
|
||||
|
||||
if ($opt['use_intl']) {
|
||||
$default_opt['date_format'] = \IntlDateFormatter::SHORT;
|
||||
$default_opt['date_format'] = isset($opt['start_time_only']) && $opt['start_time_only'] ? \IntlDateFormatter::NONE : \IntlDateFormatter::SHORT;
|
||||
if ($this->freq >= self::SECONDLY || not_empty($this->rule['BYSECOND'])) {
|
||||
$default_opt['time_format'] = \IntlDateFormatter::LONG;
|
||||
}
|
||||
@ -2111,7 +2127,7 @@ class RRule implements RRuleInterface
|
||||
$default_opt['time_format'] = \IntlDateFormatter::SHORT;
|
||||
}
|
||||
else {
|
||||
$default_opt['time_format'] = \IntlDateFormatter::NONE;
|
||||
$default_opt['time_format'] = isset($opt['start_time_only']) && $opt['start_time_only'] ? \IntlDateFormatter::SHORT : \IntlDateFormatter::NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2146,8 +2162,9 @@ class RRule implements RRuleInterface
|
||||
};
|
||||
}
|
||||
else {
|
||||
$opt['date_formatter'] = function($date) {
|
||||
return $date->format('Y-m-d H:i:s');
|
||||
$opt['date_formatter'] = function ($date) use ($opt) {
|
||||
$format = $opt['start_time_only'] ? 'H:i:s' : 'Y-m-d H:i:s';
|
||||
return $date->format($format);
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -2183,6 +2200,11 @@ class RRule implements RRuleInterface
|
||||
$parts['bymonth'] = strtr(self::i18nSelect($i18n['bymonth'], count($tmp)), array(
|
||||
'%{months}' => self::i18nList($tmp, $i18n['and'])
|
||||
));
|
||||
|
||||
if ($freq_str == 'yearly') {
|
||||
// if a yearly frequency is being displayed by month, then switch "of the year" text to be monthly
|
||||
$freq_str = 'monthly';
|
||||
}
|
||||
}
|
||||
|
||||
if (not_empty($this->rule['BYWEEKNO'])) {
|
||||
@ -2354,7 +2376,12 @@ class RRule implements RRuleInterface
|
||||
|
||||
if ($opt['include_start']) {
|
||||
// from X
|
||||
$parts['start'] = strtr($i18n['dtstart'], array(
|
||||
if ($opt['start_time_only']) {
|
||||
$value = $this->freq >= self::HOURLY ? 'startingtimeofday' : 'timeofday';
|
||||
} else {
|
||||
$value = 'dtstart';
|
||||
}
|
||||
$parts['start'] = strtr($i18n[$value], array(
|
||||
'%{date}' => $opt['date_formatter']($this->dtstart)
|
||||
));
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ interface RRuleInterface extends \ArrayAccess, \Countable, \IteratorAggregate
|
||||
public function getOccurrences($limit = null);
|
||||
|
||||
/**
|
||||
* Return all the ocurrences after a date, before a date, or between two dates.
|
||||
* Return all the occurrences after a date, before a date, or between two dates.
|
||||
*
|
||||
* @param mixed $begin Can be null to return all occurrences before $end
|
||||
* @param mixed $end Can be null to return all occurrences after $begin
|
||||
@ -49,7 +49,7 @@ interface RRuleInterface extends \ArrayAccess, \Countable, \IteratorAggregate
|
||||
*
|
||||
* @param mixed $date
|
||||
* @param int $index The index (starts at 1)
|
||||
* @return DateTimeInterface|null
|
||||
* @return \DateTimeInterface|null
|
||||
*/
|
||||
public function getNthOccurrenceAfter($date, $index);
|
||||
|
||||
@ -68,7 +68,7 @@ interface RRuleInterface extends \ArrayAccess, \Countable, \IteratorAggregate
|
||||
*
|
||||
* @param mixed $date
|
||||
* @param int $index The index (starts at 1)
|
||||
* @return DateTimeInterface|null
|
||||
* @return \DateTimeInterface|null
|
||||
*/
|
||||
public function getNthOccurrenceBefore($date, $index);
|
||||
|
||||
@ -77,7 +77,7 @@ interface RRuleInterface extends \ArrayAccess, \Countable, \IteratorAggregate
|
||||
*
|
||||
* @param mixed $date
|
||||
* @param int $index 0 returns the date, positive integer returns index in the future, negative in the past
|
||||
* @return DateTimeInterface|null
|
||||
* @return \DateTimeInterface|null
|
||||
*/
|
||||
public function getNthOccurrenceFrom($date, $index);
|
||||
|
||||
@ -97,9 +97,9 @@ interface RRuleInterface extends \ArrayAccess, \Countable, \IteratorAggregate
|
||||
public function isFinite();
|
||||
|
||||
/**
|
||||
* Return true if the rrule has no end condition (infite)
|
||||
* Return true if the rrule has no end condition (infinite)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isInfinite();
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ trait RRuleTrait
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the ocurrences after a date, before a date, or between two dates.
|
||||
* Return all the occurrences after a date, before a date, or between two dates.
|
||||
*
|
||||
* @param mixed $begin Can be null to return all occurrences before $end
|
||||
* @param mixed $end Can be null to return all occurrences after $begin
|
||||
@ -204,6 +204,22 @@ trait RRuleTrait
|
||||
else {
|
||||
$date = clone $date; // avoid reference problems
|
||||
}
|
||||
|
||||
// ensure there is no microseconds in the DateTime object even if
|
||||
// the input contained microseconds, to avoid date comparison issues
|
||||
// (see #104)
|
||||
if (version_compare(PHP_VERSION, '7.1.0') < 0) {
|
||||
$date = new \DateTime($date->format('Y-m-d H:i:s'), $date->getTimezone());
|
||||
}
|
||||
else {
|
||||
$date = $date->setTime(
|
||||
$date->format('H'),
|
||||
$date->format('i'),
|
||||
$date->format('s'),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
src/RSet.php
10
src/RSet.php
@ -202,7 +202,7 @@ class RSet implements RRuleInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a RDATE (renamed Date for simplicy, since we don't support full RDATE syntax at the moment)
|
||||
* Add a RDATE (renamed Date for simplicity, since we don't support full RDATE syntax at the moment)
|
||||
*
|
||||
* @param mixed $date a valid date representation or a \DateTime object
|
||||
* @return $this
|
||||
@ -382,7 +382,7 @@ class RSet implements RRuleInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the rrule has no end condition (infite)
|
||||
* Return true if the rrule has no end condition (infinite)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@ -488,6 +488,7 @@ class RSet implements RRuleInterface
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($offset)
|
||||
@ -497,6 +498,7 @@ class RSet implements RRuleInterface
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
@ -530,6 +532,7 @@ class RSet implements RRuleInterface
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($offset, $value)
|
||||
@ -539,6 +542,7 @@ class RSet implements RRuleInterface
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($offset)
|
||||
@ -552,7 +556,7 @@ class RSet implements RRuleInterface
|
||||
/**
|
||||
* Returns the number of recurrences in this set. It will have go
|
||||
* through the whole recurrence, if this hasn't been done before, which
|
||||
* introduces a performance penality.
|
||||
* introduces a performance penalty.
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
|
@ -64,7 +64,7 @@ class RfcParser
|
||||
/**
|
||||
* Parse both DTSTART and RRULE (and EXRULE).
|
||||
*
|
||||
* It's impossible to accuratly parse a RRULE in isolation (without the DTSTART)
|
||||
* It's impossible to accurately parse a RRULE in isolation (without the DTSTART)
|
||||
* as some tests depends on DTSTART (notably the date format for UNTIL).
|
||||
*
|
||||
* @param string $string The RFC-like string
|
||||
@ -193,7 +193,7 @@ class RfcParser
|
||||
}
|
||||
// this is an invalid rule, however we'll support it since the JS lib is broken
|
||||
// see https://github.com/rlanvin/php-rrule/issues/25
|
||||
trigger_error("This string is not compliant with the RFC (DTSTART cannot be part of RRULE). It is accepted as is for compability reasons only.", E_USER_NOTICE);
|
||||
trigger_error("This string is not compliant with the RFC (DTSTART cannot be part of RRULE). It is accepted as is for compatibility reasons only.", E_USER_NOTICE);
|
||||
}
|
||||
$parts[$key] = $value;
|
||||
}
|
||||
@ -221,7 +221,7 @@ class RfcParser
|
||||
foreach ($property['params'] as $name => $value) {
|
||||
switch (strtoupper($name)) {
|
||||
case 'TZID':
|
||||
$tz = new \DateTimeZone($value);
|
||||
$tz = self::parseTimeZone($value);
|
||||
break;
|
||||
case 'VALUE':
|
||||
switch ($value) {
|
||||
@ -284,7 +284,7 @@ class RfcParser
|
||||
// Ignore optional words
|
||||
break;
|
||||
case 'TZID':
|
||||
$tz = new \DateTimeZone($value);
|
||||
$tz = self::parseTimeZone($value);
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException("Unknown property parameter: $name");
|
||||
@ -325,4 +325,4 @@ class RfcParser
|
||||
|
||||
return new \DateTimeZone($tzid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
174
src/i18n/cz.php
Normal file
174
src/i18n/cz.php
Normal file
@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Translation file for Czech language.
|
||||
*
|
||||
* Most strings can be an array, with a value as the key. The system will
|
||||
* pick the translation corresponding to the key. The key "else" will be picked
|
||||
* if no matching value is found. This is useful for plurals.
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file.
|
||||
*
|
||||
* @author Jakub Kluvánek <jakub@kluvanek.dev>
|
||||
* @link https://github.com/rlanvin/php-rrule
|
||||
*/
|
||||
return array(
|
||||
'yearly' => array(
|
||||
'1' => 'ročně',
|
||||
'2' => 'každé %{interval} roky',
|
||||
'3' => 'každé %{interval} roky',
|
||||
'4' => 'každé %{interval} roky',
|
||||
'else' => 'každých %{interval} let'
|
||||
),
|
||||
'monthly' => array(
|
||||
'1' => 'měsíčně',
|
||||
'2' => 'každé %{interval} měsíce',
|
||||
'3' => 'každé %{interval} měsíce',
|
||||
'4' => 'každé %{interval} měsíce',
|
||||
'else' => 'každých %{interval} měsíců'
|
||||
),
|
||||
'weekly' => array(
|
||||
'1' => 'týdně',
|
||||
'2' => 'každé %{interval} týdny',
|
||||
'3' => 'každé %{interval} týdny',
|
||||
'4' => 'každé %{interval} týdny',
|
||||
'else' => 'každých %{interval} týdnů'
|
||||
),
|
||||
'daily' => array(
|
||||
'1' => 'denně',
|
||||
'2' => 'každé %{interval} dny',
|
||||
'3' => 'každé %{interval} dny',
|
||||
'4' => 'každé %{interval} dny',
|
||||
'5' => 'každé %{interval} dny',
|
||||
'else' => 'každých %{interval} dnů'
|
||||
),
|
||||
'hourly' => array(
|
||||
'1' => 'každou hodinu',
|
||||
'2' => 'každé %{interval} hodiny',
|
||||
'3' => 'každé %{interval} hodiny',
|
||||
'4' => 'každé %{interval} hodiny',
|
||||
'else' => 'každých %{interval} hodin'
|
||||
),
|
||||
'minutely' => array(
|
||||
'1' => 'každou minutu',
|
||||
'2' => 'každé %{interval} minuty',
|
||||
'3' => 'každé %{interval} minuty',
|
||||
'4' => 'každé %{interval} minuty',
|
||||
'else' => 'každých %{interval} minut'
|
||||
),
|
||||
'secondly' => array(
|
||||
'1' => 'každou sekundu',
|
||||
'2' => 'každé %{interval} sekundy',
|
||||
'3' => 'každé %{interval} sekundy',
|
||||
'4' => 'každé %{interval} sekundy',
|
||||
'else' => 'každých %{interval} sekund'
|
||||
),
|
||||
'dtstart' => ', počínaje %{date}',
|
||||
'timeofday' => ' v %{date}',
|
||||
'startingtimeofday' => ' začínající v %{date}',
|
||||
'infinite' => ', navždy',
|
||||
'until' => ', do %{date}',
|
||||
'count' => array(
|
||||
'1' => ', jednou',
|
||||
'else' => ', %{count}x'
|
||||
),
|
||||
'and' => 'a ',
|
||||
'x_of_the_y' => array(
|
||||
'yearly' => '%{x} roku', // e.g. the first Monday of the year, or the first day of the year
|
||||
'monthly' => '%{x} měsíce'
|
||||
),
|
||||
'bymonth' => ' v %{months}',
|
||||
'months' => array(
|
||||
1 => 'leden',
|
||||
2 => 'únor',
|
||||
3 => 'březen',
|
||||
4 => 'duben',
|
||||
5 => 'květen',
|
||||
6 => 'červen',
|
||||
7 => 'červenec',
|
||||
8 => 'srpen',
|
||||
9 => 'září',
|
||||
10 => 'říjen',
|
||||
11 => 'listopad',
|
||||
12 => 'prosinec'
|
||||
),
|
||||
'byweekday' => ' v %{weekdays}',
|
||||
'weekdays' => array(
|
||||
1 => 'pondělí',
|
||||
2 => 'úterý',
|
||||
3 => 'středa',
|
||||
4 => 'čtvrtek',
|
||||
5 => 'pátek',
|
||||
6 => 'sobota',
|
||||
7 => 'neděle'
|
||||
),
|
||||
'nth_weekday' => array(
|
||||
'1' => 'první %{weekday}', // e.g. the first Monday
|
||||
'2' => 'druhé %{weekday}',
|
||||
'3' => 'třetí %{weekday}',
|
||||
'else' => '%{n}. %{weekday}'
|
||||
),
|
||||
'-nth_weekday' => array(
|
||||
'-1' => 'poslední %{weekday}', // e.g. the last Monday
|
||||
'-2' => 'předposlední %{weekday}',
|
||||
'else' => '%{n}. od konce %{weekday}'
|
||||
),
|
||||
'byweekno' => array(
|
||||
'1' => ' v týdnu %{weeks}',
|
||||
'else' => ' v týdnech č.%{weeks}'
|
||||
),
|
||||
'nth_weekno' => '%{n}',
|
||||
'bymonthday' => ' ve dnech %{monthdays}',
|
||||
'nth_monthday' => array(
|
||||
'else' => '%{n}.'
|
||||
),
|
||||
'-nth_monthday' => array(
|
||||
'-1' => 'poslední',
|
||||
'-2' => 'předposlední',
|
||||
'else' => '%{n}. od konce'
|
||||
),
|
||||
'byyearday' => array(
|
||||
'1' => ' ve dnu %{yeardays}',
|
||||
'else' => ' ve dnech %{yeardays}'
|
||||
),
|
||||
'nth_yearday' => array(
|
||||
'1' => 'první',
|
||||
'2' => 'druhý',
|
||||
'3' => 'třetí',
|
||||
'else' => '%{n}.'
|
||||
),
|
||||
'-nth_yearday' => array(
|
||||
'-1' => 'poslední',
|
||||
'-2' => 'předposlední',
|
||||
'else' => '%{n}. od konce'
|
||||
),
|
||||
'byhour' => array(
|
||||
'1' => ' v hodině %{hours}',
|
||||
'else' => ' v hodiny %{hours}'
|
||||
),
|
||||
'nth_hour' => '%{n}h',
|
||||
'byminute' => array(
|
||||
'1' => ' v minutu %{minutes}',
|
||||
'else' => ' v minuty %{minutes}'
|
||||
),
|
||||
'nth_minute' => '%{n}',
|
||||
'bysecond' => array(
|
||||
'1' => ' v sekundě %{seconds}',
|
||||
'else' => ' v sekundy %{seconds}'
|
||||
),
|
||||
'nth_second' => '%{n}',
|
||||
'bysetpos' => ', ale pouze %{setpos} instance sady',
|
||||
'nth_setpos' => array(
|
||||
'1' => 'první',
|
||||
'2' => 'druhá',
|
||||
'3' => 'třetí',
|
||||
'else' => ' %{n}.'
|
||||
),
|
||||
'-nth_setpos' => array(
|
||||
'-1' => 'poslední',
|
||||
'-2' => 'předposlední',
|
||||
'else' => '%{n}. od konce'
|
||||
)
|
||||
);
|
@ -44,6 +44,8 @@ return array(
|
||||
'else' => 'Alle %{interval} Sekunden'
|
||||
),
|
||||
'dtstart' => ', ab dem %{date}',
|
||||
'timeofday' => ' um %{date}',
|
||||
'startingtimeofday' => ' ab %{date}',
|
||||
'infinite' => ', für immer',
|
||||
'until' => ', bis zum %{date}',
|
||||
'count' => array(
|
||||
@ -70,26 +72,26 @@ return array(
|
||||
11 => 'November',
|
||||
12 => 'Dezember',
|
||||
),
|
||||
'byweekday' => ' %{weekdays}',
|
||||
'byweekday' => ' am %{weekdays}',
|
||||
'weekdays' => array(
|
||||
1 => 'Montags',
|
||||
2 => 'Dienstags',
|
||||
3 => 'Mittwochs',
|
||||
4 => 'Donnerstags',
|
||||
5 => 'Freitags',
|
||||
6 => 'Samstags',
|
||||
7 => 'Sonntags',
|
||||
1 => 'Montag',
|
||||
2 => 'Dienstag',
|
||||
3 => 'Mittwoch',
|
||||
4 => 'Donnerstag',
|
||||
5 => 'Freitag',
|
||||
6 => 'Samstag',
|
||||
7 => 'Sonntag',
|
||||
),
|
||||
'nth_weekday' => array(
|
||||
'1' => 'der erste %{weekday}', // e.g. the first Monday
|
||||
'2' => 'der zweite %{weekday}',
|
||||
'3' => 'der dritte %{weekday}',
|
||||
'else' => 'der %{n}. %{weekday}'
|
||||
'1' => 'ersten %{weekday}', // e.g. the first Monday
|
||||
'2' => 'zweiten %{weekday}',
|
||||
'3' => 'dritten %{weekday}',
|
||||
'else' => '%{n}. %{weekday}'
|
||||
),
|
||||
'-nth_weekday' => array(
|
||||
'-1' => 'der letzte %{weekday}', // e.g. the last Monday
|
||||
'-2' => 'der vorletzte %{weekday}',
|
||||
'else' => 'der %{n}. letzte %{weekday}'
|
||||
'-1' => 'letzten %{weekday}', // e.g. the last Monday
|
||||
'-2' => 'vorletzten %{weekday}',
|
||||
'else' => ' %{n}. letzten %{weekday}'
|
||||
),
|
||||
'byweekno' => array(
|
||||
'1' => ' in Kalenderwoche %{weeks}',
|
||||
@ -116,9 +118,9 @@ return array(
|
||||
'else' => '%{n}.'
|
||||
),
|
||||
'-nth_yearday' => array(
|
||||
'-1' => 'der letzte',
|
||||
'-2' => 'der vorletzte',
|
||||
'else' => 'der %{n}. letzte'
|
||||
'-1' => 'letzten',
|
||||
'-2' => 'vorletzten',
|
||||
'else' => '%{n}. letzten'
|
||||
),
|
||||
'byhour' => array(
|
||||
'1' => ' zur %{hours} Stunde',
|
||||
|
@ -46,6 +46,8 @@ return array(
|
||||
'else' => 'every %{interval} seconds'
|
||||
),
|
||||
'dtstart' => ', starting from %{date}',
|
||||
'timeofday' => ' at %{date}',
|
||||
'startingtimeofday' => ' starting at %{date}',
|
||||
'infinite' => ', forever',
|
||||
'until' => ', until %{date}',
|
||||
'count' => array(
|
||||
|
@ -18,12 +18,12 @@ return array(
|
||||
),
|
||||
'weekly' => array(
|
||||
'1' => 'semanal',
|
||||
'2' => 'cualquier otra semana',
|
||||
'2' => 'semana por medio',
|
||||
'else' => 'cada %{interval} semanas' // cada 8 semanas
|
||||
),
|
||||
'daily' => array(
|
||||
'1' => 'diario',
|
||||
'2' => 'cualquier otro día',
|
||||
'2' => 'día por medio',
|
||||
'else' => 'cada %{interval} días' // cada 8 días
|
||||
),
|
||||
'hourly' => array(
|
||||
@ -39,6 +39,8 @@ return array(
|
||||
'else' => 'cada %{interval} segundos'// cada 8 segundos
|
||||
),
|
||||
'dtstart' => ', empezando desde %{date}',
|
||||
'timeofday' => ' a las %{date}',
|
||||
'startingtimeofday' => ' empezando desde %{date}',
|
||||
'infinite' => ', por siempre',
|
||||
'until' => ', hasta %{date}',
|
||||
'count' => array(
|
||||
@ -69,10 +71,10 @@ return array(
|
||||
'weekdays' => array(
|
||||
1 => 'Lunes',
|
||||
2 => 'Martes',
|
||||
3 => 'Miercoles',
|
||||
3 => 'Miércoles',
|
||||
4 => 'Jueves',
|
||||
5 => 'Viernes',
|
||||
6 => 'Sabado',
|
||||
6 => 'Sábado',
|
||||
7 => 'Domingo',
|
||||
),
|
||||
'nth_weekday' => array(
|
||||
|
@ -42,6 +42,8 @@ return array(
|
||||
'else' => 'هر %{interval} ثانیه'
|
||||
),
|
||||
'dtstart' => ', از %{date}',
|
||||
'timeofday' => ' از %{date}',
|
||||
'startingtimeofday' => ' از %{date}',
|
||||
'infinite' => ', همیشه',
|
||||
'until' => ', تا %{date}',
|
||||
'count' => array(
|
||||
|
@ -46,6 +46,8 @@ return array(
|
||||
'else' => 'joka %{interval} sekunti'
|
||||
),
|
||||
'dtstart' => ', alkaen %{date}',
|
||||
'timeofday' => ' klo %{date}',
|
||||
'startingtimeofday' => ' alkaen %{date}',
|
||||
'infinite' => ', jatkuvasti',
|
||||
'until' => ', %{date} asti',
|
||||
'count' => array(
|
||||
|
@ -44,6 +44,8 @@ return array(
|
||||
'else' => 'toutes les %{interval} secondes'
|
||||
),
|
||||
'dtstart' => ', à partir du %{date}',
|
||||
'timeofday' => ' à %{date}',
|
||||
'startingtimeofday' => ' à partir du %{date}',
|
||||
'infinite' => ', indéfiniment',
|
||||
'until' => ', jusqu\'au %{date}',
|
||||
'count' => array(
|
||||
@ -67,7 +69,7 @@ return array(
|
||||
8 => 'août',
|
||||
9 => 'septembre',
|
||||
10 => 'octobre',
|
||||
11 => 'november',
|
||||
11 => 'novembre',
|
||||
12 => 'décembre',
|
||||
),
|
||||
'byweekday' => ' le %{weekdays}',
|
||||
|
@ -41,6 +41,8 @@ return array(
|
||||
'else' => 'כל %{interval} שניות'
|
||||
),
|
||||
'dtstart' => ', החל מ%{date}',
|
||||
'timeofday' => ' החל מ%{date}',
|
||||
'startingtimeofday' => ' החל מ%{date}',
|
||||
'infinite' => ', לעד',
|
||||
'until' => ', עד %{date}',
|
||||
'count' => array(
|
||||
|
@ -38,6 +38,8 @@ return array(
|
||||
'else' => 'ogni %{interval} secondi'
|
||||
),
|
||||
'dtstart' => ', a partire dal %{date}',
|
||||
'timeofday' => ' alle %{date}',
|
||||
'startingtimeofday' => ' a partire dal %{date}',
|
||||
'infinite' => ', per sempre',
|
||||
'until' => ', fino al %{date}',
|
||||
'count' => array(
|
||||
|
143
src/i18n/ja.php
Normal file
143
src/i18n/ja.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Translation file for Japanese language.
|
||||
*
|
||||
* Most strings can be an array, with a value as the key. The system will
|
||||
* pick the translation corresponding to the key. The key "else" will be picked
|
||||
* if no matching value is found. This is useful for plurals.
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file.
|
||||
*
|
||||
* @author Taichi Kurihara <taichi.kurihara416@gmail.com>
|
||||
* @link https://github.com/Kuri-Tai/php-rrule
|
||||
*/
|
||||
return array(
|
||||
'yearly' => array(
|
||||
'1' => '毎年',
|
||||
'else' => '%{interval} 年ごと',
|
||||
),
|
||||
'monthly' => array(
|
||||
'1' => '毎月',
|
||||
'else' => '%{interval} か月ごと',
|
||||
),
|
||||
'weekly' => array(
|
||||
'1' => '毎週',
|
||||
'2' => '隔週',
|
||||
'else' => '%{interval} 週間ごと',
|
||||
),
|
||||
'daily' => array(
|
||||
'1' => '毎日',
|
||||
'2' => '隔日',
|
||||
'else' => '%{interval} 日ごと',
|
||||
),
|
||||
'hourly' => array(
|
||||
'1' => '毎時',
|
||||
'else' => '%{interval} 時間ごと',
|
||||
),
|
||||
'minutely' => array(
|
||||
'1' => '毎分',
|
||||
'else' => '%{interval} 分ごと',
|
||||
),
|
||||
'secondly' => array(
|
||||
'1' => '毎秒',
|
||||
'else' => '%{interval} 秒ごと',
|
||||
),
|
||||
'dtstart' => ', %{date} から',
|
||||
'infinite' => ', 期日なし',
|
||||
'until' => ', %{date} まで',
|
||||
'count' => array(
|
||||
'1' => ', 1 回',
|
||||
'else' => ', %{count} 回',
|
||||
),
|
||||
'and' => 'かつ ',
|
||||
'x_of_the_y' => array(
|
||||
'yearly' => 'その年の %{x}', // e.g. その年の 最初の 月曜日, もしくは その年の 最初の 日
|
||||
'monthly' => 'その月の %{x}',
|
||||
),
|
||||
'bymonth' => ' %{months}',
|
||||
'months' => array(
|
||||
1 => '1月',
|
||||
2 => '2月',
|
||||
3 => '3月',
|
||||
4 => '4月',
|
||||
5 => '5月',
|
||||
6 => '6月',
|
||||
7 => '7月',
|
||||
8 => '8月',
|
||||
9 => '9月',
|
||||
10 => '10月',
|
||||
11 => '11月',
|
||||
12 => '12月',
|
||||
),
|
||||
'byweekday' => ' %{weekdays}',
|
||||
'weekdays' => array(
|
||||
1 => '月曜日',
|
||||
2 => '火曜日',
|
||||
3 => '水曜日',
|
||||
4 => '木曜日',
|
||||
5 => '金曜日',
|
||||
6 => '土曜日',
|
||||
7 => '日曜日',
|
||||
),
|
||||
'nth_weekday' => array(
|
||||
'1' => '最初の %{weekday}', // e.g. 最初の 月曜日
|
||||
'else' => '%{n}番目の %{weekday}',
|
||||
),
|
||||
'-nth_weekday' => array(
|
||||
'-1' => '最後の %{weekday}', // e.g. 最後の 月曜日
|
||||
'else' => '最後から%{n}番目の %{weekday}',
|
||||
),
|
||||
'byweekno' => array(
|
||||
'1' => ' 第%{weeks}週目',
|
||||
'else' => ' 第%{weeks}週目',
|
||||
),
|
||||
'nth_weekno' => '%{n}',
|
||||
'bymonthday' => ' %{monthdays}',
|
||||
'nth_monthday' => array(
|
||||
'1' => '1番目の',
|
||||
'else' => '%{n}番目の',
|
||||
),
|
||||
'-nth_monthday' => array(
|
||||
'-1' => '最後の',
|
||||
'else' => '最後から%{n}番目の',
|
||||
),
|
||||
'byyearday' => array(
|
||||
'1' => ' %{yeardays}',
|
||||
'else' => ' %{yeardays}',
|
||||
),
|
||||
'nth_yearday' => array(
|
||||
'1' => '1番目の',
|
||||
'else' => '%{n}番目の',
|
||||
),
|
||||
'-nth_yearday' => array(
|
||||
'-1' => '最後の',
|
||||
'else' => '最後から%{n}番目の',
|
||||
),
|
||||
'byhour' => array(
|
||||
'1' => ' %{hours}',
|
||||
'else' => ' %{hours}',
|
||||
),
|
||||
'nth_hour' => '%{n}時',
|
||||
'byminute' => array(
|
||||
'1' => ' %{minutes}',
|
||||
'else' => ' %{minutes}',
|
||||
),
|
||||
'nth_minute' => '%{n}分',
|
||||
'bysecond' => array(
|
||||
'1' => ' %{seconds}',
|
||||
'else' => ' %{seconds}',
|
||||
),
|
||||
'nth_second' => '%{n}秒',
|
||||
'bysetpos' => ', ただし、そのうちの %{setpos} 該当するもののみ',
|
||||
'nth_setpos' => array(
|
||||
'1' => '最初の',
|
||||
'else' => '%{n}番目の',
|
||||
),
|
||||
'-nth_setpos' => array(
|
||||
'-1' => '最後の',
|
||||
'else' => '最後から%{n}番目の',
|
||||
),
|
||||
);
|
@ -46,6 +46,8 @@ return array(
|
||||
'else' => 'elke %{interval} seconden'
|
||||
),
|
||||
'dtstart' => ', wordt gestart vanaf %{date}',
|
||||
'timeofday' => ' om %{date}',
|
||||
'startingtimeofday' => ' wordt gestart vanaf %{date}',
|
||||
'infinite' => ', oneindig',
|
||||
'until' => ', tot en met %{date}',
|
||||
'count' => array(
|
||||
@ -59,28 +61,28 @@ return array(
|
||||
),
|
||||
'bymonth' => ' in %{months}',
|
||||
'months' => array(
|
||||
1 => 'Januari',
|
||||
2 => 'Februari',
|
||||
3 => 'Maart',
|
||||
4 => 'April',
|
||||
5 => 'Mei',
|
||||
6 => 'Juni',
|
||||
7 => 'Juli',
|
||||
8 => 'Augustus',
|
||||
9 => 'September',
|
||||
10 => 'Oktober',
|
||||
11 => 'November',
|
||||
12 => 'December',
|
||||
1 => 'januari',
|
||||
2 => 'februari',
|
||||
3 => 'maart',
|
||||
4 => 'april',
|
||||
5 => 'mei',
|
||||
6 => 'juni',
|
||||
7 => 'juli',
|
||||
8 => 'augustus',
|
||||
9 => 'september',
|
||||
10 => 'oktober',
|
||||
11 => 'november',
|
||||
12 => 'december',
|
||||
),
|
||||
'byweekday' => ' op %{weekdays}',
|
||||
'weekdays' => array(
|
||||
1 => 'Maandag',
|
||||
2 => 'Dinsdag',
|
||||
3 => 'Woensdag',
|
||||
4 => 'Donderdag',
|
||||
5 => 'Vrijdag',
|
||||
6 => 'Zaterdag',
|
||||
7 => 'Zondag',
|
||||
1 => 'maandag',
|
||||
2 => 'dinsdag',
|
||||
3 => 'woensdag',
|
||||
4 => 'donderdag',
|
||||
5 => 'vrijdag',
|
||||
6 => 'zaterdag',
|
||||
7 => 'zondag',
|
||||
),
|
||||
'nth_weekday' => array(
|
||||
'1' => 'de eerste %{weekday}', // e.g. the first Monday
|
||||
|
171
src/i18n/pl.php
Normal file
171
src/i18n/pl.php
Normal file
@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Translation file for Polish language.
|
||||
*
|
||||
* Most strings can be an array, with a value as the key. The system will
|
||||
* pick the translation corresponding to the key. The key "else" will be picked
|
||||
* if no matching value is found. This is useful for plurals.
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file.
|
||||
*
|
||||
* @author Rémi Lanvin <remi@cloudconnected.fr>
|
||||
* @author Janusz Paszyński <j.paszynski@itchaos.pl>
|
||||
* @link https://github.com/rlanvin/php-rrule
|
||||
*/
|
||||
return array(
|
||||
'yearly' => array(
|
||||
'1' => 'co roku',
|
||||
'2' => 'co drugi rok',
|
||||
'3' => 'co trzeci rok',
|
||||
'else' => 'co %{interval} lat'
|
||||
),
|
||||
'monthly' => array(
|
||||
'1' => 'co miesiąc',
|
||||
'else' => 'co %{interval} miesięcy'
|
||||
),
|
||||
'weekly' => array(
|
||||
'1' => 'co tydzień',
|
||||
'2' => 'co drugi tydzień',
|
||||
'3' => 'co trzeci tydzień',
|
||||
'else' => 'co %{interval} tygodni'
|
||||
),
|
||||
'daily' => array(
|
||||
'1' => 'codziennie',
|
||||
'2' => 'co drugi dzień',
|
||||
'3' => 'co trzeci dzień',
|
||||
'else' => 'co %{interval} dni'
|
||||
),
|
||||
'hourly' => array(
|
||||
'1' => 'co godzinę',
|
||||
'2' => 'co drugą godzinę',
|
||||
'3' => 'co trzecią godzinę',
|
||||
'else' => 'co %{interval} godzin'
|
||||
),
|
||||
'minutely' => array(
|
||||
'1' => 'co minutę',
|
||||
'2' => 'co dwie minuty',
|
||||
'3' => 'co trzy minuty',
|
||||
'else' => 'co %{interval} minut'
|
||||
),
|
||||
'secondly' => array(
|
||||
'1' => 'co sekundę',
|
||||
'2' => 'co dwie sekundy',
|
||||
'3' => 'co trzy sekundy',
|
||||
'4' => 'co cztery sekundy',
|
||||
'else' => 'co %{interval} sekund'
|
||||
),
|
||||
'dtstart' => ', zaczynając od %{date}',
|
||||
'timeofday' => ' o %{date}',
|
||||
'startingtimeofday' => ' zaczynając od %{date}',
|
||||
'infinite' => ', zawsze',
|
||||
'until' => ', do daty %{date}',
|
||||
'count' => array(
|
||||
'1' => ', jeden raz',
|
||||
'else' => ', %{count} razy'
|
||||
),
|
||||
'and' => 'i ',
|
||||
'x_of_the_y' => array(
|
||||
'yearly' => '%{x} roku', // e.g. the first Monday of the year, or the first day of the year
|
||||
'monthly' => '%{x} miesiąca',
|
||||
'weekly' => '%{x} tygodnia',
|
||||
),
|
||||
'bymonth' => ' w %{months}',
|
||||
'months' => array(
|
||||
1 => 'Styczeń',
|
||||
2 => 'Luty',
|
||||
3 => 'Marzec',
|
||||
4 => 'Kwiecień',
|
||||
5 => 'Maj',
|
||||
6 => 'Czerwiec',
|
||||
7 => 'Lipiec',
|
||||
8 => 'Sierpień',
|
||||
9 => 'Wrzesień',
|
||||
10 => 'Październik',
|
||||
11 => 'Listopad',
|
||||
12 => 'Grudzień',
|
||||
),
|
||||
'byweekday' => ' w %{weekdays}',
|
||||
'weekdays' => array(
|
||||
1 => 'Poniedziałek',
|
||||
2 => 'Wtorek',
|
||||
3 => 'Środa',
|
||||
4 => 'Czwartek',
|
||||
5 => 'Piątek',
|
||||
6 => 'Sobota',
|
||||
7 => 'Niedziela',
|
||||
),
|
||||
'nth_weekday' => array(
|
||||
'1' => 'w pierwszy %{weekday}', // e.g. the first Monday
|
||||
'2' => 'w drugi %{weekday}',
|
||||
'3' => 'w trzeci %{weekday}',
|
||||
'else' => 'w %{n} %{weekday}'
|
||||
),
|
||||
'-nth_weekday' => array(
|
||||
'-1' => 'w ostatni %{weekday}', // e.g. the last Monday
|
||||
'-2' => 'w przedostatni %{weekday}',
|
||||
'else' => 'w %{n} od końca %{weekday}'
|
||||
),
|
||||
'byweekno' => array(
|
||||
'1' => ' w tygodniu %{weeks}',
|
||||
'else' => ' w tygodniach numer %{weeks}'
|
||||
),
|
||||
'nth_weekno' => '%{n}',
|
||||
'bymonthday' => ' w %{monthdays}',
|
||||
'nth_monthday' => array(
|
||||
'1' => 'pierwszy',
|
||||
'2' => 'drugi',
|
||||
'3' => 'trzeci',
|
||||
'else' => '%{n}.'
|
||||
),
|
||||
'-nth_monthday' => array(
|
||||
'-1' => 'ostatni',
|
||||
'-2' => 'przedostatni',
|
||||
'-3' => 'drugi od końca',
|
||||
'else' => '%{n}. od końca'
|
||||
),
|
||||
'byyearday' => array(
|
||||
'1' => ' pierwszego %{yeardays} dnia',
|
||||
'else' => ' w dniu %{yeardays}'
|
||||
),
|
||||
'nth_yearday' => array(
|
||||
'1' => 'pierwszy',
|
||||
'2' => 'drugi',
|
||||
'3' => 'trzeci',
|
||||
'else' => '%{n}.'
|
||||
),
|
||||
'-nth_yearday' => array(
|
||||
'-1' => 'ostatni',
|
||||
'-2' => 'przedostatni',
|
||||
'else' => '%{n}. od końca'
|
||||
),
|
||||
'byhour' => array(
|
||||
'1' => ' o %{hours}',
|
||||
'else' => ' o %{hours}'
|
||||
),
|
||||
'nth_hour' => '%{n}h',
|
||||
'byminute' => array(
|
||||
'1' => ' w minucie %{minutes}',
|
||||
'else' => ' w minucie %{minutes}'
|
||||
),
|
||||
'nth_minute' => '%{n}',
|
||||
'bysecond' => array(
|
||||
'1' => ' w sekundzie %{seconds}',
|
||||
'else' => ' w sekundzie %{seconds}'
|
||||
),
|
||||
'nth_second' => '%{n}',
|
||||
'bysetpos' => ', tylko %{setpos} wystąpienie w zbiorze',
|
||||
'nth_setpos' => array(
|
||||
'1' => 'pierwszy',
|
||||
'2' => 'drugi',
|
||||
'3' => 'trzeci',
|
||||
'else' => 'the %{n}th'
|
||||
),
|
||||
'-nth_setpos' => array(
|
||||
'-1' => 'ostatni',
|
||||
'-2' => 'przedostatni',
|
||||
'else' => '%{n}. od końca'
|
||||
)
|
||||
);
|
162
src/i18n/pt.php
Normal file
162
src/i18n/pt.php
Normal file
@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Translation file for Portuguese language.
|
||||
*
|
||||
* @author Felippe Roberto Bayestorff Duarte <felippeduarte@gmail.com>
|
||||
* @link https://github.com/rlanvin/php-rrule
|
||||
*/
|
||||
|
||||
return array(
|
||||
'yearly' => array(
|
||||
'1' => 'anual',
|
||||
'else' => 'cada %{interval} anos' // cada 8 anos
|
||||
),
|
||||
'monthly' => array(
|
||||
'1' => 'mensal',
|
||||
'else' => 'cada %{interval} meses' //cada 8 meses
|
||||
),
|
||||
'weekly' => array(
|
||||
'1' => 'semanal',
|
||||
'2' => 'qualquer outra semana',
|
||||
'else' => 'cada %{interval} semanas' // cada 8 semanas
|
||||
),
|
||||
'daily' => array(
|
||||
'1' => 'diário',
|
||||
'2' => 'qualquer outro dia',
|
||||
'else' => 'cada %{interval} dias' // cada 8 dias
|
||||
),
|
||||
'hourly' => array(
|
||||
'1' => 'cada hora',
|
||||
'else' => 'cada %{interval} horas'// cada 8 horas
|
||||
),
|
||||
'minutely' => array(
|
||||
'1' => 'cada minuto',
|
||||
'else' => 'cada %{interval} minutos'// cada 8 minutos
|
||||
),
|
||||
'secondly' => array(
|
||||
'1' => 'cada segundo',
|
||||
'else' => 'cada %{interval} segundos'// cada 8 segundos
|
||||
),
|
||||
'dtstart' => ', começando de %{date}',
|
||||
'timeofday' => ' às %{date}',
|
||||
'startingtimeofday' => ' começando de %{date}',
|
||||
'infinite' => ', para sempre',
|
||||
'until' => ', até %{date}',
|
||||
'count' => array(
|
||||
'1' => ', uma vez',
|
||||
'else' => ', %{count} vezes'
|
||||
),
|
||||
'and' => 'e ',
|
||||
'x_of_the_y' => array(
|
||||
'yearly' => '%{x} do ano', // e.g. the first Monday of the year, or the first day of the year
|
||||
'monthly' => '%{x} do mês',
|
||||
),
|
||||
'bymonth' => ' em %{months}',
|
||||
'months' => array(
|
||||
1 => 'Janeiro',
|
||||
2 => 'Fevereiro',
|
||||
3 => 'Março',
|
||||
4 => 'Abril',
|
||||
5 => 'Maio',
|
||||
6 => 'Junho',
|
||||
7 => 'Julho',
|
||||
8 => 'Agosto',
|
||||
9 => 'Setembro',
|
||||
10 => 'Outubro',
|
||||
11 => 'Novembro',
|
||||
12 => 'Dezembro',
|
||||
),
|
||||
'byweekday' => ' em %{weekdays}',
|
||||
'weekdays' => array(
|
||||
1 => 'Segunda-feira',
|
||||
2 => 'Terça-feira',
|
||||
3 => 'Quarta-feira',
|
||||
4 => 'Quinta-feira',
|
||||
5 => 'Sexta-feira',
|
||||
6 => 'Sábado',
|
||||
7 => 'Domingo',
|
||||
),
|
||||
'nth_weekday' => array(
|
||||
'1' => 'o(a) primero(a) %{weekday}', // e.g. the first Monday
|
||||
'2' => 'o(a) segundo(a) %{weekday}',
|
||||
'3' => 'o(a) terceiro(a) %{weekday}',
|
||||
'else' => 'o %{n}º %{weekday}'
|
||||
),
|
||||
'-nth_weekday' => array(
|
||||
'-1' => 'o(a) último(a) %{weekday}', // e.g. the last Monday
|
||||
'-2' => 'o(a) penúltimo(a) %{weekday}',
|
||||
'-3' => 'o(a) antepeúltimo(a) %{weekday}',
|
||||
'else' => 'o %{n}º até o último %{weekday}'
|
||||
),
|
||||
'byweekno' => array(
|
||||
'1' => ' na semana %{weeks}',
|
||||
'else' => ' nas semanas # %{weeks}'
|
||||
),
|
||||
'nth_weekno' => '%{n}',
|
||||
'bymonthday' => ' no %{monthdays}',
|
||||
'nth_monthday' => array(
|
||||
'1' => 'o 1º',
|
||||
'2' => 'o 2º',
|
||||
'3' => 'o 3º',
|
||||
'21' => 'o 21º',
|
||||
'22' => 'o 22º',
|
||||
'23' => 'o 23º',
|
||||
'31' => 'o 31º',
|
||||
'else' => 'o %{n}º'
|
||||
),
|
||||
'-nth_monthday' => array(
|
||||
'-1' => 'o último dia',
|
||||
'-2' => 'o penúltimo dia',
|
||||
'-3' => 'o antepenúltimo dia',
|
||||
'-21' => 'o 21º até o último dia',
|
||||
'-22' => 'o 22º até o último dia',
|
||||
'-23' => 'o 23º até o último dia',
|
||||
'-31' => 'o 31º até o último dia',
|
||||
'else' => 'o %{n}º até o último dia'
|
||||
),
|
||||
'byyearday' => array(
|
||||
'1' => ' no %{yeardays} dia',
|
||||
'else' => ' nos %{yeardays} dias'
|
||||
),
|
||||
'nth_yearday' => array(
|
||||
'1' => 'o primero',
|
||||
'2' => 'o segundo',
|
||||
'3' => 'o tercero',
|
||||
'else' => 'o %{n}º'
|
||||
),
|
||||
'-nth_yearday' => array(
|
||||
'-1' => 'o último',
|
||||
'-2' => 'o penúltimo',
|
||||
'-3' => 'o antepenúltimo',
|
||||
'else' => 'o %{n}º até o último'
|
||||
),
|
||||
'byhour' => array(
|
||||
'1' => ' a %{hours}',
|
||||
'else' => ' a %{hours}'
|
||||
),
|
||||
'nth_hour' => '%{n}h',
|
||||
'byminute' => array(
|
||||
'1' => ' ao minuto %{minutes}',
|
||||
'else' => ' aos minutos %{minutes}'
|
||||
),
|
||||
'nth_minute' => '%{n}',
|
||||
'bysecond' => array(
|
||||
'1' => ' ao segundo %{seconds}',
|
||||
'else' => ' aos segundos %{seconds}'
|
||||
),
|
||||
'nth_second' => '%{n}',
|
||||
'bysetpos' => ', mas somente %{setpos} ocorrência deste conjunto',
|
||||
'nth_setpos' => array(
|
||||
'1' => 'o primero',
|
||||
'2' => 'o segundo',
|
||||
'3' => 'o terceiro',
|
||||
'else' => 'o %{n}º'
|
||||
),
|
||||
'-nth_setpos' => array(
|
||||
'-1' => 'o último',
|
||||
'-2' => 'o penúltimo',
|
||||
'-3' => 'o antepenúltimo',
|
||||
'else' => 'o %{n}º até o último'
|
||||
)
|
||||
);
|
@ -51,6 +51,8 @@ return array(
|
||||
'else' => 'var %{interval}:e sekund'
|
||||
),
|
||||
'dtstart' => ', börjar %{date}',
|
||||
'timeofday' => ' kl %{date}',
|
||||
'startingtimeofday' => ' börjar %{date}',
|
||||
'infinite' => ', tills vidare',
|
||||
'until' => ', t.om %{date}',
|
||||
'count' => array(
|
||||
@ -79,7 +81,7 @@ return array(
|
||||
),
|
||||
'byweekday' => ' på %{weekdays}',
|
||||
'weekdays' => array(
|
||||
1 => 'Månday',
|
||||
1 => 'Måndag',
|
||||
2 => 'Tisdag',
|
||||
3 => 'Onsdag',
|
||||
4 => 'Torsdag',
|
||||
@ -146,14 +148,14 @@ return array(
|
||||
'nth_second' => '%{n}',
|
||||
'bysetpos' => ', men bara %{setpos} tillfället i serien',
|
||||
'nth_setpos' => array(
|
||||
'1' => 'det första',
|
||||
'2' => 'det andra',
|
||||
'3' => 'det tredje',
|
||||
'else' => 'det %{n}:e'
|
||||
'1' => 'den första',
|
||||
'2' => 'den andra',
|
||||
'3' => 'den tredje',
|
||||
'else' => 'den %{n}:e'
|
||||
),
|
||||
'-nth_setpos' => array(
|
||||
'-1' => 'det sista',
|
||||
'-2' => 'det näst sista',
|
||||
'else' => 'det %{n}:e sista'
|
||||
'-1' => 'den sista',
|
||||
'-2' => 'den näst sista',
|
||||
'else' => 'den %{n}:e sista'
|
||||
)
|
||||
);
|
||||
|
168
tests/RRuleTest.php
Executable file → Normal file
168
tests/RRuleTest.php
Executable file → Normal file
@ -20,8 +20,12 @@ class RRuleTest extends TestCase
|
||||
array(array()),
|
||||
array(array('FOOBAR' => 'DAILY')),
|
||||
|
||||
array(array('FREQ' => 'foobar')),
|
||||
'invalid string freq' => [['FREQ' => 'foobar']],
|
||||
'Invalid integer frequency' => [['FREQ' => 42]],
|
||||
'Array freq' => [['FREQ' => array()]],
|
||||
'null freq' => [['FREQ' => null]],
|
||||
'object freq' => [['FREQ' => new Stdclass()]],
|
||||
|
||||
array(array('FREQ' => 'DAILY', 'INTERVAL' => -1)),
|
||||
array(array('FREQ' => 'DAILY', 'INTERVAL' => 1.5)),
|
||||
array(array('FREQ' => 'DAILY', 'UNTIL' => 'foobar')),
|
||||
@ -87,6 +91,9 @@ class RRuleTest extends TestCase
|
||||
array(array('FREQ' => 'MONTHLY', 'BYSECOND' => 61)),
|
||||
|
||||
'Invalid WKST' => [['FREQ' => 'DAILY', 'WKST' => 'XX']],
|
||||
'Null WKST' => [['FREQ' => 'DAILY', 'WKST' => null]],
|
||||
'Array WKST' => [['FREQ' => 'DAILY', 'WKST' => array()]],
|
||||
'Object WKST' => [['FREQ' => 'DAILY', 'WKST' => new stdClass()]],
|
||||
|
||||
'Invalid DTSTART (invalid date)' => [['FREQ' => 'DAILY', 'DTSTART' => new stdClass()]]
|
||||
);
|
||||
@ -241,15 +248,15 @@ class RRuleTest extends TestCase
|
||||
$this->assertEquals($occurrences, $rule->getOccurrences());
|
||||
$this->assertEquals($occurrences, $rule->getOccurrences(), 'Cached version');
|
||||
foreach ($occurrences as $date) {
|
||||
$this->assertTrue($rule->occursAt($date), $date->format('r').'in cached version');
|
||||
$this->assertTrue($rule->occursAt($date), $date->format('r').' in cached version');
|
||||
}
|
||||
$rule->clearCache();
|
||||
foreach ($occurrences as $date) {
|
||||
$this->assertTrue($rule->occursAt($date), $date->format('r').'in uncached version');
|
||||
$this->assertTrue($rule->occursAt($date), $date->format('r').' in uncached version');
|
||||
}
|
||||
$rule->clearCache();
|
||||
for ($i = 0; $i < count($occurrences); $i++) {
|
||||
$this->assertEquals($rule[$i], $occurrences[$i], 'array access uncached');
|
||||
$this->assertEquals($rule[$i], $occurrences[$i], ' array access uncached');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1815,6 +1822,43 @@ class RRuleTest extends TestCase
|
||||
$this->assertEquals($count, $rrule->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests timezone transition in Daylight Savings Time switch.
|
||||
*/
|
||||
public function testDST()
|
||||
{
|
||||
$rrule = new RRule([
|
||||
'FREQ' => 'WEEKLY',
|
||||
'DTSTART' => new \DateTime('2022-10-30T01:00', new \DateTimeZone('America/Chicago')),
|
||||
'COUNT' => 2,
|
||||
]);
|
||||
$this->assertSame('2022-11-06T01:00:00-05:00 CDT 1667714400', $rrule[1]->format('c T U'));
|
||||
}
|
||||
|
||||
public function testResumeFromPartiallyFilledCache()
|
||||
{
|
||||
// https://github.com/rlanvin/php-rrule/issues/160
|
||||
$rrule = new \RRule\RRule([
|
||||
'DTSTART' => new DateTime('2023-03-31 23:59:59.000000'),
|
||||
'FREQ' => 'MONTHLY',
|
||||
'INTERVAL' => '12',
|
||||
'WKST' => 'MO',
|
||||
'COUNT' => 3
|
||||
]);
|
||||
|
||||
// Break on first loop during first iterator use.
|
||||
foreach ($rrule as $occurrence) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Print first 3 occurrences (cache used).
|
||||
$this->assertEquals([
|
||||
date_create('2023-03-31 23:59:59'),
|
||||
date_create('2024-03-31 23:59:59'),
|
||||
date_create('2025-03-31 23:59:59')
|
||||
],$rrule->getOccurrences());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GetOccurrences
|
||||
|
||||
@ -2520,6 +2564,13 @@ class RRuleTest extends TestCase
|
||||
$this->assertInstanceOf('\RRule\RSet', $object);
|
||||
}
|
||||
|
||||
public function testCreateFromRfcStringDoesntChangeCase()
|
||||
{
|
||||
$str = "DTSTART;TZID=Europe/Paris:20200929T000000\nRRULE:FREQ=DAILY;BYSECOND=0;BYMINUTE=0;BYHOUR=9";
|
||||
$rule = RRule::createFromRfcString($str);
|
||||
$this->assertEquals($str, $rule->rfcString());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Timezone
|
||||
|
||||
@ -2827,6 +2878,24 @@ class RRuleTest extends TestCase
|
||||
], $occurrences, 'DateTimeImmutable produces valid results');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test bug #104
|
||||
* @see https://github.com/rlanvin/php-rrule/issues/104
|
||||
*/
|
||||
public function testMicrosecondsAreRemovedFromInput()
|
||||
{
|
||||
$dtstart = '2022-04-22 12:00:00.5';
|
||||
$rule = new RRule([
|
||||
'dtstart' => $dtstart,
|
||||
'freq' => 'daily',
|
||||
'interval' => 1,
|
||||
'count' => 1
|
||||
]);
|
||||
$this->assertTrue($rule->occursAt('2022-04-22 12:00:00'));
|
||||
$this->assertTrue($rule->occursAt('2022-04-22 12:00:00.5'));
|
||||
$this->assertEquals(date_create('2022-04-22 12:00:00'), $rule[0]);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Array access and countable interfaces
|
||||
|
||||
@ -2949,7 +3018,7 @@ class RRuleTest extends TestCase
|
||||
array('en_US.utf-8', array('en','en_US'), array('en','en_US')),
|
||||
array('en_US_POSIX', array('en','en_US'), array('en','en_US')),
|
||||
array('sv_SE', array('sv','sv_SE'), array('sv','sv_SE')),
|
||||
// case insentitive
|
||||
// case insensitive
|
||||
array('en_sg', array('en','en_SG'), array('en','en_SG')),
|
||||
array('sv_se', array('sv','sv_SE'), array('sv','sv_SE')),
|
||||
// with a dash
|
||||
@ -3075,7 +3144,7 @@ class RRuleTest extends TestCase
|
||||
/**
|
||||
* Tests that the RRule::i18nLoad() fails as expected on invalid $fallback settings
|
||||
*/
|
||||
public function testI18nLoadFallbackFailsWitoutIntl()
|
||||
public function testI18nLoadFallbackFailsWithoutIntl()
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$reflector = new ReflectionClass('RRule\RRule');
|
||||
@ -3166,6 +3235,36 @@ class RRuleTest extends TestCase
|
||||
"daily",
|
||||
"daily"
|
||||
),
|
||||
array(
|
||||
"DTSTART:20170202T161500Z\nRRULE:FREQ=MONTHLY;BYMONTHDAY=2",
|
||||
array('locale' => "en", 'start_time_only' => true, 'explicit_infinite' => false),
|
||||
"monthly on the 2nd of the month at 4:15 PM",
|
||||
"monthly on the 2nd of the month at 16:15:00"
|
||||
),
|
||||
array(
|
||||
"DTSTART:20170202T063000Z\nRRULE:FREQ=HOURLY;INTERVAL=7",
|
||||
array('locale' => "en", 'start_time_only' => true, 'explicit_infinite' => false),
|
||||
"every 7 hours starting at 6:30 AM",
|
||||
"every 7 hours starting at 06:30:00"
|
||||
),
|
||||
array(
|
||||
"RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=+2TU",
|
||||
array('locale' => "en", 'include_start' => false, 'explicit_infinite' => false),
|
||||
"yearly on the second Tuesday of the month in March",
|
||||
"yearly on the second Tuesday of the month in March"
|
||||
),
|
||||
array(
|
||||
"RRULE:FREQ=YEARLY;BYMONTH=3,6,9;BYDAY=+2TU",
|
||||
array('locale' => "en", 'include_start' => false, 'explicit_infinite' => false),
|
||||
"yearly on the second Tuesday of the month in March, June and September",
|
||||
"yearly on the second Tuesday of the month in March, June and September"
|
||||
),
|
||||
array(
|
||||
"RRULE:FREQ=YEARLY;BYDAY=+6WE",
|
||||
array('locale' => "en", 'include_start' => false, 'explicit_infinite' => false),
|
||||
"yearly on the 6th Wednesday of the year",
|
||||
"yearly on the 6th Wednesday of the year"
|
||||
),
|
||||
// with custom_path
|
||||
'custom_path' => array(
|
||||
"DTSTART:20170202T000000Z\nRRULE:FREQ=YEARLY;UNTIL=20170205T000000Z",
|
||||
@ -3191,13 +3290,64 @@ class RRuleTest extends TestCase
|
||||
/**
|
||||
* @dataProvider humanReadableStrings
|
||||
*/
|
||||
public function testHumanReadable($rrule, $options, $withIntl, $withoutIntl, $dtstart = null)
|
||||
public function testHumanReadableWithoutIntl($rrule, $options, $withIntl, $withoutIntl, $dtstart = null)
|
||||
{
|
||||
if ($dtstart) {
|
||||
$dtstart = new DateTime($dtstart);
|
||||
}
|
||||
$rrule = new RRule($rrule, $dtstart);
|
||||
$expected = extension_loaded('intl') ? $withIntl : $withoutIntl;
|
||||
$this->assertEquals($expected, $rrule->humanReadable($options));
|
||||
$options['use_intl'] = false;
|
||||
$this->assertEquals($withoutIntl, $rrule->humanReadable($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider humanReadableStrings
|
||||
*/
|
||||
public function testHumanReadableWithIntl($rrule, $options, $withIntl, $withoutIntl, $dtstart = null)
|
||||
{
|
||||
if (!extension_loaded('intl')) {
|
||||
$this->markTestSkipped('intl not loaded');
|
||||
}
|
||||
|
||||
if ($dtstart) {
|
||||
$dtstart = new DateTime($dtstart);
|
||||
}
|
||||
$rrule = new RRule($rrule, $dtstart);
|
||||
|
||||
// Narrow No-Break Space (NNBSP) was added in ICU72.1 before the meridian
|
||||
// as a workaround we replace unicode 0x202f char with a regular space for backwards compatibility
|
||||
if (version_compare(INTL_ICU_VERSION, '72.1') < 0) {
|
||||
// if you don't see the difference, use an editor that displays unicode
|
||||
$withIntl = str_replace(' ', ' ', $withIntl);
|
||||
}
|
||||
|
||||
$this->assertEquals($withIntl, $rrule->humanReadable($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface|string
|
||||
*
|
||||
* @dataProvider dataForTestParseDate
|
||||
*/
|
||||
public function testParseDate($dateTime, \DateTimeInterface $expectedDateTime)
|
||||
{
|
||||
$parsed = RRule::parseDate($dateTime);
|
||||
|
||||
$this->assertEquals($expectedDateTime->format('U.u'), $parsed->format('U.u'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<list<\DateTimeInterface|string>>
|
||||
*/
|
||||
public function dataForTestParseDate()
|
||||
{
|
||||
$dateTimeImmutableMicroseconds = new \DateTimeImmutable('2023-04-27 12:13:14.567');
|
||||
$dateTimeImmutableNoMicroseconds = new \DateTimeImmutable('2023-04-27 12:13:14');
|
||||
return [
|
||||
['2023-04-27 12:13:14', new DateTime('2023-04-27 12:13:14')],
|
||||
['2023-04-27 12:13:14.123', new DateTime('2023-04-27 12:13:14')],
|
||||
[new DateTime('2023-04-27 12:13:14'), new DateTime('2023-04-27 12:13:14')],
|
||||
[$dateTimeImmutableMicroseconds, $dateTimeImmutableNoMicroseconds],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,9 @@ class RfcParserTest extends TestCase
|
||||
array('EXDATE;TZID=America/New_York:19970714T083000',
|
||||
array(date_create('19970714T083000',new \DateTimeZone('America/New_York')))
|
||||
),
|
||||
array('EXDATE;TZID=W. Europe Standard Time:20230915T222222',
|
||||
array(date_create('20230915T222222',new \DateTimeZone('Europe/Berlin')))
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -90,4 +93,4 @@ class RfcParserTest extends TestCase
|
||||
$dates = RfcParser::parseExDate($string);
|
||||
$this->assertEquals($dates, $expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user