Skip to content

Commit f6faee6

Browse files
fix: Improve readability of dates (#122)
2 parents cf47d08 + 53dce12 commit f6faee6

4 files changed

Lines changed: 48 additions & 10 deletions

File tree

cmd/milestones/status.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func StatusCmd() *cobra.Command {
6868

6969
ui.PrintSuccess(ui.EmojiMilestone, fmt.Sprintf("Active Milestone: %s", ui.Bold(activeMilestone.Name)))
7070
ui.PrintInfo(4, "Project", projectName)
71-
ui.PrintInfo(4, "Started", settings.FormatTime(activeMilestone.StartTime))
71+
ui.PrintInfo(4, "Started", settings.FormatDateTime(activeMilestone.StartTime))
7272
ui.PrintInfo(4, "Duration", ui.FormatDuration(activeMilestone.Duration()))
7373
ui.PrintInfo(4, "Entries", fmt.Sprintf("%d", len(entries)))
7474
ui.PrintInfo(4, "Total Time", ui.FormatDuration(totalTime))

internal/settings/global_config.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ func DefaultGlobalConfig() *GlobalConfig {
2828
}
2929
}
3030

31+
// GetGlobalConfigPath returns the path to the global config file. If
32+
// the TMPO_DEV environment variable is set to 1 or true, a
33+
// developer-specific config path is returned.
3134
func GetGlobalConfigPath() (string, error) {
3235
home, err := os.UserHomeDir()
3336
if err != nil {
@@ -42,6 +45,8 @@ func GetGlobalConfigPath() (string, error) {
4245
return filepath.Join(tmpoDir, "config.yaml"), nil
4346
}
4447

48+
// LoadGlobalConfig loads the global config from disk. If the config
49+
// file does not exist the default config is returned.
4550
func LoadGlobalConfig() (*GlobalConfig, error) {
4651
configPath, err := GetGlobalConfigPath()
4752
if err != nil {
@@ -69,6 +74,8 @@ func LoadGlobalConfig() (*GlobalConfig, error) {
6974
return &config, nil
7075
}
7176

77+
// Save writes the GlobalConfig to the config file, creating the
78+
// config directory if necessary.
7279
func (gc *GlobalConfig) Save() error {
7380
configPath, err := GetGlobalConfigPath()
7481
if err != nil {
@@ -92,7 +99,8 @@ func (gc *GlobalConfig) Save() error {
9299
return nil
93100
}
94101

95-
// GetDisplayTimezone returns the user's configured timezone or local timezone as fallback
102+
// GetDisplayTimezone returns the user's configured timezone or the
103+
// local timezone as a fallback.
96104
func GetDisplayTimezone() *time.Location {
97105
cfg, err := LoadGlobalConfig()
98106
if err != nil || cfg.Timezone == "" {
@@ -107,11 +115,14 @@ func GetDisplayTimezone() *time.Location {
107115
return loc
108116
}
109117

110-
// ToDisplayTime converts a UTC time to the user's display timezone
118+
// ToDisplayTime converts the provided time to the user's display
119+
// timezone.
111120
func ToDisplayTime(t time.Time) time.Time {
112121
return t.In(GetDisplayTimezone())
113122
}
114123

124+
// FormatTime formats t according to the user's configured time
125+
// format. Falls back to 12-hour format "3:04 PM".
115126
func FormatTime(t time.Time) string {
116127
t = ToDisplayTime(t)
117128

@@ -127,6 +138,8 @@ func FormatTime(t time.Time) string {
127138
return t.Format("3:04 PM")
128139
}
129140

141+
// FormatTimePadded formats t with a padded hour for 12-hour formats
142+
// ("03:04 PM") or returns the 24-hour format when configured.
130143
func FormatTimePadded(t time.Time) string {
131144
t = ToDisplayTime(t)
132145

@@ -142,6 +155,8 @@ func FormatTimePadded(t time.Time) string {
142155
return t.Format("03:04 PM")
143156
}
144157

158+
// FormatDate formats t according to the user's configured date
159+
// format. Defaults to "01/02/2006" (MM/DD/YYYY).
145160
func FormatDate(t time.Time) string {
146161
t = ToDisplayTime(t)
147162

@@ -162,6 +177,7 @@ func FormatDate(t time.Time) string {
162177
}
163178
}
164179

180+
// FormatDateDashed formats t using dashes between date components.
165181
func FormatDateDashed(t time.Time) string {
166182
t = ToDisplayTime(t)
167183

@@ -182,20 +198,28 @@ func FormatDateDashed(t time.Time) string {
182198
}
183199
}
184200

201+
// FormatDateTime returns the date and time formatted according to
202+
// user preferences.
185203
func FormatDateTime(t time.Time) string {
186204
return FormatDate(t) + " " + FormatTime(t)
187205
}
188206

207+
// FormatDateTimeDashed returns the dashed date and time formatted
208+
// according to user preferences.
189209
func FormatDateTimeDashed(t time.Time) string {
190210
return FormatDateDashed(t) + " " + FormatTime(t)
191211
}
192212

213+
// FormatDateLong returns a long human-readable date string like
214+
// "Mon, Jan 2, 2006".
193215
func FormatDateLong(t time.Time) string {
194216
t = ToDisplayTime(t)
195217

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

221+
// FormatDateTimeLong returns a long human-readable date/time string
222+
// honoring the user's time format preference.
199223
func FormatDateTimeLong(t time.Time) string {
200224
t = ToDisplayTime(t)
201225

internal/ui/ui.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,15 @@ func FormatFileSize(bytes int64) string {
160160
}
161161

162162
func FormatDuration(d time.Duration) string {
163-
hours := int(d.Hours())
164-
minutes := int(d.Minutes()) % 60
165-
seconds := int(d.Seconds()) % 60
166-
167-
if hours > 0 {
163+
totalSeconds := int(d.Seconds())
164+
days := totalSeconds / 86400
165+
hours := (totalSeconds % 86400) / 3600
166+
minutes := (totalSeconds % 3600) / 60
167+
seconds := totalSeconds % 60
168+
169+
if days > 0 {
170+
return fmt.Sprintf("%dd %dh %dm %ds", days, hours, minutes, seconds)
171+
} else if hours > 0 {
168172
return fmt.Sprintf("%dh %dm %ds", hours, minutes, seconds)
169173
} else if minutes > 0 {
170174
return fmt.Sprintf("%dm %ds", minutes, seconds)

internal/ui/ui_test.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,19 @@ func TestFormatDuration(t *testing.T) {
206206
expected: "1s",
207207
},
208208
{
209-
name: "large duration",
209+
name: "large duration under 24h",
210+
duration: 23*time.Hour + 45*time.Minute + 30*time.Second,
211+
expected: "23h 45m 30s",
212+
},
213+
{
214+
name: "duration over 24h shows days",
210215
duration: 25*time.Hour + 45*time.Minute + 30*time.Second,
211-
expected: "25h 45m 30s",
216+
expected: "1d 1h 45m 30s",
217+
},
218+
{
219+
name: "multi-day duration",
220+
duration: 688*time.Hour + 30*time.Minute + 39*time.Second,
221+
expected: "28d 16h 30m 39s",
212222
},
213223
}
214224

0 commit comments

Comments
 (0)