@bobfrankston/gcal 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/gcal.ts +52 -4
  2. package/glib/gutils.ts +23 -0
  3. package/package.json +1 -1
package/gcal.ts CHANGED
@@ -90,7 +90,8 @@ async function listEvents(
90
90
  accessToken: string,
91
91
  calendarId = 'primary',
92
92
  maxResults = 10,
93
- timeMin?: string
93
+ timeMin?: string,
94
+ timeMax?: string
94
95
  ): Promise<GoogleEvent[]> {
95
96
  const params = new URLSearchParams({
96
97
  maxResults: maxResults.toString(),
@@ -98,6 +99,9 @@ async function listEvents(
98
99
  orderBy: 'startTime',
99
100
  timeMin: timeMin || new Date().toISOString()
100
101
  });
102
+ if (timeMax) {
103
+ params.set('timeMax', timeMax);
104
+ }
101
105
 
102
106
  const url = `${CALENDAR_API_BASE}/calendars/${encodeURIComponent(calendarId)}/events?${params}`;
103
107
  const res = await apiFetch(url, accessToken);
@@ -239,14 +243,18 @@ Options:
239
243
  -defaultUser <email> Set default user for future use
240
244
  -c, -calendar <id> Calendar ID (default: primary)
241
245
  -n <count> Number of events to list
246
+ -after <when> List events after this date/time
247
+ -before <when> List events before this date/time
242
248
  -v, -verbose Show event IDs and links
243
249
 
244
250
  Examples:
245
251
  gcal meeting.ics Import ICS file
246
252
  gcal list List next 10 events
253
+ gcal list -after tomorrow -before "jan 30"
247
254
  gcal add "Dentist" "Friday 3pm" "1h"
248
255
  gcal add "Lunch" "1/14/2026 12:00" "1h"
249
- gcal add "Meeting" "tomorrow 10:00"
256
+ gcal add "Meeting" "tomorrow 10am"
257
+ gcal add "Call" "3pm" "30m"
250
258
  gcal add "Appointment" "jan 15 2pm"
251
259
  gcal -defaultUser bob@gmail.com Set default user
252
260
 
@@ -266,6 +274,8 @@ interface ParsedArgs {
266
274
  help: boolean;
267
275
  verbose: boolean;
268
276
  icsFile: string; /** Direct .ics file path */
277
+ after: string; /** Filter: events after this date/time */
278
+ before: string; /** Filter: events before this date/time */
269
279
  }
270
280
 
271
281
  function parseArgs(argv: string[]): ParsedArgs {
@@ -278,7 +288,9 @@ function parseArgs(argv: string[]): ParsedArgs {
278
288
  count: 10,
279
289
  help: false,
280
290
  verbose: false,
281
- icsFile: ''
291
+ icsFile: '',
292
+ after: '',
293
+ before: ''
282
294
  };
283
295
 
284
296
  const unknown: string[] = [];
@@ -303,6 +315,14 @@ function parseArgs(argv: string[]): ParsedArgs {
303
315
  case '-n':
304
316
  result.count = parseInt(argv[++i]) || 10;
305
317
  break;
318
+ case '-after':
319
+ case '--after':
320
+ result.after = argv[++i] || '';
321
+ break;
322
+ case '-before':
323
+ case '--before':
324
+ result.before = argv[++i] || '';
325
+ break;
306
326
  case '-v':
307
327
  case '-verbose':
308
328
  case '--verbose':
@@ -428,7 +448,9 @@ async function main(): Promise<void> {
428
448
  case 'list': {
429
449
  const count = parsed.args[0] ? parseInt(parsed.args[0]) : parsed.count;
430
450
  const token = await getAccessToken(user, false);
431
- const events = await listEvents(token, parsed.calendar, count);
451
+ const timeMin = parsed.after ? parseDateTime(parsed.after).toISOString() : undefined;
452
+ const timeMax = parsed.before ? parseDateTime(parsed.before).toISOString() : undefined;
453
+ const events = await listEvents(token, parsed.calendar, count, timeMin, timeMax);
432
454
 
433
455
  if (events.length === 0) {
434
456
  console.log('No upcoming events found.');
@@ -484,6 +506,32 @@ async function main(): Promise<void> {
484
506
  const durationMins = parseDuration(duration);
485
507
  const endTime = new Date(startTime.getTime() + durationMins * 60 * 1000);
486
508
 
509
+ // Check for suspicious dates (likely parsing errors)
510
+ const now = new Date();
511
+ const twoYearsFromNow = new Date(now);
512
+ twoYearsFromNow.setFullYear(twoYearsFromNow.getFullYear() + 2);
513
+
514
+ let warning = '';
515
+ if (startTime < now) {
516
+ warning = `Date is in the past: ${formatDateTime({ dateTime: startTime.toISOString() })}`;
517
+ } else if (startTime > twoYearsFromNow) {
518
+ warning = `Date is more than 2 years away: ${formatDateTime({ dateTime: startTime.toISOString() })}`;
519
+ }
520
+
521
+ if (warning) {
522
+ console.log(`\nWarning: ${warning}`);
523
+ console.log(`Input was: "${when}"`);
524
+ const readline = await import('readline');
525
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
526
+ const response = await new Promise<string>(resolve => {
527
+ rl.question('Continue? (y/N) ', answer => { rl.close(); resolve(answer); });
528
+ });
529
+ if (response.toLowerCase() !== 'y') {
530
+ console.log('Cancelled.');
531
+ process.exit(0);
532
+ }
533
+ }
534
+
487
535
  const event: GoogleEvent = {
488
536
  summary: title,
489
537
  start: {
package/glib/gutils.ts CHANGED
@@ -307,6 +307,29 @@ export function parseDateTime(input: string): Date {
307
307
  return d;
308
308
  }
309
309
 
310
+ // Handle time with am/pm: "10am", "3pm", "10:30am", "3:45pm" - assume today
311
+ const timeAmPmMatch = lower.match(/^(\d{1,2})(?::(\d{2}))?\s*(am|pm)$/);
312
+ if (timeAmPmMatch) {
313
+ const [, hour, min, ampm] = timeAmPmMatch;
314
+ const d = new Date(now);
315
+ let h = parseInt(hour);
316
+ if (ampm === 'pm' && h < 12) h += 12;
317
+ if (ampm === 'am' && h === 12) h = 0;
318
+ d.setHours(h, parseInt(min || '0'), 0, 0);
319
+ return d;
320
+ }
321
+
322
+ // Handle bare hour: "10", "14" - interpret as today at that hour (not a month)
323
+ const bareHourMatch = input.match(/^(\d{1,2})$/);
324
+ if (bareHourMatch) {
325
+ const hour = parseInt(bareHourMatch[1]);
326
+ if (hour >= 0 && hour <= 23) {
327
+ const d = new Date(now);
328
+ d.setHours(hour, 0, 0, 0);
329
+ return d;
330
+ }
331
+ }
332
+
310
333
  // Try native Date parsing
311
334
  const parsed = new Date(input);
312
335
  if (!isNaN(parsed.getTime())) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/gcal",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Google Calendar CLI tool with ICS import support",
5
5
  "type": "module",
6
6
  "main": "gcal.ts",