diff --git a/README.md b/README.md index 2732597..c57d6d8 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ void main() { - `isAfterOrEqual`: Checks if the current DateTime is after or equal to the given DateTime - `isBeforeOrEqual`: Checks if the current DateTime is before or equal to the given DateTime - `isBetween`: Checks if the current DateTime is between the given dates +- `calendarWeek`: calculates the calendar week number of the current DateTime object. Example: diff --git a/lib/extensions/date_time.extension.dart b/lib/extensions/date_time.extension.dart index 94ede5a..0770806 100644 --- a/lib/extensions/date_time.extension.dart +++ b/lib/extensions/date_time.extension.dart @@ -63,4 +63,43 @@ extension DateTimeExtension on DateTime { final isBefore = isBeforeOrEqual(toDateTime); return isAfter && isBefore; } + + /// + /// Returns the calendar week of the year for this date. + /// + int calendarWeek() { + // Add 3 to always compare with January 4th, which is always in week 1 + // Add 7 to index weeks starting with 1 instead of 0 + final weekOfYear = ((ordinalDate - weekday + 10) ~/ 7); + + // If the week number equals zero, it means that the given date belongs to the preceding (week-based) year. + if (weekOfYear == 0) { + // The 28th of December is always in the last week of the year + return DateTime(year - 1, 12, 28).calendarWeek(); + } + + // If the week number equals 53, one must check that the date is not actually in week 1 of the following year + if (weekOfYear == 53 && + DateTime(year, 1, 1).weekday != DateTime.thursday && + DateTime(year, 12, 31).weekday != DateTime.thursday) { + return 1; + } + + return weekOfYear; + } + + /// The ordinal date, 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 get ordinalDate { + const offsets = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; + return offsets[month - 1] + day + (isLeapYear && month > 2 ? 1 : 0); + } + + /// True if this date is on a leap year. + bool get isLeapYear { + return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); + } } diff --git a/test/date_time.extension_test.dart b/test/date_time.extension_test.dart index b9b3d0d..e7fd5d3 100644 --- a/test/date_time.extension_test.dart +++ b/test/date_time.extension_test.dart @@ -14,6 +14,17 @@ void main() { expect(result, isTrue); }); + test('isToday should return false for a different date', () { + // Arrange + DateTime notToday = DateTime.now().subtract(const Duration(days: 2)); + + // Act + bool result = notToday.isToday; + + // Assert + expect(result, isFalse); + }); + test('isYesterday should return true for yesterday\'s date', () { // Arrange DateTime yesterday = DateTime.now().subtract(const Duration(days: 1)); @@ -25,8 +36,18 @@ void main() { expect(result, isTrue); }); - test('isThisYear should return true for the same year as the current date', - () { + test('isYesterday should return false for a different date', () { + // Arrange + DateTime notYesterday = DateTime.now().add(const Duration(days: 1)); + + // Act + bool result = notYesterday.isYesterday; + + // Assert + expect(result, isFalse); + }); + + test('isThisYear should return true for the same year as the current date', () { // Arrange DateTime now = DateTime.now(); @@ -37,9 +58,7 @@ void main() { expect(result, isTrue); }); - test( - 'isThisYear should return false for a different year than the current date', - () { + test('isThisYear should return false for a different year than the current date', () { // Arrange DateTime otherYear = DateTime.now().subtract(const Duration(days: 365)); @@ -51,118 +70,118 @@ void main() { }); group('isSameYear', () { - test('should return true when comparing dates from the same year', () { - // Arrange - final date1 = DateTime(2024, 1, 1); - final date2 = DateTime(2024, 12, 31); + test('should return true when comparing dates from the same year', () { + // Arrange + final date1 = DateTime(2024, 1, 1); + final date2 = DateTime(2024, 12, 31); - // Act - final result = date1.isSameYear(date2); + // Act + final result = date1.isSameYear(date2); - // Assert - expect(result, true); - }); + // Assert + expect(result, true); + }); - test('should return false when comparing dates from different years', () { - // Arrange - final date1 = DateTime(2024, 1, 1); - final date2 = DateTime(2023, 1, 1); + test('should return false when comparing dates from different years', () { + // Arrange + final date1 = DateTime(2024, 1, 1); + final date2 = DateTime(2023, 1, 1); - // Act - final result = date1.isSameYear(date2); + // Act + final result = date1.isSameYear(date2); - // Assert - expect(result, false); - }); -}); + // Assert + expect(result, false); + }); + }); -group('isSameMonth', () { - test('should return true when comparing dates from the same year and month', () { - // Arrange - final date1 = DateTime(2024, 3, 1); - final date2 = DateTime(2024, 3, 31); + group('isSameMonth', () { + test('should return true when comparing dates from the same year and month', () { + // Arrange + final date1 = DateTime(2024, 3, 1); + final date2 = DateTime(2024, 3, 31); - // Act - final result = date1.isSameMonth(date2); + // Act + final result = date1.isSameMonth(date2); - // Assert - expect(result, true); - }); + // Assert + expect(result, true); + }); - test('should return false when comparing dates from different months', () { - // Arrange - final date1 = DateTime(2024, 3, 1); - final date2 = DateTime(2024, 4, 1); + test('should return false when comparing dates from different months', () { + // Arrange + final date1 = DateTime(2024, 3, 1); + final date2 = DateTime(2024, 4, 1); - // Act - final result = date1.isSameMonth(date2); + // Act + final result = date1.isSameMonth(date2); - // Assert - expect(result, false); - }); + // Assert + expect(result, false); + }); - test('should return false when comparing dates from same month but different years', () { - // Arrange - final date1 = DateTime(2024, 3, 1); - final date2 = DateTime(2023, 3, 1); + test('should return false when comparing dates from same month but different years', () { + // Arrange + final date1 = DateTime(2024, 3, 1); + final date2 = DateTime(2023, 3, 1); - // Act - final result = date1.isSameMonth(date2); + // Act + final result = date1.isSameMonth(date2); - // Assert - expect(result, false); - }); -}); + // Assert + expect(result, false); + }); + }); -group('isSameDay', () { - test('should return true when comparing dates from the same year, month and day', () { - // Arrange - final date1 = DateTime(2024, 3, 15, 10, 30); - final date2 = DateTime(2024, 3, 15, 14, 45); + group('isSameDay', () { + test('should return true when comparing dates from the same year, month and day', () { + // Arrange + final date1 = DateTime(2024, 3, 15, 10, 30); + final date2 = DateTime(2024, 3, 15, 14, 45); - // Act - final result = date1.isSameDay(date2); + // Act + final result = date1.isSameDay(date2); - // Assert - expect(result, true); - }); + // Assert + expect(result, true); + }); - test('should return false when comparing dates from different days', () { - // Arrange - final date1 = DateTime(2024, 3, 15); - final date2 = DateTime(2024, 3, 16); + test('should return false when comparing dates from different days', () { + // Arrange + final date1 = DateTime(2024, 3, 15); + final date2 = DateTime(2024, 3, 16); - // Act - final result = date1.isSameDay(date2); + // Act + final result = date1.isSameDay(date2); - // Assert - expect(result, false); - }); + // Assert + expect(result, false); + }); - test('should return false when comparing dates from same day but different months', () { - // Arrange - final date1 = DateTime(2024, 3, 15); - final date2 = DateTime(2024, 4, 15); + test('should return false when comparing dates from same day but different months', () { + // Arrange + final date1 = DateTime(2024, 3, 15); + final date2 = DateTime(2024, 4, 15); - // Act - final result = date1.isSameDay(date2); + // Act + final result = date1.isSameDay(date2); - // Assert - expect(result, false); - }); + // Assert + expect(result, false); + }); - test('should return false when comparing dates from same day and month but different years', () { - // Arrange - final date1 = DateTime(2024, 3, 15); - final date2 = DateTime(2023, 3, 15); + test('should return false when comparing dates from same day and month but different years', () { + // Arrange + final date1 = DateTime(2024, 3, 15); + final date2 = DateTime(2023, 3, 15); - // Act - final result = date1.isSameDay(date2); + // Act + final result = date1.isSameDay(date2); - // Assert - expect(result, false); - }); -}); + // Assert + expect(result, false); + }); + }); group('isBeforeOrEqualTo or isAfterOrEqualTo', () { test('if date is not after CurrentDate', () { @@ -231,5 +250,157 @@ group('isSameDay', () { expect(curDate.isBetween(fromDate, untilDate), true); }); }); + + group('calendarWeek', () { + test('should return correct week number for January 1st', () { + // Arrange + final date = DateTime(2024, 1, 1); + + // Act + final result = date.calendarWeek(); + + // Assert + expect(result, 1); + }); + + test('should return correct week number for mid-year date', () { + // Arrange + final date = DateTime(2024, 6, 15); + + // Act + final result = date.calendarWeek(); + + // Assert + expect(result, 24); + }); + + test('should return correct week number for December 31st', () { + // Arrange + final date = DateTime(2024, 12, 31); + + // Act + final result = date.calendarWeek(); + + // Assert + expect(result, 1); + }); + + test('should return week 53 for dates that belong to week 53', () { + // Arrange + final date = DateTime(2020, 12, 28); + + // Act + final result = date.calendarWeek(); + + // Assert + expect(result, 53); + }); + }); + + group('ordinalDate', () { + test('should return 1 for January 1st', () { + // Arrange + final date = DateTime(2024, 1, 1); + + // Act + final result = date.ordinalDate; + + // Assert + expect(result, 1); + }); + + test('should return 32 for February 1st', () { + // Arrange + final date = DateTime(2024, 2, 1); + + // Act + final result = date.ordinalDate; + + // Assert + expect(result, 32); + }); + + test('should return 366 for December 31st in leap year', () { + // Arrange + final date = DateTime(2024, 12, 31); + + // Act + final result = date.ordinalDate; + + // Assert + expect(result, 366); + }); + + test('should return 365 for December 31st in non-leap year', () { + // Arrange + final date = DateTime(2023, 12, 31); + + // Act + final result = date.ordinalDate; + + // Assert + expect(result, 365); + }); + + test('should account for leap year in March', () { + // Arrange + final leapYearDate = DateTime(2024, 3, 1); + final nonLeapYearDate = DateTime(2023, 3, 1); + + // Act + final leapResult = leapYearDate.ordinalDate; + final nonLeapResult = nonLeapYearDate.ordinalDate; + + // Assert + expect(leapResult, 61); + expect(nonLeapResult, 60); + }); + }); + + group('isLeapYear', () { + test('should return true for leap year divisible by 4', () { + // Arrange + final date = DateTime(2024, 1, 1); + + // Act + final result = date.isLeapYear; + + // Assert + expect(result, true); + }); + + test('should return false for non-leap year', () { + // Arrange + final date = DateTime(2023, 1, 1); + + // Act + final result = date.isLeapYear; + + // Assert + expect(result, false); + }); + + test('should return false for year divisible by 100 but not 400', () { + // Arrange + final date = DateTime(1900, 1, 1); + + // Act + final result = date.isLeapYear; + + // Assert + expect(result, false); + }); + + test('should return true for year divisible by 400', () { + // Arrange + final date = DateTime(2000, 1, 1); + + // Act + final result = date.isLeapYear; + + // Assert + expect(result, true); + }); + }); }); }