# Drawing Calendars

## Recommended Posts

What's the cleanest way to draw a calendar for a month? I'm giving operators a way to schedule operations several month in advance as well as to review and edit previously entered schedules.

Here's what I'm thinking so far:

On startup define an array with the number of days in each month this year, correcting for leap year if appropriate. Also populate an array with an integer defining the day of the week on which each month starts.

Then, to draw the calendar, start with a static JPEG of a 7x5 grid. The date numbers are entries in a 35-element string array, initially set to 35 empty strings {"", "", ""...."","",""}. Then copy a subset of the appropriate length (days in the month) of the array {"1", "2", "3"...."28", "29", "30", "31"} to the appropriate starting point in the month's date string array. Since January 2013 begins on a Tuesday, its array would be {"", "", "1", "2", "3"...."29", "30", "31", "", ""}

Alternately, you could precalculate the 35 element arrays for all 12 months of the current year at startup, since they're not going to change, in which case you'd put them in a 2D array, so that you could access the appropriate string array using the number of the month.

Any suggestions?

##### Share on other sites

Yes: use the formatDateTime() function (along probably with the strToTime() function) to leverage the built in windows functions that handle all that. You can use strToTime() for example, to get the time of the first day of any month you want, then use formatDateTime() to find out what day of the week it is. You can then just count of the days and cycle through the day of the week. Leap year is easy to calculate, just do % 4 on the year and if its 0 then its a leap year. Since I know you are an experienced programmer, I'll leave the rest of the algorithm to you, but feel free to post questions (and your solution to share!)

##### Share on other sites

Not sure if this follows what you were suggesting or not, but it seems to do what I'm looking for.

On startup I execute the following initialization:

`global string Dates[32] = ""global string DateDisplayglobal MonthDisplayedglobal ThisMonth = StrToDouble(FormatDateTime("%m", Systime()))for(private idx = 0, idx < 32, idx++)   Dates[idx] = DoubleToStr(idx)   endforglobal DOM = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}if(StrToDouble(FormatDateTime("%y", Systime())) % 4 == 0)   DOM[2] = 29   endifglobal string DateArrayfor(private idx = 1, idx < 13, idx++)   global DOWFirst = StrToDouble(FormatDateTime("%w", StrToTime(DoubleToStr(idx) + "-1", "md")))  //  Calcalate the DOW of the first of the idx-th month this year   DateArray[idx] = Transpose(Fill("", 37), 1)  //   Worst case calendar display:  the first of a 31-day month is on a Saturday, so the 31st is on Tuesday, hence 37 active squares on the calendar   DateArray[idx][DOWFirst] = Transpose(Dates[1,DOM[idx]], 1)   // 13-element array, with elements [1] through [12] containing the text dates for the month, offset by the DOW   endfor[/CODE]`

Then by adjusting the value of the variable MonthDisplayed, the calendar is drawn automatically. I still need to display the name of the month at the top, have nav arrows to move forward and backward a month at a time, and create click zones that will display the schedule for a given day, etc., but just for the calendar display per se, it works and looks like this:

The fifth and sixth rows have to be dynamic but are simple -- just set visible equal to DateDisplay[MonthDisplayed][28] != "" and DateDisplay[MonthDisplayed][35] != ""

Is this essentially what you were suggesting or did you have an additional layer of subtlety for me?

##### Share on other sites

Sure, though I wouldn't precalc it. I'd just calc the month as I needed it. The script should be real fast, and that gives me the flexibility to go to other years.

Also, I'd probably use a 7x5 array instead of a linear array. Something like this:

`function calcDays(month, year)   global cal   cal[5][7] = -1   cal--  // cal is now full of -1   global dim = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}   dim[2] += (year % 4 == 0)   private dow = formatDateTime("%w", evaluate(year + "y" + month + "m1d"))   private dayCount = 0   for (private w = 0, w < 5, w++)      for (private d = dow, d < 7, d++)         dayCount++         cal[w][d] = dayCount         if (dayCount >= dim[month])            return         endif      endfor      dow = 0   endfor[/CODE]`
##### Share on other sites

Hmmm, weird -- my calendar screenshot didn't display. No big deal, it looked exactly like every other calendar of January 2013!

I like that dim[2] += (year % 4 == 0)! Very cool.

I'm sorta thinking along the same lines you are. OTOH, since 99% of the calls will be to the current year (farming operation), it makes sense to precalc them. However, I was also thinking about other apps where I might use this or the rare case where they want to schedule into the following year, I'm thinking once I'm happy with how it works I'll pack it up in a callable function as you suggest, but still call the function on startup to precalc the current year only, while still giving them the option to scroll to other years.

Thanks!!

##### Share on other sites

Oh, and worst case, believe it or not, you actually need a 7x6 array (1st on Friday or Saturday, 31st on Monday or Tuesday).

##### Share on other sites

Yeah, didn't think very hard on that...

##### Share on other sites

It took me by surprise too. I started out with the same idea: that 7x5 was the max case.

The weird things you learn when you delve into stuff you've never done!

##### Share on other sites

Try timing the function for just one month. I just tried it and it takes 0.01 seconds (i.e. 10 milliseconds). There is no advantage to precalc-ing.

Also, there is a typo. The evaluate() needs "" + year. The "" triggers the auto-conversion from number to string for year.

##### Share on other sites

Hmmm. OK, that makes sense, and is cleaner as you don't have to check if you're leaving the range of precalced values. I'll do it like that.

Thanks!

##### Share on other sites

Yeah, if you have variable value components showing the contents of the cal array, with the visibility parameter set to look if its -1, then all you have to do is call this function whenever you want the calender changed and the calender will update.