They needed to have a locale matching the language of the localised time string they wanted to parse, they needed to use strptime to parse the string, they needed to use timegm() to convert the result to seconds when seen as UTC. The man pages pretty much describe these things.
The interface or these things could certainly be nicer, but most of the things they bring up as issues aren't even relevant for the task they're trying to do. Why do they talk about daylight savings time being confusing when they're only trying to deal with UTC which doesn't have it?
int main(void) {
struct tm tm = {0};
const char *time_str = "Mon, 20 Jan 2025 06:07:07 GMT";
const char *fmt = "%a, %d %b %Y %H:%M:%S GMT";
// Parse the time string
if (strptime(time_str, fmt, &tm) == NULL) {
fprintf(stderr, "Error parsing time\n");
return 1;
}
// Convert to Unix timestamp (UTC)
time_t timestamp = timegm(&tm);
if (timestamp == -1) {
fprintf(stderr, "Error converting to timestamp\n");
return 1;
}
printf("Unix timestamp: %ld\n", timestamp);
return 0;
}
It is a C99 code snippet that parses the UTC time string and safely converts it to a Unix timestamp and it follows best practices from the SEI CERT C standard, avoiding locale and timezone issues by using UTC and timegm().You can avoids pitfalls of mktime() by using timegm() which directly works with UTC time.
Where is the struggle? Am I misunderstanding it?
Oh by the way, must read: https://www.catb.org/esr/time-programming/ (Time, Clock, and Calendar Programming In C by Eric S. Raymond)
I thought the default output of date(1), with TZ unset, is something like
Mon Jan 20 06:07:07 UTC 2025
That's the busybox default anywayThe first sentence of your link reads:
>The C/Unix time- and date-handling API is a confusing jungle full of the corpses of failed experiments and various other traps for the unwary, many of them resulting from design decisions that may have been defensible when the originals were written but appear at best puzzling today.
It’s twelve lines or more, if you include the imports and error handling.
Spreadsheets and SQL will coerce a string to a date without even being asked to. You might want something more structured than that, but you should be able to do it in far less than 12 lines.
C has many clunky elements like this, which makes working with it like pulling teeth.
But only when you don't want them to, when you do want them to do it it's still a pain.
In C++, maybe. In C, not necessarily. If you're not willing to reinvent the wheel why'd you choose C anyway?
I've wasted so many dreary hours trying to figure out crappy time processing APIs and libraries. Never again!
Try not to have to do this sort of thing. You might have to though, and then you'll have to figure out what adding months means for your app.
It does remove a lot of the ambiguity of "I wonder what this stdlib's quirks are in their date calculations" but it also seems like a non-trivial amount of effort to port every time.
Surely we'll have everything patched up by then..
I wonder if people will still be repeating the "Y2k myth" myth as things start to fail.
[0] https://en.wikipedia.org/wiki/Year_2038_problem#Implemented_...
The overflow happens at 2038-01-19T03:14:08Z.
ClickHouse has the "parseDateTimeBestEffort" function: https://clickhouse.com/docs/en/sql-reference/functions/type-... and here is its source code: https://github.com/ClickHouse/ClickHouse/blob/74d8551dadf735...
I won't believe anyone who tells me that handling time in c/c++ isn't perilous.
Explanation: you can learn heap sort or FFT or whatever algorithm there is and implement it. But writing your own calendar from scratch, that will do for example chron job on 3 am in the day of DST transition, that works in every TZ, is a work for many people and many months if not years...
Also, naming things, cache coherency, and off by one errors are the two hardest problems in computer science.
If you are happy for the time to perhaps be wrong around the hours timezone changes, this is an easy hack:
import time
def time_mktime_utc(_tuple):
result = time.mktime(_tuple[:-1] + (0,))
return result * 2 - time.mktime(time.gmtime(result))
If you are just using it for display this is usually fine as time zone changes are usually timed to happen when nobody is looking.[0] https://www.ibm.com/docs/en/aix/7.1?topic=c-ctime-localtime-...
mktime() parses the time string which lacks any information on time zones
then the article uses timegm() to convert it to unixtime on the assumption that it was in UTC
also it's about C
No, mktime() doesn't parse a string. Parsing the string is done by strptime(). mktime() takes the output of strptime(), which is a C structure or the equivalent in Python - a named tuple with the same fields.
> the time string lacks any information on time zones
Not necessarily. Time strings often contain a time zone. The string you happen to be parsing doesn't contain a time zone you could always append one. If it did have a time zone you could always change it to UTC. So this isn't the problem either.
The root cause of the issue is the "struct tm" that strptime() outputs didn't have field for the time zone so if the string has one, it is lost. mktime() needs that missing piece of information. It solves that problem by assuming the missing time zone is local time.
> then the article uses timegm() to convert it to unixtime on the assumption that it was in UTC
It does, but timegm() is not a POSIX function so isn't available on most platforms. gmtime() is a POSIX function and is available everywhere. It doesn't convert a "struct tm", but it does allow you to solve the core problem the article labours over, which is finding out what time zone offset mktime() used. With that piece of information it's trivial to convert to UTC, as the above code demonstrates in 2 lines.
> also it's about C
The python "time" module is a very thin wrapper around the POSIX libc functions and structures. There is a one to one correspondence, mostly with the same names. Consequently any experienced C programmer will be able translate the above python to C. I chose Python because it expresses the same algorithm much more concisely.
>>> from email.utils import parsedate_tz, mktime_tz
>>> mktime_tz(parsedate_tz("Fri, 17 Jan 2025 06:07:07"))
1737094027
It converts rfc 2822 time into POSIX timestamp ([mean solar] seconds since epoch--elapsed SI seconds not counting leap seconds).[Missing scene]
" We are releasing Http1.1 specifications whereby expirations are passed as seconds to expire instead of dates as strings."
Why such self flagellation?
I did write such code in RISC-V assembly (for a custom command line on linux to output the statx syscall output). Then, don't be scared, with a bit of motivation, you'll figure it out.
I wrote conversion code, I know what I am talking about.