C# Add days to a given date

I got fairly simple exercise to do on first look but it ended up being quite hard instead. I need to take a date as an input and also add days to the it which are also taken from the user input. I already did some functions and some simple calculations and now i have all the days taken from the date (01,01,0001 is zero) for example :

first January second year (01.01.0002) + 0 days is equal to 365 days it also calculates correctly if i add some days to it : 01.01.0002 + 12 days = 387.. it calculates leap years too. Now that i have totalDays i just need to convert it into a normal Day/Month/Year format..

IM NOT ALLOWED TO USE DATETIME

Here's my code :

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);
    }

There are two ways to "save" a date: saving separately year, month, day or saving the total number of days (or hours or minutes or seconds or milliseconds... Select an unit of measure here) from the "0 point". The DateTime of .NET for example uses 100 nanoseconds as the Tick , and 1 january 0001 as the "0 point". Unix uses seconds from 1 january 1970. Clearly the way of .NET and of Unix is more compact in memory (a single value to save) and is very useful if you want to add/subtract a quantity (simply add/subtract it). The problem is that it is more complex to convert this internal number to year/month/day or to convert a year/month/day to this number.

A simple example on how the year/month/day to internal number can be done:

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;
    }
}

The point here is that we know that there are "periods" of 400 years, and each one of these "cycles" has 365 * 400 + 97 days. After subtracting these "cycles" there are smaller "cycles" of 100 years, each one with 365 * 100 + 24 days. Then we have "cycles" of 4 years, each one with 365 * 4 + 3 days, plus the remaining years (0...3) each one with 365 days.

After having added the days for all the "full previous years", we can add the days for the "full previous months". Here we must consider the possibility that the year is a leap year.

And in the end we add the selected day.

How to write the GetDate() is left as an exercise.

Now... How can I check if the result is correct? We could write some unit tests based on the DateTime implementation... Something like:

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);
}

(I do know that this isn't a unit test... but shows how to use DateTime to do a comparison)

Note that this is an example on how to implement it. I wouldn't implement it this way. I would make an immutable struct like DateTime , with a constructor instead of a SetDate() . But yours seems to be an exercise, and I do think that one should proceed by small steps: first build correctly something, then make it "formally correct". So the first step is building a correct GetDate() / SetDate() , then you can encapsulate correctly in a more formally correct data structure. This small sample is even missing some parameter validation: you can SetDate(-1, 13, 32) :-)

The GetDate() was quite complex to build. Much more than I thought. Finally I was able to comprehend how to write it. Note that in the end it is quite similar to the implementation of Microsoft (see GetDatePart(int part) .

链接地址: http://www.djcxy.com/p/18544.html

上一篇: 计算div jquery中的左侧定位

下一篇: C#将日期添加到给定的日期