使用C#创建包含数组和循环的日历

Creating calendar with array and loops using C#

我正在尝试创建一个应用程序,它告诉您输入2016年的日期(1月1日、9月3日等)的星期几(星期一、星期二等);输入是日期,输出是该日期的星期几。它必须用循环创建,例如for循环和if语句。我不能使用库函数。开始日期为2016年1月1日,星期五。这就是我目前为止所拥有的,我很迷茫:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    public enum Days : byte { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };
    public enum Months : byte { January, February, March, April, May, June, July, August, September, October, November, December };
    class Program
    {
        public static void Main(string[] args)
        {
            const int numDays = 7;
            const int numMonths = 12;
            Months month = Months.Janurary;
            int monthCounter = 1;
            int[] daysinMonth = new int[numMonths] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
            int[] daysofWeek = new int[numDays] {1, 2, 3, 4, 5, 6, 7};
            Days dayOfWeek = Days.Sunday;
            dayOfWeek = Days.Friday;
            foreach (month in Months )
            {

我的小结是:

Using only the tools we have covered to date, find the weekday name of a specific date in 2016, working from Friday January 1 2016, using nested loops and comparisons.


既然你不想要代码,我会给你一些解释,只需要一点代码。然后,一旦这些都完成了,在这个底部有完整的代码,可以为您提供任意日期所需的所有信息。好的。

不过,您只对2016年感兴趣。因此,如果您希望todo是计算特定年份的星期几,则只需要(从完整代码中)类声明和两个函数:好的。

  • GetDayNumberFromDate(开始日期)
  • GetDayOfWeekFromDate

解释和一点代码

我会在一个静态类中完成所有这些。您不需要任何东西的instance,只需要一些函数/方法。好的。

在该类中,我将声明并初始化三个私有变量:好的。

  • 由12个整数组成的数组,每个整数表示每个月的天数。记住数组索引是从零开始的,所以您需要从一个月数(1-12)中减去一个来得到数组索引(0-11)。我还建议你发表评论,指出2016年是闰年,这就是为什么2月有29天
  • 设置为2016的私有const整数(可能命名为YearOfInterest)。它表示数字2016的符号别名。consts让你的代码更容易维护(考虑一下,如果你的老师改变主意说"不,2014年就这么做",而你在2016年的代码中使用了6到8个位置——如果你有一个const,你只需要更改其中一个位置。
  • 这表明2016年1月1日是星期五。我将使用System.DayOfWeek枚举(是的,它是库代码,但是您所做的任何操作都将是一个字符一个字符地重复)。如果你愿意的话,你可以用你的YearOfInterestconst来做一些类似的事情(如果你的老师改变了对这一年的看法,这会使事情发生变化)。

这将是一个非常简单的声明,如下所示:好的。

1
 private readonly static DayOfWeek Jan1DayOfWeek = new DateTime (YearOfInterest, 1, 1).DayOfWeek;

我觉得这比说:好的。

1
private readonly static DayOfWeek Jan1DayOfWeek = DayOfWeek.Friday;

请记住,枚举开始编号(默认情况下)为零,所以星期日为0,星期六为6。好的。

完成后,我将创建两个函数:好的。

  • 计算某一日期的Day Number(即到该日期为止的一年中经过的天数(1月1日为1,2月3日为34)。
  • 调用Day Number函数(即获取整数)并返回一周中的某一天的函数。

第一个函数(我们称之为GetDayNumberFromDate)取两个整数日期(月和日)做三件事:好的。

  • 它检查你的先决条件。因为这两个数字都是用户输入的,用户也会出错,所以您需要确保两个数字都大于或等于1,月数等于或小于12,日数等于或小于月内的天数(您很容易知道,它在您的数组中)(记住当索引到数组中)。如果不满足这些前提条件中的任何一个,则抛出异常。这很重要。如果有人输入了错误的数字,您很可能会从数组中索引(这将引发异常,但没有关于发生异常的原因的信息)。检查前提条件总是很好的
  • 它循环您的"月份中的天数"数组,从数组的开始到输入月份的前一个月(减去1),求和天数。如果有人进入4月4日,您需要添加1月(31)、2月(29)和3月(31)的天数。这将使你到达月初(好吧,上个月的最后一天)。
  • 它会在输入的月份的第几天进行添加—这将使您进入当前日期。

既然你想要在for循环上进行指导,那么这个循环最终会变成这样:好的。

1
2
3
4
int dayCount = 0;
for (int i = 0; i < month - 1; ++i) {
    dayCount += daysPerMonth[i];
}

在循环结束时,dayCount将告诉您从年初到输入日期前一个月结束的天数。当你加上输入的月份的第几天,你就得到了day number。好的。

最后,最后一个函数(称为GetDayOfWeekFromDate)也接受这两个整数(月数和月数的第几天)。它调用GetDayNumberFromDate来获取输入日期的日数,减去1(因为从0开始的计算要比从1开始的计算容易得多),并加上我们在开始时声明的Jan1DayOfWeek的整数等价物((int) Jan1DayOfWeek)。好的。

有了这个数字,我将进行"模数7"运算(thatNumber % 7)。模是计算整数除法余数的结果。这会给你一个0到6之间的数字。如果你把这个数字加回到一个DayOfWeek中,你就可以在星期天和星期六之间度过一周中的某一天。打电话给ToString,你就有了结果。好的。

如果结果是关闭的,不要感到惊讶(特别是如果结果是关闭了一个)。第一次把这件事做好是很困难的。调试就是从这里开始的。好的。

顺便说一下,我不会试图解析像"1月1日"这样的日期。你真的需要注意缩写(一月,二月,…),你会让那些不会拼写"二月"的人感到不安。只使用月和日整数。好的。

在所有这一切中,您会注意到一件事,那就是没有嵌套循环。不需要这样做,嵌套循环可能很昂贵。此代码循环已通过的月数(即少于12次)。如果你要循环数月和数天,你将循环12次30次(即大约366次)。12是较小的数字好的。完整代码如下:

我不知道你学到了什么,所以这基本上是在2005年左右。好的。

首先是一个带有一些基本"常量"的静态类:好的。

1
2
3
4
5
6
7
8
static class FigureDate {
    private static readonly int[] _leapYearDaysPerMonth = new int[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    private static readonly int[] _normalYearDaysPerMonth = new int[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

    private const int YearZero = 1900;
    //Sunday is zero in DayOfWeek
    private readonly static DayOfWeek _day0 = new DateTime(YearZero, 1, 1).DayOfWeek;
}

(顺便说一下,dayofweek是在系统名称空间中定义的enum,所以您可以免费获得它。星期日是0)好的。

我会在这个类中添加更多的东西——所有的东西(比如下面的所有函数)都在里面。好的。

首先,是一个特定的年份,一个闰年(要想知道这并不容易):好的。

1
2
3
4
5
6
7
8
9
10
public static bool IsLeapYear (int year) {
    if (year % 400 == 0) {
        return true;
    }
    if (year % 100 == 0) {
        return false;
    }
    //otherwise
    return (year % 4) == 0;
}

然后我想,从"第0天"开始,闰年是如何过去的(幸运的是,1900年不是闰年,因此减少了一次错误的可能性):好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
public static int NumLeapYearsToStartOfYear(int year) {
    if (year < YearZero) {
        throw new ArgumentException($"Only Years from {YearZero} onwards can be evaluated");
    }
    //do it brute force
    int count = 0;
    for (var y = YearZero; y <= year; ++y) {
        if (IsLeapYear(y)) {
            ++count;
        }
    }
    return count;
}

有鉴于此,我可以计算出从"第0天"到利息年开始的天数:好的。

1
2
3
 public static int DaysToStartOfYear(int year) {
     return (year - YearZero) * 365 + NumLeapYearsToStartOfYear(year);
 }

现在,切换档位,找出有趣的一天是什么"日数"(记住,2月3日是第34天)。好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static int GetDayNumberFromDate (int year, int month, int day) {
    int[] daysPerMonth = IsLeapYear(year) ? _leapYearDaysPerMonth : _normalYearDaysPerMonth;
    if (month < 1 || month > 12) {
        throw new ArgumentException($"Month number ({month}) is invalid");
    }
    if (day < 1 || day > daysPerMonth[month - 1]) {
        throw new ArgumentException($"Day/Month combination invalid for month:{month}, day:{day}");
    }
    int dayCount = 0;
    for (int i = 0; i < month - 1; ++i) {
        dayCount += daysPerMonth[i];
    }
    return dayCount + day;
}

最后把它们放在一起。正如您可能想象的那样,这段代码中有一个逐项的问题。我使用日期的"日数",它在1…n范围内,我们真的希望它是0…n-1。所以,我减去1。好的。

1
2
3
4
public static DayOfWeek GetDayOfWeekFromDate (int year, int month, int day) {
    int totalDays = DaysToStartOfYear(year) + GetDayNumberFromDate(year, month, day) - 1 + (int)_day0;
    return (DayOfWeek)(totalDays % 7);
}

如果调用该函数,然后对结果调用ToString,那么它应该可以工作。我只做了很轻微的测试。好的。

请不要将此作为您的工作提交。把它拆开,看看你能不能做得更好。如果您知道如何编写单元测试,请为每个函数编写一个测试(我通常会这样做,但不是今晚)。我向你展示了我展示给你的函数,按照我展示它们的顺序。为什么我要在我做的地方而不是在任何地方检查论点?我为什么要用readonly来代替YearZero,用const来代替YearZero?有些代码非常暴力-看看你是否能做得更好(一旦你有足够好的代码,获得更好的代码就容易多了)。好的。

最后,您应该能够解释每一行代码。好的。好啊。


您可以采用的一种方法是计算从年初到指定日期的总天数,将该天数除以7(一周中的总天数)后的余数,然后添加一年中第一天的数值。

首先,我们将在输入的月份之前添加每个月的总天数。要做到这一点,我们首先需要找出他们输入的月份,然后将其转换为一个整数,我们可以将其用作daysInEachMonth数组的索引。

我们首先可以通过使用Enum.Parse方法(在我们的例子中采用Month的类型)和字符串值(如"January")来获取它们输入的字符串的枚举,并返回enum Month值。然后我们可以将该值转换为int。默认情况下,枚举的起始值为0,递增值为1,所以Januray0December11

1
int monthNumber = (int) Enum.Parse(typeof(Month), month, true);

使用此值,我们可以循环访问此月之前的每个月,并添加截至此月的总天数:

1
2
3
4
for (int i = 0; i < monthNumber; i++)
{
    daysFromStartOfYear += daysInEachMonth[i];
}

接下来,我们将加上他们输入的月份的第几天并减去1(我们需要减去一个,因为我们处理的是数组索引和Enum值,这两个值都从0开始):

1
daysFromStartOfYear += dayDate - 1;

现在,我们需要通过增加一年中第一天的数值(其中Sunday0Saturday6来调整我们的数字。如果这不合理,只要记住,如果他们输入January 1,那么我们的daysFromStartOfYear值将是0,但我们知道一年的第一天是Friday,它的int值为5。所以我们知道我们需要在结果中加上5,以说明第一天不是从0开始(星期日)。

1
2
Day firstDayOfYear = Day.Friday;
daysFromStartOfYear += (int) firstDayOfYear;

现在剩下要做的就是将这个数字除以7(一年中的天数),得到余数,然后将这个数字转换为Day枚举中的值。如果这不合理,记住一个月内每个Tuesday的日期是7的倍数加上该月第一个Tuesday的值。通过将值除以7后的余数,我们得到一个介于06之间的数字,它将映射到我们的Day枚举。

为了得到一个数除以另一个数的余数,我们可以使用模运算符%

1
int remainder = daysFromStartOfYear % numDaysInWeek;

当我们得到这个数字时,我们可以将它强制转换为枚举以获取值:

1
Day answer = (Day) remainder;

希望这足以让你开始。尝试一下,如果你在某些特定的事情上遇到困难,或者在下面的评论中随意提问,就回来。


现在我了解到你的任务完全集中在2016年作为一年,你的简报要求你使用嵌套循环,以及你提供的代码,我建议你这样做:

1)定义targetmonth和targetmate(例如9月、3日)2)确保星期五开始3)使用此伪代码:

1
2
3
4
5
6
7
foreach mMonth in Months
    for dayOfM = 1 to daysinMonth[month] (inclusive)
        if month == targetMonth AND dayOfM == targetDate
            print dayOfWeek
            return
        else
            dayOfWeek = (dayOfWeek + 1) % 7

有用功能:

  • Enum.GetValues(typeof(Months))将以数组形式返回Months中的所有值。
  • .OfType()(包括using System.Linq命名空间)-您可以使用它将Enum.GetValues()返回的Array转换为强类型Months对象。
  • 您可以使用(int)dayofWeekdayOfWeek转换为整数,然后使用(Days)5返回(或您拥有的任何整数值)。
  • 模量%将返回计算的其余部分。1%7=1,2%7=2,…,7%7==0-这将允许您循环一周中的几天。

这是我在C语言中提出的解决方案,并重新使用了伪代码,以便不向您提供一个可复制/可复制的答案。希望有帮助。


如果您只想显示一周中的某一天,可以这样做:

1
2
DateTime date = new DateTime(2016, 1, 16);
Console.WriteLine(dateValue.ToString("dddd"));  // outputs Friday