Translating a Timestamp – Solution

Your task for this month’s Exercise is to take a timestamp string and move values from inside a string (characters) into a structure. It’s not an easy exercise, and it doesn’t have a single, best solution.

Here is the sample string, with date and time values buried inside:

"2018_05_15 12:18:22"

Assume that the string retains the same format for any date. So one approach, the first one I tried, would be to calculate the offsets for each value within the string:

year = 0;
month = 5;
year = 8;
hour = 11;
minute = 14;
second = 17;

Every value other than the year is just two-digits long. You can use these consistent offset values to reference inside the string and extract the two-digit values. That’s what I did for my solution, but I was disappointed because it had a lot of overhead.

For my second approach, I noticed that the string separates each of the values with a single character. The character is either an underscore, space, or a colon, yet these characters are non-digits. Therefore, I can plow the string and replace any non-digit value with a null character, \0. The result is a buffer that contains tiny little strings, each of a significant value. Here’s how I did that:

while(timestamp[x])
{   
    if(!isdigit(timestamp[x]))
        timestamp[x] = '\0';
    x++;
}

The isdigit() function returns TRUE when the character timestamp[x] is a digit, 0 through 9. The ! (not) negates that condition, meaning the if statement is true for non-digits. If so, the non-digit character at timestamp[x] is replaced with the null character. The effect is to slice-up the string into smaller chunks, tiny strings of digits inside a character buffer.

Once the string is sliced up, I can use a pointer to stop at each non-null character and translate that mini-string into an integer value.

To march through the buffer, I initialize a pointer variable, offset, to the base of timestamp[]:

offset = timestamp;

The first item is the year, which is extracted into the datetime structure ts:

ts.year = atoi(offset);

After obtaining that value, a while statement increments the offset pointer until it encounters a null character:

while(*(offset++));

Because variable offset is post-incremented, its address references a digit inside the buffer after the while loop stops. At that point, I use the atoi() function to convert the mini-string into an integer value, then store the value in the relevant ts structure’s member.

Here is the full code:

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

int main()
{
    char timestamp[] = "2018_05_15 12:18:22";
    struct datetime {
        int year;
        int month;
        int day;
        int hour;
        int minute;
        int second;
    } ts;
    char *offset;
    int x = 0;

    printf("Time stamp string is '%s'\n",timestamp);

    /* replace non-digits with null chars */
    while(timestamp[x])
    {
        if(!isdigit(timestamp[x]))
            timestamp[x] = '\0';
        x++;
    }

    offset = timestamp;
    ts.year = atoi(offset);
    while(*(offset++));
    ts.month = atoi(offset);
    while(*(offset++));
    ts.day = atoi(offset);
    while(*(offset++));
    ts.hour = atoi(offset);
    while(*(offset++));
    ts.minute = atoi(offset);
    while(*(offset++));
    ts.second = atoi(offset);

    printf("Timestamp structure is %d %d %d %d %d %d\n",
            ts.year,
            ts.month,
            ts.day,
            ts.hour,
            ts.minute,
            ts.second
          );

    return(0);
}

This solution proved more elegant than my original effort (which I didn’t save). Still I think it’s clunky: The clutch of statements between Lines 29 and 40 could be replaced with something better. Perhaps you came up with a solution that looks pretty and works properly. If so, great!

3 thoughts on “Translating a Timestamp – Solution

  1. You could reduce the code which sets the ts values to a single loop using pointers and pointer arithmetic to access the individual members of ts.

    Also, I think atoi stops when it gets to a non-digit so I don’t think replacing non-digits with /0 is necessary.

  2. Slight variation. I typedef’ed the struct.

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

    int main()
    {
        char timestamp[] = “2018_05_15 12:18:22”;

        typedef struct datetime {
            int year;
            int month;
            int day;
            int hour;
            int minute;
            int second;
        } datetime;

        printf(“Time stamp string is ‘%s’\n”,timestamp);

        datetime dt = {.year = atoi(timestamp),
                       .month = atoi(timestamp + 6),
                       .day = atoi(timestamp + 8),
                       .hour = atoi(timestamp + 11),
                       .minute = atoi(timestamp + 14),
                       .second = atoi(timestamp + 17)};

        printf(“Timestamp structure is %d %d %d %d %d %d\n”,
                dt.year,
                dt.month,
                dt.day,
                dt.hour,
                dt.minute,
                dt.second
              );

        return(0);
    }

  3. Nice. I like it. And it’s a new trick I’ve not used before. Thanks, Chris!

Leave a Reply