import 'course.dart'; import 'semester.dart'; import 'course_occurrence.dart'; import 'lesson_times.dart'; import 'package:timezone/timezone.dart' as tz; export 'course.dart'; export 'semester.dart'; export 'lesson_times.dart'; export 'course_occurrence.dart'; class ScheduleEngine { static DateTime schoolDate(DateTime instant) { final local = tz.TZDateTime.from(instant, tz.getLocation('Asia/Shanghai')); return DateTime.utc(local.year, local.month, local.day); } static int? weekNumber(Semester semester, DateTime schoolDate) { final date = DateTime.utc( schoolDate.year, schoolDate.month, schoolDate.day, ); final delta = date.difference(semester.firstMonday).inDays; if (delta < 0 || delta >= semester.totalWeeks * 7) return null; return delta ~/ 7 + 1; } static List occurrences( List courses, Semester semester, ) { final result = []; final zone = tz.getLocation('Asia/Shanghai'); for (final course in courses) { for (final week in course.weeks) { if (week > semester.totalWeeks) continue; final day = semester.firstMonday.add( Duration(days: (week - 1) * 7 + course.weekday - 1), ); if (day.isBefore(semester.startDate)) continue; final first = lessonSlots[course.startPeriod - 1].startMinute; final last = lessonSlots[course.endPeriod - 1].endMinute; result.add( CourseOccurrence( key: '${course.id}/${day.toIso8601String().substring(0, 10)}', course: course, week: week, start: tz.TZDateTime( zone, day.year, day.month, day.day, first ~/ 60, first % 60, ), end: tz.TZDateTime( zone, day.year, day.month, day.day, last ~/ 60, last % 60, ), ), ); } } result.sort((a, b) { final order = a.start.compareTo(b.start); return order == 0 ? a.key.compareTo(b.key) : order; }); return result; } static bool overlaps(CourseOccurrence a, CourseOccurrence b) => a.start.isBefore(b.end) && b.start.isBefore(a.end); }