C#将日期添加到给定的日期
第一次看起来我做了相当简单的练习,但最后却变得相当困难。 我需要将日期作为输入,并将日期添加到它也从用户输入中获取。 我已经做了一些功能和一些简单的计算,现在我已经从日期(01,01,0001为零)中获得了所有的日子,例如:
第一个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);
}
有两种方法可以“保存”日期:分别保存年,月,日或从“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
进行比较)
请注意,这是一个如何实现它的例子。 我不会这样实施它。 我会创建一个像DateTime
这样的不可变struct
,并使用构造函数而不是SetDate()
。 但是你的观点似乎是一个练习,而且我认为应该采取小步骤:首先正确地构建一些东西,然后使它“正式地正确”。 所以第一步是建立一个正确的GetDate()
/ SetDate()
那么你就可以正确地在一个更正式正确的数据结构封装。 这个小样本甚至缺少一些参数验证:你可以SetDate(-1, 13, 32)
:-)
GetDate()
构建起来相当复杂。 比我想象的要多得多。 最后,我能够理解如何编写它。 请注意,最后它与Microsoft的实现非常相似(请参阅GetDatePart(int part)
。