* 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

View File

@ -47,9 +47,16 @@ class BDate {
~BDate();
bool IsValid() const;
bool IsValid(const BDate& date) const;
bool IsValid(int32 year, int32 month, int32 day) const;
static BDate CurrentDate(time_type type);
BDate Date() const;
bool SetDate(const BDate& date);
bool SetDate(int32 year, int32 month, int32 day);
void GetDate(int32* year, int32* month, int32* day);
void AddDays(int32 days);
void AddYears(int32 years);
@ -58,6 +65,7 @@ class BDate {
int32 Day() const;
int32 Year() const;
int32 Month() const;
int32 Difference(const BDate& date) const;
int32 DayOfWeek() const;
int32 DayOfYear() const;
@ -74,6 +82,23 @@ class BDate {
BString LongDayName(int32 day) 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:
int32 fDay;
int32 fYear;

View File

@ -7,7 +7,7 @@
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "DateTime.h"
#include <DateTime.h>
#include <time.h>
@ -104,7 +104,7 @@ BTime::Second() const
}
// #pragma mark -
// #pragma mark - BDate
BDate::BDate()
@ -116,10 +116,8 @@ BDate::BDate()
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
BDate::IsValid() const
{
// fail if the year goes less 1583
// 10/15/1582 start of Gregorian calendar
if (fYear < 1583)
return IsValid(fYear, fMonth, fDay);
}
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;
if (fMonth < 1 || fMonth > 12)
// 'missing' days between switch julian - gregorian
if (year == 1582 && month == 10 && day > 4 && day < 15)
return false;
if (fDay < 1 || fDay > DaysInMonth())
if (month < 1 || month > 12)
return false;
if (day < 1 || day > _DaysInMonth(year, month))
return false;
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
BDate::SetDate(int32 year, int32 month, int32 day)
{
fDay = day;
fYear = year;
fMonth = month;
return _SetDate(year, month, day);
}
return IsValid();
void
BDate::GetDate(int32* year, int32* month, int32* day)
{
if (year)
*year = fYear;
if (month)
*month = fMonth;
if (day)
*day = fDay;
}
void
BDate::AddDays(int32 days)
{
days += Day();
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;
*this = JulianDayToDate(DateToJulianDay() + days);
}
@ -206,38 +227,22 @@ void
BDate::AddYears(int32 years)
{
fYear += years;
fDay = min_c(fDay, DaysInMonth());
fDay = min_c(fDay, _DaysInMonth(fYear, fMonth));
}
void
BDate::AddMonths(int32 months)
{
if (months == 0)
return;
fYear += months / 12;
fMonth += months % 12;
if (months > 0) {
while (months > 0) {
fYear++;
months -= 12;
}
fMonth += months;
if (fMonth < 1) {
fYear--;
fMonth += 12;
}
} else {
while (months < 0) {
fYear--;
months += 12;
}
fMonth += months;
if (fMonth > 12) {
fYear++;
fMonth -= 12;
}
if (fMonth > 12) {
fYear++;
fMonth -= 12;
} else if (fMonth < 1) {
fYear--;
fMonth += 12;
}
fDay = min_c(fDay, DaysInMonth());
}
@ -264,6 +269,13 @@ BDate::Month() const
}
int32
BDate::Difference(const BDate& date) const
{
return DateToJulianDay() - date.DateToJulianDay();
}
int32
BDate::WeekNumber() const
{
@ -275,7 +287,9 @@ BDate::WeekNumber() const
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;
int32 a;
@ -320,56 +334,25 @@ BDate::WeekNumber() const
int32
BDate::DayOfWeek() const
{
/*
This algorithm is taken from:
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;
// http://en.wikipedia.org/wiki/Julian_day#Calculation
return (DateToJulianDay() % 7) + 1;
}
int32
BDate::DayOfYear() const
{
/*
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;
return DateToJulianDay() - _DateToJulianDay(fYear, 1, 1) + 1;
}
bool
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;
}
@ -387,13 +370,7 @@ BDate::DaysInYear() const
int32
BDate::DaysInMonth() const
{
if (fMonth == 2 && IsLeapYear(fYear))
return 29;
const int32 daysInMonth[12] =
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
return daysInMonth[fMonth -1];
return _DaysInMonth(fYear, fMonth);
}
@ -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)