* implement julian day handling

* more get/ set functions, operators
* date until 1582 are handled in julian calendar
* date above 1582 are handled in gregorian calendar



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27264 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Karsten Heimrich
2008-09-01 13:11:29 +00:00
parent 8bd8964052
commit de13e4c0bd
2 changed files with 255 additions and 107 deletions
+25
View File
@@ -47,9 +47,16 @@ class BDate {
~BDate(); ~BDate();
bool IsValid() const; bool IsValid() const;
bool IsValid(const BDate& date) const;
bool IsValid(int32 year, int32 month, int32 day) const;
static BDate CurrentDate(time_type type); static BDate CurrentDate(time_type type);
BDate Date() const;
bool SetDate(const BDate& date);
bool SetDate(int32 year, int32 month, int32 day); bool SetDate(int32 year, int32 month, int32 day);
void GetDate(int32* year, int32* month, int32* day);
void AddDays(int32 days); void AddDays(int32 days);
void AddYears(int32 years); void AddYears(int32 years);
@@ -58,6 +65,7 @@ class BDate {
int32 Day() const; int32 Day() const;
int32 Year() const; int32 Year() const;
int32 Month() const; int32 Month() const;
int32 Difference(const BDate& date) const;
int32 DayOfWeek() const; int32 DayOfWeek() const;
int32 DayOfYear() const; int32 DayOfYear() const;
@@ -74,6 +82,23 @@ class BDate {
BString LongDayName(int32 day) const; BString LongDayName(int32 day) const;
BString LongMonthName(int32 month) const; BString LongMonthName(int32 month) const;
int32 DateToJulianDay() const;
static BDate JulianDayToDate(int32 julianDay);
bool operator!=(const BDate& date) const;
bool operator==(const BDate& date) const;
bool operator<(const BDate& date) const;
bool operator<=(const BDate& date) const;
bool operator>(const BDate& date) const;
bool operator>=(const BDate& date) const;
private:
int32 _DaysInMonth(int32 year, int32 month) const;
bool _SetDate(int32 year, int32 month, int32 day);
int32 _DateToJulianDay(int32 year, int32 month, int32 day) const;
private: private:
int32 fDay; int32 fDay;
int32 fYear; int32 fYear;
+230 -107
View File
@@ -7,7 +7,7 @@
* Stephan Aßmus <[email protected]> * Stephan Aßmus <[email protected]>
*/ */
#include "DateTime.h" #include <DateTime.h>
#include <time.h> #include <time.h>
@@ -104,7 +104,7 @@ BTime::Second() const
} }
// #pragma mark - // #pragma mark - BDate
BDate::BDate() BDate::BDate()
@@ -116,10 +116,8 @@ BDate::BDate()
BDate::BDate(int32 year, int32 month, int32 day) BDate::BDate(int32 year, int32 month, int32 day)
: fDay(day),
fYear(year),
fMonth(month)
{ {
_SetDate(year, month, day);
} }
@@ -131,15 +129,34 @@ BDate::~BDate()
bool bool
BDate::IsValid() const BDate::IsValid() const
{ {
// fail if the year goes less 1583 return IsValid(fYear, fMonth, fDay);
// 10/15/1582 start of Gregorian calendar }
if (fYear < 1583)
bool
BDate::IsValid(const BDate& date) const
{
return IsValid(date.fYear, date.fMonth, date.fDay);
}
bool
BDate::IsValid(int32 year, int32 month, int32 day) const
{
// no year 0 in Julian and we can't handle nothing before 1.1.4713 BC
if (year == 0 || year < -4713
|| (year == -4713 && month < 1)
|| (year == -4713 && month < 1 && day < 1))
return false; return false;
if (fMonth < 1 || fMonth > 12) // 'missing' days between switch julian - gregorian
if (year == 1582 && month == 10 && day > 4 && day < 15)
return false; return false;
if (fDay < 1 || fDay > DaysInMonth()) if (month < 1 || month > 12)
return false;
if (day < 1 || day > _DaysInMonth(year, month))
return false; return false;
return true; return true;
@@ -164,41 +181,45 @@ BDate::CurrentDate(time_type type)
} }
BDate
BDate::Date() const
{
return BDate(fYear, fMonth, fDay);
}
bool
BDate::SetDate(const BDate& date)
{
return _SetDate(date.fYear, fMonth, fDay);
}
bool bool
BDate::SetDate(int32 year, int32 month, int32 day) BDate::SetDate(int32 year, int32 month, int32 day)
{ {
fDay = day; return _SetDate(year, month, day);
fYear = year; }
fMonth = month;
return IsValid();
void
BDate::GetDate(int32* year, int32* month, int32* day)
{
if (year)
*year = fYear;
if (month)
*month = fMonth;
if (day)
*day = fDay;
} }
void void
BDate::AddDays(int32 days) BDate::AddDays(int32 days)
{ {
days += Day(); *this = JulianDayToDate(DateToJulianDay() + days);
if (days > 0) {
while (days > DaysInMonth()) {
days -= DaysInMonth();
fMonth ++;
if (fMonth == 13) {
fMonth = 1;
fYear ++;
}
}
} else {
while (days <= 0) {
fMonth --;
if (fMonth == 0) {
fMonth = 12;
fYear --;
}
days += DaysInMonth();
}
}
fDay = days;
} }
@@ -206,38 +227,22 @@ void
BDate::AddYears(int32 years) BDate::AddYears(int32 years)
{ {
fYear += years; fYear += years;
fDay = min_c(fDay, DaysInMonth()); fDay = min_c(fDay, _DaysInMonth(fYear, fMonth));
} }
void void
BDate::AddMonths(int32 months) BDate::AddMonths(int32 months)
{ {
if (months == 0) fYear += months / 12;
return; fMonth += months % 12;
if (months > 0) { if (fMonth > 12) {
while (months > 0) { fYear++;
fYear++; fMonth -= 12;
months -= 12; } else if (fMonth < 1) {
} fYear--;
fMonth += 12;
fMonth += months;
if (fMonth < 1) {
fYear--;
fMonth += 12;
}
} else {
while (months < 0) {
fYear--;
months += 12;
}
fMonth += months;
if (fMonth > 12) {
fYear++;
fMonth -= 12;
}
} }
fDay = min_c(fDay, DaysInMonth()); fDay = min_c(fDay, DaysInMonth());
} }
@@ -264,6 +269,13 @@ BDate::Month() const
} }
int32
BDate::Difference(const BDate& date) const
{
return DateToJulianDay() - date.DateToJulianDay();
}
int32 int32
BDate::WeekNumber() const BDate::WeekNumber() const
{ {
@@ -275,7 +287,9 @@ BDate::WeekNumber() const
Note: it will work only within the Gregorian Calendar Note: it will work only within the Gregorian Calendar
*/ */
if (!IsValid()) if (!IsValid() || fYear < 1582
|| (fYear == 1582 && fMonth < 10)
|| (fYear == 1582 && fMonth == 10 && fDay < 15))
return 0; return 0;
int32 a; int32 a;
@@ -320,56 +334,25 @@ BDate::WeekNumber() const
int32 int32
BDate::DayOfWeek() const BDate::DayOfWeek() const
{ {
/* // http://en.wikipedia.org/wiki/Julian_day#Calculation
This algorithm is taken from: return (DateToJulianDay() % 7) + 1;
Frequently Asked Questions about Calendars
Version 2.8 Claus Tøndering 15 December 2005
Note: it will work only within the Gregorian Calendar
*/
if (!IsValid())
return -1;
int32 a = (14 - fMonth) / 12;
int32 y = fYear - a;
int32 m = fMonth + 12 * a - 2;
int32 d = (fDay + y + (y / 4) - (y / 100) + (y / 400) + ((31 * m) / 12)) % 7;
return d;
} }
int32 int32
BDate::DayOfYear() const BDate::DayOfYear() const
{ {
/* return DateToJulianDay() - _DateToJulianDay(fYear, 1, 1) + 1;
Note: this function might fail for 1582...
http://en.wikipedia.org/wiki/Gregorian_calendar:
The last day of the Julian calendar was Thursday October 4, 1582
and this was followed by the first day of the Gregorian calendar,
Friday October 15, 1582 (the cycle of weekdays was not affected).
*/
if (!IsValid())
return -1;
const int kFirstDayOfMonth[2][12] = {
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
};
if (IsLeapYear(fYear))
return kFirstDayOfMonth[1][fMonth -1] + fDay;
return kFirstDayOfMonth[0][fMonth -1] + fDay;
} }
bool bool
BDate::IsLeapYear(int32 year) const BDate::IsLeapYear(int32 year) const
{ {
if (year < 1582) {
if (year < 0) year++;
return (year % 4) == 0;
}
return year % 400 == 0 || year % 4 == 0 && year % 100 != 0; return year % 400 == 0 || year % 4 == 0 && year % 100 != 0;
} }
@@ -387,13 +370,7 @@ BDate::DaysInYear() const
int32 int32
BDate::DaysInMonth() const BDate::DaysInMonth() const
{ {
if (fMonth == 2 && IsLeapYear(fYear)) return _DaysInMonth(fYear, fMonth);
return 29;
const int32 daysInMonth[12] =
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
return daysInMonth[fMonth -1];
} }
@@ -465,7 +442,153 @@ BDate::LongMonthName(int32 month) const
} }
// #pragma mark - int32
BDate::DateToJulianDay() const
{
return _DateToJulianDay(fYear, fMonth, fDay);
}
BDate
BDate::JulianDayToDate(int32 julianDay)
{
BDate date;
const int32 kGregorianCalendarStart = 2299161;
if (julianDay >= kGregorianCalendarStart) {
// http://en.wikipedia.org/wiki/Julian_day#Gregorian_calendar_from_Julian_day_number
int32 j = julianDay + 32044;
int32 dg = j % 146097;
int32 c = (dg / 36524 + 1) * 3 / 4;
int32 dc = dg - c * 36524;
int32 db = dc % 1461;
int32 a = (db / 365 + 1) * 3 / 4;
int32 da = db - a * 365;
int32 m = (da * 5 + 308) / 153 - 2;
date.fYear = ((j / 146097) * 400 + c * 100 + (dc / 1461) * 4 + a) - 4800 +
(m + 2) / 12;
date.fMonth = (m + 2) % 12 + 1;
date.fDay = int32((da - (m + 4) * 153 / 5 + 122) + 1.5);
} else {
// http://en.wikipedia.org/wiki/Julian_day#Calculation
julianDay += 32082;
int32 d = (4 * julianDay + 3) / 1461;
int32 e = julianDay - (1461 * d) / 4;
int32 m = ((5 * e) + 2) / 153;
date.fDay = e - (153 * m + 2) / 5 + 1;
date.fMonth = m + 3 - 12 * (m / 10);
int32 year = d - 4800 + (m / 10);
if (year <= 0)
year--;
date.fYear = year;
}
return date;
}
bool
BDate::operator!=(const BDate& date) const
{
return DateToJulianDay() != date.DateToJulianDay();
}
bool
BDate::operator==(const BDate& date) const
{
return DateToJulianDay() == date.DateToJulianDay();
}
bool
BDate::operator<(const BDate& date) const
{
return DateToJulianDay() < date.DateToJulianDay();
}
bool
BDate::operator<=(const BDate& date) const
{
return DateToJulianDay() <= date.DateToJulianDay();
}
bool
BDate::operator>(const BDate& date) const
{
return DateToJulianDay() > date.DateToJulianDay();
}
bool
BDate::operator>=(const BDate& date) const
{
return DateToJulianDay() >= date.DateToJulianDay();
}
bool
BDate::_SetDate(int32 year, int32 month, int32 day)
{
fDay = -1;
fYear = -1;
fMonth = -1;
bool valid = IsValid(year, month, day);
if (valid) {
fDay = day;
fYear = year;
fMonth = month;
}
return valid;
}
int32
BDate::_DaysInMonth(int32 year, int32 month) const
{
if (month == 2 && IsLeapYear(year))
return 29;
const int32 daysInMonth[12] =
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
return daysInMonth[month -1];
}
int32
BDate::_DateToJulianDay(int32 _year, int32 month, int32 day) const
{
int32 year = _year;
if (year < 0) year++;
int32 a = (14 - month) / 12;
int32 y = year + 4800 - a;
int32 m = month + (12 * a) - 3;
// http://en.wikipedia.org/wiki/Julian_day#Calculation
if (year > 1582
|| (year == 1582 && month > 10)
|| (year == 1582 && month == 10 && day >= 15)) {
return day + (((153 * m) + 2) / 5) + (365 * y) + (y / 4) -
(y / 100) + (y / 400) - 32045;
} else if (year < 1582
|| (year == 1582 && month < 10)
|| (year == 1582 && month == 10 && day <= 4)) {
return day + (((153 * m) + 2) / 5) + (365 * y) + (y / 4) - 32083;
}
// http://en.wikipedia.org/wiki/Gregorian_calendar:
// The last day of the Julian calendar was Thursday October 4, 1582
// and this was followed by the first day of the Gregorian calendar,
// Friday October 15, 1582 (the cycle of weekdays was not affected).
return -1;
}
// #pragma mark - BDateTime
BDateTime::BDateTime(const BDate &date, const BTime &time) BDateTime::BDateTime(const BDate &date, const BTime &time)