Custom Date Formatting: strftime() & strptime()

Custom Date Formatting: strftime() & strptime()
Chapter 10 Series — Part 5: Format and Parse Date/Time Strings
%Y-%m-%d
Format Specifiers
Print & Parse
Locale
Aware

Why Not Just Use ctime()?

ctime() and asctime() always produce the same fixed format: Wed May 15 10:30:00 2025\n. But real applications need flexibility. A bank might need 15/05/2025. A log file might need 2025-05-15T10:30:00Z. A mobile app might need Wednesday, 15 May. That’s where strftime() comes in.

Keywords in This Post

strftime() strptime() Format Specifiers ISO 8601 Locale currTime()

📄 strftime() — Printing Time in Any Format

strftime() works like printf() for dates. You provide a format string with percent-prefixed conversion specifiers (like %Y for 4-digit year, %m for month), and it fills your buffer with the formatted string.

#include <time.h>

size_t strftime(char *outstr, size_t maxsize,
                const char *format, const struct tm *timeptr);
/* Returns number of bytes written (not counting \0), or 0 on error */
#include <time.h>
#include <stdio.h>

int main(void)
{
    time_t t = time(NULL);
    struct tm *tm_ptr = localtime(&t);
    char buf[128];

    /* ISO 8601 date format */
    strftime(buf, sizeof(buf), "%Y-%m-%d", tm_ptr);
    printf("ISO date   : %s\n", buf);   /* e.g. 2025-05-15 */

    /* Indian date format */
    strftime(buf, sizeof(buf), "%d/%m/%Y", tm_ptr);
    printf("DD/MM/YYYY : %s\n", buf);   /* e.g. 15/05/2025 */

    /* Full verbose format */
    strftime(buf, sizeof(buf), "%A, %d %B %Y, %H:%M:%S %Z", tm_ptr);
    printf("Verbose    : %s\n", buf);
    /* e.g. Wednesday, 15 May 2025, 10:30:00 IST */

    return 0;
}

Note: Unlike ctime(), strftime() does not automatically append a newline. You control exactly what goes in the buffer. If the output would overflow maxsize bytes, strftime() returns 0 and the buffer contents are undefined.

📋 strftime() Format Specifier Reference
Specifier What it expands to Example output
%Y 4-digit year 2025
%y 2-digit year 25
%m Month as 2-digit number (01-12) 05
%B Full month name May
%b Abbreviated month name May
%d Day of month (01-31) 15
%A Full weekday name Wednesday
%a Abbreviated weekday name Wed
%H Hour (24-hour clock, 00-23) 10
%I Hour (12-hour clock, 01-12) 10
%p AM or PM AM
%M Minute (00-59) 30
%S Second (00-60) 00
%F ISO date, same as %Y-%m-%d 2025-05-15
%T Time, same as %H:%M:%S 10:30:00
%Z Timezone abbreviation IST
%j Day of year (001-366) 135
%% Literal percent character %

🔧 Practical strftime() Patterns
Use Case Format String Example Output
Log file timestamp “%Y-%m-%d %H:%M:%S” 2025-05-15 10:30:00
ISO 8601 UTC “%FT%TZ” 2025-05-15T10:30:00Z
Indian date format “%d-%m-%Y” 15-05-2025
12-hour AM/PM “%I:%M %p” 10:30 AM
Day of week + date “%A, %d %B %Y” Thursday, 15 May 2025
Filename-safe timestamp “%Y%m%d_%H%M%S” 20250515_103000

🔢 strptime() — Parsing a Date String

strptime() is the reverse of strftime(). It reads a date/time string and fills a struct tm according to a format. Think of it like scanf() but for dates.

#define _XOPEN_SOURCE   /* Required to enable strptime on Linux */
#include <time.h>

char *strptime(const char *str, const char *format, struct tm *timeptr);
/* Returns pointer to first unprocessed char in str, or NULL on parse error */
#define _XOPEN_SOURCE
#include <time.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    struct tm t;
    char *remaining;
    char buf[64];

    /* Parse an Indian-format date string */
    memset(&t, 0, sizeof(struct tm));
    remaining = strptime("15/05/2025", "%d/%m/%Y", &t);

    if (remaining == NULL) {
        fprintf(stderr, "Parse failed\n");
        return 1;
    }

    t.tm_isdst = -1;
    time_t epoch = mktime(&t);
    printf("Parsed time_t : %ld\n", (long)epoch);

    /* Now print it back in a different format */
    strftime(buf, sizeof(buf), "%A, %d %B %Y", &t);
    printf("Formatted     : %s\n", buf);
    /* Output: Thursday, 15 May 2025 */

    return 0;
}

Real-world use case: A server receives a date/time string from a REST API response (e.g., "2025-05-15T10:30:00"). You use strptime() to parse it into a struct tm, convert to time_t with mktime(), then do calculations like “how many days ago was this event?”

Important: strptime() never sets tm_isdst. Always set t.tm_isdst = -1 before passing the struct to mktime(), otherwise DST handling may give you a wrong time_t value. Also, zero-initialise the struct with memset(&t, 0, sizeof(t)) before calling strptime() to avoid garbage values in unset fields.

🛠 Building a Reusable currTime() Helper

A common pattern in Linux programs is a small helper that returns a formatted current-time string. Here is how to build one using strftime():

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

#define BUF_SIZE 128

/* Returns current time as a formatted string.
   'fmt' is a strftime format string; pass NULL for a sensible default.
   Returns NULL on error. */
const char *curr_time(const char *fmt)
{
    static char buf[BUF_SIZE];
    time_t t;
    struct tm *tm_ptr;
    size_t n;

    t      = time(NULL);
    tm_ptr = localtime(&t);
    if (tm_ptr == NULL)
        return NULL;

    n = strftime(buf, BUF_SIZE,
                 (fmt != NULL) ? fmt : "%Y-%m-%d %H:%M:%S",
                 tm_ptr);
    return (n == 0) ? NULL : buf;
}

int main(void)
{
    printf("Now (default) : %s\n", curr_time(NULL));
    printf("ISO 8601      : %s\n", curr_time("%FT%T"));
    printf("Filename-safe : %s\n", curr_time("%Y%m%d_%H%M%S"));
    return 0;
}

Up Next in This Series

Part 6 covers Timezones — the TZ environment variable, tzset(), and how Linux manages timezone data in /usr/share/zoneinfo.

Next: Linux Timezones → All Linux Posts

Leave a Reply

Your email address will not be published. Required fields are marked *