# Sunrise/Sunset Times Widget Feature **Date:** 2026-03-23 **Status:** Design Approved ## Overview Add sunrise and sunset time display to both small and medium widgets. Times are calculated locally using the existing NOAA solar position algorithm, formatted as HH:mm, and updated daily at midnight based on user location. ## Requirements - Display sunrise/sunset times in HH:mm format on widgets - Calculate times locally using existing `SunTimes` utility - Use user's current location for calculations - Update once daily at midnight (aligned with current refresh policy) - Show nothing if location permission is denied or unavailable - Apply to both small and medium widget families ## Architecture ### Data Model Extend `SolvervDef` to include sunrise/sunset times: ``` SolvervDef ├── date: Date ├── bg: String ├── sunriseTime: Date? (optional, time only) └── sunsetTime: Date? (optional, time only) ``` Extend `SolvervEntry` to propagate the times: ``` SolvervEntry ├── date: Date ├── def: SolvervDef (contains sunrise/sunset) ``` ### Timeline Provider Logic Update `Provider.getTimeline()`: 1. Get user's cached location (latitude, longitude) 2. If location unavailable → create entry with nil sunrise/sunset 3. Initialize `SunTimes(latitude, longitude, date: currentDate)` 4. Extract sunrise and sunset times 5. Create entry with times 6. Refresh at next midnight ### Data Calculation Flow ``` Location Permission Check ↓ (if granted) Get Cached Coordinates ↓ SunTimes Calculator ├─ Input: latitude, longitude, date ├─ Output: sunrise Date, sunset Date └─ Fallback: nil (polar regions, edge cases) ↓ Format as HH:mm ↓ SolvervDef/Entry ↓ Widget Display ``` ### Error Handling | Scenario | Behavior | |----------|----------| | Location permission denied | No sunrise/sunset displayed | | Location unavailable/stale | No sunrise/sunset displayed | | SunTimes returns nil (polar) | No sunrise/sunset displayed | | Date parsing fails | Widget shows current time's calculation | ## Implementation Details ### Changes to SolvervDef Add properties and computed properties for formatted times: - `sunriseTime: Date?` — raw sunrise time - `sunsetTime: Date?` — raw sunset time - `sunriseFormatted: String` — "HH:mm" or empty - `sunsetFormatted: String` — "HH:mm" or empty ### Changes to Provider Modify `getTimeline()` to: 1. Fetch location from cache (use existing location service) 2. If available, calculate sun times via `SunTimes` 3. Pass times to `SolvervDef` constructor 4. Return timeline refreshing at next midnight ### Widget View Updates **SmallWidgetView:** Add sunrise/sunset display (layout TBD) **MediumWidgetView:** Add sunrise/sunset display (layout TBD) Display format: sunrise time and sunset time as HH:mm when available. ## Timeline & Refresh - **Frequency:** Once per day at midnight - **Rationale:** Sun times change gradually; daily updates are sufficient - **Alignment:** Matches existing solstice countdown refresh policy ## Testing - Test with various locations (tropics, temperate, near poles) - Test without location permission - Test at date boundaries (midnight refresh) - Verify formatted times display correctly - Verify nil times result in no display ## Future Considerations - Visualization of sunrise/sunset (charts, gradients) - Daylight duration calculation and trend - Multiple locations support - Custom location override