Skip to content

Commit

Permalink
Merge branch 'master' into fix/kiritimati
Browse files Browse the repository at this point in the history
  • Loading branch information
davidgoli authored Nov 10, 2023
2 parents 387f5bc + 62e7320 commit 6059fae
Show file tree
Hide file tree
Showing 14 changed files with 150 additions and 92 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ jobs:
- run: npm install -g codecov nyc
- run: yarn install
- run: yarn build
- run: TZ=America/Vancouver yarn test
- run: TZ=America/Los_Angeles yarn test
- run: TZ=Africa/Nairobi yarn test
- run: TZ=Pacific/Kiritimati yarn test
- run: TZ=Asia/Tokyo yarn test-ci
- run: LANG=en_CA TZ=America/Vancouver yarn test
- run: LANG=en_US TZ=America/Los_Angeles yarn test
- run: LANG=sw_KE TZ=Africa/Nairobi yarn test
- run: LANG=en_KI TZ=Pacific/Kiritimati yarn test
- run: LANG=jp_JP TZ=Asia/Tokyo yarn test-ci
- run: nyc report --reporter=json && codecov -t ${{ secrets.CODECOV_REPO_TOKEN }} -f coverage/*.json
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
### Changelog

- 2.7.2 (2023-02-10)

- Bugfixes:
- Fix rezonedDate ([#523](https://github.com/jakubroztocil/rrule/issues/523))
- Export datetime ([#551](https://github.com/jakubroztocil/rrule/issues/551))
- Fixes types for `before()` and `after()` ([#560](https://github.com/jakubroztocil/rrule/issues/560))
- Update README (https://github.com/jakubroztocil/rrule/pull/543)

- 2.7.1 (2022-07-10)

- Internal:
Expand Down
131 changes: 76 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[![js-standard-style][js-standard-image]][js-standard-url]
[![Downloads][downloads-image]][downloads-url]
[![Gitter][gitter-image]][gitter-url]
[![codecov.io](http://codecov.io/github/jakubroztocil/rrule/coverage.svg?branch=master)](http://codecov.io/github/jakubroztocil/rrule?branch=master)
[![codecov.io](http://codecov.io/github/jkbrzt/rrule/coverage.svg?branch=master)](http://codecov.io/github/jkbrzt/rrule?branch=master)

rrule.js supports recurrence rules as defined in the [iCalendar
RFC](https://tools.ietf.org/html/rfc5545), with a few important
Expand All @@ -21,7 +21,7 @@ to natural language.

### Quick Start

- [Demo app](http://jakubroztocil.github.io/rrule/)
- [Demo app](http://jkbrzt.github.io/rrule/)
- # For contributors and maintainers: the code for the demo app is only on `gh-pages` branch

#### Client Side
Expand All @@ -32,8 +32,8 @@ $ yarn add rrule

Alternatively, download manually:

- [rrule.min.js](https://jakubroztocil.github.io/rrule/dist/es5/rrule.min.js) (bundled, minified)
- [rrule.js](https://jakubroztocil.github.io/rrule/dist/es5/rrule.js) (bundled, not minified)
- [rrule.min.js](https://jkbrzt.github.io/rrule/dist/es5/rrule.min.js) (bundled, minified)
- [rrule.js](https://jkbrzt.github.io/rrule/dist/es5/rrule.js) (bundled, not minified)

```html
<script src="rrule/dist/es5/rrule.min.js"></script>
Expand Down Expand Up @@ -175,9 +175,9 @@ Dates in JavaScript are tricky. `RRule` tries to support as much flexibility as

By default, `RRule` deals in ["floating" times or UTC timezones](https://tools.ietf.org/html/rfc5545#section-3.2.19). If you want results in a specific timezone, `RRule` also provides [timezone support](#timezone-support). Either way, JavaScript's built-in "timezone" offset tends to just get in the way, so this library simply doesn't use it at all. All times are returned with zero offset, as though it didn't exist in JavaScript.

**The bottom line is the returned "UTC" dates are always meant to be interpreted as dates in your local timezone. This may mean you have to do additional conversion to get the "correct" local time with offset applied.**
**THE BOTTOM LINE: Returned "UTC" dates are always meant to be interpreted as dates in your local timezone. This may mean you have to do additional conversion to get the "correct" local time with offset applied.**

For this reason, it is highly recommended to use timestamps in UTC eg. `new Date(Date.UTC(...))`. Returned dates will likewise be in UTC (except on Chrome, which always returns dates with a timezone offset). It's recommended to use the provided `datetime` helper, which
For this reason, it is highly recommended to use timestamps in UTC eg. `new Date(Date.UTC(...))`. Returned dates will likewise be in UTC (except on Chrome, which always returns dates with a timezone offset). It's recommended to use the provided `datetime()` helper, which
creates dates in the correct format using a 1-based month.

For example:
Expand All @@ -202,7 +202,7 @@ date.getUTCDate() // --> 1
date.getUTCHours() // --> 18
```

If you want to get the same times in true UTC, you may do so eg. using [Luxon](https://moment.github.io/luxon/#/):
If you want to get the same times in true UTC, you may do so (e.g., using [Luxon](https://moment.github.io/luxon/#/)):

```ts
rule.all().map(date =>
Expand Down Expand Up @@ -282,7 +282,7 @@ iCalendar RFC. Only `freq` is required.
<th>Option</th>
<th>Description</th>
</tr>
<thead>
</thead>
<tbody>
<tr>
<td><code>freq</code></td>
Expand Down Expand Up @@ -491,7 +491,7 @@ rule.all(function (date, i) {
##### `RRule.prototype.between(after, before, inc=false [, iterator])`

Returns all the occurrences of the rrule between `after` and `before`.
The inc keyword defines what happens if `after` and/or `before` are
The `inc` keyword defines what happens if `after` and/or `before` are
themselves occurrences. With `inc == true`, they will be included in the
list, if they are found in the recurrence set.

Expand Down Expand Up @@ -582,8 +582,8 @@ var rule = new RRule(options)

#### Natural Language Text Methods

These methods provide an incomplete support for text`RRule` and
`RRule`text conversion. You should test them with your input to see
These methods provide an incomplete support for text`RRule` and
`RRule`text conversion. You should test them with your input to see
whether the result is acceptable.

##### `RRule.prototype.toText([gettext, [language]])`
Expand Down Expand Up @@ -634,38 +634,38 @@ var rule = new RRule(options)
new RRuleSet([(noCache = false)])
```

The RRuleSet instance allows more complex recurrence setups, mixing multiple
The `RRuleSet` instance allows more complex recurrence setups, mixing multiple
rules, dates, exclusion rules, and exclusion dates.

Default `noCache` argument is `false`, caching of results will be enabled,
improving performance of multiple queries considerably.

##### `RRuleSet.prototype.rrule(rrule)`

Include the given rrule instance in the recurrence set generation.
Include the given `rrule` instance in the recurrence set generation.

##### `RRuleSet.prototype.rdate(dt)`

Include the given datetime instance in the recurrence set generation.
Include the given datetime instance `dt` in the recurrence set generation.

##### `RRuleSet.prototype.exrule(rrule)`

Include the given rrule instance in the recurrence set exclusion list. Dates
Include the given `rrule` instance in the recurrence set exclusion list. Dates
which are part of the given recurrence rules will not be generated, even if
some inclusive rrule or rdate matches them. NOTE: EXRULE has been (deprecated
some inclusive rrule or rdate matches them. **NOTE:** `EXRULE` has been (deprecated
in RFC 5545)[https://icalendar.org/iCalendar-RFC-5545/a-3-deprecated-features.html]
and does not support a DTSTART property.
and does not support a `DTSTART` property.

##### `RRuleSet.prototype.exdate(dt)`

Include the given datetime instance in the recurrence set exclusion list. Dates
included that way will not be generated, even if some inclusive rrule or
rdate matches them.
Include the given datetime instance `dt` in the recurrence set exclusion list. Dates
included that way will not be generated, even if some inclusive `rrule` or
`rdate` matches them.

##### `RRuleSet.prototype.tzid(tz?)`

Sets or overrides the timezone identifier. Useful if there are no rrules in this
RRuleSet and thus no DTSTART.
`RRuleSet` and thus no `DTSTART`.

##### `RRuleSet.prototype.all([iterator])`

Expand Down Expand Up @@ -709,36 +709,57 @@ rrulestr(rruleStr[, options])

The `rrulestr` function is a parser for RFC-like syntaxes. The string passed
as parameter may be a multiple line string, a single line string, or just the
RRULE property value.
`RRULE` property value.

Additionally, it accepts the following keyword arguments:

`cache`
If True, the rruleset or rrule created instance will cache its results.
Default is not to cache.

`dtstart`
If given, it must be a datetime instance that will be used when no DTSTART
property is found in the parsed string. If it is not given, and the property
is not found, datetime.now() will be used instead.

`unfold`
If set to True, lines will be unfolded following the RFC specification. It
defaults to False, meaning that spaces before every line will be stripped.

`forceset`
If set to True a rruleset instance will be returned, even if only a single rule
is found. The default is to return an rrule if possible, and an rruleset if necessary.
<dl>

`compatible`
If set to True, the parser will operate in RFC-compatible mode. Right now it
means that unfold will be turned on, and if a DTSTART is found, it will be
considered the first recurrence instance, as documented in the RFC.
<dt><code>cache</code></dt>
<dd>
If <code>true</code>, the <code>rruleset</code> or <code>rrule</code> created instance
will cache its results.
Default is not to cache.
</dd>

<dt><code>dtstart</code></dt>
<dd>
If given, it must be a datetime instance that will be used when no
<code>DTSTART</code> property is found in the parsed string.
If it is not given, and the property is not found,
<code>datetime.now()</code> will be used instead.
</dd>

<dt><code>unfold</code></dt>
<dd>
If set to <code>true</code>, lines will be unfolded following the RFC specification.
It defaults to <code>false</code>, meaning that spaces before every line will be stripped.
</dd>

<dt><code>forceset</code></dt>
<dd>
If set to <code>true</code>, an <code>rruleset</code> instance will be returned,
even if only a single rule is found.
The default is to return an <code>rrule</code> if possible, and
an <code>rruleset</code> if necessary.
</dd>

<dt><code>compatible</code></dt>
<dd>
If set to <code>true</code>, the parser will operate in RFC-compatible mode.
Right now it means that unfold will be turned on, and if a <code>DTSTART</code> is found,
it will be considered the first recurrence instance, as documented in the RFC.
</dd>

<dt><code>tzid</code></dt>
<dd>
If given, it must be a string that will be used when no <code>TZID</code>
property is found in the parsed string.
If it is not given, and the property is not found, <code>'UTC'</code> will
be used by default.
</dd>

`tzid`
If given, it must be a string that will be used when no `TZID` property is found
in the parsed string. If it is not given, and the property is not found, `'UTC'`
will be used by default.
</dl>

---

Expand Down Expand Up @@ -771,8 +792,8 @@ rruleSet.rrule(
rruleSet.rdate(start)
```

- Unlike documented in the RFC, every keyword is valid on every frequency (the
RFC documents that `byweekno` is only valid on yearly frequencies, for example).
- Unlike documented in the RFC, every keyword is valid on every frequency. (The
RFC documents that `byweekno` is only valid on yearly frequencies, for example.)

### Development

Expand All @@ -798,21 +819,21 @@ $ yarn build

#### Authors

- [Jakub Roztocil](http://roztocil.co/)
([@jakubroztocil](http://twitter.com/jakubroztocil))
- [Jakub Roztocil](http://roztocil.co)
([@jkbrzt](http://twitter.com/jkbrzt))
- Lars Schöning ([@lyschoening](http://twitter.com/lyschoening))
- David Golightly ([@davigoli](http://twitter.com/davigoli))

Python `dateutil` is written by [Gustavo
Niemeyer](http://niemeyer.net/).
Niemeyer](http://niemeyer.net).

See [LICENCE](https://github.com/jakubroztocil/rrule/blob/master/LICENCE) for
See [LICENCE](https://github.com/jkbrzt/rrule/blob/master/LICENCE) for
more details.

[npm-url]: https://npmjs.org/package/rrule
[npm-image]: http://img.shields.io/npm/v/rrule.svg
[ci-url]: https://github.com/jakubroztocil/rrule/actions
[ci-image]: https://github.com/jakubroztocil/rrule/workflows/Node%20CI/badge.svg
[ci-url]: https://github.com/jkbrzt/rrule/actions
[ci-image]: https://github.com/jkbrzt/rrule/workflows/Node%20CI/badge.svg
[downloads-url]: https://npmjs.org/package/rrule
[downloads-image]: http://img.shields.io/npm/dm/rrule.svg?style=flat-square
[js-standard-url]: https://github.com/feross/standard
Expand All @@ -822,4 +843,4 @@ more details.

#### Related projects

- https://rrules.com/ — RESTful API to get back occurrences of RRULEs that conform to RFC 5545.
- https://rrules.com — RESTful API to get back occurrences of RRULEs that conform to RFC 5545.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rrule",
"version": "2.7.1",
"version": "2.7.2",
"description": "JavaScript library for working with recurrence rules for calendar dates.",
"homepage": "http://jakubroztocil.github.io/rrule/",
"license": "BSD-3-Clause",
Expand Down
17 changes: 17 additions & 0 deletions src/dateutil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,20 @@ export const untilStringToDate = function (until: string) {
)
)
}

const dateTZtoISO8601 = function (date: Date, timeZone: string) {
// date format for sv-SE is almost ISO8601
const dateStr = date.toLocaleString('sv-SE', { timeZone })
// '2023-02-07 10:41:36'
return dateStr.replace(' ', 'T') + 'Z'
}

export const dateInTimeZone = function (date: Date, timeZone: string) {
const localTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
// Date constructor can only reliably parse dates in ISO8601 format
const dateInLocalTZ = new Date(dateTZtoISO8601(date, localTimeZone))
const dateInTargetTZ = new Date(dateTZtoISO8601(date, timeZone ?? 'UTC'))
const tzOffset = dateInTargetTZ.getTime() - dateInLocalTZ.getTime()

return new Date(date.getTime() - tzOffset)
}
13 changes: 2 additions & 11 deletions src/datewithzone.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { timeToUntilString } from './dateutil'
import { dateInTimeZone, timeToUntilString } from './dateutil'

export class DateWithZone {
public date: Date
Expand Down Expand Up @@ -34,15 +34,6 @@ export class DateWithZone {
return this.date
}

const localTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
const dateInLocalTZ = new Date(
this.date.toLocaleString(undefined, { timeZone: localTimeZone })
)
const dateInTargetTZ = new Date(
this.date.toLocaleString(undefined, { timeZone: this.tzid ?? 'UTC' })
)
const tzOffset = dateInTargetTZ.getTime() - dateInLocalTZ.getTime()

return new Date(this.date.getTime() - tzOffset)
return dateInTimeZone(this.date, this.tzid)
}
}
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ export { RRuleSet } from './rruleset'

export { rrulestr } from './rrulestr'
export { Frequency, ByWeekday, Options } from './types'
export { Weekday, WeekdayStr } from './weekday'
export { Weekday, WeekdayStr, ALL_WEEKDAYS } from './weekday'
export { RRuleStrOptions } from './rrulestr'
export { datetime } from './dateutil'
3 changes: 3 additions & 0 deletions src/nlp/parsetext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,15 @@ export default function parseText(text: string, language: Language = ENGLISH) {
options.freq = RRule.WEEKLY
options.byweekday = [RRule.MO, RRule.TU, RRule.WE, RRule.TH, RRule.FR]
ttr.nextSymbol()
AT()
F()
break

case 'week(s)':
options.freq = RRule.WEEKLY
if (ttr.nextSymbol()) {
ON()
AT()
F()
}
break
Expand Down Expand Up @@ -198,6 +200,7 @@ export default function parseText(text: string, language: Language = ENGLISH) {
options.byweekday.push(RRule[wkd] as ByWeekday)
ttr.nextSymbol()
}
AT()
MDAYs()
F()
break
Expand Down
4 changes: 4 additions & 0 deletions src/nlp/totext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@ export default class ToText {
} else if (this.byweekday) {
this._byweekday()
}

if (this.origOptions.byhour) {
this._byhour()
}
}
}

Expand Down
Loading

0 comments on commit 6059fae

Please sign in to comment.