1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
# 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
|