UNPKG

@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
"use strict"; 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