@wyteco/berkeley-ical
Version:
A command line tool to easily export your classes from the Berkeley Academic Guide to your calendar in iCal (.ics) format. Without having a student account lol!
112 lines • 4.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CourseSchema = exports.weekdayLookup = exports.weekdays = void 0;
exports.parseTimeString = parseTimeString;
exports.parseDateString = parseDateString;
exports.combineDateAndTime = combineDateAndTime;
exports.findFirstMeeting = findFirstMeeting;
const zod_1 = require("zod");
const date_fns_1 = require("date-fns");
// ----------------------------------------------------------------------
/**
* Parse a time-only string (e.g. `02:00 pm`) as a local JS Date pinned to 1970-01-01.
*/
function parseTimeString(timeString) {
const parsed = (0, date_fns_1.parse)(timeString.trim(), 'hh:mm aa', new Date(0));
if (isNaN(parsed.getTime())) {
console.log('');
throw new Error(`Invalid time string: "${timeString}"`);
}
return parsed;
}
// ----------------------------------------------------------------------
/**
* Parse a date-only string (e.g. `Jan 21, 2025`) as a local JS date.
*/
function parseDateString(dateString) {
const parsed = (0, date_fns_1.parse)(dateString.trim(), 'MMM d, yyyy', new Date());
if (isNaN(parsed.getTime())) {
console.log('');
throw new Error(`Invalid date string: "${dateString}"`);
}
return parsed;
}
// ----------------------------------------------------------------------
/**
* Combine a date and a time JS Date into a single JS Date.
*
* @example
* dateOnly => 2025-01-21T00:00 local
* timeOnly => 1970-01-01T14:00 local
* We want 2025-01-21T14:00 local
*/
function combineDateAndTime(dateOnly, timeOnly) {
/**
* Extract the year, month, and day from dateOnly.
*/
const year = dateOnly.getFullYear();
const month = dateOnly.getMonth();
const day = dateOnly.getDate();
/**
* Extract the hours and minutes from timeOnly.
*/
const hours = timeOnly.getHours();
const minutes = timeOnly.getMinutes();
return new Date(year, month, day, hours, minutes);
}
// ----------------------------------------------------------------------
/**
* We store the weekdays in a constant array to avoid
* typos and to make it easier to update the code
* if the website changes.
* Note that `Date.getDay` assumes Sunday is 0, Monday is 1, etc.
*/
exports.weekdays = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
exports.weekdayLookup = {
MO: ['mo', 'mon', 'monday'],
TU: ['tu', 'tue', 'tuesday'],
WE: ['we', 'wed', 'wednesday'],
TH: ['th', 'thu', 'thursday'],
FR: ['fr', 'fri', 'friday'],
SA: ['sa', 'sat', 'saturday'],
SU: ['su', 'sun', 'sunday'],
};
// ----------------------------------------------------------------------
/**
* Given a date (e.g. Jan 21, 2025) and a list of valid ICS day codes
* (e.g. ["TU","TH"]), find the earliest date that matches one of the days.
* For example, if the date is Jan 21, 2025 (a Tuesday) and the days are
* ["TU","TH"], the function will return Jan 21, 2025.
*/
function findFirstMeeting(startDate, meetingDays) {
// Turn the meetingDays into a set of numeric weekdays:
const meetingDayIndexes = meetingDays.map((day) => exports.weekdays.indexOf(day));
// We'll loop from 'start' forward until we find a weekday that matches:
let current = new Date(startDate);
while (true) {
const dayOfWeek = current.getDay();
if (meetingDayIndexes.includes(dayOfWeek)) {
return current;
}
current = (0, date_fns_1.addDays)(current, 1);
}
}
// ----------------------------------------------------------------------
/**
* Define the schema for the course data.
* This schema is used to validate the extracted data from the website.
*/
exports.CourseSchema = zod_1.z.object({
title: zod_1.z.string(),
description: zod_1.z.string(),
instructors: zod_1.z.array(zod_1.z.string()),
startDate: zod_1.z.date(),
endDate: zod_1.z.date().nullable(),
meetingDays: zod_1.z.array(zod_1.z.enum(exports.weekdays)),
meetingStartTime: zod_1.z.date(),
meetingEndTime: zod_1.z.date().nullable(),
location: zod_1.z.string(),
numberOfEnrollments: zod_1.z.number(),
capacity: zod_1.z.number(),
});
//# sourceMappingURL=helpers.js.map