Schedules

pub package License: MIT

A pure-Dart package for generating and managing recurring events.

Features

  • Determine if an event occurs on a specific day
  • Generate a list of n events
  • Generate a list of events until x date
  • Math-based algorithms for better performance

Schedule Types

  • One-time: An event that occurs only once
  • Daily: An event that occurs every n days
  • Weekly: An event that occurs on one or more weekdays every n weeks
  • Monthly: An event that occurs on one or more days every n months
  • MonthlyWeekday: An event that occurs on the x_th weekday every n months
  • Yearly: An event that occurs on a particular date every n years

Getting Started

Add the package to your pubspec.yaml:

dependencies:
  schedules: ^1.1.0

Or install directly:

dart pub add schedules

Usage

Import the package:

import 'package:schedules/schedules.dart';

Schedule Types

Singular (one-time event)

An event that occurs only once:

final singular = Singular(
  date: DateTime(2023, 01, 01),
);

Daily

Repeats every n days:

// Every other day, beginning on January 1, 2023
final daily = Daily(
  startDate: DateTime(2023, 01, 01),
  frequency: 2,
);

Weekly

Repeats every n weeks on the specified weekdays:

// Every other week on Monday and Thursday, 
// beginning on January 1, 2023
final weekly = Weekly(
  startDate: DateTime(2023, 01, 01),
  frequency: 2,
  weekdays: [DateTime.monday, DateTime.thursday],
);

Monthly

Repeats every n months on the specified days:

// Every month on the 1st and 15th,
// beginning on January 1, 2023
final monthly = Monthly(
  startDate: DateTime(2023, 01, 01),
  frequency: 1,
  days: [1, 15],
);

MonthlyWeekday

Repeats every n months on a specific weekday of a specific week:

// First Monday of every month,
// beginning on January 1, 2023
final firstMonday = MonthlyWeekday(
  startDate: DateTime(2023, 01, 01),
  frequency: 1,
  weekday: DateTime.monday,
  weekOfMonth: 1, // First occurrence
);

// Last Friday of every other month
final lastFriday = MonthlyWeekday(
  startDate: DateTime(2023, 01, 01),
  frequency: 2,
  weekday: DateTime.friday,
  weekOfMonth: -1, // Last occurrence
);

// Third Thursday of every month
final thirdThursday = MonthlyWeekday(
  startDate: DateTime(2023, 01, 01),
  frequency: 1,
  weekday: DateTime.thursday,
  weekOfMonth: 3,
);

Parameters:

  • weekday: The day of the week (1=Monday, 7=Sunday)
  • weekOfMonth: Which occurrence of the weekday (1-5, or -1 for last)
  • frequency: How often in months (1=every month, 2=every other month, etc.)

Week of Month Values:

  • 1 = First occurrence (e.g., first Monday)
  • 2 = Second occurrence (e.g., second Friday)
  • 3 = Third occurrence (e.g., third Tuesday)
  • 4 = Fourth occurrence (e.g., fourth Wednesday)
  • 5 = Fifth occurrence (e.g., fifth Thursday) - only exists in some months
  • -1 = Last occurrence (e.g., last Friday) - finds the last occurrence, regardless of count

Yearly

Repeats every n years on the specified date:

// Every 3 years on January 1st, 
// beginning on January 1, 2023
final yearly = Yearly(
  startDate: DateTime(2023, 01, 01),
  frequency: 3,
);

End Dates

All schedule types (except Singular) support an optional end date:

// A schedule that runs every Sunday for 3 months
final projectSchedule = Weekly(
  startDate: DateTime(2023, 1, 1),
  frequency: 1,
  weekdays: [DateTime.sunday],
  endDate: DateTime(2023, 3, 31),
);

// This will only return occurrences within the date range
final occurrences = projectSchedule.getOccurrencesUntil(
  DateTime(2023, 4, 1),
).toList();

// Contains: [2023-01-01, 2023-01-08, 2023-01-15, 2023-01-22, 2023-01-29, 
//           2023-02-05, 2023-02-12, 2023-02-19, 2023-02-26, 2023-03-05, 
//           2023-03-12, 2023-03-19, 2023-03-26]

Excluding Dates

Skip specific dates when generating occurrences:

final workSchedule = Weekly(
  startDate: DateTime(2023, 1, 1),
  frequency: 1,
  weekdays: [DateTime.monday, DateTime.wednesday, DateTime.friday],
);

// Exclude holidays and vacation days
final excludedDates = [
  DateTime(2023, 1, 2), // New Year's Day (observed)
  DateTime(2023, 1, 16), // MLK Day
];

// Get work days for the next 2 weeks, excluding holidays
final workDays = workSchedule.getNextNOccurrences(
  6, 
  exclude: excludedDates,
).toList();

// Contains: [2023-01-04 (Wed), 2023-01-06 (Fri), 2023-01-09 (Mon), 
//           2023-01-11 (Wed), 2023-01-13 (Fri)]

Date Ranges

Get all occurrences within a specific date range:

final billSchedule = Monthly(
  startDate: DateTime(2023, 1, 1),
  frequency: 1,
  days: [1, 15], // 1st and 15th of each month
);

// Get bill due dates for Q1 2023
final q1Bills = billSchedule.getOccurrencesUntil(
  DateTime(2023, 4, 1),
  from: DateTime(2023, 1, 1),
).toList();

// Contains: [2023-01-01, 2023-01-15, 2023-02-01, 2023-02-15, 2023-03-01, 2023-03-15]

// Get bill dates for just February
final febBills = billSchedule.getOccurrencesUntil(
  DateTime(2023, 3, 1),
  from: DateTime(2023, 2, 1),
).toList();

// Contains: [2023-02-01, 2023-02-15]

API

All schedule types provide these methods:

  • occursOn(DateTime date) - Check if the schedule occurs on a specific date
  • getNextNOccurrences(int n, {DateTime? from, Iterable<DateTime>? exclude}) - Get the next N occurrences
  • getOccurrencesUntil(DateTime end, {DateTime? from, Iterable<DateTime>? exclude}) - Get all occurrences until a specific date

Concrete Examples

Payday Schedule

// Bi-weekly paydays on Fridays, starting January 6, 2023
final payday = Weekly(
  startDate: DateTime(2023, 1, 6), // First Friday
  frequency: 2, // Every 2 weeks
  weekdays: [DateTime.friday],
);

// Check if today is payday
final today = DateTime.now();
if (payday.occursOn(today)) {
  print('It\'s payday!');
}

// Get paydays for the next 3 months
final nextPaydays = payday.getOccurrencesUntil(
  DateTime(2023, 4, 1),
  from: DateTime(2023, 1, 1),
).toList();

// Contains: [2023-01-06, 2023-01-20, 2023-02-03, 2023-02-17, 2023-03-03, 2023-03-17]

Medication Reminder

// Take medication daily
final medication = Daily(
  startDate: DateTime(2023, 1, 1),
  frequency: 1, // Daily
);

// Check if it's medication day
final today = DateTime.now();
if (medication.occursOn(today)) {
  print('Take your medication today');
}

// Get next 3 medication days
final nextDoses = medication.getNextNOccurrences(3);
// Output: [2023-01-01, 2023-01-02, 2023-01-03]

Bill Due Dates

// Rent due on the 1st, utilities on the 15th
final rent = Monthly(
  startDate: DateTime(2023, 1, 1),
  frequency: 1,
  days: [1],
);

final utilities = Monthly(
  startDate: DateTime(2023, 1, 15),
  frequency: 1,
  days: [15],
);

// Check if any bills are due today
final today = DateTime.now();

// Get bill due dates for Q1 2023
final q1Bills = [
  ...rent.getOccurrencesUntil(DateTime(2023, 4, 1), from: DateTime(2023, 1, 1)),
  ...utilities.getOccurrencesUntil(DateTime(2023, 4, 1), from: DateTime(2023, 1, 1)),
].toList()..sort();

// Contains: [2023-01-01 (rent), 2023-01-15 (utilities), 2023-02-01 (rent), 
//           2023-02-15 (utilities), 2023-03-01 (rent), 2023-03-15 (utilities)]

Board Meeting Schedule

// Board meetings on the first Monday of every month
final boardMeeting = MonthlyWeekday(
  startDate: DateTime(2023, 1, 1),
  frequency: 1,
  weekday: DateTime.monday,
  weekOfMonth: 1, // First Monday
);

// Team retrospectives on the last Friday of every other month
final retrospective = MonthlyWeekday(
  startDate: DateTime(2023, 1, 1),
  frequency: 2, // Every 2 months
  weekday: DateTime.friday,
  weekOfMonth: -1, // Last Friday
);

// Check if today is a meeting day
final today = DateTime.now();
if (boardMeeting.occursOn(today)) {
  print('Board meeting today!');
}

// Get next 6 board meetings
final nextMeetings = boardMeeting.getNextNOccurrences(6).toList();
// Contains: [2023-01-02, 2023-02-06, 2023-03-06, 2023-04-03, 2023-05-01, 2023-06-05]

// Get retrospectives for the year
final yearRetrospectives = retrospective.getOccurrencesUntil(
  DateTime(2024, 1, 1),
  from: DateTime(2023, 1, 1),
).toList();
// Contains: [2023-01-27, 2023-03-31, 2023-05-26, 2023-07-28, 2023-09-29, 2023-11-24]

Work Schedule with Holidays

// Work Monday-Friday
final workDays = Weekly(
  startDate: DateTime(2023, 1, 2), // First Monday
  frequency: 1,
  weekdays: [
    DateTime.monday,
    DateTime.tuesday,
    DateTime.wednesday,
    DateTime.thursday,
    DateTime.friday,
  ],
);

// Define holidays
final holidays = [
  DateTime(2023, 1, 2), // New Year's Day (observed)
  DateTime(2023, 1, 16), // MLK Day
];

// Check if today is a work day (excluding holidays)
final today = DateTime.now();
final isWorkDay = workDays.occursOn(today) && !holidays.any(today.isSameDateAs);

if (isWorkDay) {
  print('Today is a work day');
} else {
  print('Today is not a work day');
}

// Get work days for first 2 weeks of January, excluding holidays
final workDaysJan = workDays.getOccurrencesUntil(
  DateTime(2023, 1, 15),
  from: DateTime(2023, 1, 1),
  exclude: holidays,
).toList();

// Contains: [2023-01-03 (Tue), 2023-01-04 (Wed), 2023-01-05 (Thu), 2023-01-06 (Fri),
//           2023-01-09 (Mon), 2023-01-10 (Tue), 2023-01-11 (Wed), 2023-01-12 (Thu)]

License

MIT License - see LICENSE file for details.

Libraries

schedules