GitHub - gogrinimish/LocalHours: Cross-platform time tracking app for macOS, iOS, and Android

6 min read Original article ↗

Local Hours

A Simple Local-first Timesheet App

Build and Test License: MIT Platform

A local-first, cross-platform time tracking app for macOS, iOS, and Android (planned). Track your hours, generate timesheets, and email them to approvers — all without accounts, servers, or data lock-in.

Download on the App Store

Learn more: Local Hours website

Screenshots

macOS Menu Bar

Menu bar popover Menu bar icon (timer running)
macOS menu bar Menu bar icon for running timer

Mac app About

About window
Mac app About window

Timer

Timer running Timesheet preview
Timer running state Timesheet preview

Settings

Folder & timezone Email template Send reminder
Settings – folder and timezone Settings – email template Settings – timesheet reminder

Email

Embedded timesheet email draft
Embedded timesheet email draft

Features

Core Functionality

  • Quick Time Tracking: Start and stop the clock with a single tap or click
  • Work Descriptions: Add notes when you stop the clock to capture what you worked on
  • Timesheet Generation: Automatically compile tracked time into clean, ready-to-send timesheets
  • Email Integration: Generate emails to approvers with embedded timesheets or attachments
  • Cross-Device Sync: Keep data in a shared folder (iCloud, Google Drive, OneDrive) for seamless multi-device sync

Platform-Specific Features

  • macOS: Menu bar access and notifications
  • iOS: Widgets, long-press actions, and notifications
  • Android: PLANNED

Cross-Device Sync

Local Hours keeps your data in sync across devices using your cloud storage folder (iCloud, Google Drive, OneDrive). Here's how sync works:

Same-Device Sync (iOS App ↔ Widget)

Trigger Update Speed
Start/stop timer in main app Immediate (via WidgetKit reload)
Start timer from widget Immediate (widget already knows)
Timeline refresh (backup) Every 60 seconds when tracking, every 5 minutes when idle

Cross-Device Sync (macOS ↔ iOS)

Action Other Device Update
Start/stop timer 5-30 seconds (depends on cloud sync speed)
Edit entries 5-30 seconds (depends on cloud sync speed)

How it works:

  • Apps poll storage files every 5 seconds to detect changes
  • When a change is detected, the app updates its state automatically
  • Cloud sync speed (iCloud, Google Drive, etc.) is the main factor in cross-device delay
  • Both devices must be online for sync to occur

Note: Cross-device sync relies on your cloud storage provider. Sync speed varies based on network conditions, device state (active vs. background), and the provider's infrastructure. Typical delays are 5-30 seconds on the same WiFi network.

Installation

Prerequisites

For Development

  • macOS 15+ with Xcode 16+
  • Skip for Android support
# Install Skip
brew install skiptools/skip/skip

# Verify installation
skip checkup

Building from Source

  1. Clone the repository:
git clone https://github.com/gogrinimish/LocalHours.git
cd LocalHours
  1. Open in Xcode:
open LocalHours.xcodeproj
  1. Build and run:
    • For macOS: Select LocalHoursMac scheme
    • For iOS: Select LocalHours scheme
    • For Android: Select LocalHours scheme and choose Android device/emulator

Pre-built Releases

Download the latest release from the Releases page.

Note: The releases are unsigned. For macOS, you may need to right-click and select "Open" on first launch. For iOS, you'll need to sideload using AltStore, Sideloadly, or a similar tool.

Configuration

All configuration is stored in JSON files within your chosen storage folder, enabling sync across devices:

YourTimesheetFolder/
├── config.json              # App configuration
├── timesheets/              # Auto-saved period timesheets (backup; one file per period)
│   ├── period-20260203_20260209.json   # Deterministic name so macOS and iOS don't both create one
│   └── ...
└── time-entries/            # Raw time entry data
    └── entries.json

When your reminder time arrives for a period (e.g. Friday 5pm for a weekly timesheet), the app auto-saves that period’s timesheet into timesheets/. This gives you a backup if entries.json is ever lost, and supports future features like choosing a different period to send for approval.

Configuration Options

Setting Description
storageFolder Path to the folder for storing timesheets
timezone Timezone for timesheet calculations (e.g., "America/New_York")
notificationTime Time to send reminder notification (e.g., "17:00")
emailTemplate Custom email template for timesheet submissions (placeholders: {{userName}}, {{periodStart}}, {{periodEnd}}, {{totalHours}}, {{entriesSummary}})
approverEmail Email address of the timesheet approver
notificationDays Days of week to send notifications (1=Sunday … 7=Saturday; e.g. [6] for Friday)

Usage

macOS Menu Bar

  1. Click the clock icon in the menu bar
  2. Click "Start" to begin tracking
  3. When finished, click "Stop" and enter a description
  4. At the configured time, you'll receive a notification to send your timesheet
  5. Click "Send Timesheet" to email it to your approver

iOS/Android Widgets

  1. Add the Local Hours widget to your home screen
  2. Tap the play button to start tracking
  3. Tap the stop button to stop and add a description
  4. Use the app for configuration and viewing history

Privacy

Local Hours is built from the ground up to be local-first and privacy-preserving:

  • Your data, your files: All data is stored as files in a folder you control
  • No servers, no accounts: The app works fully offline and never requires sign-ups
  • Bring your own sync: Use iCloud, Google Drive, or OneDrive to sync across devices
  • No analytics, no tracking: Nothing is collected, measured, or phoned home
  • Open source by design: Inspect the code and see exactly how your data is handled

Feedback & Bugs

Found a bug or have a suggestion? Please open an issue. We use issues for:

  • Bug reports – describe the problem, steps to reproduce, and your environment (OS, app version).
  • Feature requests – describe the feature and how you’d use it.
  • General feedback – ideas, UX improvements, or documentation fixes.

Check existing issues first to avoid duplicates.

Collaborators Wanted

Local Hours is a solo project today. If you’d like to help shape the next set of features, we’re especially interested in collaborators for:

  • Android & Windows apps – Extend the app to Android (e.g. via Skip) and Windows (e.g. Swift on Windows or a separate stack). Experience with cross-platform or native mobile/desktop is a plus.
  • Multiple projects / timesheets – Design and implement support for multiple projects or timesheet “workspaces” (e.g. per client, per contract) with clear data model and UI.

If you’re interested, open an issue with the label collaborator or good first project, or comment on the relevant roadmap issues.

Contributing

Contributions are welcome! Please read our Contributing Guidelines before submitting a pull request.

Development Setup

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Make your changes
  4. Run tests: swift test
  5. Commit: git commit -m 'Add amazing feature'
  6. Push: git push origin feature/amazing-feature
  7. Open a Pull Request

Support the Project

If Local Hours is useful to you, you can support its development:

Buy Me A Coffee

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Built with Skip for cross-platform support
  • Icons designed with accessibility in mind