cstrftimeformat-specifiers

What is the historical significance of %s in the strftime function and why is it not documented?


I have been using tools like iperf3 when using its --timestamps feature that uses strftime under the hood for formatting time. Although I have seen examples that show the conversion specifier %s used for epoch timestamp, I have not been able to see official documentation on its use. In the source code for strftime.c, there are no comments/notes mentioning its absence from documentation. While the functionality seems to work well for my purposes, I've come across suggestions or mentions (mostly related to the python datetime module) cautioning against reliance on this format.

I'm curious to understand the implications of using %s, including any historical context. Any insights or knowledge on this topic would be appreciated!


Solution

  • %s is not part of the C standard, it depends on the implementation. Here on MacOS, which has BSD roots, it is.

         %s    is replaced by the number of seconds since the Epoch, UTC (see mktime(3)).
    

    The manual explains...

    STANDARDS

    The strftime() function conforms to ISO/IEC 9899:1990 (“ISO C90”) with a lot of extensions including ‘%C’, ‘%D’, ‘%E*’, ‘%e’, ‘%G’, ‘%g’, ‘%h’, ‘%k’, ‘%l’, ‘%n’, ‘%O*’, ‘%R’, ‘%r’, ‘%s’, ‘%T’, ‘%t’, ‘%u’, ‘%V’, ‘%z’, and ‘%+’.

    The peculiar week number and year in the replacements of ‘%G’, ‘%g’, and ‘%V’ are defined in ISO 8601: 1988.


    Epoch time is the number of seconds since the epoch, so %s.

    If we look at the first version of strftime.c in NetBSD we see this.

        188  1.1  mrg           case 's':
        189  1.1  mrg               if (!_secs(t))
        190  1.1  mrg                   return(0);
        191  1.1  mrg               continue;
    

    _secs for seconds is just a thin wrapper around mktime. Likely the C standard people felt %s was redundant with mktime, and the BSD people felt it was convenient.


    Many languages implemented in C, such as Python, still rely on strftime and so only document the standard ones and some common extensions. For example, in Python datetime.now().strftime("%:z") on MacOS simply prints :z because MacOS strftime does not implement %:z. Whereas Ruby has its own strftime implementation and Time.now.strftime('%:z') works.