I started learning C in the late 1980s on a Tandy/Radio Shack (TRS-80) Color Computer 3. At the time, my college was teaching Pascal and BASIC, so I was learning everything on my own with the help of a friend of mine.
My friend Mark was a great Commodore 64 assembly programmer and he had began programming in C on the Amiga. He helped me with my first real C program which was a user-designed text adventure system for a BBS.
Special thanks goes to another friend of mine, Tim, who was the inspiration of this project. Tim (aka COCO KID) had ran the ADVENTURE SYSTEM BBS in Lufkin, Texas on a CoCo 1. It was set up like a text adventure. If you did “GO WEST” and there was nothing there, it would prompt you to create a room and enter information about it. You could wander around and check mail by doing “LOOK MAILBOX” and stuff. It was great fun, and I decided I wanted something like this for my OS-9 BBS I was running at the time. (This was probably the OS-9 Level 2 BBS by Keith Alphonso of Alpha Software in Louisana. I type things like this in so they show up in Google in case anyone else ever remembers the OS9L2BBS package.)
For learning C, I used the manual from the Microware OS-9 C Compiler and a Pocket C Reference Guide (not sure of the exact name of it, but I still have it somewhere). This was original K&C C — I wouldn’t see an ANSI-C compiler until I started working for Microware years later and began using their Ultra-C compiler. It was quite an experience starting out as a self-taught hobbyist using a company’s products, and eventually actually working for that company. My entire technical career is all based on me sticking with that 8/16-bit CoCo computer and learning OS-9 and C. I look back and imagine what would have happened if I would have just given in and became a DOS or Windows 3 user. Would I have even kept coding?
The reason I share this story is to point out that I started using C a very long time ago — before ANSI C (C89) or C++. I was away from C for some years, and when I started working with it again many things had changed — like the C99 standard finally offering a way to get an integer of a specific size. (“int” is 16-bits on one of the systems I program on at work, and 32-bits on another. Things like this always made writing architecturally portable code challenging.)
Recently, I began working on a new project — a barcode ticket system for a Halloween haunted house project. Since I know C the best, I have been writing it in C and within a few evenings had a working prototype that handled almost all the desired features.
Lest night, I was trying to work around a problem where the system would start rejecting tickets after midnight when the day changed. Normally, tickets sold are fully used up by the guest, but if they had to leave early or just ran out of time to visit all the attractions, the park lets them return on a Thursday or Sunday to use up the rest of their ticket.
My system was now validating the ticket against the current day, with a ticket being valid on the day it was sold, and then also on Thursdays or Sundays. While I was thinking about this, I realized that if a ticket was sold on Friday, and the park stayed open late that night, at midnight the day changed to Saturday and now the ticket would not be valid (since it would only be good on the day it was activated, or a Thursday or Sunday). Fortunately, I thought about this before writing any code. I am pretty sure there would be a lynching if my system suddenly started refusing admission to a park full of guests on a busy Friday night in October!
I proposed several simple solutions to this. One was simply having the system require being activated each day of operation, and recording that state. Tickets sold that day would be active until the system was shut down, or until the system was started up on a different day. This was very simple, but would mean yet another bit of information to keep in sync on about 12 different computer systems running throughout the park. The more parts, the more chance of failure. And failure was not an option (see “lynching” above).
Instead, I decided to simply allow the tickets to have a grace period before they truly expire. If this was set to two hours, the ticket would be valid within two hours of the end of the activation day. Thus, a ticket sold on Friday would be valid until 2am on Saturday morning. As long as the grace period were long enough to cover the latest hour of operation, but short enough that it didn’t overlap with opening time the next day, this would solve the problem. (The overlap issue was also something I thought about before coding. If a ticket were sold at midnight, and the grace period were 12 hours, that means it would still be honored by the system until noon the next day. If the park opened at 11am the next day, it would be allowing tickets for that first hour that really shouldn’t be allowed at all on that day.)
Normally we wouldn’t worry about situations like this, especially in a (mostly) controlled environment running 2-5 days a week from 7pm to midnight (or 1am-ish). But, if this system were used elsewhere, say, for Go Karts and Mini Golf and other activities, there could indeed be times when “Summer Fun Late NIght” tickets might overlap with “Early Bird Special” tickets the next day. I wanted to make sure my system was as flexible as possible.
But I digress…
The purpose of this article (and the next one) is to discuss something in C I had never done: subtracting time.
If a ticket was activated (sold) on a Friday, then it gets scanned when the system thinks it is Saturday (minutes after midnight on Friday). it would be rejected. I wanted to make it check the grace period by taking “current time minus 2 hours” (or whatever it is configured to) and see if that time would have worked. The logic would look like this:
if ticket is not valid for current day,
check to see if ticket is valid for current day – offset hours.
But how do you do that? In C, the standard time.h functions let you represent time as either a time_t value, or a struct tm structure. The tm structure contains entries for hour, minute, seconds, year, month, day, etc. The time_t value is … something. Most systems treat it like the number of seconds since some date (like January 1, 1970). If this is the case, then subtracting six hours is really easy:
timeNow = time(NULL); // Get current time.
timeEarlier = timeNow – (2*60*60); // Subtract 2 hours of seconds.
Problem solved. Moving on…
But wait… That assumption, which might be the case on a Unix-style system, might not be the case everywhere. The C standard says that time_t is an “arithmetic type capable of representing times” but it doesn’t specify what that value actually is. It could be in seconds, microseconds, or metric seconds. It also does not state what date that time would be based off of.
Any C program that makes the assumption that time_t is “seconds since 1970” is not guaranteed to be portable to other C systems. Period.
Read that again and let it soak in.
I see C programs do math with time_t values all the time. If you are only writing for Windows, and Microsoft never changes this, you are fine. Ditto for folks who only write for Unix or Linux. But does QNX work this way? OS-9? OSE? PSOS? I really don’t know, and I don’t want to be lazy and write non-portable code when there is a “right way” to do this.
Unfortunately, I did not know the right way to do this. Even though I started programming in C in the late 1980s, and even though I had used C time functions before, I had never tried to subtract time from time.
Some Bing searches (Microsoft gives me Bing Rewards points which pay for my Hulu Plus subscription so I don’t have to – ask me if you want a referral link) led me to a number of discussions of people asking this very question, and many very bad answers to how to do it (like doing math on time_t values).
In the next part of this article, I will share with you some things about C’s time functions that I was not aware of, as well as provide example code on how it works.