diff --git a/src/dateTime.cpp b/src/dateTime.cpp index 1af0554e..f3ba2396 100644 --- a/src/dateTime.cpp +++ b/src/dateTime.cpp @@ -794,6 +794,7 @@ const int datetime::getMinute() const { return (m_minute); } const int datetime::getSecond() const { return (m_second); } const int datetime::getZone() const { return (m_zone); } const int datetime::getWeekDay() const { return (utility::datetimeUtils::getDayOfWeek(m_year, m_month, m_day)); } +const int datetime::getWeek() const { return utility::datetimeUtils::getWeekOfYear(m_year, m_month, m_day); } void datetime::setYear(const int year) { m_year = year; } void datetime::setMonth(const int month) { m_month = std::min(std::max(month, 1), 12); } diff --git a/src/utility/datetimeUtils.cpp b/src/utility/datetimeUtils.cpp index 3bec7dbf..8606a275 100644 --- a/src/utility/datetimeUtils.cpp +++ b/src/utility/datetimeUtils.cpp @@ -252,5 +252,75 @@ const int datetimeUtils::getDayOfWeek(const int year, const int month, const int } +const int datetimeUtils::getWeekOfYear(const int year, const int month, const int day) +{ + // Algorithm from http://personal.ecu.edu/mccartyr/ISOwdALG.txt + + const bool leapYear = ((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0; + const bool leapYear_1 = (((year - 1) % 4) == 0 && ((year - 1) % 100) != 0) || ((year - 1) % 400) == 0; + + // 4. Find the DayOfYearNumber for Y M D + static const int DAY_OF_YEAR_NUMBER_MAP[12] = + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + int DayOfYearNumber = day + DAY_OF_YEAR_NUMBER_MAP[month - 1]; + + if (leapYear && month > 2) + DayOfYearNumber += 1; + + // 5. Find the Jan1Weekday for Y (Monday=1, Sunday=7) + const int YY = (year - 1) % 100; + const int C = (year - 1) - YY; + const int G = YY + YY / 4; + const int Jan1Weekday = 1 + (((((C / 100) % 4) * 5) + G) % 7); + + // 6. Find the Weekday for Y M D + const int H = DayOfYearNumber + (Jan1Weekday - 1); + const int Weekday = 1 + ((H - 1) % 7); + + // 7. Find if Y M D falls in YearNumber Y-1, WeekNumber 52 or 53 + int YearNumber, WeekNumber; + + if (DayOfYearNumber <= (8 - Jan1Weekday) && Jan1Weekday > 4) + { + YearNumber = year - 1; + + if (Jan1Weekday == 5 || (Jan1Weekday == 6 && leapYear_1)) + WeekNumber = 53; + else + WeekNumber = 52; + } + else + { + YearNumber = year; + } + + // 8. Find if Y M D falls in YearNumber Y+1, WeekNumber 1 + if (YearNumber == year) + { + const int I = (leapYear ? 366 : 365); + + if ((I - DayOfYearNumber) < (4 - Weekday)) + { + YearNumber = year + 1; + WeekNumber = 1; + } + } + + // 9. Find if Y M D falls in YearNumber Y, WeekNumber 1 through 53 + if (YearNumber == year) + { + const int J = DayOfYearNumber + (7 - Weekday) + (Jan1Weekday - 1); + + WeekNumber = J / 7; + + if (Jan1Weekday > 4) + WeekNumber -= 1; + } + + return WeekNumber; +} + + } // utility } // vmime diff --git a/tests/utility/datetimeUtilsTest.cpp b/tests/utility/datetimeUtilsTest.cpp index d018081f..3bb9e3ec 100644 --- a/tests/utility/datetimeUtilsTest.cpp +++ b/tests/utility/datetimeUtilsTest.cpp @@ -39,6 +39,8 @@ VMIME_TEST_SUITE_BEGIN VMIME_TEST(testGetDaysInMonthLeapYear) VMIME_TEST(testToUniversalTime) VMIME_TEST(testToLocalTime) + VMIME_TEST(testGetDayOfWeek) + VMIME_TEST(testGetWeekOfYear) VMIME_TEST_LIST_END @@ -124,5 +126,40 @@ VMIME_TEST_SUITE_BEGIN VASSERT_EQ("7", 120, local.getZone()); } + void testGetDayOfWeek() + { + VASSERT_EQ("1", vmime::datetime::WEDNESDAY, datetimeUtils::getDayOfWeek(1969, 12, 31)); + VASSERT_EQ("2", vmime::datetime::FRIDAY, datetimeUtils::getDayOfWeek(1976, 4, 9)); + VASSERT_EQ("3", vmime::datetime::TUESDAY, datetimeUtils::getDayOfWeek(1987, 6, 23)); + VASSERT_EQ("4", vmime::datetime::SATURDAY, datetimeUtils::getDayOfWeek(1990, 1, 13)); + VASSERT_EQ("5", vmime::datetime::MONDAY, datetimeUtils::getDayOfWeek(1999, 9, 20)); + VASSERT_EQ("6", vmime::datetime::THURSDAY, datetimeUtils::getDayOfWeek(2003, 2, 27)); + VASSERT_EQ("7", vmime::datetime::SATURDAY, datetimeUtils::getDayOfWeek(2005, 11, 19)); + VASSERT_EQ("8", vmime::datetime::WEDNESDAY, datetimeUtils::getDayOfWeek(2012, 5, 16)); + VASSERT_EQ("9", vmime::datetime::FRIDAY, datetimeUtils::getDayOfWeek(2027, 3, 12)); + } + + void testGetWeekOfYear() + { + VASSERT_EQ("1.1", 52, datetimeUtils::getWeekOfYear(2003, 12, 27)); + VASSERT_EQ("1.2", 52, datetimeUtils::getWeekOfYear(2003, 12, 28)); + VASSERT_EQ("1.3", 1, datetimeUtils::getWeekOfYear(2003, 12, 29)); + VASSERT_EQ("1.4", 1, datetimeUtils::getWeekOfYear(2004, 1, 4)); + VASSERT_EQ("1.5", 2, datetimeUtils::getWeekOfYear(2004, 1, 5)); + VASSERT_EQ("1.6", 2, datetimeUtils::getWeekOfYear(2004, 1, 11)); + + VASSERT_EQ("2.1", 52, datetimeUtils::getWeekOfYear(2004, 12, 26)); + VASSERT_EQ("2.2", 53, datetimeUtils::getWeekOfYear(2004, 12, 27)); + VASSERT_EQ("2.3", 53, datetimeUtils::getWeekOfYear(2005, 1, 2)); + VASSERT_EQ("2.4", 1, datetimeUtils::getWeekOfYear(2005, 1, 3)); + VASSERT_EQ("2.5", 1, datetimeUtils::getWeekOfYear(2005, 1, 4)); + VASSERT_EQ("2.6", 2, datetimeUtils::getWeekOfYear(2005, 1, 11)); + + VASSERT_EQ("3.1", 9, datetimeUtils::getWeekOfYear(2027, 3, 7)); + VASSERT_EQ("3.2", 10, datetimeUtils::getWeekOfYear(2027, 3, 8)); + VASSERT_EQ("3.3", 10, datetimeUtils::getWeekOfYear(2027, 3, 14)); + VASSERT_EQ("3.4", 11, datetimeUtils::getWeekOfYear(2027, 3, 15)); + } + VMIME_TEST_SUITE_END diff --git a/vmime/dateTime.hpp b/vmime/dateTime.hpp index b047a6a4..cdb7a980 100644 --- a/vmime/dateTime.hpp +++ b/vmime/dateTime.hpp @@ -200,6 +200,7 @@ public: const int getSecond() const; const int getZone() const; const int getWeekDay() const; + const int getWeek() const; void getTime(int& hour, int& minute, int& second, int& zone) const; void getTime(int& hour, int& minute, int& second) const; diff --git a/vmime/utility/datetimeUtils.hpp b/vmime/utility/datetimeUtils.hpp index a20be877..a80cf09e 100644 --- a/vmime/utility/datetimeUtils.hpp +++ b/vmime/utility/datetimeUtils.hpp @@ -78,6 +78,15 @@ public: * @return the day of the week, Sunday is 0, Monday is 1 (see datetime::DaysOfWeek enum) */ static const int getDayOfWeek(const int year, const int month, const int day); + + /** Return the week number in the year (ISO 8601). + * + * @param year year in 4-digit format + * @param month month (1-12), January is 1, December is 12 (see datetime::Months enum) + * @param day month day (1-31) + * @return the week number (1 is the first week of the year) + */ + static const int getWeekOfYear(const int year, const int month, const int day); };