Java basics: Dates and Time

Table of Contents

Local date

Now

Let's start with the basics, we just want to represent the date today and print that local date now:

public class TimeTest {
    public static void main(String[] args) {
        LocalDate now = LocalDate.now();
        System.out.println(now);
    }
}

output: 
2022-08-29

Process finished with exit code 0

What if we want to pick a specific date in the future of today? We can use the plusDays, plusWeeks, plusMonths or plusYears for this:

        System.out.println(now.plusYears(5));

output: 
2027-08-29

Process finished with exit code 0

If you want a more generic way you can also use this:

        System.out.println(now.plus(3, ChronoUnit.YEARS));

output:
2025-08-29

Process finished with exit code 0

There is also a minus Method, which does exactly the opposite of the plus method.

Set a Date

There is a method, which allows you to specify a year, month and a day of month:

        LocalDate newYears = LocalDate.of(2020,1,1);
        System.out.println(newYears);

output:
2020-01-01

Process finished with exit code 0

Let's say that we wanted to know what day of the week was New Year's Day in 2020. That's really easy to do because there are some really convenient methods on our local date classes to give us that kind of information so we can do a get.

        System.out.println(newYears.getDayOfWeek());

output:

WEDNESDAY

Process finished with exit code 0


Same exists for:

  • getDayOfMonth
  • getDayOfYear
  • getMonth

Dates until

This returns a stream of local dates between the New Years date and now, for example. We can use the streams API and lambdas on Date: if we do something like a for each, we can print out each of these dates:

        System.out.println(newYears.getDayOfWeek());
        newYears.datesUntil(LocalDate.now())
                .forEach(System.out::println);


output: 

2020-01-01
2020-01-02
..
2022-08-26
2022-08-27
2022-08-28

Process finished with exit code 0

If you didn't want that to generate each and every single day, you can actually specify with an overloaded version of the *datesUntil* method a period, like so:

        newYears.datesUntil(LocalDate.now(), Period.ofMonths(1))
                .forEach(System.out::println);

output:

2020-01-01
2020-02-01
...
2022-06-01
2022-07-01
2022-08-01

Process finished with exit code 0

Local Time

The class local dates is quite similar to the local dates class and very helpful. Basically, all the things that we can do that make sense with local date, we can also do with local time.

Now

public class TimeTestTime {
    public static void main(String[] args) {
        LocalTime now = LocalTime.now();
        System.out.println(now);
    }
}

output:

06:17:48.708787

Process finished with exit code 0

Please note: Local date represents literally a date with no time component associated with it. Local time represents a time with no date associated with it.

Set a time

Of course you can also create a custom time and compere one time object with another:

        LocalTime lt1 = LocalTime.of(10,30);
        LocalTime lt2 = LocalTime.of(10,31);
        System.out.println(lt1.equals(lt2));

output:

false

Process finished with exit code 0

Local date time

This class allows us the full culmination of having both the date and the time together. Here we can specify a year, month, day, minute, year, month, day of month, hour, minute, second, nanoseconds. We can even build a local date time out of a local date and a local time, which makes sense, right?

public class TimeTestDateTime {
    public static void main(String[] args) {
        LocalDate ld1 = LocalDate.of(2000,1,1);
        LocalTime lt1 = LocalTime.of(10,30);
        LocalDate ld2 = LocalDate.of(2000,1,1);
        LocalTime lt2 = LocalTime.of(10,30);

        LocalDateTime ltd1 = LocalDateTime.of(ld1,lt1);
        LocalDateTime ltd2 = LocalDateTime.of(ld2,lt2);
        System.out.println(ltd1.equals(ltd2));
    }
}

output:

true

Process finished with exit code 0

Measuring how much time elapsed

You can easily just extract the local date from a local date time or the local time from a local date time. Or we can calculate the difference between two dates:

        Period diff = Period.between(ld1, ld2);
        System.out.printf("%d years, %d months, %d days%n", diff.getYears(), diff.getMonths(), diff.getDays());

output:

2 years, 5 months, 9 days

Process finished with exit code 0

The period method allows us measuring differences in terms of days, weeks, months and years. The first argument is meant to be the starting point or the earlier date, and then the second argument is meant to be the later date.

If we want to measure in smaller terms of just days, lets say we want to know the elapsed hours, minutes, seconds or milliseconds between two times, then we need to use the duration method, just like we used the period method before. For each duration method there is a toX method as well as a toXPart method. The toHoursPart method simply gives us back the actual number for the hours that we were receiving in the original duration object itself when we just called to string on the duration itself. This is just returning back the hours portion of that, whereas the toHours method, actually just takes the total number of seconds for the entire duration and divides it by 3600 being the number of seconds in an hour.

Instant

Instant is actually quite similar, to local date time, except that local date, local time and local date time are really all meant for human consumption. So if you're writing programs that are going to be dealing with dates and times that are meant for humans to enter or to read, you'd want to be using these classes generally speaking.

On the other hand, though functionally somewhat similar to the local date, time has a different purpose, and that is for machines to share timing with each other.

        System.out.println(Instant.now());

output:

2022-08-30T08:37:45.926007Z

Process finished with exit code 0

But the instant actually tracks time in terms of seconds. And this is actually part of a legacy in computing that dates back to a specific date in time 1970 January 1st, which is known as epoch time. The creators of the very popular operating system Unix chose to track time in their operating system from that date. And that's around the period of time when Unix was released. So that's kind of the birth time of Unix. And so they just chose 1970 January 1st, and presumably because so many programs written from that period of time forward used this notion of time tracking called the epoch time. It has persisted to this day, which is kind of weird when you really think about it. There may be times as a developer when you need to specify dates and times to other systems. Some of these other systems may require that you pass them date time information using epoch time. And if you need to do that, you can use the instant class to do so.

Time Zones

What if you have a WebApp which is used worldwide over different timezones? Which time should you use to save the data? How can you guarantee that the timestamps over different timezones are correctly displayed for each user?

Luckily there is a class to help us: ZonedDateTime. You need to provide a LocalDate or LocalDateTime as well as a ZoneId. If you use ZoneId.systemDefault() then Java uses the Timezone of your local Computer. Keep in mind that most often your software will run on a server and you don't want to use the timezone of your server, because most often your server does not run in the same TimeZone as you are:

System.out.println(ZonedDateTime.of(ltd1, ZoneId.systemDefault()));

example output:

2000-01-01T10:30+01:00[Europe/Zurich]

Process finished with exit code 0

but we can set the Timezone to anything we want, like so:

System.out.println(ZonedDateTime.of(ltd1, ZoneId.of("-5")));

output:

2000-01-01T10:30-05:00

One possible thing which we could do to solve the time difference between users, is that we could normalize the time in one standard time zone, which will typically be UTC. We need to store everything in UTC on the Server.

Let's say that our users want their message to be sent at moon on Christmas. And let's also say that our user lives in Switzerland, this is the information that get's transmitted from their Web browser to our servers. So far we have the information about the xmas date and time, as well as the users TimeZone:

        LocalDateTime xmas = LocalDateTime.of(2022,12,25,12,00);
        // Switzerland - GMT+01
        ZonedDateTime zxmas = ZonedDateTime.of(xmas, ZoneId.of("+1"));

to normalize this, we can now use this:

        System.out.println(zxmas.withZoneSameInstant(ZoneId.of("+0")));

output:

2022-12-25T11:00Z

Process finished with exit code 0

And this is finally the information we want to store in the database, if the database does not support TimeZones.

If you have a greenfield approach, or if you can control the database as much as your want, you should go ahead and store the Date, Time and TimeZone information all together in the database.

Temporal Adjuster

This is a weird name as long as you don't know what it is for. This class allows us to make adjustments to time. What does that mean? It means warping time in ways that humans might say in English, like for example, the Friday after next, or the next Thursday and things like that. So let's say you wanted to figure out the next Tuesday from that date on d1:

System.out.println(ld1.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)));

output:

2000-01-04

Process finished with exit code 0

So basically, what this means is from the date that we're specifying here the d1, whatever that date is, create another date that starts at that same point in time d1 and then generate a new date, which would represent the next and then whatever day of the week in this particular case. There are a lot more methods on the TermporalAdjusters like lastDayOfYear, lastDayOfMonth, firstInMonth only to name a few.

Immutability

All of these new daytime classes LocalDate, LocalTime, LocalDate, TimeZone, DateTime, et cetera are all designed to be immutable. Immutable is a term that you'll hear from time to time in software development, and it just means that you can't change the properties of that object after it has been created. This is considered to be a very desirable trait of objects frequently in programming, especially when you deal with functional programming styles.

So the DateTime API just embraces the immutable pattern throughout, but that comes with some consequences. That means that when we create an instance of a local data or time or any of these other classes, and we want to change some property of one of those objects, we can't just make a change. Instead, what we can do is we can basically clone an object and change things in the clone as it's being cloned.

System.out.println(ld1.withMonth(3).withDayOfMonth(15));

output:

2000-03-15

Process finished with exit code 0