Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
**Library for working with recurrence rules for calendar dates.**

<p style="text-align:center;">
<img src="https://img.shields.io/npm/v/@offload-project/rrule" alt="npm version">
<img src="https://img.shields.io/github/package-json/v/offload-project/rrule" alt="version">
<img alt="GitHub Actions Release Workflow Status" src="https://img.shields.io/github/actions/workflow/status/offload-project/rrule/release.yml?label=Build">
<img alt="GitHub Actions Test Workflow Status" src="https://img.shields.io/github/actions/workflow/status/offload-project/rrule/test.yml?label=Test">
<img src="https://img.shields.io/npm/l/@offload-project/rrule" alt="license">
</p>

rrule.js supports recurrence rules as defined in the [iCalendar RFC](https://tools.ietf.org/html/rfc5545), with a few
Expand Down
3 changes: 2 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
"indentWidth": 2,
"lineWidth": 120
},
"linter": {
"enabled": true,
Expand Down
24 changes: 5 additions & 19 deletions src/cache.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { clone, cloneDates } from './dateutil';
import { clone, cloneDates } from './date';
import { isArray } from './helpers';
import IterResult, { type IterArgs } from './iterresult';

Expand All @@ -7,10 +7,7 @@ export type CacheKeys = 'before' | 'after' | 'between';
/** Max cached entries per query method (before/after/between) */
const MAX_CACHE_SIZE = 64;

function argsMatch(
left: IterArgs[keyof IterArgs] | undefined,
right: IterArgs[keyof IterArgs] | undefined,
) {
function argsMatch(left: IterArgs[keyof IterArgs] | undefined, right: IterArgs[keyof IterArgs] | undefined) {
if (Array.isArray(left)) {
if (!Array.isArray(right)) return false;
if (left.length !== right.length) return false;
Expand All @@ -35,11 +32,7 @@ export class Cache {
* @param {Array,Date} value - an array of dates, one date, or null
* @param {Object?} args - _iter arguments
*/
public _cacheAdd(
what: CacheKeys | 'all',
value: Date[] | Date | null,
args?: Partial<IterArgs>,
) {
public _cacheAdd(what: CacheKeys | 'all', value: Date[] | Date | null, args?: Partial<IterArgs>) {
if (value) {
value = value instanceof Date ? clone(value) : cloneDates(value);
}
Expand All @@ -63,10 +56,7 @@ export class Cache {
* @return [] - cached, but zero occurrences (all/between)
* @return [Date1, DateN] - cached (all/between)
*/
public _cacheGet(
what: CacheKeys | 'all',
args?: Partial<IterArgs>,
): Date | Date[] | false | null {
public _cacheGet(what: CacheKeys | 'all', args?: Partial<IterArgs>): Date | Date[] | false | null {
let cached: Date | Date[] | false | null = false;
const argsKeys = args ? (Object.keys(args) as (keyof IterArgs)[]) : [];
const findCacheDiff = (item: IterArgs) => {
Expand Down Expand Up @@ -102,10 +92,6 @@ export class Cache {
this._cacheAdd(what, cached, args);
}

return isArray(cached)
? cloneDates(cached)
: cached instanceof Date
? clone(cached)
: cached;
return isArray(cached) ? cloneDates(cached) : cached instanceof Date ? clone(cached) : cached;
}
}
29 changes: 0 additions & 29 deletions src/callbackiterresult.ts

This file was deleted.

79 changes: 46 additions & 33 deletions src/dateutil.ts → src/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,8 @@ import type { Time } from './datetime';

type Datelike = Pick<Date, 'getTime'>;

export const datetime = (
y: number,
m: number,
d: number,
h = 0,
i = 0,
s = 0,
) => new Date(Date.UTC(y, m - 1, d, h, i, s));
export const datetime = (y: number, m: number, d: number, h = 0, i = 0, s = 0) =>
new Date(Date.UTC(y, m - 1, d, h, i, s));

/**
* General date-related utilities.
Expand Down Expand Up @@ -45,26 +39,11 @@ export const PY_WEEKDAYS = [6, 0, 1, 2, 3, 4, 5];
* py_date.timetuple()[7]
*/
export const getYearDay = (date: Date) => {
const dateNoTime = new Date(
date.getUTCFullYear(),
date.getUTCMonth(),
date.getUTCDate(),
);
return (
Math.ceil(
(dateNoTime.valueOf() - new Date(date.getUTCFullYear(), 0, 1).valueOf()) /
ONE_DAY,
) + 1
);
const dateNoTime = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
return Math.ceil((dateNoTime.valueOf() - new Date(date.getUTCFullYear(), 0, 1).valueOf()) / ONE_DAY) + 1;
};

export const isLeapYear = (year: number) =>
(year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;

export const isDate = (value: unknown): value is Date => value instanceof Date;

export const isValidDate = (value: unknown): value is Date =>
isDate(value) && !Number.isNaN(value.getTime());
export const isLeapYear = (year: number) => (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;

/**
* @return {Number} the date's timezone offset in ms
Expand Down Expand Up @@ -95,21 +74,17 @@ export const toOrdinal = (date: Date) => daysBetween(date, ORDINAL_BASE);
/**
* @see - <http://docs.python.org/library/datetime.html#datetime.date.fromordinal>
*/
export const fromOrdinal = (ordinal: number) =>
new Date(ORDINAL_BASE.getTime() + ordinal * ONE_DAY);
export const fromOrdinal = (ordinal: number) => new Date(ORDINAL_BASE.getTime() + ordinal * ONE_DAY);

export const getMonthDays = (date: Date): number => {
const month = date.getUTCMonth();
return month === 1 && isLeapYear(date.getUTCFullYear())
? 29
: MONTH_DAYS[month]!;
return month === 1 && isLeapYear(date.getUTCFullYear()) ? 29 : MONTH_DAYS[month]!;
};

/**
* @return {Number} python-like weekday
*/
export const getWeekday = (date: Date): number =>
PY_WEEKDAYS[date.getUTCDay()]!;
export const getWeekday = (date: Date): number => PY_WEEKDAYS[date.getUTCDay()]!;

/**
* @see: <http://docs.python.org/library/calendar.html#calendar.monthrange>
Expand Down Expand Up @@ -204,3 +179,41 @@ export const dateInTimeZone = (date: Date, timeZone: string) => {

return new Date(date.getTime() - tzOffset);
};

export class DateWithZone {
public date: Date;
public tzid?: string | null;

constructor(date: Date, tzid?: string | null) {
if (Number.isNaN(date.getTime())) {
throw new RangeError('Invalid date passed to DateWithZone');
}
this.date = date;
this.tzid = tzid;
}

private get isUTC() {
return !this.tzid || this.tzid.toUpperCase() === 'UTC';
}

public toString() {
const datestr = timeToUntilString(this.date.getTime(), this.isUTC);
if (!this.isUTC) {
return `;TZID=${this.tzid}:${datestr}`;
}

return `:${datestr}`;
}

public getTime() {
return this.date.getTime();
}

public rezonedDate() {
if (this.isUTC) {
return this.date;
}

return dateInTimeZone(this.date, this.tzid!);
}
}
53 changes: 9 additions & 44 deletions src/datetime.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getWeekday, MAXYEAR, monthRange } from './dateutil';
import { getWeekday, MAXYEAR, monthRange } from './date';
import { divmod, empty, pymod } from './helpers';
import { Frequency, type ParsedOptions } from './types';

Expand All @@ -8,12 +8,7 @@ export class Time {
public second: number;
public millisecond: number;

constructor(
hour: number,
minute: number,
second: number,
millisecond: number,
) {
constructor(hour: number, minute: number, second: number, millisecond: number) {
this.hour = hour;
this.minute = minute;
this.second = second;
Expand All @@ -37,10 +32,7 @@ export class Time {
}

getTime() {
return (
(this.hour * 60 * 60 + this.minute * 60 + this.second) * 1000 +
this.millisecond
);
return (this.hour * 60 * 60 + this.minute * 60 + this.second) * 1000 + this.millisecond;
}
}

Expand Down Expand Up @@ -82,15 +74,7 @@ export class DateTime extends Time {

getTime() {
return new Date(
Date.UTC(
this.year,
this.month - 1,
this.day,
this.hour,
this.minute,
this.second,
this.millisecond,
),
Date.UTC(this.year, this.month - 1, this.day, this.hour, this.minute, this.second, this.millisecond),
).getTime();
}

Expand Down Expand Up @@ -156,16 +140,10 @@ export class DateTime extends Time {
}
}

public addMinutes(
minutes: number,
filtered: boolean,
byhour: number[],
byminute: number[],
) {
public addMinutes(minutes: number, filtered: boolean, byhour: number[], byminute: number[]) {
if (filtered) {
// Jump to one iteration before next day
this.minute +=
Math.floor((1439 - (this.hour * 60 + this.minute)) / minutes) * minutes;
this.minute += Math.floor((1439 - (this.hour * 60 + this.minute)) / minutes) * minutes;
}

for (;;) {
Expand All @@ -176,29 +154,16 @@ export class DateTime extends Time {
this.addHours(hourDiv, false, byhour);
}

if (
(empty(byhour) || byhour.includes(this.hour)) &&
(empty(byminute) || byminute.includes(this.minute))
) {
if ((empty(byhour) || byhour.includes(this.hour)) && (empty(byminute) || byminute.includes(this.minute))) {
break;
}
}
}

public addSeconds(
seconds: number,
filtered: boolean,
byhour: number[],
byminute: number[],
bysecond: number[],
) {
public addSeconds(seconds: number, filtered: boolean, byhour: number[], byminute: number[], bysecond: number[]) {
if (filtered) {
// Jump to one iteration before next day
this.second +=
Math.floor(
(86399 - (this.hour * 3600 + this.minute * 60 + this.second)) /
seconds,
) * seconds;
this.second += Math.floor((86399 - (this.hour * 3600 + this.minute * 60 + this.second)) / seconds) * seconds;
}

for (;;) {
Expand Down
39 changes: 0 additions & 39 deletions src/datewithzone.ts

This file was deleted.

Loading