Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating calendar with array and loops using C#

Tags:

arrays

c#

I'm trying to create an application that tells you the day of the week (Monday, Tuesday, etc;) when a date from 2016 is entered (January 1, September 3rd, etc;) Input is the date, output is the day of the week for that date. It has to be created with loops such as a for loop and if statements. I cannot use library functions. Starting date is January 1st, 2016, a Friday. This is what I have so far, i'm pretty lost:

    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 )
            {

My brief is:

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.

like image 866
Jackie Avatar asked Jun 27 '26 01:06

Jackie


2 Answers

Since you don't want code, I'll give you some explanation and just a little code. Then, once that's all done, there's full code at the bottom of this that will give you all you need for an arbitrary date.

However you are interested only in the year 2016. As a result, if you do want todo is calculate the day of the week for a specific year, all you need (from the full code) is the class declaration, and two functions:

  • GetDayNumberFromDate
  • GetDayOfWeekFromDate

Explanation and Just a Little Code

I would do all this in a static class. You don't need an instance of anything, just a few functions/methods.

In that class I would declare and intialize three private variables:

  • An array of 12 integers, each of represents the number of days in each month. Remember that array indexing starts at zero, so you need to subtract one form a month number (1-12) to get the array index (0-11). I also suggest you put a comment noting that 2016 was a leap year and that's why February has 29 days
  • A private const integer (perhaps named YearOfInterest) that is set to 2016. It represents a symbolic alias for the number 2016. Consts make your code easier to maintain (consider if you teacher changes his mind and says "no, do it for 2014" and you used 2016 in six or eight places in your code - if you had a const, you'd only need to change one of those places.
  • Something that represents the fact that January 1, 2016 is a Friday. I would use the System.DayOfWeek enum (yeah, it's library code, but anything you do will be pretty much a character-by-character repeat of that). If you wanted to, you could use your YearOfInterest const int and do something like (which will allow things to work if your teacher changes his mind about the year)

That would be a very simple declaration like the following:

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

I think it's a lot cleaner than just saying:

private readonly static DayOfWeek Jan1DayOfWeek = DayOfWeek.Friday;

Remember that enums start numbering (by default) at zero, so Sunday is 0 and Saturday is 6.

With that done, I'd create two functions:

  • One that calculates the Day Number for a date (i.e., the number of days that have passed in the year until that date (the day number for Jan 1 is 1, and for Feb. 3 is 34).
  • One that calls your Day Number function (i.e., getting an integer) and returns a day of the week.

The first function (let's call it GetDayNumberFromDate) takes a two integer date (month and day) does three things:

  • It checks your pre-conditions. Since both those number are user-entered, and users make mistakes you need to make sure than both numbers are equal to or greater than 1, that the month number is equal to or less than 12, an that the day number is equal to or less than the number of days in the month (which you conveniently know - it's in your array (remember to subtract one when you index into that array). If any of these preconditions are not met, throw an exception. This is important. If someone enters a wrong number, you will likely index out of your array (which will throw an exception, but without the information about why it happened). Checking pre-conditions is always good
  • It loops over your "days in a month" array, from the beginning of the array until the month before the month entered (minus one) summing the number of days. If someone enters April 4th, you want to add the number of days in January (31), February (29) and March (31). That will get you to the beginning of the month (well, the last day of the previous month).
  • It adds in the day of the month entered - that gets you to the current day.

Since you wanted coaching on the for loop, that loop would end up looking like this:

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

At the end of the loop, dayCount would tell you the number of days from the start of the year until the end of the month before the entered date. When you add in the day of the month that was entered, presto, you get the day number.

Finally, the last function (call it GetDayOfWeekFromDate) also takes those same two integers (month number and day of month number). It calls GetDayNumberFromDate to get the day number for the entered date, subtracts one (since calculations starting from 0 are much easier than from 1) and adds in the integer equivalent of that Jan1DayOfWeek we declared at the beginning ((int) Jan1DayOfWeek).

With that in hand, I'd take that number and do a "modulus 7" operation (thatNumber % 7). A modulus is the result of calculating the remainder of an integer division. That will get you a number between 0 and 6. If you cast that number back into a DayOfWeek it will get you a day of the week between Sunday and Saturday. Call ToString on that result, and you have your result.

Do not be surprised if the result is off (particularly if it's off by one). Getting this right the first time is tricky. That's where debugging comes in.

By the way I would not try to parse a date like "January 1". You'd really need to take care of abbreviations (Jan, Feb, ...), and you'd upset folks who can't spell "February". Just use month and day integers.

One thing you will notice in all this is that there are no nested loops. There's no need for that, and nested loops can be expensive. This code loops over the number of months that have passed (i.e., less than 12 times). If you were to loop over the months and the days in the month, you would be loop 12 times 30-ish times (i.e., about 366 times). 12 is a smaller number

The full code follows:

I don't know what you have learned, so this is mostly in pretty basic circa 2005 C#.

First a static class with a few basic "constants":

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

(By the way, DayOfWeek is a enum defined in the System namespace, so you get it for free. Sunday is 0)

I'll be adding more stuff to that class - everything (i.e., all the functions below) goes in there.

First, is a particular year a leap year (which is never trivial to figure out):

public static bool IsLeapYear (int year) {
    if (year % 400 == 0) {
        return true;
    }
    if (year % 100 == 0) {
        return false;
    }
    //otherwise
    return (year % 4) == 0;
}

Then I figure how may leap years have passed since "day 0" (luckily, 1900 is not a leap year, so the likelihood of an off-by-one error is reduced):

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

Armed with that, I can figure out how many days from "day 0" till the start of the year of interest:

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

Now, switching gears, figure out what "day number" the interesting day is (remember, Feb 3 is Day number 34).

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

and finally put it all together. As you might imagine, there's an off by one issue in this code. I use the "Day Number" of the date, and it is in the 1...N range, and we really want it to be 0...N-1. So, I subtract 1.

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

if you call that function and then call ToString on the result, it should work. I've only very lightly tested it.

Please don't just submit this as your work. Take it apart, see if you can do it better. If you know how to write unit tests, write a test for each function (which I would normally do, but not tonight). Figure out shy I showed you the functions I showed you in the order I showed you them. Why do I check arguments where I do and not everywhere? Why do I use readonly where I use it and const for YearZero? Some of the code is very brute force - see if you can do better (it's a lot easier to get better code once you have good enough code).

At the end, you should be able to explain every line of code.

like image 156
Flydog57 Avatar answered Jun 29 '26 15:06

Flydog57


Now that I understand your assignment is entirely focused on 2016 as a year, that your brief requires you to use nested loops, and the code you've provided, I recommend this:

1) Define targetMonth and targetDate (e.g. September, 3) 2) Ensure daysOfWeek starts as Friday 3) Use this pseudocode:

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

Useful functions:

  • Enum.GetValues(typeof(Months)) will return all of the values from Months as an array.
  • .OfType<Months>() (include using System.Linq namespace) - you can use this to convert the Array returned by Enum.GetValues() into strongly typed Months objects.
  • You can cast dayOfWeek to an integer using (int)dayofWeek and back by using (Days)5 (or whatever integer value you have).
  • Modulus % will return the remainder of your calculation. 1 % 7 = 1, 2 % 7 = 2, ..., 7 % 7 == 0 - this will allow you to loop through the days of the week.

This was the solution I came up with in C# and worked back to pseudocode so as not to provide you with a copy/pastable answer. I hope it helps.

like image 30
DiplomacyNotWar Avatar answered Jun 29 '26 14:06

DiplomacyNotWar