java.util.Date and Calendar to java.time: The Complete Migration Guide (Java 8–21)

Every experienced Java developer has a date-related war story. Mine came from a batch financial-reconciliation job that silently skipped three months of records because Calendar.MONTH uses zero-based indexing β€” January is 0, not 1 β€” and the off-by-one was invisible in unit tests that only ran against the current month. The bug surfaced in a quarterly audit, not in CI. That afternoon I started migrating our entire codebase to java.time, and I have not looked back since.

The java.time package (JSR-310, shipped in Java 8) is not merely a cleaner calendar API β€” it is a complete redesign of how Java represents time. Every type is immutable and thread-safe, month numbers start at 1, year offsets are gone, and the API forces you to be explicit about whether a value carries a timezone. This guide covers every migration scenario you will encounter when moving a real codebase from java.util.Date, java.util.Calendar, java.sql.Date, and SimpleDateFormat to their modern equivalents. All code has been tested on Java 21.0.3 (Eclipse Temurin) and is fully backward-compatible to Java 8.

The guide is structured so you can jump to any section independently. If you only need to migrate SQL types for a Hibernate project, jump to Section 6. If you are fixing Jackson serialisation in a REST API, jump to Section 10. The pitfalls section at the end contains the five mistakes I see most often in code reviews β€” read it before you consider the migration done.

Continue reading java.util.Date and Calendar to java.time: The Complete Migration Guide (Java 8–21)

JUnit 6

JUnit 6 represents the next evolution in Java testing, building on the solid foundation of JUnit 5 while introducing modern features, improved API design, and enhanced developer experience. Whether you’re upgrading from JUnit 5 or starting fresh, this comprehensive guide covers everything you need to know about JUnit 6β€”from core concepts to advanced patterns.

Why JUnit 6?

JUnit 6 continues the modular architecture introduced in JUnit 5, with refinements focused on reducing cognitive load, improving discoverability, and providing better integration with modern Java features like records, sealed classes, and virtual threads. The framework emphasizes backward compatibility while offering clear upgrade paths for existing projects.

Getting Started with JUnit 6

Before diving into advanced topics, get acquainted with the fundamentals of JUnit 6.

Core Testing Patterns

Master the essential patterns that form the foundation of professional test suites.

Mocking and Test Doubles

Integrate popular mocking frameworks with JUnit 6 for effective unit testing in isolation.

Extensions and Customization

Extend JUnit 6 with custom extensions for specialized testing needs.

Integration Testing

Combine JUnit 6 with Spring and other frameworks for comprehensive integration tests.

Performance and Best Practices

Optimize your tests for speed and maintainability.

Troubleshooting and Quality

Learn what to watch out for and how to solve common problems.

Advanced Topics

Deep dives into specialized scenarios and cutting-edge patterns.

AI Prompts for JUnit 6

Use AI tools to accelerate your JUnit 6 testing workflow.

Quick Reference

Key Annotations: @Test, @DisplayName, @BeforeEach, @AfterEach, @BeforeAll, @AfterAll, @ParameterizedTest, @RepeatedTest, @Nested, @Disabled, @EnabledOnJava, @EnabledOnOs

Common Assertions: assertEquals, assertNotEquals, assertTrue, assertFalse, assertNull, assertNotNull, assertSame, assertArrayEquals, assertIterableEquals, assertThrows, assertDoesNotThrow, assertAll

Extension Points: BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback, TestInstancePostProcessor, ParameterResolver, TestTemplateInvocationContextProvider

Next Steps

JUnit 6 provides a comprehensive, modular foundation for testing Java applications of any size. Whether you’re writing unit tests for a small utility, integration tests for a microservice, or full test suites for enterprise systems, JUnit 6’s flexible extension model and rich feature set help you write clear, maintainable tests that improve code quality and accelerate development.

Start with the fundamentals, master the core patterns, and progressively adopt advanced techniques as your testing needs grow. The investment in learning JUnit 6 thoroughly pays dividends in every project you build.

20 AI Prompts for Java Concurrency and CompletableFuture

Java concurrency is the topic that trips up senior engineers more than almost anything else in the ecosystem. It is not that the APIs are hard to read β€” CompletableFuture.thenCompose() is not a complicated method signature. The problem is that the consequences of getting it wrong are invisible until production load hits: a thread pool sized for 50 concurrent requests that quietly saturates at 200, a CompletableFuture chain that swallows exceptions because no one added exceptionally(), a fan-out that completes 99 of 100 calls in 50ms and then waits 30 seconds for the one that timed out. I have spent more time debugging Java concurrency bugs than I care to admit. The ones that hurt most were not deadlocks β€” those are at least loud. They were the silent ones: a shared SimpleDateFormat causing random NumberFormatException in production, a ForkJoinPool getting saturated by blocking I/O calls that belonged in a separate executor, a CompletableFuture chain that worked perfectly in tests because the test executor ran everything synchronously. These 20 prompts encode the diagnostic questions and implementation patterns I reach for in those situations. AI assistants are unusually well-suited to Java concurrency work. They know the java.util.concurrent API surface in full, understand the difference between thenApply and thenApplyAsync, can read a thread dump and identify the contention point, and will not lose track of which executor each stage of a pipeline is running on. The prompts below are structured to give the AI exactly what it needs to help: the right diagnostic data for debugging prompts, and the right context (pool sizes, workload type, latency targets) for implementation prompts.

This post gives you 20 copy-paste AI prompts for Java concurrency and CompletableFuture, progressing from fundamentals through advanced production patterns and into debugging. Each prompt is explained so you know when to reach for it and what context to provide for the best output.

For background on the underlying mechanics, see the Java Concurrency Deep Dive and Virtual Threads vs Platform Threads benchmarks. For the performance analysis layer, see 10 AI Prompts for Java Performance Optimization.

Continue reading 20 AI Prompts for Java Concurrency and CompletableFuture

8086 Interrupt System: IVT, ISR Writing, and Hardware Interrupts

Every keypress, timer tick, and disk operation on an 8086 system is ultimately driven by interrupts. When hardware raises a signal, the CPU stops what it’s doing, saves its position, runs a handler routine, and returns to exactly the instruction it left β€” all in hardware, with zero polling overhead. The mechanism that makes this work is a 1 KB lookup table at the very bottom of memory, and a precise six-step acknowledgment sequence that every interrupt triggers. Understanding both is fundamental to writing system-level 8086 code.

Continue reading 8086 Interrupt System: IVT, ISR Writing, and Hardware Interrupts

8086 Flag Register: All 9 Flags, Conditional Jumps, and Critical Traps

Nine bits in a 16-bit register β€” that’s the entire flag register of the 8086. But those nine bits govern every conditional branch, every signed and unsigned comparison, every string direction, every interrupt decision. Knowing which flag to check after which operation β€” and especially the four traps that catch almost everyone β€” is what separates code that works from code that works most of the time.

Continue reading 8086 Flag Register: All 9 Flags, Conditional Jumps, and Critical Traps

8086 Stack Operations: SS:SP, PUSH/POP, CALL/RET, and Stack Frames

Every time you call a procedure, the 8086 stack quietly saves a return address, shuffles a stack pointer, and creates a structured frame of memory that the callee can use for parameters and local variables. The moment things go wrong β€” a missed POP, a wrong RET, a mismatched call convention β€” the crash that follows feels completely unrelated to the actual bug. This post makes the mechanics explicit, from the exact bytes SP points to on every PUSH, to how BP creates a stable window into a procedure’s own data that survives any number of nested calls.

Continue reading 8086 Stack Operations: SS:SP, PUSH/POP, CALL/RET, and Stack Frames

8086 Addressing Modes: EA Calculation, String Instructions, and REP Prefixes

Every memory instruction on the 8086 asks the same question: where is the data? The answer is an Effective Address (EA) β€” a 16-bit offset computed entirely inside the Execution Unit before the Bus Interface Unit ever touches the address bus. How that EA is computed is what defines the addressing mode. The choice you make directly affects instruction size, clock cycles, and code readability, so picking the right mode for each situation matters.

Continue reading 8086 Addressing Modes: EA Calculation, String Instructions, and REP Prefixes