2016-03-05 156 views
-1

我在第一眼看上去做了相當簡單的練習,但最終變得相當困難。我需要將日期作爲輸入,並將日期添加到它也從用戶輸入中獲取。我已經做了一些功能和一些簡單的計算,現在我從日期(01,01,0001爲零)中獲取所有日期,例如:C#在指定日期添加日期

第一個1月第二年(01.01.0002)+ 0天是相等的到365天它也計算正確,如果我添加一些天:01.01.0002 + 12天= 387 ..它也計算閏年。現在,我已經totalDays我只是需要將其轉換成一個正常的日/月/年的格式..

IM不允許使用DATETIME

這裏是我的代碼:

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

* GetTotalDays(new [] {days,months,years})*請勿。請勿將三個完全斷開連接的數字它們是三個參數,它們應該有名稱,比如* year *,* month *,* day *,不是'date [2]','date [1]','date [0]' – xanatos

+0

我是計劃爲他們創建枚舉,但我沒有 – KOPEUE

+0

創建一個類或結構來保存數據並進行計算。也許你不能*使用* DateTime,但你可以模仿它做什麼 – Plutonix

回答

1

有是「保存」日期的兩種方法:分別保存年份,月份,日期或從「0點」保存總天數(或小時或分鐘或秒或毫秒...選擇此處的度量單位)。 .NET的DateTime例如使用100納秒作爲Tick,0001年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做一個比較)

請注意,這是一個如何實現它的例子。我不會這樣實施它。我會做一個不變的structDateTime,用構造函數而不是SetDate()。但是你的觀點似乎是一個練習,而且我認爲應該採取小步驟:首先正確地構建一些東西,然後使它「正式地正確」。所以第一步是建立一個正確的,然後你可以正確地封裝在一個更正式的數據結構。這個小樣本甚至缺少一些參數驗證:您可以SetDate(-1, 13, 32) :-)

GetDate()構建起來相當複雜。比我想象的要多得多。最後,我能夠理解如何編寫它。請注意,最終它與微軟的實施非常相似(請參閱GetDatePart(int part)

+0

謝謝你的深入答案,但你離開了我需要轉換成一年的日子到我的一部分嗎?我仍然試圖瞭解代碼(即時通訊初學者在C#)和IM有點困惑,我們從SetDate得到的是幾天?我在我的問題中也發佈了類似的實現,並且我的問題正在將日期轉換爲日/月/年 – KOPEUE

+0

SetDate()完成且工作正常。 AddDays()是完整的並且正常工作。缺少的是將內部日數('TotalDaysFrom00010101')轉換爲年/月/日(在'GetDate()'中完成的事情)。但是,如果有一個工作的SetDate(),它應該是可行的(即使因爲它很容易測試)...執行一個日期的SetDate()並嘗試用GetDate() – xanatos

+0

爲什麼GetDate將年份,月份,日期作爲參數不應該只需要幾天並且回饋年,月,日? – KOPEUE