mailcli uses a layered approach to time handling that balances user-friendliness with machine-readability.
┌─────────────────────────────────────────────────────┐
│ User Input Layer │
│ • Input: 2026-03-19 (local timezone) │
│ • Parsed in local timezone for user convenience │
│ • Example: --since 2026-03-19 │
└─────────────────┬───────────────────────────────────┘
│ Converted to UTC
▼
┌─────────────────────────────────────────────────────┐
│ Internal Storage Layer │
│ • All times stored as UTC │
│ • Consistent timezone for comparisons │
│ • Example: 2026-03-18T16:00:00Z │
└─────────────────┬───────────────────────────────────┘
│ Converted for display
▼
┌─────────────────────────────────────────────────────┐
│ Output Layer │
│ • JSON: ISO 8601 (RFC3339) with timezone │
│ • Text: Local timezone with offset info │
└─────────────────────────────────────────────────────┘
# User inputs date in local timezone
$ mailcli mail search --since 2026-03-19
# Internally converted to UTC
# If user is in UTC+8: 2026-03-19 00:00:00 → 2026-03-18T16:00:00Z{
"uid": 12345,
"subject": "Test Email",
"date": "2026-03-19T15:30:45+08:00"
}- Format: RFC3339 (ISO 8601 subset)
- Includes: Timezone offset
- Purpose: Machine-readable, API-friendly
Date: 2026-03-19 15:30:45 (UTC+08:00)
- Format: Local timezone with offset
- Includes: Human-readable offset information
- Purpose: Easy to read for humans
ParseDateInLocalTZ(dateStr string): Parse user input in local timezone, return UTCFormatInLocalTZ(t time.Time): Format time in local timezoneFormatInLocalTZWithOffset(t time.Time): Format with offset informationFormatRFC3339(t time.Time): Format as ISO 8601 (RFC3339)ToUTC(t time.Time): Convert time to UTCNowUTC(): Get current time in UTC
import "github.com/keepmind9/mailcli/pkg/timeutil"
// Parse user input (local timezone)
sinceDate, err := timeutil.ParseDateInLocalTZ("2026-03-19")
// Result: 2026-03-18T16:00:00Z (assuming UTC+8)
// Format for JSON output
jsonDate := timeutil.FormatRFC3339(sinceDate)
// Result: "2026-03-18T16:00:00Z"
// Format for text output
textDate := timeutil.FormatInLocalTZWithOffset(sinceDate)
// Result: "2026-03-19 00:00:00 (UTC+08:00)"✅ User-Friendly: Users input dates in their local timezone ✅ Consistent: All internal operations use UTC ✅ Machine-Readable: JSON output uses standard ISO 8601 format ✅ Clear: Text output shows timezone offset explicitly ✅ Compatible: Works with any timezone worldwide
- Input: Interpreted in user's local timezone
- Storage: Always UTC (consistent)
- Display: Converted back to local timezone
- JSON: RFC3339 with timezone offset (no ambiguity)
See pkg/timeutil/timeutil_test.go for comprehensive tests.
go test ./pkg/timeutil/...