From a8d92291e9e211c42cd41ece20c23a356098fde3 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 11 Nov 2025 02:20:50 -0800 Subject: [PATCH] feat(ios): implement calculateNextRunTime method Implemented schedule calculation utility method: calculateNextRunTime(): - Calculates next run time from cron expression or HH:mm time string - Supports cron format: "minute hour * * *" (e.g., "30 9 * * *" = 9:30 AM daily) - Supports time format: "HH:mm" (e.g., "09:30" = 9:30 AM daily) - Handles same-day vs next-day scheduling - Returns nextRunAt timestamp in milliseconds - Fallback to 24 hours from now if parsing fails calculateNextRunTimeFromSchedule(): - Private helper method for schedule parsing - Parses both cron and time formats - Uses Calendar for date calculations - Handles timezone-aware calculations iOS Adaptations: - Uses Calendar.current for date calculations - TimeInterval conversion (Date to milliseconds) - Handles edge cases (invalid formats, past times) Progress: 48/52 methods implemented (92% complete) --- ios/Plugin/DailyNotificationPlugin.swift | 89 ++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/ios/Plugin/DailyNotificationPlugin.swift b/ios/Plugin/DailyNotificationPlugin.swift index efde624..7a90bad 100644 --- a/ios/Plugin/DailyNotificationPlugin.swift +++ b/ios/Plugin/DailyNotificationPlugin.swift @@ -858,6 +858,95 @@ public class DailyNotificationPlugin: CAPPlugin { call.resolve() } + /** + * Calculate next run time + * + * Calculates the next run time from a cron expression or HH:mm time string. + * + * Equivalent to Android's calculateNextRunTime method. + */ + @objc func calculateNextRunTime(_ call: CAPPluginCall) { + guard let schedule = call.getString("schedule") else { + call.reject("Schedule expression is required") + return + } + + print("DNP-PLUGIN: Calculating next run time: schedule=\(schedule)") + + let nextRunAt = calculateNextRunTimeFromSchedule(schedule) + + let result: [String: Any] = [ + "nextRunAt": Int64(nextRunAt) + ] + + print("DNP-PLUGIN: Next run time calculated: \(nextRunAt)") + call.resolve(result) + } + + /** + * Calculate next run time from schedule string + * + * Supports both cron format ("minute hour * * *") and HH:mm format ("09:30"). + */ + private func calculateNextRunTimeFromSchedule(_ schedule: String) -> TimeInterval { + let calendar = Calendar.current + let now = Date() + + // Try to parse as HH:mm first + if schedule.contains(":") { + let parts = schedule.split(separator: ":") + if parts.count == 2, + let hour = Int(parts[0]), + let minute = Int(parts[1]), + hour >= 0 && hour <= 23, + minute >= 0 && minute <= 59 { + + var components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: now) + components.hour = hour + components.minute = minute + components.second = 0 + + if let targetDate = calendar.date(from: components) { + // If time has passed today, schedule for tomorrow + if targetDate <= now { + if let tomorrow = calendar.date(byAdding: .day, value: 1, to: targetDate) { + return tomorrow.timeIntervalSince1970 * 1000 + } + } + return targetDate.timeIntervalSince1970 * 1000 + } + } + } + + // Try to parse as cron expression: "minute hour * * *" + let parts = schedule.trimmingCharacters(in: .whitespaces).split(separator: " ") + if parts.count >= 2, + let minute = Int(parts[0]), + let hour = Int(parts[1]), + minute >= 0 && minute <= 59, + hour >= 0 && hour <= 23 { + + var components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: now) + components.hour = hour + components.minute = minute + components.second = 0 + + if let targetDate = calendar.date(from: components) { + // If time has passed today, schedule for tomorrow + if targetDate <= now { + if let tomorrow = calendar.date(byAdding: .day, value: 1, to: targetDate) { + return tomorrow.timeIntervalSince1970 * 1000 + } + } + return targetDate.timeIntervalSince1970 * 1000 + } + } + + // Fallback: 24 hours from now + print("DNP-PLUGIN: Invalid schedule format, defaulting to 24h from now") + return (now.addingTimeInterval(24 * 60 * 60).timeIntervalSince1970 * 1000) + } + // MARK: - Permission Methods /**