Guide to Time and Date in Java (Part 1)

DZone 's Guide to

Guide to Time and Date in Java (Part 1)

In part 1, Tomasz Nurkiewicz provides insight into several time and date tools for Java, and how to avoid major issues with building time into applications.

· Java Zone ·
Free Resource

Properly handling dates, time, time zones, daylight saving time, leap years, and such has been my pet peeve for a long time. This article is not a comprehensive guide to time domain; see Date and time in Java—much more detailed but slightly, ahem, dated. It's still relevant, but doesn't cover java.time from Java 8. Here, I want to cover the absolute minimum that every junior Java developer should be aware of...

Image title

When Did an Event Happen?

Philosophy and quantum physics aside, we may treat time as a one-dimensional metric, a real number value. This value keeps growing when time passes by. If one event appeared after another, we assign greater time to that event. Two events happening simultaneously have the same time value. For practical reasons, in computer systems we store time in a discrete integer, mainly because computer clocks tick discretely. Therefore we can store time as an integer value. By convention, we assign time = 0 to January 1st, 1970 but in Java we increment this value every millisecond, not second like in UNIX time. Historically using 32-bit signed integer in UNIX time will cause year 2038 problem. Thus, Java stores time in 64-bit integer, which is sufficient even if you increment it a thousand times more often. That being said, the simplest, yet valid way of storing time in Java is... 

long primitive:

long timestamp = System.currentTimeMillis();

The problem with long is that it's so prevalent, that using it for storing time undermines the type system. It may be an ID, may be a hash value, and can be anything. Also, long doesn't have any meaningful methods related to time domain. The very first approach to wrap long in a more meaningful object was java.util.date, known since Java 1.0:

Date now = new Date();

 Date class however has numerous flaws:

  1. It does not represent... date. Seriously, officially date is "[...] the day of the month or year as specified by a number [...]" [1] whereas in Java it represents point in time without any specific calendar (day/month/year).
  2. Its toString() is misleading, displaying calendar date and time in system timezone. Not only has it misled thousands of developers to think that Date has a timezone attached. Moreover, it shows time, but date should only represent day, not hour.
  3. It has 20+ deprecated methods, including getYear()parse(String), and many constructors. These methods are deprecated for a reason, because they lead you to believe Daterepresents, you know, date.
  4. java.sql.Date extends java.util.Date and is actually much more accurate because it indeed represents calendar date (DATE in SQL). However, this narrows the functionality of base class Date, thus violating Liskov substitution principle. Don't believe me?java.util.Date.toInstant() works as expected but java.sql.Date.toInstant()fails unconditionally with UnsupportedOperationException...
  5. Worst of them all, Date is mutable.

Ever wondered why old and grumpy developers in your team are so excited about immutability? Imagine a piece of code that adds one minute to any Date. Simple, huh?

Date addOneMinute(Date in) {
    in.setTime(in.getTime() + 1_000 * 60);
    return in;

Looks fine, right? All test cases pass because who on earth would ever validate that input parameters are intact after testing code?

Date now = new Date();

The output may look as follows:

Tue Jul 26 22:59:22 CEST 2016
Tue Jul 26 23:00:22 CEST 2016
Tue Jul 26 23:00:22 CEST 2016

Did you notice that now the value was actually changed after adding one minute? When you have a function that takes Date and returns Date you would never expect it to modify its parameters! It's like having a function taking x and y numbers and returning the sum of them. If you discover that x was somehow modified during the course of addition, all your assumptions are ruined. By the way, that is the reason why java.lang.Integer is immutable. Or String. Or BigDecimal.

This is not a contrived example. Imagine a ScheduledTask class with a single method:

class ScheduledTask {
    Date getNextRunTime();

What happens if I say:

ScheduledTask task = //...
task.getNextRunTime().setTime(new Date());

Does changing the returned Date have effect on the next run time? Or, maybe ScheduledTask returns a copy of its internal state that you are free to modify? Maybe we will leave ScheduledTask in some inconsistent state? If Date was immutable, no such problem would ever arise.

Interestingly, every Java developer will become furious if you confuse Java with JavaScript. But guess what, Date in JavaScript has the exact same flaws as java.util.Date and seems like a bad example of copy-paste. 

Date in JavaScript is mutable, has a misleading toString(), and no support for time zones whatsoever.

A great alternative to Data is java.time.Instant. It does precisely what it claims: stores an instant in time. Instant does not have date or calendar related methods, its toString() uses familiar ISO format in UTC time zone (more on that later), and most importantly: it's immutable. If you want to remember when a particular event happened, Instant is the best you can get in plain Java:

Instant now = Instant.now();
Instant later = now.plusSeconds(60);

Notice that Instant does not have plusMinutes(), plusHours(), and so on. Minutes, hours, and days are concepts related to calendar systems, whereas Instant is geographically and culturally agnostic.


Once again, we've barely scratched the surface here. Stay tuned for part two where we'll discuss the delightful quirks of working with time zones!

date and time, java, time

Published at DZone with permission of Tomasz Nurkiewicz , DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}