I recently built KhmerCalendarBar, a macOS menu bar app that displays the Cambodian Chhankitek lunisolar calendar. Here's what I learned.
Why
Cambodia uses a lunisolar calendar called Chhankitek alongside the Gregorian calendar. Public holidays like Khmer New Year, Pchum Ben, and Water Festival are determined by this system. I wanted quick access to Khmer dates without opening a browser.
The Stack
-
SwiftUI with
MenuBarExtra(.window style) - Swift Package Manager — zero external dependencies
-
create-dmgfor styled DMG installers
Architecture
The app uses a single CalendarViewModel (@MainActor, ObservableObject) that manages all state. Views are composable SwiftUI components:
KhmerCalendarBarApp
└─ MenuBarExtra (popover)
└─ PopoverContentView
├─ TodayHeaderView
├─ MonthNavigationView
├─ CalendarGridView
├─ HolidayListView
├─ YearOverviewView
├─ ClockView
├─ SettingsView
└─ FooterView
The Hard Part: Chhankitek Conversion
The Khmer lunisolar calendar is complex:
- Months alternate between 29 and 30 days
- Intercalary months (Adhikameas) are added periodically
- The Buddhist era year differs from Gregorian by 543
- Each year has an animal cycle (like Chinese zodiac but different)
- Waxing and waning phases are tracked per day
The conversion engine maps Gregorian dates to Khmer dates including lunar day, month name, era year, and animal cycle.
Live Menu Bar Updates
The menu bar shows Khmer date + time. Two timers handle updates:
- Midnight timer — recalculates date at day change
- Minute timer — updates time display every 30 seconds
Key Takeaways
-
MenuBarExtrawith.windowstyle gives you a proper popover -
@MainActoron the ViewModel prevents threading issues - Timer-based updates need
[weak self]to avoid retain cycles -
UNUserNotificationCenterrequires explicit authorization on macOS -
create-dmgmakes distribution easy without an Apple Developer account
Try It
- Website: https://khmercalendarbar.rithytep.online
- GitHub: https://github.com/RithyTep/KhmerCalendarBar
- License: MIT
Contributions welcome!

Top comments (0)