Why I Hate Java, Chapter No. Too-High-to-Count

Published at 10:31 on 13 June 2025

The time and date situation. Specifically, the new “improved” one.

Yeah, I get it. The old one is full of deprecated legacy cruft calls. Its fundamental time type is mutable (which is undesirable when writing multithreaded code). Plus it has an absolutely horrible name: Date refers to an object that holds the number of milliseconds since the UNIX time epoch.

On that latter point, I have no idea why the early Java dev team chose such a lousy name. They really had no excuse. About eight years prior, the Posix team chose time_t for their analogous data type. That had only one-second resolution so they later came up with a struct timeval with microsecond resolutions. Both are fundamentally reasonable name choices. Calling a time a time makes just so much more sense than calling a time a date. Just goes to show how long the brain rot has been prevalent in the Java world.

Speaking of that brain rot, Baeldung is one of the go-to sources for information in the Java world and their article on the new time and date API is a total hoot. It’s just one whopper after another. Let’s pick out a few of them, shall we?

Working with dates in Java used to be hard.
No, it didn’t. Not really, once you got over the weird naming convention of calling a time a date, and the fact that there were two classes for formatting a time. Because of course there were. This is Java, and we can’t do anything right. Got to write one weak, lame API and then a second one (also limited) to make up for the weakness and lameness of the first.
These were only suitable for the most basic tasks.
Bullshit, they were suitable for virtually every routine task. Only a tiny percentage of programs delved into the minutiae of dates, times, time zones, and calendars enough for the standard library to be insufficient, and that was not reason enough to clutter up that standard library with support for rarely-needed edge cases. It’s a reason for a third-party library of extended, specialized time and date functions to exist.
A first advantage of the new API is clarity
Bwahahahahahahahahaha! This whopper is so bad I could write several paragraphs about just how much of a whopper it is.

And now I shall write those paragraphs.

The new library has no fewer than nine absolute and two relative time classes. No, I am not making this up. They all differ in various subtle ways from each other, ways that will require one to spend no small amount of time reading documentation to figure it all out. And figure it all out you must, or your code will throw fatal exceptions and fail to run if you use the wrong class in the wrong place. This is apparently what Java-heads consider “clarity,” and feel so strongly that it is such that they set the term in boldface.

And that’s just times. Then there’s time zones. There are two time zone classes, again because of course they are. (Making three types of time zone in total once you add the legacy one which is still around. Oh, wait, I also forgot that there are now time zone ID objects, which differ from actual time zones in various subtle ways.) The two new zone types also differ in various subtle ways, and you have to study the documentation a while to understand these differences, too.

The most common uses by far for a time object are to create it and to print it. Manipulations are so far down on the list that they can be disregarded for purposes of discussing the most common uses. Let’s print the date in the preferred format for the current locale. For the old Date option, it is relatively simple:

Date now = new Date();
DateFormat formatter = DateFormat.getDateTimeInstance();
System.out.println(formatter.format(now));

After a lot of reading, eventually one figures out that the Instant (you might think they would call it Time or TimeStamp, but apparently some types just never learn, sigh) class is the closest analogue to the old Date class, and the new formatting class is DateTimeFormatter. Great!

Instant now = new Instant();
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime();
System.out.println(formatter.format(now));

But that won’t even compile. Turns out you can’t construct an Instant, the constructor is now private. You must call the now static method:

Instant now = Instant.now();
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime();
System.out.println(formatter.format(now));

Nope, still lose. Turns out formatting has been dumbed down. Formatters are now too stupid to come up with a preferred default style. You must tell it the style. To do that, you have to learn about (and introduce) an extra style-specifying class.

Instant now = Instant.now();
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
System.out.println(formatter.format(now));

Yay! It compiles! Uh oh, it throws a weird exception when running.

Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: MonthOfYear
	at java.base/java.time.Instant.getLong(Instant.java:604)
	at java.base/java.time.format.DateTimePrintContext$1.getLong(DateTimePrintContext.java:205)
	at java.base/java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:308)
	at java.base/java.time.format.DateTimeFormatterBuilder$TextPrinterParser.format(DateTimeFormatterBuilder.java:3363)
	at java.base/java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2402)
	at java.base/java.time.format.DateTimeFormatterBuilder$LocalizedPrinterParser.format(DateTimeFormatterBuilder.java:4848)
	...

Now you get the fun of delving into the Temporal interface and temporal accessors. Which it turns out doesn’t even help. The problem is not even there, it is someplace else. It turns out that the new formatter will accept arguments it is incapable of processing. There are nine date/time objects, remember? Which ones work at which times is a complicated set of rules. So not so fast! You have to take on the cognitive load of understanding all that too.

Eventually you hit on it. Time zones used to be part of formatting. Makes sense, right? A point in time is a point in time. What the clock shows for that point in time depends on your local time zone. But that’s too simple and logical for Java, so we’ve fixed it for you. Some edge cases might want to store times together with time zones. It’s easy enough to do that on one’s own (just define a class containing a time and a zone), but hey, let’s shove complexity at the programmer and support that edge case. It’s the Java way. Might as well support the notion of a time of day discombobulated from any notion of a zone, too, just in case someone might want that. As well as a whole batch of other possibilities. And since we want to format those objects, too, we can’t have a zone in the formatter. It must be in the time object itself. So we are passing the wrong sort of time object if we want to print out a local time with a zone.

After more studying yet, you finally determine that the one time of day class out of nine which we need is ZonedDateTime. Of course, Java being Java, you can’t just call the ZonedDateTime constructor with the Instant you wish to print. The constructor is again private and you must call a static method. And to top it all off, that static method is too stupid to have any notion of a default local time zone so you must fetch and furnish that as well. And at long last, you have arrived at the code to do what you want:

Instant now = Instant.now();
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
ZonedDateTime printable = ZonedDateTime.ofInstant(now, ZoneId.systemDefault());
System.out.println(formatter.format(printable));

Of course, most programmers never get that far. The ones whose eyes don’t glaze over after learning there are nine different time types (they’re in a hurry and don’t have time for this lunacy), are probably going to give up when they run into the UnsupportedTemporalTypeException getting thrown on them. Again, they just don’t have time for this crap. The boss is wondering when the new feature that sales promised the important new customer will be ready. Trusty old Date works just fine, doesn’t spring weird surprises on them, and isn’t even officially deprecated (and probably won’t be for a good long time).

And then you have hardcore Java-heads expressing frustration and mystification as to why so many programmers are continuing to use the legacy API. Gee, I dunno why that might be. It’s just a mystery.

And the problem is, it’s like this all over the place in Java. Gratuitous complexity everywhere. Code in Java, and the cognitive load you must contend with is at least an order of magnitude greater than it needs to be, if reasonable and sane software design prevailed in the Java world.

Site Instability

Published at 21:12 on 7 June 2025

The robots are back, and crawling the hell out of my Mercurial repository that really can’t take that sort of rate of access. So this site has been more down than up recently.

robots.txt has been adjusted accordingly, but the offending robots are apparently working on cached data and still making banned requests. So I have temporarily turned Web access to the repository off. The requests continue, but now they get relatively cheap 404 responses involving only Apache, instead of having to fork and exec a Python CGI script to process each request, so they are no longer doing much harm.

When the robots stop trying to access that CGI script (probably in a day or so), I will re-enable Web access.

Fixing iPhone Video Colour

Published at 09:15 on 31 March 2025

Executive Summary

Export your video from the Photos app in original, unmodified format. Then, in the Finder (yes, the Finder), right-click on the video file you exported and choose Services → Encode Selected Video Files, and choose your encoding (1080p in my case). The result will be an HD video that can be shared on YouTube and which will not be all desaturated and overexposed.

The Details

The first time I tried importing a video shot on my iPhone into DaVinci Resolve it happened: the video was all washed-out and overexposed. It brought back bad memories of uploading still photos to the Web and viewing them on my Mac in the late aughts, as the overall effect was quite similar.

Then, the fault was Apple software botching colour management. Specifically, Safari was assuming that any image file without colour management data embedded in it should be displayed using the native Apple colour space. The latter has a wider gamut than the de-facto standard sRGB colour space, and using it to view unconverted sRGB data causes photos that look overexposed and desaturated, i.e. “washed out.” The same web page would have photos that would look just fine on Linux and Windows systems.

Mac fanboys at this stage would get all pompous about how “Apple does colour management right” when in fact Apple was getting it massively wrong. Yes, Apple did use a colour space that provided a wider gamut than Windows or Linux. Yes, Apple system tools and libraries had support for reading colour space data before Linux and Windows did, but their handling of data with no colour space information was flawed; what should have been interpreted as sRGB was instead being interpreted as being in the native system colour space.

So it was Apple’s fault. The workaround was to always embed colour space data in every image saved for Web use, and to always save that data in sRGB form. Windows and Linux would ignore the colour space information but the image data would be sRGB so it would display correctly there. Apple software would see the sRGB colour space metadata and do the necessary conversion before passing it on for display.

Eventually, Apple fixed their broken colour management, but old habits die hard and I still save still images in the above way for Web use.

I don’t know exactly who is at fault here, but:

  • The iPhone camera application is being weird. The Rec.709 gamma and colour space are the industry defaults for 1080p (i.e. “HD”) video (in fact, they were developed for use in HD video), yet if you tell your iPhone to shoot video in “HD” mode, it uses the Rec.2020 colour space with the Rec.2100 HLG gamma. You get an oddball video file instead of a standard HD one.
  • The Photos app on both the iPhone and the desktop Macs will display the resulting video just fine, as will QuickTime Player and iMovie.
  • When you import the video into DaVinci Resolve, the result looks all washed-out.
  • When you export the video from the Photos app and tell it to use 1080p format, it does convert the colour space, but it does a poor job of it. The result looks somewhat washed-out and it has weird colour shifts.
  • When you add a colour space transform to DaVinci Resolve’s colour processing, it also does a poor job of conversion.

So at this stage my money is on it mostly being DaVinci Resolve’s fault. It seems to be ignoring colour space information and assuming everything is Rec.709. It also seems deficient in reasonable defaults for colour space conversion (if the Finder can do it and get acceptable results without a lot of tedious tweaking by hand, DaVinci Resolve should offer a way to do this as well).

But Apple doesn’t completely escape blame here. If video colour space conversion is so tricky to get right (and I think this is part of the problem), then why use the troublesome Rec.2020 colour space when the user is telling the Camera app to shoot HD videos?

Apple fanboys should at this stage have a nice hot steaming cup of STFU. Yes, I know that Rec.2020 is “better” in the sense that it has a wider gamut and finer resolution than the industry standard, and thus preserves the ability to do more recovery of correct information in postprocessing. But the user has told the Camera app to shoot an HD video. That is critical. When the rest of the world talks about an “HD” video, they are talking about a video in the Rec.709 colour space, not some oddball Franken-video with the HD resolution but a non-HD colour space that will massively fail when shown on most video players on most platforms. Preserve the ability to shoot and save with greater colour resolution, yes, but don’t call it “HD” video if it’s not recording standard HD video.

There is, thankfully, a way to do a colour space conversion that produces acceptable results. It is hidden in, of all places, the Finder. See the executive summary above.

This, too, is Apple’s fault. The conversion should not be hidden in the Finder. It should not be in the Finder at all. It should be an option in the Photos app. (Well, it is, but that option doesn’t do a good job. Apple needs to fix the colour space conversion in Photos and clean up the Finder to not have the feature creep it does.)

To reiterate, it all brings back bad memories of what life was like fifteen or so years ago with still images. Implementing colour management in ways that could be theoretically superior to industry standards, but botching the implementation and making life needlessly difficult for your users, just seems to be in Apple’s genes. And mostly ignoring the desirability of embedding colour space info in media files seems to be in everyone else’s genes.

Looking for Alternatives to Digital Ocean

Published at 09:27 on 23 February 2025

Let me start by saying I have been nothing but satisfied with Digital Ocean, which I use to host this blog, so far. The issue here is not anything Digital Ocean has done, it is what Digital Ocean is highly likely to do in the future.

Digital Ocean is a capitalist enterprise based in a country undergoing a transition from democracy to fascism. The historical role of the capitalist class is to line up in support behind fascist regimes whenever they arise.

I do not know the politics of Digital Ocean’s top management and board of directors. It does not much matter. Even if both are dominated by political progressives, compliance with the rapidly-emerging fascist regime is highly likely.

In a capitalist corporation, management serves at the pleasure of the board, whose prime mission is to act in the interest of the corporation’s stockholders and their desire to maximize profits. Profits are not maximized by staking out adversarial positions to an authoritarian regime.

The above makes compliance the expected outcome, and the historical record of capitalism under fascism bears this theory out.

Yes, even under the Nazis there were businesses like DEF (Oskar Schindler’s firm) and Ernst Leitz GmbH (the makers of Leica cameras) that tried to do the right thing as much as they could. But even they were heavily constrained, and did a lot of complying (both manufactured materiel for the Wehrmacht). Even if Digital Ocean follows in their footsteps (and odds are against it, good guys like Leitz and Schindler are the exceptions that prove a general rule), they will still have to make a public show of being loyal Trump fascists.

Even in the optimistic case, then, this site is likely to end up as collateral damage should it remain on Digital Ocean.

Hence, it is now time for me to move this site elsewhere, which brings me to the requirements for what “elsewhere” should ideally be.

  1. As little US connection as possible. Ideally this would be an organization that is neither owned by US capital, managed or overseen by US citizens, based in the USA, nor physically hosted in the USA.
  2. Cloud hosting that lets me run my own installation of WordPress on my own installation of Linux. I am not interested in sharing an OS installation or a WordPress installation with others; past experience has taught me that both are insufficient to my needs.
  3. The ability to assign a static IP address to a virtual server.
  4. A provider that offers an S3-compatible cloud storage service, since I use such to keep this site backed up.

Any suggestions as to the above would be greatly appreciated!

The Final Nail in the Coffin for Go

Published at 16:47 on 25 June 2024

The same Go program I had to fight with for two days to get to the point where it was still unfinished, but:

  • I had satisfied my curiosity that it was, indeed, possible to do the particular thing I was struggling with in Go, and
  • It had been painful enough to prove to me that I should not consider Go a language of choice.

… Has now been coded to the same stage of completion in C++. It took half the time, half the effort, and under half the lines of code that it did in Go.

And that is as an absolute novice C++ programmer, writing his second C++ program ever. I had been experimenting with Go for about a year before I recently gave up on it.

It’s not that C++ is good, mind you. It’s a total cruft fest that should have been put out of business by something more modern at least 15 years ago. But it’s still possible to do things in C++ without the language and/or standard library persistently getting in your way like they do in Go.

Go is so bad it is literally worse than C++.

Go: Fooled Me Twice, Shame on Me

Published at 09:32 on 21 June 2024

I just can’t seem to learn. I so much want there to be a better alternative (i.e. a good, modern programming language that compiles down to machine code) to C/C++ out there. And I just can’t stop thinking that Go might be it. Then I keep running into problems. Go (and its libraries) continually keep making it very difficult to do clever things.

My obstacle this time is how Go parses command-line arguments. Most modern languages do this by building a collection of objects to describe the allowed command-line syntax, making a parse call, and receiving in return a collection of objects describing the options and arguments found.

Go is different, probably because it was written by individuals skeptical of object-oriented programming. Instead, it is all based on passing pointers into an argument-parsing subsystem. When one initiates a parse, the pointed-to variables get set in ways reflecting what the user typed on the command line.

That might work well enough in the simple case, but my case is not so simple. I am trying to write a family of related commands, each with a set of standard arguments, and most with some custom, command-specific arguments as well. Moreover, there is a configuration file, and it is possible to get values from there if they are not specified on the command line with options.

In Java, I have done that by using the Apache Commons CLI library, subclassing the main class that holds the syntax description, and having it auto-populate itself with the standard arguments. Then my subcommands all use that class, and automagically get the standard options they need. No fuss, no muss, no repeated code, and all the options are in a single place.

Then I pull those parsed option values into the class representing a parsed configuration file, so that a command-line option will overwrite the in-memory copy of an in-file option. Presto! All the configurable values I need are now all in one place. And not only that, it was simple and easy to accomplish.

Go’s pointer-based argument parsing makes this basically impossible. Oh, there’s an alternate argument parsing library out there, but it is likewise broken by design, because it is pointer-based as well. Wait! There is a “value interface” that might offer an out? Nope, sorry, no escape: it only seems to support string values (even Boolean flags are unsupported!), and it is incompletely documented.

I keep running into this sort of crap with Go. And only with Go. Other modern languages just don’t seem to have this degree of pervasive brokenness. The Python standard library, for example, parses arguments much in the same way that Apache library does in Java. Even the hoary old cruft-fest of a programming language that is C++ has a popular third-party library that does the right thing.

It’s not just argument parsing. Previously, I struggled with how Go’s character set support can’t signal an explicit error condition when it encounters invalid input. Java, Python, Ruby, C++: all can do this if requested. Not Go, at least not out of the box and not without a lot of extra effort.

It’s bad enough to make me seriously question if there’s any problem space out there for which Go is the most appropriate solution. I know there are large, successful software systems written in Go, but my own personal experiences make me suspect strongly that those projects were much harder to get to their current state of completeness than if they had been written in some other, less limiting, less pervasively crippled by bad design, environment.

Perhaps it’s all just me, and others don’t feel the suck so much. Frankly, I don’t think so. But that’s just one more person. More damning, I think, is the verdict of Go’s original sponsor, Google. If Go really was the way to fill the need for a more modern language that compiles to machine code, then Google would not be sponsoring the Carbon project.

It’s all a shame, because to reiterate I really want there to be a better alternative to C/C++ out there. Alas, Go does not seem to be it.

Well, That Sure Sucked

Published at 15:46 on 17 June 2024

Yet again, a MySQL auto-update ran while I was out of town. Yet again, the auto-update auto-trashed my database.

In baseball, a hitter has three chances to hit the ball. But that is in a situation where another pitch takes under a minute to make. Life is not a baseball game. I just pissed away most of a day recovering from a MySQL failure. Again. Two strikes and you’re out, MySQL!

I am now back up, with all auto-updates disabled, and using MariaDB instead of MySQL.

Postscript

Published at 18:43 on 2 June 2024

It sucked even more than I expected it to. This is because in addition to all the issues I mentioned previously, the site is crap from a technological point of view: slow, laggy, and full of flimsy server-side Javascript that does things like randomly cause the contents of your coding window to disappear. Plus it doesn’t run at all unless I disable some of the protections on my browser.

At least I had fun sending the stupid thing arbitrary machine code. Which, of course, was harmless and only printed a message linking to the aforementioned article.

I highly doubt I will get selected for an actual interview, but why would I want to be, given what sort of garbage they feel comfortable shoving at people?

Why HackerRank Sucks

Published at 12:02 on 31 May 2024

Foreword

I have thought of writing this article more than once before. I just got assigned yet another HackerRank test as part of an interview process. Sometimes, I have blown such things off entirely. Sometimes, I attempt to do my best on answering them.

This time, I plan to do something different. I will have a link here in my response to the problem; if you are reading this as a result of following that link, then here is why I responded the way I did.

Introduction

HackerRank sucks for two main reasons:

  1. The general irrelevance of undergraduate computer science exercises to real-world programming, and
  2. The artificiality of HackerRank’s time constraints.

Irrelevance of Problems

In my experience, HackerRank’s exercises tend to be thinly-disguised rehashings of undergraduate computer science homework assignments. Such assignments tend to be heavily based on coding implementations of basic data structures, often trees of various sorts, that have very limited real-world relevance to solving problems on the job. I have written on this here before. It’s really not a surprise, given how the usage of such problems is basically mandated by the general nature of the constraints of the HackerRank platform.

Effectively, such tests screen for either recency of undergraduate coursework (and thus lack of practical, on-the-job experience), or willingness to spend time brushing up on a skill set whose real-world utility is extremely limited (and thus for unquestioning submission to authority and willingness to obey pointless rules). The first is diametrically opposed to the goal of filling a senior-level position, and the second is diametrically opposed to the sort of environment that I personally thrive in.

Artificiality of Time Constraints

Real-world programming tasks generally do not pop unexpectedly out of nowhere, with no advance warning. They are usually foreseeable as part of the natural trends of evolution of software systems and the organizations they support. As such, the prudent developer has usually already spent some time thinking about such issues. Sometimes, of course, they do pop up unannounced (long-latent bugs sometimes manifest, and sometimes have severe impact). Even then, they don’t come with a count-down timer and a hard artificial deadline. It is possible to take a walk (I get some of my best ideas outdoors) or to bounce ideas off colleagues.

With HackerRank, there is no such subtlety. The clock is ticking, the artificial deadline is rapidly approaching, and it will be enforced without mercy. (Sometimes you even get a so-called proctored test, in which you must enable a spy camera. Leaving the room is considered cheating. Lucky you.) You will solve the problem in one sitting, and you will do it now. And the problem has limited real-world relevance at best.

Conclusion

Maybe I will submit an actual solution, and maybe I will not. It will depend on the exercise and how I feel about it at the time I submit my response. At any rate, the link here will be the most important part of that response.

Really, there is not much more to say, so it is time to wrap this up. HackerRank sucks, and now you know why.

Take That, iPhone!

Published at 15:01 on 11 April 2024

After a recent update, my iPhone started letting me control my headphone volume from threshold of pain loud to insane instant deafness loud. I guess some aging hipster who ruined his hearing going to too many rock concerts without hearing protection got appointed to a QC position at Apple.

Based on what I had read about human sound perception, I guessed I needed about 6 dB of attenuation to tame the thing. A simple matter of adding four resistors to the picture (two for each channel, one in series to cut the voltage in half, and another in parallel to restore the impedance the audio amplifier sees to what used to be pre-attenuator).

The worst part about it was all the fiddly soldering (those connectors have some tiny terminals). But it works, and 6 dB was indeed the correct amount of attenuation needed to restore sanity to the device.