問題
C ++で日付を保持するためのクラスを作成していますが、次の問題が見つかりました。
N
基準日(私の場合は西暦0001年1月1日)からの日数があります。これには、基準日から経過したうるう日も含まれます。この数値を年、月、日に効率的に変換するにはどうすればY
M
D
よいですか?
私はこれを可能な限り効率的に行いたいので、最良の実装は明らかにO(1)の複雑さを持ちます。
次のセクションでは、私がすでに学んだことのいくつかを説明します。
うるう年
1年が飛躍するかどうかを判断するには、いくつかのルールがあります。
- 4で割り切れる年は飛躍的です
- ルール1の例外:100で割り切れる年は飛躍しません
- ルール2の例外:400で割り切れる年は飛躍的です
これは、次のようなコードに変換されます。
bool IsLeapYear(int year)
{
// Corrected after Henrick's suggestion
if (year % 400 == 0) return true;
if ((year % 4 == 0) && (year % 100 != 0)) return true;
return false;
}
1年前に何年飛躍しているかを計算する効率的な方法は次のとおりです。
int LeapDaysBefore(int year)
{
// Years divisible by 4, not divisible by 100, but divisible by 400
return ((year-1)/4 - (year-1)/100 + (year-1)/400);
}
月の計算
年を見つけたら、現在の年までの日数を計算し、この数値をNから引くことができます。これにより、その年の日がわかります。
毎月の開始日番号を表にしておくと、簡単に月を計算できます。また、年が飛躍し、月が2以上の場合に1を加算する関数を作成しました。
// What day each month starts on (counting from 0)
int MonthDaySt[] = { 0, 31, 59, 90, 120, 151, 181, 212,
243, 273, 304, 334, 365 };
int MonthDayStart(int month, bool leap)
{
if (leap && month >= 2) return MonthDaySt[month]+1;
return MonthDaySt[month];
}
私の考え
私のアルゴリズムはかなり複雑で、次のようになります。
void GetDate(int N, int &Y, int &M, int &D)
{
int year_days;
// Approximate the year, this will give an year greater or equal
// to what year we are looking for.
Y = N / 365 + 1;
// Find the actual year, counting leap days
do {
Y--;
// Calculate the actual number of days until the
// approximate year
year_days = Y * 365 + LeapDaysBefore(year);
} while (year_days > N);
// Add 1, because we start from year 1 AD (not 0)
Y++;
// Calculate month
uint64_t diff = N - year_days; // Will give us the day of the year
bool leap = IsLeapYear(Y); // Is current year leap?
// Use table to find month
M = 0;
while (MonthDayStart(M, leap) <= diff && M <= 12)
M++;
// Calculate day
D = diff - MonthDayStart(M - 1, leap) + 1;
}
この関数にはいくつかのバグがある可能性があります(たとえば、Nが0の場合は機能しませんでした)。
その他の注意事項
この質問の元のアルゴリズムからいくつかの変更を加えたので、私のアルゴリズムがまだ正しいことを願っています。何かを見逃したり、何かが間違っていた場合は、それを変更するように知らせてください。そして、長い質問でごめんなさい。