5 files changed,
82 insertions(+),
4 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-01-21 01:49:14 +0200
Authored at:
2026-01-19 01:33:22 +0200
Change ID:
rrslxkonksmpzpmynkuslvuomwozoupk
Parent:
435b61e
M
go.sum
路路路 18 18 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 19 19 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 20 20 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 21 -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 22 -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 23 21 github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= 24 22 github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= 25 23 github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
M
internal/handlers/repo.go
路路路 14 14 "strings" 15 15 "time" 16 16 17 - "github.com/dustin/go-humanize" 18 17 "github.com/yuin/goldmark" 19 18 "github.com/yuin/goldmark/extension" 20 19 "github.com/yuin/goldmark/renderer/html" 21 20 "olexsmir.xyz/mugit/internal/git" 21 + "olexsmir.xyz/mugit/internal/humanize" 22 22 ) 23 23 24 24 func (h *handlers) index(w http.ResponseWriter, r *http.Request) {
A
internal/humanize/time.go
路路路 1 +package humanize 2 + 3 +import ( 4 + "fmt" 5 + "time" 6 +) 7 + 8 +// Time returns a human-readable relative time string (e.g., "3 hours ago"). 9 +func Time(t time.Time) string { 10 + return formatDuration(time.Since(t)) + " ago" 11 +} 12 + 13 +func formatDuration(d time.Duration) string { 14 + switch { 15 + case d < time.Minute: 16 + return "less than a minute" 17 + case d < 2*time.Minute: 18 + return "1 minute" 19 + case d < time.Hour: 20 + return fmt.Sprintf("%d minutes", d/time.Minute) 21 + case d < 2*time.Hour: 22 + return "1 hour" 23 + case d < 24*time.Hour: 24 + return fmt.Sprintf("%d hours", d/time.Hour) 25 + case d < 48*time.Hour: 26 + return "1 day" 27 + case d < 30*24*time.Hour: 28 + return fmt.Sprintf("%d days", d/(24*time.Hour)) 29 + case d < 60*24*time.Hour: 30 + return "1 month" 31 + case d < 365*24*time.Hour: 32 + return fmt.Sprintf("%d months", d/(30*24*time.Hour)) 33 + case d < 2*365*24*time.Hour: 34 + return "1 year" 35 + default: 36 + return fmt.Sprintf("%d years", d/(365*24*time.Hour)) 37 + } 38 +}
A
internal/humanize/time_test.go
路路路 1 +package humanize 2 + 3 +import ( 4 + "testing" 5 + "time" 6 +) 7 + 8 +func TestFormatDuration(t *testing.T) { 9 + tests := []struct { 10 + d time.Duration 11 + want string 12 + }{ 13 + {0, "less than a minute"}, 14 + {30 * time.Second, "less than a minute"}, 15 + {1 * time.Minute, "1 minute"}, 16 + {90 * time.Second, "1 minute"}, 17 + {2 * time.Minute, "2 minutes"}, 18 + {43 * time.Minute, "43 minutes"}, 19 + {1 * time.Hour, "1 hour"}, 20 + {90 * time.Minute, "1 hour"}, 21 + {2 * time.Hour, "2 hours"}, 22 + {23 * time.Hour, "23 hours"}, 23 + {24 * time.Hour, "1 day"}, 24 + {36 * time.Hour, "1 day"}, 25 + {48 * time.Hour, "2 days"}, 26 + {29 * 24 * time.Hour, "29 days"}, 27 + {30 * 24 * time.Hour, "1 month"}, 28 + {45 * 24 * time.Hour, "1 month"}, 29 + {60 * 24 * time.Hour, "2 months"}, 30 + {364 * 24 * time.Hour, "12 months"}, 31 + {365 * 24 * time.Hour, "1 year"}, 32 + {500 * 24 * time.Hour, "1 year"}, 33 + {730 * 24 * time.Hour, "2 years"}, 34 + {1000 * 24 * time.Hour, "2 years"}, 35 + } 36 + 37 + for _, tt := range tests { 38 + got := formatDuration(tt.d) 39 + if got != tt.want { 40 + t.Errorf("formatDuration(%v) = %q, want %q", tt.d, got, tt.want) 41 + } 42 + } 43 +}