mirror of
https://github.com/taigrr/gico.git
synced 2026-04-15 09:40:55 -07:00
fix(types): correct leap year calculation for century years
The naive year%4==0 check incorrectly treats years like 1900 and 2100 as leap years. Add types.IsLeapYear and types.YearLength helpers using the full Gregorian calendar rule (divisible by 4, except centuries unless divisible by 400). Replace all 8 occurrences across the codebase.
This commit is contained in:
@@ -54,10 +54,7 @@ func (paths RepoSet) GetRepoAuthors() ([]string, error) {
|
||||
}
|
||||
|
||||
func (paths RepoSet) GetRepoCommits(year int, authors []string) ([][]types.Commit, error) {
|
||||
yearLength := 365
|
||||
if year%4 == 0 {
|
||||
yearLength++
|
||||
}
|
||||
yearLength := types.YearLength(year)
|
||||
|
||||
commits := make([][]types.Commit, yearLength)
|
||||
for i := 0; i < yearLength; i++ {
|
||||
@@ -109,10 +106,6 @@ func (paths RepoSet) GetRepoCommits(year int, authors []string) ([][]types.Commi
|
||||
}
|
||||
|
||||
func (paths RepoSet) FrequencyChan(year int, authors []string) (types.Freq, error) {
|
||||
yearLength := 365
|
||||
if year%4 == 0 {
|
||||
yearLength++
|
||||
}
|
||||
cache, ok := GetCachedGraph(year, authors, paths)
|
||||
if ok {
|
||||
return cache, nil
|
||||
@@ -152,10 +145,7 @@ func (paths RepoSet) FrequencyChan(year int, authors []string) (types.Freq, erro
|
||||
}
|
||||
|
||||
func YearFreqFromChan(cc chan types.Commit, year int) types.Freq {
|
||||
yearLength := 365
|
||||
if year%4 == 0 {
|
||||
yearLength++
|
||||
}
|
||||
yearLength := types.YearLength(year)
|
||||
freq := make([]int, yearLength)
|
||||
for commit := range cc {
|
||||
freq[commit.TimeStamp.YearDay()-1]++
|
||||
@@ -206,10 +196,7 @@ func (repo Repo) GetCommitChan() (chan types.Commit, error) {
|
||||
}
|
||||
|
||||
func FreqFromChan(cc chan types.Commit, year int) types.Freq {
|
||||
yearLength := 365
|
||||
if year%4 == 0 {
|
||||
yearLength++
|
||||
}
|
||||
yearLength := types.YearLength(year)
|
||||
freq := make([]int, yearLength)
|
||||
for commit := range cc {
|
||||
if commit.TimeStamp.Year() != year {
|
||||
|
||||
@@ -26,10 +26,7 @@ func (paths RepoSet) GetWeekFreq(authors []string) (types.Freq, error) {
|
||||
return types.Freq{}, err
|
||||
}
|
||||
freq = append(curFreq, freq...)
|
||||
today += 365
|
||||
if curYear%4 == 0 {
|
||||
today++
|
||||
}
|
||||
today += types.YearLength(curYear)
|
||||
}
|
||||
|
||||
week := freq[today-6 : today+1]
|
||||
@@ -37,10 +34,7 @@ func (paths RepoSet) GetWeekFreq(authors []string) (types.Freq, error) {
|
||||
}
|
||||
|
||||
func (paths RepoSet) Frequency(year int, authors []string) (types.Freq, error) {
|
||||
yearLength := 365
|
||||
if year%4 == 0 {
|
||||
yearLength++
|
||||
}
|
||||
yearLength := types.YearLength(year)
|
||||
gfreq := make(types.Freq, yearLength)
|
||||
for _, p := range paths {
|
||||
repo, err := OpenRepo(p)
|
||||
@@ -115,11 +109,7 @@ func (repo Repo) GetCommitSet() (CommitSet, error) {
|
||||
}
|
||||
|
||||
func (cs CommitSet) ToYearFreq() types.Freq {
|
||||
year := cs.Year
|
||||
yearLength := 365
|
||||
if year%4 == 0 {
|
||||
yearLength++
|
||||
}
|
||||
yearLength := types.YearLength(cs.Year)
|
||||
freq := make([]int, yearLength)
|
||||
for _, v := range cs.Commits {
|
||||
freq[v.TimeStamp.YearDay()-1]++
|
||||
|
||||
@@ -48,6 +48,19 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
// IsLeapYear returns true if year is a leap year per the Gregorian calendar.
|
||||
func IsLeapYear(year int) bool {
|
||||
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
||||
}
|
||||
|
||||
// YearLength returns 366 for leap years and 365 otherwise.
|
||||
func YearLength(year int) int {
|
||||
if IsLeapYear(year) {
|
||||
return 366
|
||||
}
|
||||
return 365
|
||||
}
|
||||
|
||||
func (c Commit) String() string {
|
||||
return fmt.Sprintf("%s\t%s\t%s\t%s",
|
||||
c.TimeStamp.Format("0"+time.Kitchen),
|
||||
|
||||
@@ -122,6 +122,45 @@ func TestFreqMerge(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsLeapYear(t *testing.T) {
|
||||
tests := []struct {
|
||||
year int
|
||||
want bool
|
||||
}{
|
||||
{2024, true}, // divisible by 4
|
||||
{2025, false}, // not divisible by 4
|
||||
{1900, false}, // divisible by 100 but not 400
|
||||
{2000, true}, // divisible by 400
|
||||
{2100, false}, // divisible by 100 but not 400
|
||||
{2400, true}, // divisible by 400
|
||||
{1996, true}, // divisible by 4
|
||||
{2023, false}, // not divisible by 4
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(time.Date(tt.year, 1, 1, 0, 0, 0, 0, time.UTC).Format("2006"), func(t *testing.T) {
|
||||
got := IsLeapYear(tt.year)
|
||||
if got != tt.want {
|
||||
t.Errorf("IsLeapYear(%d) = %v, want %v", tt.year, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestYearLength(t *testing.T) {
|
||||
if YearLength(2024) != 366 {
|
||||
t.Error("expected 366 for 2024")
|
||||
}
|
||||
if YearLength(2025) != 365 {
|
||||
t.Error("expected 365 for 2025")
|
||||
}
|
||||
if YearLength(1900) != 365 {
|
||||
t.Error("expected 365 for 1900 (century year, not leap)")
|
||||
}
|
||||
if YearLength(2000) != 366 {
|
||||
t.Error("expected 366 for 2000 (divisible by 400)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDataSetOperations(t *testing.T) {
|
||||
ds := NewDataSet()
|
||||
now := time.Now().Truncate(24 * time.Hour)
|
||||
|
||||
10
ui/ui.go
10
ui/ui.go
@@ -5,6 +5,8 @@ import (
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
|
||||
"github.com/taigrr/gico/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -66,12 +68,10 @@ func (m model) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
// YearLen returns the number of days in a year.
|
||||
// Deprecated: Use types.YearLength instead.
|
||||
func YearLen(year int) int {
|
||||
yearLen := 365
|
||||
if year%4 == 0 {
|
||||
yearLen++
|
||||
}
|
||||
return yearLen
|
||||
return types.YearLength(year)
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
|
||||
Reference in New Issue
Block a user