Skip to content

Instantly share code, notes, and snippets.

@Maistho
Last active March 22, 2023 21:52
Show Gist options
  • Save Maistho/e38da422ad5c097c635ccf708ec68251 to your computer and use it in GitHub Desktop.
Save Maistho/e38da422ad5c097c635ccf708ec68251 to your computer and use it in GitHub Desktop.
Calculate the iso week of year in dart
extension DateWeekExtensions on DateTime {
int get isoWeekOfYear {
// Get the monday of week 1
final DateTime mondayWeek1 = _isoWeek1Monday();
// If this date is before the first week of the year, it is the same week as the last week of the previous year.
if (isBefore(mondayWeek1)) {
return DateTime(year - 1, 12, 31).isoWeekOfYear;
}
final int ordinalWeek1Monday = mondayWeek1.ordinalDate();
final int ordinal = ordinalDate();
// Calculate number of days after monday week 1
int diffInDays = ordinal - ordinalWeek1Monday;
// If the monday occurs on the previous year, we'll need to add a year to the diff
if (year > mondayWeek1.year) {
diffInDays += 365;
// If it's a leap year and the leap day is before the date checked, add an extra day
if (isLeapYear && DateTime(year, 2, 29).isBefore(this)) {
diffInDays += 1;
}
}
// Divide the difference by 7 to get the number of weeks
int week = (diffInDays ~/ 7) + 1;
// The week wraps around to 1 if week 53 doesn't contain a thursday.
// TODO: write more tests for this. Does this work with other dates than dec 31st?
if (week == 53 && weekday < DateTime.thursday) {
return 1;
}
return week;
}
/**
* Calculates the ordinal date
* The ordinal date is the number of days since December 31st the previous year.
* January 1st has the ordinal date 1
* December 31st has the ordinal date 365, or 366 in leap years
*/
int ordinalDate() {
final DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
final monthsBefore = DAYS_IN_MONTH.getRange(0, month - 1);
int days = monthsBefore.length > 0
? monthsBefore.reduce((value, element) => value + element)
: 0;
if (month > 2 && isLeapYear) {
days += 1;
}
return days + day;
}
/**
* Check if this date is on a leap year
*/
bool get isLeapYear {
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
/**
* Gets the date of the monday of ISO week 1 this year
*/
DateTime _isoWeek1Monday() {
final jan4 = DateTime(year, 1, 4); // Jan 4 is always in week 1
return DateTime(
jan4.year,
jan4.month,
jan4.day - jan4.weekday + 1,
);
}
}
import 'package:test/test.dart';
import './datetime_iso_week_of_year.dart';
void t(int year, List<int> expectations) {
group("$year", () {
test("January 1st", () {
expect(DateTime(year, 1, 1).isoWeekOfYear, expectations[0]);
});
test("Last of February", () {
expect(
DateTime(year, 2, DateTime(year).isLeapYear ? 29 : 28).isoWeekOfYear,
expectations[1],
);
});
test("December 31st", () {
expect(DateTime(year, 12, 31).isoWeekOfYear, expectations[2]);
});
});
}
void main() {
group("week_of_year", () {
t(2000, [52, 9, 52]);
t(2001, [1, 9, 1]);
t(2002, [1, 9, 1]);
t(2003, [1, 9, 1]);
t(2004, [1, 9, 53]);
t(2005, [53, 9, 52]);
t(2006, [52, 9, 52]);
t(2007, [1, 9, 1]);
t(2008, [1, 9, 1]);
t(2009, [1, 9, 53]);
t(2010, [53, 8, 52]);
t(2011, [52, 9, 52]);
t(2012, [52, 9, 1]);
t(2013, [1, 9, 1]);
t(2014, [1, 9, 1]);
t(2015, [1, 9, 53]);
t(2016, [53, 9, 52]);
t(2017, [52, 9, 52]);
t(2018, [1, 9, 1]);
t(2019, [1, 9, 1]);
t(2020, [1, 9, 53]);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment