Skip to content
Open
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
62 changes: 41 additions & 21 deletions main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,23 @@ import { assertEquals } from 'https://deno.land/std@0.154.0/testing/asserts.ts';
import { Event, EventConfig, Calendar } from './mod.ts';
import { short_utc } from './date.ts';

Deno.test('UTC event',
() => {
const cfg: EventConfig = {
title: 'test',
beginDate: [2022, 8, 1, 10, 10],
endDate: [2022, 8, 1, 11, 10],
desc: 'Hello',
zone: 'utc' // Universal time required
};
const evt = new Event(cfg);
const lines = evt.toLines()
assertEquals(lines[0][0], "BEGIN")
assertEquals(lines[0][1], "VEVENT")
Deno.test('UTC event', () => {
const cfg: EventConfig = {
title: 'test',
beginDate: [2022, 8, 1, 10, 10],
endDate: [2022, 8, 1, 11, 10],
desc: 'Hello',
zone: 'utc', // Universal time required
};
const evt = new Event(cfg);
const lines = evt.toLines();
assertEquals(lines[0][0], 'BEGIN');
assertEquals(lines[0][1], 'VEVENT');

// Note 'Z' suffix for UTC (precaution against daylight saving time surprises)
assertEquals(lines[3], ["DTSTART", "20220801T101000Z"])
assertEquals(lines[4], ["DTEND", "20220801T111000Z"])

},
);
// Note 'Z' suffix for UTC (precaution against daylight saving time surprises)
assertEquals(lines[3], ['DTSTART', '20220801T101000Z']);
assertEquals(lines[4], ['DTEND', '20220801T111000Z']);
});

Deno.test({
name: 'calendar',
Expand All @@ -49,11 +46,11 @@ Deno.test({
desc: 'Implement a module to generate .ics files',
organizer: {
name: 'Sam Worthington',
email: 'sam@dev.com'
email: 'sam@dev.com',
},
url: 'https://www.google.com/',
location: 'ABC Tank Warehouse',
geo: { lat: 10.4, lon: 44.5 }
geo: { lat: 10.4, lon: 44.5 },
};

const cfg2: EventConfig = {
Expand All @@ -78,3 +75,26 @@ Deno.test({
console.log(calendar.toString());
},
});

Deno.test('UTC event custom ID', () => {
const customId = '03bdde99-ede2-410e-b1b1-4c2a09d65b1f';
const cfg: EventConfig = {
customId,
title: 'test custom ID event',
beginDate: [2022, 8, 1, 10, 10],
endDate: [2022, 8, 1, 11, 10],
desc: 'Hello',
zone: 'utc', // Universal time required
};
const evt = new Event(cfg);
const lines = evt.toLines();

assertEquals(lines[0], ['BEGIN', 'VEVENT']);

assertEquals(lines[1][0], 'UID');
assertEquals(lines[1][1], customId);

assertEquals(lines[lines.length - 1], ['END', 'VEVENT']);

console.log(evt.toString());
});
50 changes: 34 additions & 16 deletions main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@ const eventBegin: ContentLine = ['BEGIN', 'VEVENT'];

const eventEnd: ContentLine = ['END', 'VEVENT'];

const parseGeo = (geo?: {lat: number, lon: number}) => {
const parseGeo = (geo?: { lat: number; lon: number }) => {
return geo ? `${geo.lat};${geo.lon}` : undefined;
}
};

const parseOrganizer = (organizer?: {name: string, email: string}) => {
return organizer? `CN=${organizer.name}:mailto:${organizer.email}` : undefined
}
const parseOrganizer = (organizer?: { name: string; email: string }) => {
return organizer
? `CN=${organizer.name}:mailto:${organizer.email}`
: undefined;
};

export class Event {
zone: Zone = 'local'
constructor (protected config: EventConfig) {
if (config.zone) this.zone = config.zone
zone: Zone = 'local';
constructor(protected config: EventConfig) {
if (config.zone) this.zone = config.zone;
if (config.duration !== undefined) {
// Duration is provided
if (!(config.beginDate instanceof Date)) {
Expand All @@ -48,13 +50,24 @@ export class Event {
}

toLines(): ContentLine[] {
const uid = crypto.randomUUID();
const { title, desc, rrule, alarm, location, url, organizer, geo, htmlContent } = this.config;
const {
customId,
title,
desc,
rrule,
alarm,
location,
url,
organizer,
geo,
htmlContent,
} = this.config;
const uid = customId ?? crypto.randomUUID();

const result = [
eventBegin,
['UID', uid],
['DTSTAMP', parseDate(new Date(), "utc")],
['DTSTAMP', parseDate(new Date(), 'utc')],
['DTSTART', parseDate(this.config.beginDate, this.zone)],
['DTEND', parseDate(this.config.endDate!, this.zone)],
['SUMMARY', title],
Expand All @@ -66,17 +79,21 @@ export class Event {
['ORGANIZER', parseOrganizer(organizer)],
...parseAlarm(alarm),
eventEnd,
].filter(line => line[1] !== undefined) as ContentLine[];
].filter((line) => line[1] !== undefined) as ContentLine[];

return result;
}

toString() {
return stringifyLines(this.toLines());
}
}

export class Calendar {
constructor(protected events: Event[]) {}

toLines(): ContentLine[] {
const eventLines = this.events.map(evt => evt.toLines()).flat();
const eventLines = this.events.map((evt) => evt.toLines()).flat();

return [calendarBegin, ...calendarProps, ...eventLines, calendarEnd];
}
Expand All @@ -87,6 +104,7 @@ export class Calendar {
}

export interface EventConfig {
customId?: string;
title: string;
beginDate: DateData;
endDate?: DateData;
Expand All @@ -96,10 +114,10 @@ export interface EventConfig {
alarm?: AlarmConfig;
location?: string;
url?: string;
organizer?: { name: string; email: string; dir?: string; };
geo?: { lat: number; lon: number; };
organizer?: { name: string; email: string; dir?: string };
geo?: { lat: number; lon: number };
htmlContent?: string;
zone?: Zone // default to local
zone?: Zone; // default to local
}

export interface AlarmConfig {
Expand Down