Yesterday

Difficulty: ★ ★ ☆ ☆

The time.h header defines a handful of functions useful for discovering and manipulating today’s date. Especially the localtime() function, which translates a time_t (Unix Epoch) value into date fields for output or manipulation. It’s a wonderful tool, but what does it tell you about yesterday?

Your task for this month’s Exercise is to write code to output yesterday’s date. This seems like a trivial undertaking, but it might be more complex than you can imagine: Obtaining yesterday’s date isn’t as simple as subtracting “one” from today’s day-of-the-month value. For example, if today’s the first, yesterday wasn’t the zeroth.

To get you started, I’m providing the following code skeleton. It only outputs today’s date, but hopefully it offers enough support to help you generate and output yesterday’s date as well.

2023_05_01-Lesson.c

#include <stdio.h>
#include <time.h>

int main()
{
    time_t now;
    struct tm *today;
    const char *day[] = { "Sunday", "Monday",
        "Tuesday", "Wednesday", "Thursday",
        "Friday", "Saturday" };
    const char *month[] = { "January", "February",
        "March", "April", "May", "June", "July",
        "August", "September", "October",
        "November", "December" };

    /* get today's date */
    time(&now);
    today = localtime(&now);
    printf("Today is %s, %d %s %d\n",
            day[today->tm_wday],
            today->tm_mday,
            month[today->tm_mon],
            today->tm_year+1900
          );

    /* output yesterday's date */

    return(0);
}

Here’s the code’s output:

Today is Monday, 1 May 2023

And here is the output from my solution:

Today is Monday, 1 May 2023
Yesterday was Sunday, 30 April 2023

I’m eager to see how you solve this challenge. My solution doesn’t cheat, but it’s close!

8 thoughts on “Yesterday

  1. Without your last two sentences, Iʼm not completely sure that Iʼd have thought about it myself… at least not initially.

    Now, while I canʼt be sure that your sneaky solution is identical to mine, Iʼd bet it involves the notion of Unix time as well as – disregarding leap seconds – the magic quantity 24*60*60.

    Well, weʼll see next Sunday☺

  2. I’d like to apologise in advance for the complete mess this code is bound to end up as! Here goes:

    #include<stdio.h>
    #include<stdlib.h>
    #include<time.h>

    int main(int argc, char* argv[])
    {
        // hard code 1 May 2023
        time_t t = 1682952002;

        struct tm* tm_local = localtime(&t);

        char datestring[64];

        // create and print nicely formatted string
        strftime(datestring, 64, “%A %d %B %Y”, tm_local);
        puts(datestring);

        // decrement day of month
        // so it’s now 0 (!)
        tm_local->tm_mday–;

        // create and print nicely formatted string (again)
        // this will print Monday 00 May 2023
        // which is obviously wrong
        strftime(datestring, 64, “%A %d %B %Y”, tm_local);
        puts(datestring);

        // overwrite t (the time_t variable, long int) 
        // with a new tm (struct) normalised by the mktime function
        t = mktime(tm_local);
        tm_local = localtime(&t);

        // create and print nicely formatted string (yet again)
        // this will print Monday 00 May 2023
        // which is now correct
        strftime(datestring, 64, “%A %d %B %Y”, tm_local);
        puts(datestring);

        return EXIT_SUCCESS;
    }
    The output is:

    Monday 01 May 2023
    Monday 00 May 2023
    Sunday 30 April 2023

    The magic mktime function also lets you mess around with the other members of a tm struct and even if you overshoot or undershoot the valid ranges it’ll sort out the mess and create a valid time_t.

  3. Sorry, I forgot to edit the last comment. Should be:

    // create and print nicely formatted string (yet again)
    // this will print Sunday 30 April 2023
    // which is now correct

  4. Well, I surmised that you were alluding to calculations on the Unix time. If this is cheating is in the eye of the beholder, I guess…

  5. Small addition to the above: while I tried a time()-based implementation first, I realized that such a solution would only work starting from January 1ˢᵗ, 1970 (00:00:00 UTC).

    Finding this a bit too limiting, I set out to implement my own Julian/Gregorian calendar logic, which—as it now stands—allows one to iterate over dates in the following range:

    0004-03-01 C.E., JDN 1722578 (Julian calendar), up to
    3440-12-31 C.E., JDN 2977859 (Gregorian calendar)

    Note that, while the Julian calendar took effect on 1 January 45 BCE, the roman pontifices initially added leap days every 3 (instead of 4) years. This “Leap year error” was later corrected by Augustus, but the upshot of all this is that the first (Julian) date whose day is known with certainty is 1 March 4 CE (a Saturday).

    On the other hand, the last date was chosen based on E. R. Hopeʼs Gregorian calendar drift function (as given in Journal of the Royal Astronomical Society of Canada, Vol. 58⁽¹⁾, 1964, p.4), which indicates that current leap year rules will add up to an (as of now unscheduled) intercalary day in the year 3441.

    Also, perhaps surprisingly, I decided against using Julian Day Numbers⁽²⁾ (JDN) in my implementation (maybe I shouldnʼt have); instead, I chose to implement my own Julian/Gregorian calendar logic.

    In any case, as usual I have uploaded project files for Codelite (Linux) as well as Visual Studio (Windows) on GitHub (should anyone be interested):
    https://tinyurl.com/56ke585d

    ⁽¹⁾ https://articles.adsabs.harvard.edu/full/1964JRASC..58….3H
    ⁽²⁾ https://en.wikipedia.org/wiki/Julian_day

  6. Well, I think you and I are quite a bit alike in this regard—I love to play with code until it does what itʼs supposed to do.

    Admittedly, in this case I may have invested too much time on my free Sunday… but at least I had my fun, so no harm done I guess 😉

Leave a Reply