[HN Gopher] Rethinking the C Time API
       ___________________________________________________________________
        
       Rethinking the C Time API
        
       Author : oliverkwebb
       Score  : 61 points
       Date   : 2025-02-16 14:26 UTC (8 hours ago)
        
 (HTM) web link (oliverkwebb.github.io)
 (TXT) w3m dump (oliverkwebb.github.io)
        
       | tempodox wrote:
       | The author could use a lesson in visual design.
       | 
       | https://www.contrastrebellion.com
        
         | grg0 wrote:
         | Good resource.
         | 
         | The way I fix bad websites is by telling Firefox not to load
         | external fonts and font styles.
        
       | turtleyacht wrote:
       | Updated link to referenced work _Time, Clock, and Calendar
       | Programming in C:_
       | 
       | http://www.catb.org/esr/time-programming/
        
         | oliverkwebb wrote:
         | Thanks, the link esr provides in his website
         | (https://www.catb.org/~esr/faqs/time-programming/index.html)
         | leads to a 404
        
       | mjburgess wrote:
       | 95% of the supposed issues with C could be solved by a new
       | standard library, integrating the debugger into the compiler as
       | the default build/run environment (with auto address
       | sanitisation, frame protection, etc. etc.), and a default strict
       | mode error checking.
       | 
       | It would then be actually really hard to successfully run a C
       | program (in the debugger) with any problems. Under these
       | conditions it'd be easy to imagine most C programs running with
       | fewer bugs (, leaks, etc.) than Rust programs.
        
         | pjmlp wrote:
         | Except it is easier to introduce a new programming language
         | than having a committee driven language to adopt a new standard
         | library.
         | 
         | Neither ISO nor OpenGroup would care about it.
         | 
         | Remember that since 1989, no actions were taken to improve its
         | security.
         | 
         | Even the few functions that have been added still use
         | pointer/length pairs without any means to validate they are the
         | correct pair.
        
           | Someone wrote:
           | > Remember that since 1989, no actions were taken to improve
           | its security.
           | 
           | Not much, but not nothing, either. _gets_ was deprecated in
           | C99 and removed in C11
           | (https://en.wikipedia.org/wiki/C_file_input/output#gets)
        
             | pjmlp wrote:
             | Yet the scanf and fgets possible exploits are still there.
        
           | rdpintqogeogsaa wrote:
           | > Remember that since 1989, no actions were taken to improve
           | its security.
           | 
           | Technically, gets() was removed from the standard library in
           | C11[0]. However, that is far from a semantically meaningful
           | overhaul of the standard library. I nonetheless felt the need
           | to point out that there was a very specific effort for the
           | sake of completeness.
           | 
           | [0] https://en.cppreference.com/w/c/io/gets
        
             | pjmlp wrote:
             | Which is great, except for all those stubborn folks not
             | using anything beyond C99, and scanf and fgets are still
             | possible attack vectors, when getting sizes wrong.
        
               | ctoshiningc wrote:
               | Have you tried talking to them?
        
           | nine_k wrote:
           | Create a language with semantics exactly like C, but the
           | standard library completely replaced. Call it in a way that
           | avoids trademark disputes (e.g. CWSL, C With Sane Library).
           | Get GCC and LLVM support it, which should be reasonably easy,
           | because both support compiling code that does not rely on
           | libc (though OS entry point code, etc should be wired in).
           | 
           | Would it be easy enough to port important C code to it, given
           | that most of the libc-supplied functions, and functions
           | transitively depending on these, would have to be rewritten?
           | Would it be worthwhile, compared to rewriting such code it in
           | Zig, Rust, or Ada?
        
             | pjmlp wrote:
             | There have been multiple attempts at this, how many folks
             | heard of Safe-C, C3,... ?
             | 
             | Only bothering to list two examples, there are many others,
             | even Cyclone is partially C compatible, guess what came out
             | of Cyclone.
        
               | oguz-ismail wrote:
               | >Safe-C
               | 
               | import statements are a deal breaker
               | 
               | >C3
               | 
               | shit syntax
               | 
               | >Cyclone
               | 
               | vaporware
        
               | MyOutfitIsVague wrote:
               | Why in the world would import statements be a deal
               | breaker?
        
               | oguz-ismail wrote:
               | > The .h and .c files of a component must always be
               | stored in the same folder so that the compiler can find
               | them
        
               | pjmlp wrote:
               | Learn to use your compiler switches.
        
             | shakna wrote:
             | D began life as a re-engineered C++. (I'm sure Walter will
             | correct me if I say any more).
             | 
             | There's a Safe C++ extension proposed for Clang [0].
             | 
             | But those are C++, not C. A little different kettle of
             | fish.
             | 
             | Gnome's Vala [1] aims to be the "smoothest C off-ramp". It
             | does compile to C, but with GObject taking control of
             | everything.
             | 
             | There's CheckedC [2], which adds optional bounds checking
             | to C, and was backed by Microsoft until recently.
             | 
             | There's the Linux kernel's nolibc [3], which I've enjoyed
             | the heck out of using, but it is rather constrained.
             | 
             | There's C's own Annex K [4], that almost nobody has
             | implemented, and every compiler developer hates and can
             | poke holes in. GCC and LLVM have both repeatedly said they
             | won't support it. (So much as easy to get them to support
             | things...)
             | 
             | GCC already has a number of memory safe languages, though.
             | Most of which, because they're part of the same compiler
             | suite, can interact with other languages that GCC has. Like
             | the D or Go frontends.
             | 
             | [0] https://discourse.llvm.org/t/rfc-a-clangir-based-
             | safe-c/8324...
             | 
             | [1] https://vala.dev/
             | 
             | [2] https://www.checkedc.org/
             | 
             | [3] https://lwn.net/Articles/920158/
             | 
             | [4] https://www.open-
             | std.org/jtc1/sc22/wg14/www/docs/n1106.txt
        
           | chrsig wrote:
           | eh, i think i disagree. a new stdlib on its own wouldn't come
           | with a lot of abi/linking baggage that tends to hold up
           | tooling and migrations when you start introducing a different
           | language. (see recent rust/linux drama).
           | 
           | i mean, if the committee members can make it happen or not, i
           | don't know. but it's still a worthy thing to explore, I
           | think. there's going to be a lot of C code that will need a
           | very gradual migration path to safer apis for a very very
           | long time.
        
         | Keyframe wrote:
         | might as well throw in MISRA-C checker into it.
        
         | oliverkwebb wrote:
         | The core of C is pointer arithmetic; This creates a
         | fundamentally unsafe environment.
         | 
         | You can't even do anything in C without some asm (syscall
         | wrappers) because C was meant to boil down and streamline
         | PDP-11 assembly (Your computer is not a fast PDP-11) to a set
         | of consistent principles. The consequence of this is that the
         | core of the language is pointers and pointer arithmetic, and
         | raw unabstracted pointers are fundamentally unsafe to work
         | with.
         | 
         | Using the rust type system I can essentially confirm code is
         | bug and edge-case free with exhaustive matching and unit
         | testing (C's lack of tooling blessed the world with autoconf
         | and cmake btw). Not to mention rusts ability to abstract away
         | necessary boilerplate gives me more time to think about my code
         | instead of pointer arithmetic and allocation heuristics.
         | 
         | The "cure" for C is a language that abstracts away raw pointers
         | and memory allocation.
        
           | oguz-ismail wrote:
           | > You can't even do anything in C without some asm (syscall
           | wrappers)
           | 
           | Which language can do anything without some asm and support
           | as many platforms as C?
        
             | oliverkwebb wrote:
             | > Which language can do anything without some asm and
             | support as many platforms as C?
             | 
             | I wouldn't be surprised if someone figured out how to do
             | the interrupts and register control necessary to invoke
             | syscalls with pure LISP/Scheme/CL :P
             | 
             | P.S. anything that compiles with LLVM and has an ingrained
             | way to do print() that doesn't invoke libc, although
             | there's a blurry line here between "pure asm"/"compiles to
             | asm" that involves trusting-trust-style bootstrapping of
             | features into the compiler
        
               | oguz-ismail wrote:
               | You don't know what you're talking about
        
               | nolist_policy wrote:
               | > > Which language can do anything without some asm and
               | support as many platforms as C?
               | 
               | > I wouldn't be surprised if someone figured out how to
               | do the interrupts and register control necessary to
               | invoke syscalls with pure LISP/Scheme/CL :P
               | 
               | Haha
               | 
               | > P.S. anything that compiles with LLVM and has an
               | ingrained way to do print() that doesn't invoke libc,
               | although there's a blurry line here between "pure
               | asm"/"compiles to asm" that involves trusting-trust-style
               | bootstrapping of features into the compiler
               | 
               | Actually LLVM IR has no concept of syscalls, you have to
               | use inline assembly inside your IR to issue syscalls.
        
               | kazinator wrote:
               | What haha; we had that decades ago: systems programmed in
               | Lisp from the bare metal.
               | 
               | Here is what assembly code looks like in (ccl) Clozure
               | Common Lisp:
               | 
               | https://github.com/Clozure/ccl/blob/master/level-0/X86/x8
               | 6-h...
               | 
               | ARM version of same file:
               | 
               | https://github.com/Clozure/ccl/blob/master/level-0/ARM/ar
               | m-h...
        
           | mjburgess wrote:
           | You can write every program you want to without pointer
           | arithmetic.
           | 
           | If you mean that a dereference of a memory location involves
           | the compiler emitting pointer arithmetic instructions --
           | that's true of all languages.
           | 
           | If you want your language to completely disguise the machine
           | from you, and "abstract away" memory allocation, you're going
           | to pay a high complexity cost to do so.
           | 
           | If you have never run C in a debugger, with the massive
           | amount of highly sophisticated tooling available to C
           | debuggers, then you're operating from a profoundly mistaken
           | starting point for evaluating the viability of C for modern
           | safe sofware development.
           | 
           | C debuggers and tooling are vastly more powerful than Rust's
           | static type system, and catch a much wider array of memory
           | problems (, and bugs) than the Rust compiler can catch.
           | Static verification is far more limited than the dynamic
           | verification a sophisticated debugger can perform.
           | 
           | People's undergrad C course is a terrible basis on which to
           | evaluate what C is today. The reason C is associated with a
           | lack of security is that almost all software is written in C,
           | written in a time when either the internet didnt exist or
           | didnt imply an adversarial _local_ environment.
           | 
           | Running a network cable through every facet of our O/S and
           | software breaks many assumptions about the entire history of
           | programming -- which C predominated in. This is a very poor
           | basis on which to generalize the capabilities of a well-
           | specified C programming environment (which today, is much
           | more powerful than Rust's compiler).
        
             | mschuster91 wrote:
             | > People's undergrad C course is a terrible basis on which
             | to evaluate what C is today. The reason C is associated
             | with a lack of security is that almost all software is
             | written in C, written in a time when either the internet
             | didnt exist or didnt imply an adversarial local
             | environment.
             | 
             | The problem is - undergrad C is about the lowest common
             | denominator that all tooling understands and that _all
             | people_ understand. Of course you 're probably not going to
             | have to go as low as C89 like sqlite or, until 2022, the
             | Linux kernel [1], but still, the long support cycles of
             | many distributions make it challenging to move standards
             | upgrades forward.
             | 
             | [1] https://www.zdnet.com/article/linus-torvalds-prepares-
             | to-mov...
        
             | LegionMammal978 wrote:
             | > C debuggers and tooling are vastly more powerful than
             | Rust's static type system, and catch a much wider array of
             | memory problems (, and bugs) than the Rust compiler can
             | catch. Static verification is far more limited than the
             | dynamic verification a sophisticated debugger can perform.
             | 
             | Is there any dynamic verifier that fully validates all
             | acesses w.r.t. the object trees specified by the C
             | standard? Tools like ASan and UBSan won't detect a write to
             | one field running into another field, only a write
             | overrunning the complete object. (Compiler-level hardening
             | might catch that to some extent, but it's limited to TU
             | boundaries.) Not to mention things like 'misuse of restrict
             | pointers' that I've never seen any verifiers for, except
             | for special cases like overlapping memcpy() buffers.
             | 
             | Meanwhile, Rust does have its own dynamic verifier, called
             | Miri [0], which checks just about every language-level rule
             | at runtime. The main drawbacks are that it's slow and
             | doesn't support calling arbitrary C functions, but it would
             | be hard to get that to work short of the Valgrind route of
             | emulating the whole process on an instruction level.
             | 
             | [0] https://github.com/rust-lang/miri
        
               | mjburgess wrote:
               | If you're redoing the c std, which was my initial point,
               | you can introduce a debug allocator with metadata about
               | object layout. Coupled with some debugger support, i'd
               | imagine you can get there. And if the C std was willing
               | to allow constexpr to do more at compile time, you
               | wouldn't need explicit debugger support and could just
               | use constexpr to modify the compiler.
               | 
               | There's also nothing stopping debuggers reifying the C at
               | debug-time into this metadata.
               | 
               | My claim is 95% can be fixed by just normalizing what is
               | current practice at the stdlib level and compiler level.
               | By extending constexpr, i think you could get to 100%.
               | Given that this is the case, why even both with the
               | nightmare of Rust.
        
               | LegionMammal978 wrote:
               | Oh, you sounded like you were talking about something
               | that already exists today, rather than something that
               | you'd like to exist. The main problem with taking dynamic
               | verification all the way is making it work with ABI
               | boundaries, which won't be going anywhere in at least the
               | next decade. You'd need everyone to migrate to a
               | universal ABI that can convey all needed metadata, and
               | while I've seen a few proposals for that, none of them
               | have gone anywhere.
        
               | mjburgess wrote:
               | I was more saying that the scope of issues mainstream
               | tooling catches, includes ones that the rust compiler
               | doesn't catch, and many more than enough for what are
               | common memory safety issues -- there are some issues that
               | aren't caught, sure -- but the operations which can cause
               | those bugs are well-defined, most programs wouldnt have
               | to use them, and a new std lib would help.
               | 
               | When people propagandize about C, they're universally
               | unaware that the normal process of development basically
               | addresses most of the problems Rust is supposed to be
               | solving, and more than the rust compiler alone solves.
               | The remainder are 95% to do with libc, which should just
               | be thrown out.
               | 
               | A smidge more compile-time eval with constexpr, and the
               | use case for Rust could disappear. It's a great shame
               | that C is run by a standards process that's determined to
               | relegate it to electric motors and digital watches from
               | the 80s.
        
             | jcranmer wrote:
             | > C debuggers and tooling are vastly more powerful than
             | Rust's static type system
             | 
             | As someone who's worked on C debuggers and tooling... I
             | really have no idea what you're talking about. C's core
             | semantics are just so weak that it's not really possible to
             | express a lot of the things you can express in the type
             | system, and that's before we get to the necessary lossiness
             | that debuggers and tooling have to work with (e.g., you
             | can't just ascribe types to memory in C because C--in
             | practice--is way too loose with types for that to be
             | meaningful).
             | 
             | For an example from something I've worked on, Linux manages
             | to have two different arrays for the GPRs for a thread
             | register context, one that's used for ptrace and one that's
             | used for signal contexts. Helpfully, the header files give
             | you macros to map register names to numbers so that you can
             | say regs[RAX] instead of regs[0]. But the offsets are
             | different, so you have to remember that you need to use
             | regs[REG_RAX] instead of regs[RAX], and there is absolutely
             | no tooling in the world that can tell you when you get it
             | wrong because there is no expressible difference between
             | the two scenarios in C. Meanwhile, in Rust, I can wrap the
             | accessors in newtypes so that I can _only_ use the correct
             | set of constants to index into the array, which makes the
             | error state literally impossible to construct.
             | 
             | That's the real value of a static type system--you can use
             | it to make errors _literally impossible_ to specify in an
             | API.
        
               | mjburgess wrote:
               | I don't see the difference between having a typed access
               | API (V inline reg_at(enum K k) {} ) and overloading the
               | indexer.
               | 
               | If your point is that historical C APIs have overused an
               | untyped operation, that's part of my point about a new
               | std lib. Rust APIs can still provide an untyped indexer,
               | it's just bad API design.
               | 
               | What I'm imagining a new std lib would be doing is having
               | debug allocators, metadata against types, etc. Ie., a std
               | library designed for the debugger along with a release
               | version.
        
               | jcranmer wrote:
               | Using different enums doesn't help, because C will
               | happily let you cast enum A to enum B implicity.
               | 
               | In Rust, I _can_ express an API which can 't be used
               | incorrectly. In C, I _can 't_. Sometimes, in C, you can
               | _sometimes_ get to the point where you use conventions
               | that means maybe static or dynamic analysis tools _might_
               | be able to flag the misuse of the API, but very often,
               | such tools have extremely poor tradeoffs between
               | precision and accuracy, far worse than exists in Rust
               | with just the vanilla compiler.
        
               | mjburgess wrote:
               | -Wconversion , no?
               | 
               | My point isn't that you exhaust all the features of Rust
               | with a better stdlib and "debugger-oriented programming"
               | -- my point is that you can get 95% of the way there with
               | trivial complexity costs.
               | 
               | Rust imposes significant program design costs which can
               | be very detrimental to otherwise trivial performant
               | memory management, to faster iteration of software
               | design, and so on. These aren't free lang. features.
        
         | ctoshiningc wrote:
         | The Rust Evangelism Strike Force clearly hasn't been defunded
         | by DOGE (yet). Respectfully, if you want to use Rust, just use
         | Rust. If C doesn't suit you, you don't have to use it, and you
         | don't have to make unreasonable demands of the standards body.
        
         | josephg wrote:
         | I think this is a really good idea, and zig shows how it could
         | work.
         | 
         | But this?
         | 
         | > Under these conditions it'd be easy to imagine most C
         | programs running with fewer bugs (, leaks, etc.) than Rust
         | programs.
         | 
         | This is a crazy goal. You will never out-rust rust by adding a
         | few runtime checks to C, while in debug mode. Fewer bugs than
         | rust code is a wild goal.
         | 
         | I don't think you understand just how much rust's design
         | prevents you from shipping bugs. It's due to a combination of
         | so, so many things. Like: references instead of pointers,
         | unsafe blocks, sum types & match instead of unions, no implicit
         | nullability, unwrapping optional values is explicit, the result
         | type and #[must_use], bounds checks, the borrow checker
         | preventing use after free, ownership semantics, Send & Sync for
         | thread safety, and I'm sure plenty more.
         | 
         | It's common to write very complex, threaded rust code and have
         | it work first time. Well, the first time it compiles. Coming
         | from C, it's wild. Or, really, just about any other language.
         | 
         | To get the same result in C wouldn't just need a "strict mode".
         | You would need to ban raw pointers - which would make it no
         | longer C. And you'd need to make functions return more than an
         | (easily ignored) status code. Ie, you want a result type. For
         | bounds checking, you'd need a language level data structure for
         | slices / arrays (pointer + length). You'd have to do away with
         | void pointers for "generic" parameters. And probably 100 other
         | tiny, breaking changes that the C community will never accept.
         | 
         | And for all that, you would essentially get zig. Zig does all
         | these things.
         | 
         | But that would still get you worse bug density than rust
         | because you don't have a borrow checker. It'll get you close -
         | Runtime checks in debug mode will detect your use after frees -
         | if you have a good test suite. But they won't prevent aliasing.
         | Or (I think) help with thread safety. For that, you need a
         | borrow checker. You need rust.
        
       | shakna wrote:
       | > strftime() has to write to a string of a fixed length it can
       | not dynamically allocate (This is less legacy than it is bad
       | design)
       | 
       | There's good reason for this. I disagree that it's a bad design.
       | 
       | strftime can legitimately produce zero-length strings, in a non-
       | error state. You do not want an allocation on the heap, that is
       | empty.
       | 
       | You'd end up with more error states to track, and more confusion
       | around whether the function had succeeded. (Especially when using
       | %c).
        
         | CamperBob2 wrote:
         | A zero-length C string is still one byte long, for better or
         | for worse.
        
           | shakna wrote:
           | Right. So you have an extra byte you need to free. One that
           | you can't introspect, and reading will cause a memory fault,
           | because it won't be NULL-terminated (0-length means 0
           | length). And not freeing, because the assumption of 0-length
           | is violated, leads to a memory leak.
           | 
           | So instead of just checking one return value, now you have to
           | check two. And people are not great at even handling a single
           | NULL check. Few people check malloc's return, as awful as
           | that is.
           | 
           | Design should be intuitive as possible. You can't assume
           | they'll even look at a manpage.
           | 
           | If something returns a length, then people assume that length
           | is what will be allocated. A valid 0-length time string,
           | violates that assumption, and will cause problems down the
           | line.
           | 
           | If someone is forced to do the allocation themselves, then
           | there's a greater chance they'll actually notice that they
           | need to free it.
        
             | CamperBob2 wrote:
             | What? No, the null terminator _is_ the single byte in
             | question. That 's how an empty string is represented in C.
             | It's not the same thing as a NULL pointer, as you may be
             | thinking.
        
       | mastax wrote:
       | I wasn't aware that on non-x86 platforms long double is often
       | implemented with quadruple precision. I had assumed it was an
       | x87-specific hack. On ARM64 windows/macos long double is
       | apparently 64-bits which could be a problem.
       | 
       | Personally something about that solution is unsatisfying. Feels
       | like it'd be slow, even though that wouldn't matter 95% of the
       | time. I'd rather have 128-bit integer of nanoseconds.
       | 
       | https://en.wikipedia.org/wiki/Long_double
        
       | immibis wrote:
       | The requirements for time in computers have increased drastically
       | since C was invented.
       | 
       | Time used to mean what we write down or see on a clock:
       | 2024-08-13 02:27 PM. The computer had an electronic clock built
       | in, so it could save you a little effort of looking at the clock
       | and copying the numbers. And that was all it did. If your clock
       | was a few minutes off, that was no big deal. People knew clocks
       | only agreed to within one or two minutes. People knew clocks were
       | different in far away lands. Some people knew that you have to
       | adjust your clocks twice per year. The computer clock was just
       | like any other clock but happened to be inside a computer.
       | 
       | Now we expect a globally synchronized unique identifier for each
       | instant, regardless of timezone. This is hard to deliver.
       | Computers use these for synchronization amongst themselves, so
       | they have to be accurate to milliseconds or better. This is hard
       | to deliver. We expect computers to handle requests from far away
       | lands with as much grace as requests from the local operator, and
       | deliver results in a format the people in those lands expect.
       | This is hard to deliver. We expect computers to process requests
       | about the past using information that was current in the past,
       | all over the world. This is hard to deliver. We expect computers
       | to automatically adjust their own clocks twice a year, not just
       | on the dates everyone in your local area does, but for users in
       | all parts of the world on their respective dates. This is hard to
       | deliver. And we still haven't got graceful handling of completely
       | different calendar systems.
        
         | Gibbon1 wrote:
         | Interesting thing about hardware real time clocks. They are
         | usually as bad as the C time API.
         | 
         | I worked with a guy that designed three RTC clock cards for an
         | industrial bus system
         | 
         | The first had registers for hours minutes, seconds, day, month,
         | year. He was proud it handed the leap years correctly. It had a
         | battery back up.
         | 
         | The second design just had a 48 bit counter that counted ticks.
         | You could read and and write it using latches so the
         | read/writes were atomic.
         | 
         | The third design was a read only 48 bit counter and a 1024 bit
         | battery back ram.
         | 
         | In the second and third design the conversion from time to a
         | string is done in software. In the third the clock just
         | provides a free running timer and you store an offset in
         | battery backed ram along with other time related meta data.
         | 
         | The vast majority of hardware RTC clocks today are implemented
         | like the first example. It's a little infuriating since my
         | coworker figured out how to do it right 45 years ago.
        
       | ajross wrote:
       | > Out of all the components of C, its time API is probably the
       | one most plagued with legacy cruft.
       | 
       | First off, no, locales and wide characters exist. This statement
       | is just laughable.
       | 
       | But even as to time: that seems really unfair. This whole area is
       | a footgun and has been the source of bad implementation after bad
       | implementation, in basically every environment. But among those:
       | The "struct tm" interface is notable for being:
       | 
       | 1. Very early, arriving in C89 and the first drafts of POSIX,
       | with working implementations back into the mid 80's.
       | 
       | 2. Complete and correct, able to give correct calendar
       | information for arbitrary named time zones in an extensible and
       | maintainable way. LOTS of other attempts got stuff like this
       | wrong.
       | 
       | 3. Relatively easy to use, with a straightfoward struct and a
       | linear epoch value, with conversion functions in each direction,
       | and only a few footguns (the mix of 0- and 1-indexing was
       | unfortunate). There are even a few quality of life additions like
       | support for "short" month names, etc...
       | 
       | Really, these routines _remain useful even today_ , especially
       | since their use is guaranteed to integrate with your distro's
       | time zone database which must be constantly updated to track
       | legal changes.
       | 
       | There's stuff to complain about, but... no, I think the premise
       | of the article is dead wrong.
        
         | chrsig wrote:
         | i don't read that as a condemnation of usefulness of
         | integrating w/ the system tzdb (every language stdlib does this
         | under the hood)
         | 
         | my biggest issue is that the structs and function definitions
         | are pretty opaquely named and make it very hard to learn let
         | alone teach newcomers.
        
         | ajross wrote:
         | > 1. Very early, arriving in C89 and the first drafts of POSIX,
         | with working implementations back into the mid 80's.
         | 
         | I looked it up. In fact it's much earlier than that. The API
         | arrived in time.h via v7 Unix in 1979.
         | 
         | And it remains, unchanged, pervasively used, and most
         | importantly _still used for new code_ , four and a half decades
         | later. Rather than "legacy cruft", this constitutes one of the
         | most successful utility APIs in human history.
        
         | ctoshiningc wrote:
         | I mean, this blog post is the kind of uneducated posturing that
         | makes the JavaScript kiddies happy, because "crufty, old C is
         | so bad, see!?". But none of them know enough to know that it's
         | basically all bullshit.
        
           | oliverkwebb wrote:
           | Javascript is a famously cruft free language, as we all know.
           | 
           | My real question is how you created an account, found this
           | article, presumably read through it, and wrote out multiple
           | comments insulting me all within two minutes.
        
       | npalli wrote:
       | Among the many improvements, time is one area where C++ has
       | become better than old school C cruft. In c++20/std::chrono, the
       | Lua like code is just this -                 auto now =
       | system_clock::now();       zoned_time local_time{current_zone(),
       | now};       std::cout << std::format("{:%a %b %d %T}\n",
       | local_time);
        
         | burntsushi wrote:
         | Or Rust, with Jiff:                   println!("{}",
         | jiff::Zoned::now().strftime("%a %b %d %T"));
        
         | pjmlp wrote:
         | Some C++23 flavour, :)                 auto now =
         | system_clock::now();       zoned_time
         | local_time{current_zone(), now};       std::println("{:%a %b %d
         | %T}", local_time);
        
       | bloak wrote:
       | In an article like this I would have liked to see some mention of
       | TAI (https://en.wikipedia.org/wiki/International_Atomic_Time) as
       | one of the alternatives to UTC. Unfortunately there are several
       | different universal times. Apparently there's also a "Galileo
       | System Time", for example.
        
       | asveikau wrote:
       | I fail to see TFA's concerns or take them very seriously.
       | 
       | > time() unnecessarily takes a pointer argument to write to
       | 
       | Minor cosmetic issue.
       | 
       | > strftime() has to write to a string of a fixed length it can
       | not dynamically allocate (This is less legacy than it is bad
       | design)
       | 
       | This is often a good way to structure string functions in C. The
       | fact that TFA repeated the constant 40 instead of using sizeof()
       | immediately signals that they are unfamiliar with the idioms. A
       | "you problem".
       | 
       | Doing heap allocation where it is not required could be a problem
       | for some use cases.
       | 
       | > localtime() needs the pointer to a time_t value even though it
       | does not change it because of register size concerns on PDP-11's
       | 
       | Also minor and cosmetic.
       | 
       | > sleep() cannot sleep for sub-second amounts of time, usleep()
       | is deprecated and it's alternative nanosleep() requires you to
       | define variables
       | 
       | sleep(3) is not really a "time function" in the sense of the
       | others mentioned, it is a thread scheduler function. As such it
       | kind of exists in a different universe. This is also shown by the
       | fact that it's part of POSIX and not the C standard, like time(2)
       | is.
        
         | ctoshiningc wrote:
         | Another classic case of "if everyone does something differently
         | than you do, it might be worth investigating why". The hubris
         | to think that basic C time functions have been "broken" all
         | this time, and that nobody noticed or cared. What a joke.
        
           | oliverkwebb wrote:
           | For a definition of "nobody" that includes Eric S Raymond,
           | one of the most prominent figures in the linux world who's
           | article (https://www.catb.org/esr/time-
           | programming/index.html) I reference multiple times.
           | 
           | [ _plonk_ ](https://www.catb.org/jargon/html/P/plonk.html)
        
             | 1718627440 wrote:
             | > Eric S Raymond [...] prominent figure[...] in the linux
             | world
             | 
             | Doesn't he work for Microsoft?
        
               | oliverkwebb wrote:
               | He was the publisher of the Halloween documents (from my
               | understanding leaked by a whistleblower to him) and has
               | always been a firm opponent to Windows in all his works.
               | Are you thinking of Poettering?
        
               | 1718627440 wrote:
               | <del> Isn't this his blog:
               | https://devblogs.microsoft.com/oldnewthing/ ?
               | 
               | It is hosted by Microsoft and talks about implementation
               | details of Windows. So I always assumed, that he works
               | there.</del>
               | 
               | My bad, that's Raymond Chen.
        
           | forrestthewoods wrote:
           | Many functions in the C API are quite badly designed. The
           | hidden global state in locale for example. HN regularly has
           | articles about nasty bugs that boil down to "C API has
           | several major deficiencies that cause great pain and
           | suffering.
        
       | Joker_vD wrote:
       | > keep in mind that Integers support One percision, and there's a
       | trade off between resolution and the bounds of your epoch,
       | Floating point values support all percisions, there is no such
       | trade off.
       | 
       | Yeah, except with integers you get guaranteed precision across
       | all of your data range while with floating point, it is
       | ridiculously easy to accidentally lose precision without noticing
       | it when e.g. shifting time deltas from the past into the future.
       | 
       | Not to mention that using floating-point number of seconds since
       | epoch means that the times around the epoch are always given
       | better precision than the timestamp around the current time which
       | is really not what you want, and the situation only worsens with
       | time.
        
       | Animats wrote:
       | Time parsing and formatting is prone to extended bikeshedding. I
       | once raised the issue that Python had five parsers for ISO 8601
       | date formats, and they were all broken in some way. It took a
       | decade to resolve that. By then I'd moved on to Rust.
        
       | atiedebee wrote:
       | I don't think having strftime return a malloc'd pointer is a good
       | idea. The string won't be large at all and can easily fit onto
       | the stack (just like it was done in the example code). If I want
       | to use a custom allocator to store the string, I can. If I want
       | to malloc the string I can.
        
       ___________________________________________________________________
       (page generated 2025-02-16 23:01 UTC)