-1

一見するとかなり簡単なエクササイズができましたが、代わりに非常に難しくなりました. 入力として日付を取得し、ユーザー入力から取得した日付を追加する必要があります。私はすでにいくつかの関数といくつかの簡単な計算を行っていましたが、今では日付からすべての日を取得しています (01,01,0001 はゼロです)。

最初の 1 月 2 年目 (01.01.0002) + 0 日は 365 日と等しく、それに数日を追加すると正しく計算されます: 01.01.0002 + 12 日 = 387 ..うるう年も計算します。totalDays ができたので、通常の日/月/年の形式に変換する必要があります..

日時の使用は許可されていません

これが私のコードです:

private static int[] daysPerMonth = new int[12];
    private static int days;
    private static int months;
    private static int years;
    private static int add;

    private static void Main()
    {
        Console.Write("Enter day : ");
        int.TryParse(Console.ReadLine(), out days);
        Console.Write("Enter Month : ");
        int.TryParse(Console.ReadLine(), out months);
        Console.Write("Enter Year : ");
        int.TryParse(Console.ReadLine(), out years);
        Console.Write("Enter days to add : ");
        int.TryParse(Console.ReadLine(), out add);
        int totalDays = GetTotalDays(new[] {days, months, years});
        totalDays += add;
        TransformIntoDate(totalDays);

        Console.ReadKey();
    }

    private static void TransformIntoDate(int inputDays)
    {

    }
    private static int GetTotalDays(IReadOnlyList<int> date)
    {
        int totalDays = 0;
        for (int i = date[2]; i > 1; i--)
        {
            if (IsLeap(i))
            {
                daysPerMonth = new[] {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
                totalDays += 366;
            }
            else
            {
                daysPerMonth = new[] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
                totalDays += 365;
            }
        }
        for (int i = 1; i <= date[1]; i++)
        {
            if (i == date[1])
            {
                totalDays += date[0] - 1;
            }
            else
            {
                totalDays += daysPerMonth[i];
            }
        }
        return totalDays;
    }

    private static bool IsLeap(int year)
    {
        if (year%400 == 0) return true;
        return (year%4 == 0) && (year%100 != 0);
    }
4

1 に答える 1

2

日付を「保存」するには 2 つの方法があります。年、月、日を個別に保存する方法と、「0 ポイント」からの合計日数 (または時間、分、秒、ミリ秒 ... ここで測定単位を選択) を保存する方法です。 . DateTimeたとえば、.NET の は として 100 ナノ秒を使用し、Tick0001 年 1 月 1 日を「0 ポイント」として使用します。Unix は 1970 年 1 月 1 日から秒を使用します。明らかに、.NET と Unix の方法はメモリ内でよりコンパクトであり (保存する単一の値)、数量を加算/減算する場合 (単純に加算/減算する) に非常に役立ちます。問題は、この内部数値を年/月/日に変換したり、年/月/日をこの数値に変換したりするのがより複雑なことです。

年/月/日から内部番号への変換方法の簡単な例:

public class MyDate
{
    public int TotalDaysFrom00010101 { get; private set; }

    private const int DaysIn400YearCycle = 365 * 400 + 97;
    private const int DaysIn100YearCycleNotDivisibleBy400 = 365 * 100 + 24;
    private const int DaysIn4YearCycle = 365 * 4 + 1;

    private static readonly int[] DaysPerMonthNonLeap = new[] 
    {
        31, 
        31 + 28, 
        31 + 28 + 31, 
        31 + 28 + 31 + 30, 
        31 + 28 + 31 + 30 + 31, 
        31 + 28 + 31 + 30 + 31 + 30, 
        31 + 28 + 31 + 30 + 31 + 30 + 31, 
        31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, 
        31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, 
        31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, 
        31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, 
        31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 // Useless
    };

    private static readonly int[] DaysPerMonthLeap = new[] 
    {
        31, 
        31 + 29, 
        31 + 29 + 31, 
        31 + 29 + 31 + 30, 
        31 + 29 + 31 + 30 + 31, 
        31 + 29 + 31 + 30 + 31 + 30, 
        31 + 29 + 31 + 30 + 31 + 30 + 31, 
        31 + 29 + 31 + 30 + 31 + 30 + 31 + 31, 
        31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30, 
        31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, 
        31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, 
        31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 // Useless
    };

    public static bool IsLeap(int year)
    {
        if (year % 400 == 0) return true;
        return (year % 4 == 0) && (year % 100 != 0);
    }

    public void SetDate(int year, int month, int day)
    {
        TotalDaysFrom00010101 = 0;

        {
            int year2 = year - 1;

            // Full 400 year cycles
            TotalDaysFrom00010101 += (year2 / 400) * DaysIn400YearCycle;
            year2 %= 400;

            // Remaining 100 year cycles (0...3)
            if (year2 >= 100)
            {
                year2 -= 100;
                TotalDaysFrom00010101 += DaysIn100YearCycleNotDivisibleBy400;

                if (year2 >= 100)
                {
                    year2 -= 100;
                    TotalDaysFrom00010101 += DaysIn100YearCycleNotDivisibleBy400;

                    if (year2 >= 100)
                    {
                        year2 -= 100;
                        TotalDaysFrom00010101 += DaysIn100YearCycleNotDivisibleBy400;
                    }
                }
            }

            // Full 4 year cycles
            TotalDaysFrom00010101 += (year2 / 4) * DaysIn4YearCycle;
            year2 %= 4;

            // Remaining non-leap years
            TotalDaysFrom00010101 += year2 * 365;
        }

        // Days from the previous month
        if (month > 1)
        {
            // -2 is because -1 is for the 1 January == 0 index, plus -1 
            // because we must add only the previous full months here. 
            // So if the date is 1 March 2016, we must add the days of 
            // January + February, so month 3 becomes index 1.
            TotalDaysFrom00010101 += DaysPerMonthNonLeap[month - 2];

            if (month > 2 && IsLeap(year))
            {
                TotalDaysFrom00010101 += 1;
            }
        }

        // Days (note that the "instant 0" in this class is day 1, so
        // we must add day - 1)
        TotalDaysFrom00010101 += day - 1;
    }

    public void GetDate(out int year, out int month, out int day)
    {
        int days = TotalDaysFrom00010101;

        // year
        {
            year = days / DaysIn400YearCycle * 400;
            days %= DaysIn400YearCycle;

            if (days >= DaysIn100YearCycleNotDivisibleBy400)
            {
                year += 100;
                days -= DaysIn100YearCycleNotDivisibleBy400;

                if (days >= DaysIn100YearCycleNotDivisibleBy400)
                {
                    year += 100;
                    days -= DaysIn100YearCycleNotDivisibleBy400;

                    if (days >= DaysIn100YearCycleNotDivisibleBy400)
                    {
                        year += 100;
                        days -= DaysIn100YearCycleNotDivisibleBy400;
                    }
                }
            }

            year += days / DaysIn4YearCycle * 4;
            days %= DaysIn4YearCycle;

            // Special case: 31 dec of a leap year
            if (days != 1460)
            {
                year += days / 365;
                days %= 365;
            }
            else
            {
                year += 3;
                days = 365;
            }

            year++;
        }

        // month
        {
            bool isLeap = IsLeap(year);

            int[] daysPerMonth = isLeap ? DaysPerMonthLeap : DaysPerMonthNonLeap;

            for (month = 0; month < daysPerMonth.Length; month++)
            {
                if (daysPerMonth[month] > days)
                {
                    if (month > 0)
                    {
                        days -= daysPerMonth[month - 1];
                    }

                    break;
                }
            }

            month++;
        }

        // day
        {
            day = days;
            day++;
        }
    }

    public void AddDays(int days)
    {
        TotalDaysFrom00010101 += days;
    }
}

ここでのポイントは、400 年の「期間」があり、これらの「サイクル」のそれぞれに日があることを知っているということです365 * 400 + 97。これらの「サイクル」を差し引いた後、100 年の小さな「サイクル」があり、それぞれに365 * 100 + 24日数があります。次に、4 年の「サイクル」があり、それぞれに365 * 4 + 3日数があり、さらに残りの年数 (0...3) がそれぞれ 365 日です。

すべての「前年度全体」の日数を追加した後、「前月全体」の日数を追加できます。ここで、その年がうるう年である可能性を考慮する必要があります。

最後に、選択した日を追加します。

の書き方はGetDate()練習問題として残します。

今...結果が正しいかどうかを確認するにはどうすればよいですか? 実装に基づいていくつかの単体テストを作成できますDateTime...次のようなもの:

var date = default(DateTime);
var endDate = new DateTime(2017, 1, 1);

while (date < endDate)
{
    int year = date.Year;
    int month = date.Month;
    int day = date.Day;

    int totalDays = (int)(date - default(DateTime)).TotalDays;

    var md = new MyDate();
    md.SetDate(year, month, day);

    if (totalDays != md.TotalDaysFrom00010101)
    {
        Console.WriteLine("{0:d}: {1} vs {2}", date, totalDays, md.TotalDaysFrom00010101);
    }

    int year2, month2, day2;
    md.GetDate(out year2, out month2, out day2);

    if (year != year2 || month != month2 || day != day2)
    {
        Console.WriteLine("{0:d}: {1:D4}-{2:D2}-{3:D2} vs {4:D4}-{5:D2}-{6:D2}", date, year, month, day, year2, month2, day2);
    }

    date = date.AddDays(1);
}

(これは単体テストではないことは知っていますがDateTime、比較を行うための使用方法を示しています)

これは実装方法の例であることに注意してください。私はこのように実装しません。の代わりにコンストラクターを使用して、不変structのようなものを作成します。しかし、あなたのものは演習のようであり、小さなステップで進める必要があると思います: 最初に何かを正しく構築し、次にそれを「形式的に正しい」ものにします。したがって、最初のステップは正しい/を構築することです。次に、より形式的に正しいデータ構造に正しくカプセル化できます。この小さなサンプルには、いくつかのパラメーター検証が欠けています: できます:-)DateTimeSetDate()GetDate()SetDate()SetDate(-1, 13, 32)

GetDate()構築は非常に複雑でした。思ったよりずっと。やっと書き方が理解できました。最終的には、Microsoft の実装と非常に似ていることに注意してください ( GetDatePart(int part).

于 2016-03-05T18:58:44.187 に答える