Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/history/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func LogCmd() *cobra.Command {
}
entries, err = db.GetEntriesByMilestone(projectName, logMilestone)
} else if logToday {
start := time.Now().Truncate(24 * time.Hour)
start := time.Now().Truncate(24 * time.Hour).UTC()
end := start.Add(24 * time.Hour)
entries, err = db.GetEntriesByDateRange(start, end)
} else if logWeek {
Expand All @@ -57,7 +57,7 @@ func LogCmd() *cobra.Command {
weekday = 7 // sunday
}

start := now.AddDate(0, 0, -weekday+1).Truncate(24 * time.Hour)
start := now.AddDate(0, 0, -weekday+1).Truncate(24 * time.Hour).UTC()
end := start.AddDate(0, 0, 7)
entries, err = db.GetEntriesByDateRange(start, end)
} else if logProject != "" {
Expand Down
4 changes: 2 additions & 2 deletions cmd/history/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func StatsCmd() *cobra.Command {

if statsToday {
now := time.Now()
start = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
start = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).UTC()
end = start.Add(24 * time.Hour)
periodName = "Today"
} else if statsWeek {
Expand All @@ -49,7 +49,7 @@ func StatsCmd() *cobra.Command {
weekday = 7
}

start = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).AddDate(0, 0, -weekday+1)
start = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).UTC().AddDate(0, 0, -weekday+1)
end = start.AddDate(0, 0, 7)
periodName = "This Week"
} else {
Expand Down
32 changes: 32 additions & 0 deletions internal/settings/global_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,29 @@ func (gc *GlobalConfig) Save() error {
return nil
}

// getDisplayTimezone returns the user's configured timezone or local timezone as fallback
func getDisplayTimezone() *time.Location {
cfg, err := LoadGlobalConfig()
if err != nil || cfg.Timezone == "" {
return time.Local
}

loc, err := time.LoadLocation(cfg.Timezone)
if err != nil {
return time.Local
}

return loc
}

// toDisplayTime converts a UTC time to the user's display timezone
func toDisplayTime(t time.Time) time.Time {
return t.In(getDisplayTimezone())
}

func FormatTime(t time.Time) string {
t = toDisplayTime(t)

cfg, err := LoadGlobalConfig()
if err != nil || cfg.TimeFormat == "" || cfg.TimeFormat == "Keep current" {
return t.Format("3:04 PM")
Expand All @@ -106,6 +128,8 @@ func FormatTime(t time.Time) string {
}

func FormatTimePadded(t time.Time) string {
t = toDisplayTime(t)

cfg, err := LoadGlobalConfig()
if err != nil || cfg.TimeFormat == "" || cfg.TimeFormat == "Keep current" {
return t.Format("03:04 PM")
Expand All @@ -119,6 +143,8 @@ func FormatTimePadded(t time.Time) string {
}

func FormatDate(t time.Time) string {
t = toDisplayTime(t)

cfg, err := LoadGlobalConfig()
if err != nil || cfg.DateFormat == "" || cfg.DateFormat == "Keep current" {
return t.Format("01/02/2006")
Expand All @@ -137,6 +163,8 @@ func FormatDate(t time.Time) string {
}

func FormatDateDashed(t time.Time) string {
t = toDisplayTime(t)

cfg, err := LoadGlobalConfig()
if err != nil || cfg.DateFormat == "" || cfg.DateFormat == "Keep current" {
return t.Format("01-02-2006")
Expand All @@ -163,10 +191,14 @@ func FormatDateTimeDashed(t time.Time) string {
}

func FormatDateLong(t time.Time) string {
t = toDisplayTime(t)

return t.Format("Mon, Jan 2, 2006")
}

func FormatDateTimeLong(t time.Time) string {
t = toDisplayTime(t)

cfg, err := LoadGlobalConfig()
if err != nil || cfg.TimeFormat == "" || cfg.TimeFormat == "Keep current" {
return t.Format("Jan 2, 2006 at 3:04 PM")
Expand Down
43 changes: 33 additions & 10 deletions internal/storage/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,25 @@ func Initialize() (*Database, error) {
return nil, fmt.Errorf("failed to create index: %w", err)
}

return &Database{db: db}, nil
// settings table for tracking migrations and other metadata
_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
updated_at DATETIME NOT NULL
)
`)
if err != nil {
return nil, fmt.Errorf("failed to create settings table: %w", err)
}

database := &Database{db: db}

if err := database.runMigrations(); err != nil {
return nil, fmt.Errorf("failed to run migrations: %w", err)
}

return database, nil
}

func isColumnExistsError(err error) bool {
Expand All @@ -114,7 +132,7 @@ func (d *Database) CreateEntry(projectName, description string, hourlyRate *floa
result, err := d.db.Exec(
"INSERT INTO time_entries (project_name, start_time, description, hourly_rate, milestone_name) VALUES (?, ?, ?, ?, ?)",
projectName,
time.Now(),
time.Now().UTC(),
description,
rate,
milestone,
Expand Down Expand Up @@ -144,11 +162,14 @@ func (d *Database) CreateManualEntry(projectName, description string, startTime,
milestone = sql.NullString{String: *milestoneName, Valid: true}
}

startTimeUTC := startTime.UTC()
endTimeUTC := endTime.UTC()

result, err := d.db.Exec(
"INSERT INTO time_entries (project_name, start_time, end_time, description, hourly_rate, milestone_name) VALUES (?, ?, ?, ?, ?, ?)",
projectName,
startTime,
endTime,
startTimeUTC,
endTimeUTC,
description,
rate,
milestone,
Expand Down Expand Up @@ -244,7 +265,7 @@ func (d *Database) GetLastStoppedEntry() (*TimeEntry, error) {
func (d *Database) StopEntry(id int64) error {
_, err := d.db.Exec(
"UPDATE time_entries SET end_time = ? WHERE id = ?",
time.Now(),
time.Now().UTC(),
id,
)

Expand Down Expand Up @@ -526,9 +547,11 @@ func (d *Database) GetCompletedEntriesByProject(projectName string) ([]*TimeEntr
}

func (d *Database) UpdateTimeEntry(id int64, entry *TimeEntry) error {
startTimeUTC := entry.StartTime.UTC()

var endTime sql.NullTime
if entry.EndTime != nil {
endTime = sql.NullTime{Time: *entry.EndTime, Valid: true}
endTime = sql.NullTime{Time: entry.EndTime.UTC(), Valid: true}
}

var hourlyRate sql.NullFloat64
Expand All @@ -545,7 +568,7 @@ func (d *Database) UpdateTimeEntry(id int64, entry *TimeEntry) error {
UPDATE time_entries
SET project_name = ?, start_time = ?, end_time = ?, description = ?, hourly_rate = ?, milestone_name = ?
WHERE id = ?
`, entry.ProjectName, entry.StartTime, endTime, entry.Description, hourlyRate, milestoneName, id)
`, entry.ProjectName, startTimeUTC, endTime, entry.Description, hourlyRate, milestoneName, id)

if err != nil {
return fmt.Errorf("failed to update entry: %w", err)
Expand All @@ -567,7 +590,7 @@ func (d *Database) CreateMilestone(projectName, name string) (*Milestone, error)
"INSERT INTO milestones (project_name, name, start_time) VALUES (?, ?, ?)",
projectName,
name,
time.Now(),
time.Now().UTC(),
)

if err != nil {
Expand Down Expand Up @@ -719,7 +742,7 @@ func (d *Database) GetAllMilestones() ([]*Milestone, error) {
func (d *Database) FinishMilestone(id int64) error {
_, err := d.db.Exec(
"UPDATE milestones SET end_time = ? WHERE id = ?",
time.Now(),
time.Now().UTC(),
id,
)

Expand Down Expand Up @@ -774,4 +797,4 @@ func (d *Database) GetEntriesByMilestone(projectName, milestoneName string) ([]*

func (d *Database) Close() error {
return d.db.Close()
}
}
Loading