Motivation
個人で作った、子供の成長を記録するアプリで、DateTime型の記録日を月齢毎に分けて表示したくなった⇨DateTimeを月齢へ変換する関数を作成
Problems
いざやろうとすると、なんか考慮しなきゃいけないこと多くね?って気づいた
- 月またぎ
- 誕生日付きよりも前の月
- 閏年
- 基準日が誕生日よりも早い
- 誕生日が月末
- 法律上の誕生日 (なんだそれ)
Action
とりあえずテストで外堀を埋める
その後に、テストが通るよう条件分岐しながらyearとmonthを算出
LunarAge convertToLunarAge({
required DateTime datetime,
required DateTime birthday,
}) {
// From the Japan low, the base date is one day before of the birthday
// ref: https://sayumi-aug.hateblo.jp/entry/2022/03/01/160427
final DateTime actualBirthday = birthday.add(Duration(days: -1));
final Duration duration = datetime.difference(actualBirthday);
final int year = duration.inDays ~/ 365;
int month = 0;
if (_isFasterThanBirth(datetime, actualBirthday)) {
return LunarAge(year: year, month: month);
}
if (actualBirthday.month > datetime.month) {
month = datetime.month + 12 - actualBirthday.month;
} else {
month = datetime.month - actualBirthday.month;
}
if (datetime.day < actualBirthday.day) {
month -= 1;
}
if (datetime.month != actualBirthday.month &&
_isComparisonBetweenMonthEnds(datetime, actualBirthday)) {
month += 1;
}
return LunarAge(year: year, month: month);
}
LunarAgeGroup groupByLunarAge(List<RecordItem> records, DateTime birthday) {
final LunarAgeGroup lunarAgeGroup = {};
for (RecordItem record in records) {
final LunarAge lunarAge = convertToLunarAge(
datetime: record.date,
birthday: birthday,
);
if (lunarAgeGroup[lunarAge] == null) {
lunarAgeGroup[lunarAge] = [];
}
lunarAgeGroup[lunarAge]!.add(record);
}
return lunarAgeGroup;
}
bool _isFasterThanBirth(DateTime datetime, DateTime birthday) {
return birthday.year >= datetime.year && birthday.month > datetime.month;
}
bool _isComparisonBetweenMonthEnds(DateTime datetime, DateTime birthday) {
return _isMonthEnd(datetime) && _isMonthEnd(birthday);
}
bool _isMonthEnd(DateTime datetime) {
const Map<int, int> endDay = {
1: 31,
2: 28,
3: 31,
4: 30,
5: 31,
6: 30,
7: 31,
8: 31,
9: 30,
10: 31,
11: 30,
12: 31,
};
return datetime.day >= endDay[datetime.month]!;
}
最後に、既存の記録を月齢毎に分けて表示
Zaregoto
既に先人がやっていそうだが、とりあえず面白くはあるので車輪の再発明でも良いかと思っている今日この頃。後絶対テストは足りていない。多分閏年挟むと破綻する。でもってWidgetの使い方下手じゃね?もっとシンプルな方法あるはず。