Skip to content
Merged
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
49 changes: 47 additions & 2 deletions internal/service/personal_calendar_item.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,11 @@ func (s *PersonalCalendarItemService) List(
return []domain.PersonalCalendarItem{}, nil
}

// TODO: datesからYearとSemestersを判定してフィルタに設定する
timetableItems, err := s.timetableItemRepo.List(ctx, domain.TimetableItemListFilter{})
year, semesters := determineSemestersFromDates(dates)
timetableItems, err := s.timetableItemRepo.List(ctx, domain.TimetableItemListFilter{
Year: year,
Semesters: semesters,
})
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -306,6 +309,48 @@ func dateRange(dates []time.Time) (*time.Time, *time.Time) {
return &minDate, &maxDate
}

func determineSemestersFromDates(dates []time.Time) (*int, []domain.CourseSemester) {
if len(dates) == 0 {
return nil, nil
}

yearMap := make(map[int]struct{})
semesterMap := make(map[domain.CourseSemester]struct{})

for _, date := range dates {
yearMap[date.Year()] = struct{}{}

month := date.Month()
if month >= 4 && month <= 9 {
Comment on lines +320 to +324
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yearMap[date.Year()] だと暦年ベースになり、1〜3月の date では日本の大学の「年度(4月始まり)」とズレます(例: 2026-01 は本来 2025 年度の H2 になりそう)。ここでは date.Month() < time.April の場合に year を 1 減らした「年度」を使って yearMap に入れるなど、年度計算を入れた方が subjects.year フィルタと整合します。

Copilot uses AI. Check for mistakes.
semesterMap[domain.CourseSemesterH1] = struct{}{}
semesterMap[domain.CourseSemesterQ1] = struct{}{}
semesterMap[domain.CourseSemesterQ2] = struct{}{}
semesterMap[domain.CourseSemesterAllYear] = struct{}{}
} else {
semesterMap[domain.CourseSemesterH2] = struct{}{}
semesterMap[domain.CourseSemesterQ3] = struct{}{}
semesterMap[domain.CourseSemesterQ4] = struct{}{}
semesterMap[domain.CourseSemesterAllYear] = struct{}{}
}
}
Comment on lines +320 to +335
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Year の推定が暦年(date.Year())ベースになっており、学年/年度が4月開始の想定だと1〜3月が前年扱いになりません。例えば 2026/01 は学年度 2025 となるケースが多く、現在の実装だと yearMap が分断されて Year フィルタが nil になったり、誤った年度で絞り込む可能性があります。月が1〜3月の場合は年度を date.Year()-1 として扱うなど、学年度の定義に合わせて Year を算出してください。

Copilot uses AI. Check for mistakes.

var year *int
if len(yearMap) == 1 {
for y := range yearMap {
y2 := y
year = &y2
break
}
}
Comment on lines +337 to +344
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

len(yearMap) != 1 のときに year=nil になるため、timetableItemRepo.List 側で subjects.year が一切絞られず、該当ユーザーの履修登録とは無関係な年度の時間割までまとめて取得する挙動になります。dates が年跨ぎ(例: 月表示の週が12月末〜1月頭)になるケースがあるなら、年度ごとにクエリを分けて結果をマージする/フィルタ側を Years []int のように拡張する等で、常に年度で絞れるようにした方がスケールしやすいです。

Copilot uses AI. Check for mistakes.

semesters := make([]domain.CourseSemester, 0, len(semesterMap))
for sem := range semesterMap {
semesters = append(semesters, sem)
}

Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

map から slice への変換結果が非決定順になるため、呼び出し側がスライスの順序に依存している場合(比較、ログ、キャッシュキー、SQLのIN句生成とテストの期待など)に結果が不安定になります。return 前に semesters を定義済みの順序(例: AllYear, H1, Q1, Q2, H2, Q3, Q4 などドメイン規約)でソートして、出力を決定的にするのが安全です。

Suggested change
semesterOrder := map[domain.CourseSemester]int{
domain.CourseSemesterAllYear: 0,
domain.CourseSemesterH1: 1,
domain.CourseSemesterQ1: 2,
domain.CourseSemesterQ2: 3,
domain.CourseSemesterH2: 4,
domain.CourseSemesterQ3: 5,
domain.CourseSemesterQ4: 6,
}
sort.Slice(semesters, func(i, j int) bool {
return semesterOrder[semesters[i]] < semesterOrder[semesters[j]]
})

Copilot uses AI. Check for mistakes.
return year, semesters
}

func weekdayToDayOfWeek(w time.Weekday) domain.DayOfWeek {
switch w {
case time.Sunday:
Expand Down
Loading