Read lines successively
You seem to be stuck on this idea of reading pairs of lines together. No need for this simultaneous reading. Read one line, then read the next. You now have a pair of inputs to process. Then proceed to read the next line and the one after that. Process again. Lather, rinse, repeat.
Perhaps you are concerned about the performance implications of reading from storage. The BufferedReader
implementation handles the challenge of efficiently reading from storage, buffering chunks of data from the file in cache memory. So your pairs of lines are likely together in memory already. You can tweak the amount of buffer but the default is usually fine. A BufferedReader
can usually read millions of lines per second, so not likely to be real problem. Beware of premature optimization.
If you are really concerned about performance, use java.nio.file.Files.readAllLines
to read all the lines into memory at once. But, obviously, you must have enough free memory for the size of your input file. So the BufferedReader
is safer in case of unexpectedly large files.
Example code
In this example code we label each pair of lines read as odd-numbered and even-numbered given that we expect pairs.
Find your file in storage. Mine is named log.txt
in my home folder.
// Locate file in storage.
String homeFolder = System.getProperty ( "user.home" );
String fileName = "log.txt";
Path p = FileSystems.getDefault ().getPath ( homeFolder , fileName );
Define a DateTimeFormatter
to match the expected format of our input data. Note that we are using the modern java.time classes. Avoid the old legacy date-time classes that have proven to be so terribly confusing and troublesome.
This format used in your data file is terrible: Assumes English, Abuses English with incorrect abbreviations, and is tricky to parse with SPACE characters. Instead of inventing such patterns, I strongly recommend using standard ISO 8601 formats when generating strings that represent date-time values.
// Define parser for expected format of data in file.
// Bad choice of format. Should have written data in standard ISO 8601 format instead.
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "dd MMM uuuu HH:mm:ss" , Locale.US );
Read pairs of lines successively. Parse each pair of inputs. Since the input data unfortunately lacks any indication of offset-from-UTC or time zone, we parse as LocalDateTime
.
The elapsed time between them will be calculated in a Duration
using generic 24-hour days while ignoring anomalies such as Daylight Saving Time (DST). Better to be using values with explicit offset or time zone the omission leaves us wondering about the true meaning of these date-time inputs.
// Read through the lines in the file, reading pairs of odd-numbered and even-numbered lines for start and stop moments.
try ( BufferedReader reader = Files.newBufferedReader ( p , StandardCharsets.UTF_8 ) ) {
String lineOdd, lineEven;
while ( ( lineOdd = reader.readLine () ) != null ) {
if ( ( lineEven = reader.readLine () ) != null ) {
LocalDateTime ldtStart = LocalDateTime.parse ( lineOdd , f );
LocalDateTime ldtStop = LocalDateTime.parse ( lineEven , f );
Duration duration = Duration.between ( ldtStart , ldtStop );
long millis = duration.getSeconds ();
int nanos = duration.getNano ();
System.out.println ( "ldtStart: " + ldtStart + " | ldtStop: " + ldtStop + " | duration: " + duration + " | Sleep for millis: " + millis + " + nanos: " + nanos );
// Thread.sleep ( millis , nanos );
} else { // Else even-numbered line was null. We expect pairs of input lines for start-stop moments.
System.out.println ( "ERROR - Failed to find even-numbered line for stop-moment." );
}
}
} catch ( IOException x ) {
System.err.format ( "IOException: %s%n" , x );
}
Given this data:
20 Mar 2012 19:10:33
20 Mar 2012 19:10:34
20 Mar 2012 19:10:41
20 Mar 2012 19:10:55
20 Mar 2012 19:11:02
20 Mar 2012 19:11:22
…when run…
ldtStart: 2012-03-20T19:10:33 | ldtStop: 2012-03-20T19:10:34 | duration: PT1S | Sleep for millis: 1 + nanos: 0
ldtStart: 2012-03-20T19:10:41 | ldtStop: 2012-03-20T19:10:55 | duration: PT14S | Sleep for millis: 14 + nanos: 0
ldtStart: 2012-03-20T19:11:02 | ldtStop: 2012-03-20T19:11:22 | duration: PT20S | Sleep for millis: 20 + nanos: 0
A better way
If your input file was meant to represent a pair of moments on the timeline, then record them as such. The ISO 8601 standard defines an interval as a pair of date-time strings concatenated with a SOLIDUS (slash) character.
And as discussed above, use standard ISO 8601 formats for each date-time value. Always include an offset-from-UTC or time zone with such values. When you intend UTC as the offset, you can simply append a Z
, short for Zulu
.
Instant
So 20 Mar 2012 19:10:33
becomes 2012-03-20T19:10:33Z
. A value such as 2012-03-20T19:10:33Z
can be processed as a Instant
object. The Instant
class represents a moment on the timeline in UTC with a resolution of nanoseconds.
And the pair in standard format would be 2012-03-20T19:10:33Z/2012-03-20T19:10:34Z
. So your input file should be:
2012-03-20T19:10:33Z/2012-03-20T19:10:34Z
2012-03-20T19:10:41Z/2012-03-20T19:10:55Z
2012-03-20T19:11:02Z/2012-03-20T19:11:22Z
Interval
Such interval pairs can be represented as objects by the Interval
class found in the ThreeTen-Extra project. This project extends the java.time classes built into Java. This project is also a proving ground for future possible additions to java.time.
Interval interval = Interval.of( start , stop );
The Interval
class can parse and generate the lines just shown directly as they comply fully with ISO 8601. No need to specify any formatting patterns.
String output = interval.toString(); // Example: 2012-03-20T19:10:33Z/2012-03-20T19:10:34Z
…or going the other direction…
Interval interval = Interval.parse( "2012-03-20T19:10:33Z/2012-03-20T19:10:34Z" );
You can get a Duration
from an Interval
.
Duration duration = interval.toDuration();
So what is the difference between Interval
and Duration
? The first is tied to specific moments on the timeline; the second is not attached to the timeline.
Standard duration format: PnYnMnDTnHnMnS
Similarly, if you want log the duration as a span of time unattached to the timeline, let the Duration
class read and write the standard ISO 8601 format for durations PnYnMnDTnHnMnS
where the P
marks the beginning and the T
separates any year-months-days from the hours-minutes-seconds. You see this format in the example output above as generated by the Duration::toString
method. For example, PT14S
is fourteen seconds.
Duration duration = Duration.between ( start , stop );
String output = duration.toString(); // Example: PT14S
…and going the other direction…
Duration duration = Duration.parse( "PT14S" );
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old date-time classes such as java.util.Date
, .Calendar
, & java.text.SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP (see How to use…).
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.