Perpetual Calendar?

Short version:

How can this be achieved in Codea? My 20 year old Pocket Secretary with nearly no memory or processing power churns this out like no tomorrow (pun intended) :slight_smile: .

Longer version:

I started a little project to keep track of employee’s work schedule for this year and upcoming years. There are three crews that work 24 hours and off 48 hours with a calculatable time off every month. I do this now in a PC program called Calendar Creator and just print out the year in advance. Instead of print, I thought it might be cool to have the schedule on my iPad and add a few features (payday, holidays, etc.) like in Calendar Creator. This part of the project should be relatively easy. The standard Perpetual Calendar part is what is giving me a hard time. I was hoping to do this project without any help but alas I am stumped.

I’m not looking for code but perhaps a point in the right direction of how to fill in the dates of the calendar automatically as the month/year is advanced via touch buttons. I have completed this year manually and thought why not use the standard formula that has been around for many years to do this for me. I found a few Lua and Java scripts that failed to work after being massaged for Codea. I even tried only snippets of the code but to no avail.

There doesn’t seem to be very much info about os.date in the Codea or Lua manuals that I could see but a web search turned up a bit more.

I found the following quite useful info on the PtokaX Wiki at: http://wiki.ptokax.ch/doku.php/scriptinghelp/osdate

os.date ([format [, time]])

Returns a string or a table containing date and time, formatted according to the given string format.

If the time argument is present, this is the time to be formatted (see the os.time function for a description of this value). Otherwise, date formats the current time.

If format starts with ‘!’, then the date is formatted in Coordinated Universal Time. After this optional character, if format is the string “*t”, then date returns a table with the following fields: year (four digits), month (1–12), day (1–31), hour (0–23), min (0–59), sec (0–61), wday (weekday, Sunday is 1), yday (day of the year), and isdst (daylight saving flag, a boolean). Example for the previous sentence: os.date(“*t”).year = 2006

If format is not “*t”, then date returns the date as a string, formatted according to the same rules as the C function strftime. The acceptable formats are as the following:

”%a” The abbreviated weekday name. Example: Thu.
”%A” The full weekday name. Example: Thursday.
”%b” The abbreviated month name. Example: Sep.
”%B” The full month name. Example: September.
”%d” The two-digit day of the month padded with leading zeroes if applicable. Example: 09.
”%e” The day of the month space padded if applicable. Example: 9.
”%H” The two-digit military time hour padded with a zero if applicable. Example: 16.
”%I” The two-digit hour on a 12-hour clock padded with a zero if applicable. Example: 04.
”%j” The three-digit day of the year padded with leading zeroes if applicable. Example: 040.
”%k” The two-digit military time hour padded with a space if applicable. Example: 9.
”%l” The hour on a 12-hour clock padded with a space if applicable. Example: 4.
”%m” The two-digit month padded with a leading zero if applicable. Example: 09.
”%M” The two-digits minute padded with a leading zero if applicable. Example: 02.
”%p” Either AM or PM. Language dependent.
”%S” The two-digit second padded with a zero if applicable. Example: 04.
”%w” The numeric day of the week ranging from 0 to 6 where 0 is Sunday. Example: 0.
”%x” The language-aware standard date representation. For most languages, this is just the same as %B %d, %Y. Example: September 06, 2002.
”%X” The language-aware time representation. For most languages, this is just the same as %I:%M %p. Example: 04:31 PM.
”%y” The two-digit year padded with a leading zero if applicable. Example: 01.
”%Y” The four-digit year. Example: 2001.

When called without arguments, date returns a reasonable date and time representation that depends on the host system and on the current locale (that is, os.date() is equivalent to os.date(”%c”)).

I only tried 4 or 5 of these and they all worked. Perhaps this could be added to the manual at some point.

Problem is, at least to my newbie eyes, this is current time and I can’t find a way to move forward or backwards in time.

Thanks for any help that may be provided.

@Keebo

I’m not sure if this is what you’re after. In this example, I set t equal to the beginning of 2012. To move forward or backward, add or subtract 86400 seconds (1day) to t.


function setup()
    -- set time t to the beginning of 2012
    t=os.time{year=2012,month=1,day=1}
    
    -- set date d to t
    d=os.date("*t",t)
    
    -- print year, month, day 
    print(string.format("Year %d   Month %d   Day %d",d.year,d.month,d.day))
    
    -- add 1 day (86400 seconds) to t
    t = t + 86400
    
    -- set new date
    d=os.date("*t",t) 
    
    -- print year, month, day
    print(string.format("Year %d   Month %d   Day %d",d.year,d.month,d.day))     

end 

@dave1707, that looks really usable. I think I can glean info you provided into my code though I didn’t really want to sound desperate.

Thanks a lot for the pointer.

@dave1707 That is actually not a good way of adding a day.
Calendar and dates are REALLY hard to get right because they are not only mathematical, but also political and change a lot.

For example, a day is not always 84600 seconds long. There are occasionally leap seconds which add an extra second to some days. Also when moving from/to daylight savings times there is an extra hour or an hour lost. It averages out, but the actual day length does change.

tl;dr You shouldn’t just use ad hoc date/time solutions or it almost certainly will have bugs.

That said I can’t help with your actual problem @Keebo!

How about one of these (http://c2.com/cgi/wiki?PerpetualCalendarAlgorithm) solutions?

Thanks @Reefwing, although I didn’t come across this particular page, many of those algorithms popped up a lot on my searches. I tried to convert several of them but was unsuccessful. I have gained some knowledge from looking through them though.

@Dylan, I agree. There is a lot more going on than simple mathematics. At least there are a few algorithms tackling the leap issues.

My main problem was trying to figure out how to utilize os.time and os.date but I am getting along a little better now after seeing @dave1707’s example.

Thanks all.

@Dylan
Speaking of leap seconds, I have a few shortwave radios, and whenever a leap second is added I’ll tune to one of the WWV time signal stations. At 23:59 GMT, you can actually count the seconds (beeps) and hear the 61 seconds in that last minute. As for adding the 86400 seconds, I have to disagree with you. After the seconds are added, you will have to check the isdst flag (is daylight savings time). Depending on how the flag is set at the start and end of the calculation will determine if you need to add or subtract 3600 seconds (1 hour) or not do anything. Since a day is not exactly 86400 seconds, that is taken care of by the leap years and accounted for in the date function. As for leap seconds, those are not determined in advance and can’t be factored in so the end result may be a second or more off depending on how far into the future you go. But for the most part, adding 86400 per day and using the isdst flag should be good enough.

Update: I ran into a problem with os.date() when trying to add 10,000 days. I came across this on Wikipedia.

“The year 2038 problem may cause some computer software to fail at some point near the year 2038. The problem affects all software and systems that both store system time as a signed 32-bit integer, and interpret this number as the number of seconds since 00:00:00 UTC on Thursday, 1 January 1970. The furthest time that can be represented this way is 03:14:07 UTC on Tuesday, 19 January 2038”.

So as long as you don’t add more than 9311 days (25y, 5m, 27d) from today 7/23/2012, you should be OK.

Thanks for that info @dave1707, I can see where that could come in handy.

Luckily, I am progressing along quite nicely without having to add seconds. With your code example(mostly the formatting of os.time and os.date), further research, and lots of trial and error, I can simply add or subtract a month and determine where to insert the 1st date of that month compared to which day of the month it starts on.

I have the short month(February) working and I am working on the 30 day and 31 day months to soon be followed by the cycling leap year.

I have a feeling that this leg work could be accomplished much easier by using standard calendar functions within the Apple runtime API’s but since I am not a licensed Apple developer(no Macintosh here) I can only guess.

At the very least, I have nearly obtained my goal and should have a working project(though not an app) and have learned bunches in the process.

I will be glad to post my code when finished if anyone is interested. I am certain that the code could and should be streamlined using more tables and such but I have yet to gain those skills. Soon hopefully.

Thanks again for everyone’s help.

You might also want to look into using Julian Days. This is a system for representing dates as single numbers that covers a span of many centuries and is used by astronomers and historians. A short intro is here: http://tycho.usno.navy.mil/mjd.html but this is just one of many. There are algorithms for handling Julian Days by Meeus, Danby and others, both in print and online. For Codea it is probably best to use “modified Julian days” because these use smaller values, and Codea does not use double reals.