Exploring a directory with the Java 8 Stream API

We saw an example of using the Java 8 Stream API in File I/O in this post. We saw how the Files object was enhanced with the lines() method to open a line reader stream to a text file.

There are other enhancements related to streams that make is simple to explore a directory on your hard drive. The following code example will collect all folders and files within the c:\gitrepos folder and add them to an ArrayList:

Path gitReposFolderPath = Paths.get("c:\\gitrepos");
gitReposFolderPath.toFile().getName();
try (Stream<Path> foldersWithinGitReposStream = Files.list(gitReposFolderPath))            
{
    List<String> elements = new ArrayList<>();
    foldersWithinGitReposStream.forEach(p -> elements.add(p.toFile().getName()));            
    System.out.println(elements);
}
catch (IOException ioe)
{

}

I got the following output:

[cryptographydotnet, dotnetformsbasedmvc5, entityframeworksixdemo, owinkatanademo, signalrdemo, singletondemoforcristian, text.txt, webapi2demo, windowsservicedemo]

The code returns both files and folders one level below the top directory, i.e. the “list” method does not dive into the subfolders. I put a text file into the folder – text.txt – just to test whether in fact all elements are returned.

Say you only need files – you can use the filter method:

foldersWithinGitReposStream.filter(p -> p.toFile().isFile()).forEach(p -> elements.add(p.toFile().getName())); 

This will only collect text.txt.

Let’s try something slightly more complex. We’ll organise the elements within the directory into a Map of Boolean and List of Paths. The key indicates whether the group of files are directories or not. We can use the collect method that we saw in this post:

try (Stream<Path> foldersWithinGitReposStream = Files.list(gitReposFolderPath))            
{
    Map<Boolean, List<Path>> collect = foldersWithinGitReposStream.collect(Collectors.groupingBy(p -> p.toFile().isDirectory()));
    System.out.println(collect);
}

This prints the following:

{false=[c:\gitrepos\text.txt], true=[c:\gitrepos\cryptographydotnet, c:\gitrepos\dotnetformsbasedmvc5, c:\gitrepos\entityframeworksixdemo, c:\gitrepos\owinkatanademo, c:\gitrepos\signalrdemo, c:\gitrepos\singletondemoforcristian, c:\gitrepos\webapi2demo, c:\gitrepos\windowsservicedemo]}

So we successfully grouped the paths.

As mentioned above the “list” method goes only one level deep. The “walk” method in turn digs deeper and extracts sub-directories as well:

try (Stream<Path> foldersWithinGitReposStream = Files.walk(gitReposFolderPath))
{
    List<String> elements = new ArrayList<>();
    foldersWithinGitReposStream.filter(p -> p.toFile().isFile()).forEach(p -> elements.add(p.toFile().getAbsolutePath()));
    System.out.println(elements);
}

We can also instruct the walk method to go n levels down with an extra integer argument:

try (Stream<Path> foldersWithinGitReposStream = Files.walk(gitReposFolderPath, 3))

View all posts related to Java here.

Advertisement

Reading text files using the Stream API in Java 8

We discussed the Java 8 Stream API thoroughly on this blog starting here. We mostly looked at how the API is applied to MapReduce operations to analyse data in a stream.

The same API can be applied to File I/O. Java 8 adds a new method called “lines” to the BufferedReader object which opens a Stream of String. From then on it’s just standard Stream API usage to filter the lines in the file – and perform other operations on them in parallel such as filtering out the lines that you don’t need.

Here’s an example how you can read all lines in a file:

String filename = "c:\\logs\\log.txt";
File logFile = new File(filename);
try (BufferedReader reader = new BufferedReader(new FileReader(logFile));)
{
    StringBuilder fileContents = new StringBuilder();
    Stream<String> fileContentStream = reader.lines();
    fileContentStream.forEach(l -> fileContents.append(l).append(System.lineSeparator()));
    System.out.println(fileContents.toString());
}
catch (IOException ioe)
{

}

We simply append each line in the stream to a StringBuilder.

In my case the log.txt file has the following contents:

hello
this is a line
next line
this is another line
…and this is yet another line
goodbye

As we’re dealing with the Stream API the usual Map, Filter and Reduce methods are all available to be performed on the text. E.g. what if you’re only interested in those lines that start with “this”? Easy:

fileContentStream.filter(l -> l.startsWith("this"))
                    .forEach(l -> fileContents.append(l).append(System.lineSeparator()));

The StringBuilder will now only hold the following lines:

this is a line
this is another line

You can also use the Path and Files objects that were introduced in Java 7. The Files object too was extended with a method to get hold of the Stream object. The below example is equivalent to the above:

Path logFilePath = Paths.get("C:\\logs\\log.txt");
try (Stream<String> fileContentStream = Files.lines(logFilePath))            
{
    StringBuilder fileContents = new StringBuilder();
    fileContentStream.filter(l -> l.startsWith("this"))
            .forEach(l -> fileContents.append(l).append(System.lineSeparator()));
    System.out.println(fileContents.toString());
}
catch (IOException ioe)
{

}

View all posts related to Java here.

Localising dates in Java 8 using DateTimeFormatter

Introduction

In this post we saw how to format dates according to some ISO and RCF standards. They can help you to quickly format a date in a standardised way. However, if you’re looking for date localisation then you’ll need something else.

By localising dates we mean that we want to show dates in an application according to the user’s region. A Japanese user will want to see the dates according to the Japanese date convention. You can store UTC dates internally according to an ISO standard but follow some local convention when presenting it on the screen.

Locales

A Locale represents a region and one or more corresponding cultures, most often with a country and one or more languages. You can easily list all available Locales:

Locale[] locales = Locale.getAvailableLocales();
        for (Locale locale : locales)
        {
            System.out.println(locale.getCountry());
            System.out.println(locale.getDisplayCountry());
            System.out.println(locale.getDisplayLanguage());
        }

You’ll see values such as…

PE
Peru
Spanish
ID
Indonesia
Indonesian
GB
United Kingdom
English

Some locales are stored as static properties of the Locale object, e.g.:

Locale.JAPAN
Locale.FRANCE
Locale.US

We’ll need to use the ZonedDateTime object to format a date according to a Locale. The following code will format the UTC date according to the US standard:

ZonedDateTime utcDateZoned = ZonedDateTime.now(ZoneId.of("Etc/UTC"));
DateTimeFormatter pattern = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).withLocale(Locale.US);
System.out.println(utcDateZoned.format(pattern));

The output will be Friday, November 21, 2014 1:45:14 PM UTC.

Let’s see the UTC dates in France and Japan:

DateTimeFormatter pattern = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).withLocale(Locale.FRANCE);
System.out.println(utcDateZoned.format(pattern));

… vendredi 21 novembre 2014 13 h 50 UTC

DateTimeFormatter pattern = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).withLocale(Locale.JAPAN);
System.out.println(utcDateZoned.format(pattern));

2014年11月21日 13時51分34秒 UTC

View all posts related to Java here.

Formatting dates in Java 8 using DateTimeFormatter

Introduction

Formatting dates – and numbers for that matter – can be a complex matter. The DateTimeFormatter class provides pre-defined formats that adhere to ISO and RCF specifications.

DateTimeFormatter

The following date related classes we’ve seen on this blog, i.e.

…have a method called “format” which accepts a DateTimeFormatter class. This class has a number of pre-defined formats that you can readily use in your application. Note that not all such formats will be available. The availability depends on the exact object type of the date class. E.g. ISO_ZONED_DATE_TIME won’t work with LocalDateTime as it is meant to format zoned dates. Also, ISO_DATE_TIME won’t be available for the LocalDate class as it has no concept of time units below the level of days.

Let’s test all the predefined values with the above date types.

LocalDate

We run the following code and check the output:

System.out.println("Running example with LocalDate class.");
LocalDate now = LocalDate.now();
        try
        {
            System.out.println("ISO_DATE: " + now.format(DateTimeFormatter.ISO_DATE));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_DATE is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("BASIC_ISO_DATE: " + now.format(DateTimeFormatter.BASIC_ISO_DATE));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("BASIC_ISO_DATE is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_DATE_TIME: " + now.format(DateTimeFormatter.ISO_DATE_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_DATE_TIME is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_INSTANT: " + now.format(DateTimeFormatter.ISO_INSTANT));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_INSTANT is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_LOCAL_DATE: " + now.format(DateTimeFormatter.ISO_LOCAL_DATE));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_LOCAL_DATE is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_LOCAL_DATE_TIME: " + now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_LOCAL_DATE_TIME is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_LOCAL_TIME: " + now.format(DateTimeFormatter.ISO_LOCAL_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_LOCAL_TIME is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_OFFSET_DATE: " + now.format(DateTimeFormatter.ISO_OFFSET_DATE));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_OFFSET_DATE is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_OFFSET_DATE_TIME: " + now.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_OFFSET_DATE_TIME is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_OFFSET_TIME: " + now.format(DateTimeFormatter.ISO_OFFSET_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_OFFSET_TIME is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_ORDINAL_DATE: " + now.format(DateTimeFormatter.ISO_ORDINAL_DATE));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_ORDINAL_DATE is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_TIME: " + now.format(DateTimeFormatter.ISO_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_TIME is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_WEEK_DATE: " + now.format(DateTimeFormatter.ISO_WEEK_DATE));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_WEEK_DATE is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("ISO_ZONED_DATE_TIME: " + now.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("ISO_ZONED_DATE_TIME is not supported: " + e.getMessage());
        }
        try
        {
            System.out.println("RFC_1123_DATE_TIME: " + now.format(DateTimeFormatter.RFC_1123_DATE_TIME));
        } catch (UnsupportedTemporalTypeException e)
        {
            System.out.println("RFC_1123_DATE_TIME is not supported: " + e.getMessage());
        }

We get the following output:

Running example with LocalDate class.
ISO_DATE: 2014-11-01
BASIC_ISO_DATE: 20141101
ISO_DATE_TIME is not supported: Unsupported field: HourOfDay
ISO_INSTANT is not supported: Unsupported field: InstantSeconds
ISO_LOCAL_DATE: 2014-11-01
ISO_LOCAL_DATE_TIME is not supported: Unsupported field: HourOfDay
ISO_LOCAL_TIME is not supported: Unsupported field: HourOfDay
ISO_OFFSET_DATE is not supported: Unsupported field: OffsetSeconds
ISO_OFFSET_DATE_TIME is not supported: Unsupported field: HourOfDay
ISO_OFFSET_TIME is not supported: Unsupported field: HourOfDay
ISO_ORDINAL_DATE: 2014-305
ISO_TIME is not supported: Unsupported field: HourOfDay
ISO_WEEK_DATE: 2014-W44-6
ISO_ZONED_DATE_TIME is not supported: Unsupported field: HourOfDay
RFC_1123_DATE_TIME is not supported: Unsupported field: HourOfDay

LocalTime

We run the same code as above but change the class type:

System.out.println("Running example with LocalTime class.");
LocalTime now = LocalTime.now();

…and here’s the output, again with Locale set to Sweden:

Running example with LocalTime class.
ISO_DATE is not supported: Unsupported field: Year
BASIC_ISO_DATE is not supported: Unsupported field: Year
ISO_DATE_TIME is not supported: Unsupported field: Year
ISO_INSTANT is not supported: Unsupported field: InstantSeconds
ISO_LOCAL_DATE is not supported: Unsupported field: Year
ISO_LOCAL_DATE_TIME is not supported: Unsupported field: Year
ISO_LOCAL_TIME: 22:02:52.932
ISO_OFFSET_DATE is not supported: Unsupported field: Year
ISO_OFFSET_DATE_TIME is not supported: Unsupported field: Year
ISO_OFFSET_TIME is not supported: Unsupported field: OffsetSeconds
ISO_ORDINAL_DATE is not supported: Unsupported field: Year
ISO_TIME: 22:02:52.932
ISO_WEEK_DATE is not supported: Unsupported field: WeekBasedYear
ISO_ZONED_DATE_TIME is not supported: Unsupported field: Year
RFC_1123_DATE_TIME is not supported: Unsupported field: DayOfMonth

Most formats are not supported by LocalTime as there’s no notion of days, months etc. in that object.

LocalDateTime

Run the same code with “now” set as follows:

System.out.println("Running example with LocalDateTime class.");
LocalDateTime now = LocalDateTime.now();

…and I got the following output:

Running example with LocalTime class.
ISO_DATE: 2014-11-01
BASIC_ISO_DATE: 20141101
ISO_DATE_TIME: 2014-11-01T22:07:24.329
ISO_INSTANT is not supported: Unsupported field: InstantSeconds
ISO_LOCAL_DATE: 2014-11-01
ISO_LOCAL_DATE_TIME: 2014-11-01T22:07:24.329
ISO_LOCAL_TIME: 22:07:24.329
ISO_OFFSET_DATE is not supported: Unsupported field: OffsetSeconds
ISO_OFFSET_DATE_TIME is not supported: Unsupported field: OffsetSeconds
ISO_OFFSET_TIME is not supported: Unsupported field: OffsetSeconds
ISO_ORDINAL_DATE: 2014-305
ISO_TIME: 22:07:24.329
ISO_WEEK_DATE: 2014-W44-6
ISO_ZONED_DATE_TIME is not supported: Unsupported field: OffsetSeconds
RFC_1123_DATE_TIME is not supported: Unsupported field: OffsetSeconds

ZonedDateTime

This is probably the most interesting case as it supports all the predefined formats in DateTimeFormatter. Let’s look at an example with Australia/Adelaide:

System.out.println("Running example with ZonedDateTime class.");
ZoneId brisbane = ZoneId.of("Australia/Adelaide");
ZonedDateTime now = ZonedDateTime.of(LocalDateTime.now(), brisbane);

Running example with ZonedDateTime class.
ISO_DATE: 2014-11-01+10:30
BASIC_ISO_DATE: 20141101+1030
ISO_DATE_TIME: 2014-11-01T22:13:48.87+10:30[Australia/Adelaide]
ISO_INSTANT: 2014-11-01T11:43:48.870Z
ISO_LOCAL_DATE: 2014-11-01
ISO_LOCAL_DATE_TIME: 2014-11-01T22:13:48.87
ISO_LOCAL_TIME: 22:13:48.87
ISO_OFFSET_DATE: 2014-11-01+10:30
ISO_OFFSET_DATE_TIME: 2014-11-01T22:13:48.87+10:30
ISO_OFFSET_TIME: 22:13:48.87+10:30
ISO_ORDINAL_DATE: 2014-305+10:30
ISO_TIME: 22:13:48.87+10:30
ISO_WEEK_DATE: 2014-W44-6+10:30
ISO_ZONED_DATE_TIME: 2014-11-01T22:13:48.87+10:30[Australia/Adelaide]
RFC_1123_DATE_TIME: Sat, 1 Nov 2014 22:13:48 +1030

So you see that these predefined formatters follow the accepted ISO and RFC specifications and won’t actually show the localised date format the way dates are formatted in e.g. Japan or the US.

We’ll take a look at date localisation using the DateTimeFormatter in the next post in this series.

View all posts related to Java here.

Time zones in Java 8 Date and Time API

Introduction

I know for a fact that all programmers love working with time zones. Chances are high that you, as a reader of this blog, are also a programmer so I bet you also just love time zones. Let’s see what Java 8 offers as far as time zones are concerned.

Time zones

So far in this series on the date and time in Java 8 we always worked with the local time zone found on your computer. All date-related classes, such as LocalTime or LocalDateTime allow you to easily set the time zone. Here’s an example with the LocalDateTime class:

ZoneId zoneId = ZoneId.of("Europe/Budapest");
LocalDateTime now = LocalDateTime.now(zoneId);

Where are these string values coming from? They follow what Internet Assigned Numbers Authority (IANA) has in its database. That page doesn’t offer a readable list of time zones, they are only available in compressed .tar.gz files. In case you don’t want to deal with those you can find the time zones on Wikipedia.

You can get the full list of time zones in Java 8 as follows:

Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();

There’s support for 586 time zones which should be enough for all cases.

You can view the default time zone of your environment like this:

ZoneId systemDefault = ZoneId.systemDefault();

…which in my case returned “Europe/Berlin”.

Setting the time zone will also take care of the summer and winter time adjustments automatically.

ZonedDateTime

What if you want to set the parts of the date individually and set the time zone at the same time? We need to turn to the ZonedDateTime class. The static “of” method has many overloads for granular access. Here’s an example that takes a LocalDateTime instance and a ZoneId, we convert the Budapest time to the Brisbane time zone:

ZoneId zoneId = ZoneId.of("Europe/Budapest");
LocalDateTime now = LocalDateTime.now(zoneId);
ZoneId brisbane = ZoneId.of("Australia/Adelaide");
ZonedDateTime zonedDate = ZonedDateTime.of(now, brisbane);

Here’s another example that builds a zoned time out of LocalDate and LocalTime:

ZoneId canadaTz = ZoneId.of("Canada/Central");
LocalDate canadaDate = LocalDate.of(2014, Month.MARCH, 15);
LocalTime canadaTime = LocalTime.of(13, 24, 12);
ZonedDateTime canadaZonedTime = ZonedDateTime.of(canadaDate, canadaTime, canadaTz);

The ZonedDateTime class behaves in much the same way as LocalDateTime and has very similar methods, like “plus”, “minus”, “get” etc. I won’t repeat them here, you can check out the following posts to see how they behave:

You can find the difference in minutes between two zoned times as follows:

ZoneId canadaTz = ZoneId.of("Canada/Central");
LocalDate canadaDate = LocalDate.of(2014, Month.MARCH, 15);
LocalTime canadaTime = LocalTime.of(13, 24, 12);
ZonedDateTime canadaZonedTime = ZonedDateTime.of(canadaDate, canadaTime, canadaTz);
        
ZoneId santoDomingoTz = ZoneId.of("America/Santo_Domingo");
LocalDate santoDomingoDate = LocalDate.of(2014, Month.MARCH, 15);
LocalTime santoDomingoTime = LocalTime.of(13, 24, 12);
ZonedDateTime santoDomingoZonedTime = ZonedDateTime.of(santoDomingoDate, santoDomingoTime, santoDomingoTz);
        
long until = santoDomingoZonedTime.until(canadaZonedTime, ChronoUnit.MINUTES);

“until” will be 60 minutes at the time of writing this post as Canada observes Daylight Saving Time whereas Dominica in the Caribbean region does not. “until” will be 120 minutes as soon as DST is over.

You can easily switch the time zone of a ZonedDateTime instance:

ZonedDateTime converted = santoDomingoZonedTime.withZoneSameInstant(ZoneId.systemDefault());

View the next post here which takes up formatting zoned date times.

View all posts related to Java here.

Adjusting the date in Java 8 Date and Time API

Introduction

We saw a couple of new concepts in the Java 8 Date and Time API on this blog:

All the above classes expose methods called “with” with a couple of overloads. LocalDate, LocalTime and LocalDateTime come with other methods whose names start with “with”, such as withSeconds or withMonth depending on the supported level of time unit. The “with” methods adjust some value of the date-related instances and return a new instance.

Examples

Here’s how you can adjust the day within the month of the LocalDate instance:

LocalDate currentLocalDate = LocalDate.now();
LocalDate dayOfMonthAdjusted = currentLocalDate.withDayOfMonth(12);

The above code will set the day to the 12th of the month of “currentLocalDate” and return the new LocalDate instance “dayOfMonthAdjusted”. Here come some similar adjusters:

LocalDate currentLocalDate = LocalDate.now();
LocalDate dayOfYearAdjusted = currentLocalDate.withDayOfYear(234);

I wrote this post in the year of 2014 so the year value of currentLocalDate was 2014. withDayOfYear will set the day within the year starting from Jan 01. The value of 234 will set the “dayOfYearAdjusted” to 2014-08-22.

The withMonth and withYear modify the month and year values respectively.

LocalTime has similar methods: withHour, withMinute, withSecond and withNano that behave the same way as e.g. withMonth in the case of the LocalDate class.

LocalDateTime has all those methods available: from withYear down to withNano as that class supports these levels of granularity.

TemporalAdjuster

The “with” methods of the previous section are not available for the Instant class. There’s however an interesting overload of “with” for both LocalDate and Instant, the one which accepts a TemporalAdjuster object. The following example will set find the most recent Monday relative to the current day:

LocalDate previousMonday = currentLocalDate.with(TemporalAdjusters.previous(DayOfWeek.MONDAY));

Similarly, you can find the following Monday:

LocalDate nextMonday = currentLocalDate.with(TemporalAdjusters.next(DayOfWeek.MONDAY));

The following code will find the 3rd Monday of the current month:

LocalDate thirdMonday = currentLocalDate.with(TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.MONDAY));

TemporalAdjusters has some more interesting constants, the method names are pretty descriptive:

  • firstDayOfMonth
  • firstDayOfNextMonth
  • lastInMonth and firstInMonth which accepts a DayOfWeek enumeration, i.e. you can find the first/last Monday, Tuesday etc. of a given month
  • lastDayOfMonth

Read the next part on Java 8 Dates here.

View all posts related to Java here.

Java 8 Date and time API: the Instant class

The Date and time API in Java 8 has been completely revamped. Handling dates, time zones, calendars etc. had been cumbersome and fragmented in Java 7 with a lot of deprecated methods. Developers often had to turn to 3rd party date handlers for Java such as Joda time.

One of many new key concepts in the java.time package of Java 8 is the Instant class. It represents a point of time in a continuous timeline where this time point is accurate to the level of nanoseconds.

The immutable Instant class comes with some default built-in values:

Instant now = Instant.now();
Instant unixEpoch = Instant.EPOCH;
Instant minimumInstant = Instant.MIN;
Instant maximumInstant = Instant.MAX;
  • “now”, as the name suggests, represent the current date, or current instance of time in the UTC time zone.
  • “unixEpoch” will be the traditional UNIX epoch date from 1970 January 1 midnight. This is also an important point of reference of the timeline of the Instant class. E.g. the date 2014-01-01 will have a positive number as the “seconds since EPOCH” whereas 1960-01-01 will get a negative value for the same property.
  • The minimum value of the Instant class is exactly 1 billion years ago, denoted as ‘-1000000000-01-01T00:00Z’ in the documentation. This is the starting point of the timeline of the Instant class
  • The maximum value is consequently the last moment of the year of 1 billion. This is the end point of the timeline of the Instant class

Example

Suppose you’d like to measure the time it takes to run a method:

Instant start = Instant.now();
Thread.sleep(10000);
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
long seconds = duration.getSeconds();

There we see yet another new object from the java.time package, namely Duration. The Duration object allows to easily retrieve the time span between two dates and retrieve the days, hours, seconds etc. of that time span. In this case “seconds” will be equal to 10 as expected.

Note, however, that the Instant class cannot be used for date purposes such as February 23 2013. There’s no concept of years, months and days in the Instant class like we have in Date and Calendar in Java 7. If you’re looking to handle dates then LocalDate, LocalDateTime and LocalTime classes will be more useful. Check out the the link at the end of this post to find the posts on these and various other date-related classes in Java 8.

Duration

The Duration class has a number of useful methods. Duration is very similar to the Period class we saw in the post referenced in the previous paragraph. Here come some examples.

Duration.between

Suppose you have two Duration classes and you’d like to see which one was longer. The “compareTo” method will help you:

Instant startOne = Instant.now();
Thread.sleep(1000);
Instant endOne = Instant.now();
Duration durationOne = Duration.between(startOne, endOne);

Instant startTwo = Instant.now();
Thread.sleep(100);
Instant endTwo = Instant.now();
Duration durationTwo = Duration.between(startTwo, endTwo);
    
int compareTo = durationOne.compareTo(durationTwo);

compareTo will be 1 in the above example as the first part of the comparison, i.e. durationOne is longer. It will be -1 if comparisonTwo is longer and 0 if they are of equal length.

divideBy

You can also divide a duration by a value to see how many sections of that value fit into a duration:

Duration dividedBy = durationOne.dividedBy(10);
long toMillis = dividedBy.toMillis();

Here we want to divide durationOne, i.e. 100 millis by 10 millis. The variable “dividedBy” will almost always get the value 10 as 100 / 10 = 10 but the exact timing can depend on the code execution when “startOne” and “startTwo” are created, so you might see 11 sometimes.

isZero

This is to check if two instances happened at the same time, i.e. there’s no duration between them:

Duration zeroDuration = Duration.between(startOne, startOne);
boolean zero = zeroDuration.isZero();

“zero” will be true in this case.

isNegative

isNegative will occur if the end date occurred before the start date. I’m not sure how that scenario can occur but let’s deliberately supply the wrong values to the between method:

Duration negativeDuration = Duration.between(endOne, startOne);
boolean negative = negativeDuration.isNegative();

“negative” will be true.

Plus and minus methods

You’ll find a range of methods whose names start with “plus” and “minus”. They are meant to add and subtract time units to and from a Duration instance. Examples:

Duration minusMinutes = durationOne.minusMinutes(10);
Duration plusDays = durationOne.plusDays(2);
Duration plus = durationOne.plus(durationTwo);

Read the next post on Java 8 Dates here.

View all posts related to Java here.

Java 8 Date and time API: the LocalDateTime class

Introduction

In this post we saw how to represent dates on the level of days, such as 2014-10-05 using the LocalDate class. This post discussed the usage of LocalTime to show the point of time within the 24-hr clock, such as 11:45:43.

LocalDate has no concept of time units below the day level. LocalTime has no concept of time above the level of hours. However, what if you need to represent the date as 2014-10-05 11:45:43, i.e. with both the day and time sections? You can turn to the aptly named LocalDateTime class which marries LocalTime and LocalDate.

The usage of LocalDateTime is very similar to both LocalDate and LocalTime. You can quickly read through the posts referenced above for further information. Most date-related methods are common for LocalDate, LocalTime and LocalDateTime.

LocalDateTime

You can get the current local date-time as follows:

LocalDateTime now = LocalDateTime.now();

This will get the current date according to the default time zone of your computer.

You can construct a new LocalDateTime instance using the various static “of” methods, e.g.

LocalDateTime someDateInPast = LocalDateTime.of(2014, Month.MAY, 23, 10, 23, 43);

You can add/subtract some units of time using the “plus” and “minus” methods. The “until” method will find the time span between the two time points in the provided unit of measurement:

LocalDateTime later = now.plusMinutes(321);
long until = now.until(later, ChronoUnit.MINUTES);

“until” will be 321 minutes as expected.

We saw that in the case of LocalDate and LocalTime not all enumeration types of ChronoUnit are supported which is due to the allowed level of granularity. LocalDateTime allows for all values in the enumeration, from nanoseconds to eras – defined as 1 billion years in Java 8, i.e. you can measure the difference between two LocalDateTime instances in terms of nanoseconds ranging to eras – as long as the “long” type supports them which might not be the case with nanoseconds given a large enough time range.

The isAfter and isBefore methods work as the method names imply:

LocalDateTime now = LocalDateTime.now();
LocalDateTime someDateInPast = LocalDateTime.of(2014, Month.MAY, 23, 10, 23, 43);
boolean before = now.isBefore(later);
boolean after = now.isAfter(later);

“before” will be true and “after” will be false as expected.

You can extract the LocalDate and LocalTime portions of LocalDateTime using the toLocalDate and toLocalTime methods:

LocalDate toLocalDate = now.toLocalDate();
LocalTime toLocalTime = now.toLocalTime();

You can extract the various portions of the LocalDateTime instance using the various “get” methods, such as:

LocalDateTime someDateInPast = LocalDateTime.of(2014, Month.MAY, 23, 10, 23, 43);
DayOfWeek dayOfWeek = someDateInPast.getDayOfWeek();
int dayOfYear = someDateInPast.getDayOfYear();
int year = someDateInPast.getYear();

The returned values are “FRIDAY”, 143 – i.e. the date in someDateInPast was the 143rd day in the year of 2014 -, and 2014 respectively.

View all posts related to Java here.

Java 8 Date and time API: the LocalTime class

Introduction

In this post we saw how to handle local date values to the level of days with the LocalDate object. A typical point in time handled through this object is e.g. 2014-03-02. There’s no concept of hours and minutes in that object.

LocalTime

The “time of day” equivalent of LocalDate is LocalTime and its usage is very similar. I recommend you read through the post referenced above as many methods, like the “plus” and “minus” ones still apply in the same form. LocalTime will have no concept of days, months and years. You can use this class if e.g. some of your logic depends on the time of day every day, regardless of the calendar day.

Here’s how you can find the current time of day:

LocalTime now = LocalTime.now();

This will find the current time in the default time zone of your computer.

You can also create a time using the “of” static method. You’ll set the time to 5:32am as follows:

LocalTime early = LocalTime.of(5, 32);

You can add/subtract some units of time using the “plus” and “minus” methods. The “until” method will find the difference between the two time points in the provided unit of measurement:

LocalTime now = LocalTime.now();
LocalTime later = now.plusHours(2);
long until = now.until(later, ChronoUnit.MINUTES);

“until” will be 120 as there are 120 minutes from “now” until “now + 2 hrs” of course. However, if you run this code at e.g. 23:30 in your time zone then “until” will be a negative value as 23:30 plus 2 hrs is 01:30. There’s no “next day” in LocalTime so “until” in that case will be -1320 which is the same as -22 hrs.

Only those ChronoUnit enumerations are valid that make sense for the LocalTime class: Minutes, hours, seconds, etc., anything under the level of days. If you’re not sure then you can check if the ChronoUnit is supported using the isSupported method:

boolean supported = now.isSupported(ChronoUnit.CENTURIES);

The above code will yield “false”.

The isAfter and isBefore methods work as the method names imply:

LocalTime now = LocalTime.now();
LocalTime later = now.plusMinutes(10);
boolean before = now.isBefore(later);
boolean after = now.isAfter(later);

However, be careful with the return values. Just like above, it depends on when during the day you run this code so don’t assume that “before” will always be true and “after” will always be false in the above example. If you run this code at 23:58 then the return values will be the exact opposite as 23:58 + 10 minutes = 00:08 which will be before 23:58 and 23:58 comes after 00:08.

You can use the overridden “compareTo” method in a similar manner – it will return -1, 0 or 1 depending on which side of the comparison comes first – but again the result will depend on the exact timing.

In the next post we’ll look at the LocalDateTime class.

View all posts related to Java here.

Java 8 Date and time API: the LocalDate class

Introduction

The Date and time API in Java 8 has been completely revamped. Handling dates, time zones, calendars etc. had been cumbersome and fragmented in Java 7 with a lot of deprecated methods. Developers often had to turn to 3rd party date handlers for Java such as Joda time.

One of many new key concepts in the java.time package of Java 8 is the immutable LocalDate class. A LocalDate represents a date at the level of days such as April 04 1988.

LocalDate

You can easily get hold of the current date of the default time zone of the local computer – in my case it’s CET:

LocalDate localDate = LocalDate.now();

…or you can construct a date using the static “of” method:

LocalDate someDayInApril = LocalDate.of(1988, Month.APRIL, 4);

LocalDate comes with the following constants:

  • LocalDate.MAX: ranges until year-month-day of ‘+999999999-12-31’
  • LocalDate.MIN: reaches as far back as years-months-days ‘-999999999-01-01’

Period

The Period class is strongly related to LocalDate. You can find the time span between two LocalDate instances using the “until” method which returns a Period object:

Period timeSpan = someDayInApril.until(localDate);
int years = timeSpan.getYears();
int months = timeSpan.getMonths();
int days = timeSpan.getDays();

At the time of writing this post there were 26 years, 6 months and 27 days between the two dates.

The Period class has a static “between” method to achieve the same:

Period between = Period.between(someDayInApril, localDate);

…which yields the same time difference as the “until” method above.

Normalisation

A period can be normalised so that values like 13 months can be changed to 1 year and 1 month instead. Let’s create a 23-month period and normalise it:

Period ofMonths = Period.ofMonths(23);
Period normalized = ofMonths.normalized();

“normalized” will have 1 year and 11 months for the getYears and getMonths values respectively. “ofMonth” would have 0 years and 23 months instead.

Let’s test this with days:

Period ofDays = Period.ofDays(4322);
Period daysNormalised = ofDays.normalized();

In this case, however, both will yield getDays = 4322, years and months will be 0. This is expected as the number of days in a month can vary so the calculation would be based on some average, like 30 days at best but that result would almost certainly be incorrect. The previous example succeeded as the number of months in a year is set.

Zero period

The isZero() method of Period returns true if we compare two periods and they refer to the same date:

Period zeroPeriod = someDayInApril.until(someDayInApril);
boolean zero = zeroPeriod.isZero();

…zero will be “true”.

Negative period

Negative periods occur if we compare two LocalDate instances and take the later date as point of reference in the comparison:

Period negativePeriod = localDate.until(someDayInApril);
boolean negative = negativePeriod.isNegative();

“negative” will be true. The year, month and day values will be -26, -6 and -27.

You can easily transform that into a positive period though:

Period negated = negativePeriod.negated();

The year, month and day values of “negated” will be 26, 6 and 27.

Multiplication

You can multiply a Period with an integer which will multiply the year, month and day values with that integer without normalising the date. So in case you need to get a twice as long period you can do as follows:

Period twiceAsLong = ofDays.multipliedBy(2);

Plus and minus methods

Both the LocalDate and the Period classes have methods whose names start with “plus” or “minus” which serve to add or subtract a certain amount of time to and from a date/period. Examples:

LocalDate plusDays = localDate.plusDays(20);
LocalDate minusYears = localDate.minusYears(15);
Period minusMonths = between.minusMonths(11);

You can probably guess what these operations do.

Total number of time units

What if you’d like to know the total number of days between two LocalDate instances? The “until” method has an overload that you can use. E.g. here’s how you find the total number of days between “localDate” and “someDayInApril”:

long until = someDayInApril.until(localDate, ChronoUnit.DAYS);

…which at this time gives 9706 days. The ChronoUnit enumeration in the java.time.temporal package has other values to find the total number of “x” between two local dates but not all of them are supported in the “until” operation. E.g. you cannot calculate the number of hours or nanoseconds between two LocalDate instances. Any level of detail more fine grained than DAYS will throw an exception of type java.time.temporal.UnsupportedTemporalTypeException as LocalDate is only available at the day level: there’s no concept of hours, minutes etc. in the case of LocalDate.

In the next post we’ll look at the LocalTime class.

View all posts related to Java here.

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: