Writing and reading LocalDateTime from file most efficiently - java

What is the fastest way to write LocalDateTime instance to file and then read it from file and convert it back to LocalDateTime object?
I used to save milliseconds and then convert it to Date object. It looked pretty fast but now I am dealing with Java 8's LocalDateTime and it's not clear what would be most efficient way to save and retrieve it from file.
I don't think using DateTimeFormater is a good idea as it requires more resources to convert it to String and then parse the String.
Time zone is not relevant.

If you want to save the milliseconds and timezone is not important, you can use the java.time.Instant class - with only the LocalDateTime there's no way to get the milliseconds, because this class has no timezone/offset information.
// get the current date
Instant instant = Instant.now();
// get milliseconds (equivalent to java.util.Date.getTime())
long millis = instant.toEpochMilli();
// get Instant from milliseconds
Instant instant = Instant.ofEpochMilli(millis);
If you have a LocalDateTime, though, you can easily convert it to an Instant:
LocalDateTime d = LocalDateTime.now();
Instant instant = d.atOffset(ZoneOffset.UTC).toInstant();
This code obviously assumes that the values in the LocalDateTime correspond to an UTC date and time. To convert the Instant back to a LocalDateTime:
LocalDateTime d = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
PS: have you measured your system's performance to know if "fast" is really an issue? Anyway, I'm doing things the "standard" way (the most straightforward way based on what the API offers), is that what you want?
Maybe you can think that creating an Instant as an "intermediary" object makes things less "fast" (but you'll need to measure that anyway). If that's the case, you can get the millis from the LocalDateTime directly (assuming that it corresponds to a date and time in UTC):
// get the current date
LocalDateTime d = LocalDateTime.now();
// get milliseconds value
long millis = d.toEpochSecond(ZoneOffset.UTC) * 1000 + d.get(ChronoField.MILLI_OF_SECOND);
// get LocalDateTime from millis
LocalDateTime d = LocalDateTime.ofEpochSecond(millis / 1000, (int) (millis % 1000) * 1000000, ZoneOffset.UTC);
It's important to note that java.time classes have nanoseconds precision, so getting the milliseconds makes you lose this precision.
If you don't want to lose the nanoseconds precision and don't necessarily need to work with the millis value, you can store 2 different numbers (epoch day and nano of day):
// get the current date
LocalDateTime d = LocalDateTime.now();
// get values from LocalDateTime
long epochDay = d.toLocalDate().toEpochDay();
long nanoOfDay = d.toLocalTime().toNanoOfDay();
// save both values to file
// retrieve the LocalDateTime from the values
LocalDateTime d = LocalDateTime.of(LocalDate.ofEpochDay(epochDay), LocalTime.ofNanoOfDay(nanoOfDay));
This doesn't require the conversion to UTC, but it requires 2 numbers instead of one. You might think that creating a LocalDate and a LocalTime makes things slower, but these 2 objects are always created internally by LocalDateTime (in all cases).
Not sure, though, if all this math is "faster" enough than using an Instant. It's a matter of testing to see which one is best for your case.
But for me, the most "efficient" in terms of clarity and code-easy-to-maintain is using an Instant (or using the last approach of epoch day and nano of day). And unless you're dealing with millions of records, I'm not sure if that'll really be a performance issue.
I've made a simple test (run each case above 10 million times), and the last approach (using epoch day and nano of day) seems to be the fastest. But the difference is less than 1 second. Only by running 2 billion times I've got a 20-seconds difference, so if you're dealing with this many records, maybe it's worth it.
Regarding other resources (memory usage, CPU, I/O), I didn't check. But anyway, performance issues are very specific to each environment: depending on how the system is designed, how the system's parts/modules/components interact with each other, and many other factors, you can have different bottlenecks in each situation.
In the end, you'll have to test each approach and see which one performs best in your system. Or you can conclude that it doesn't make a significant difference (and for cases with less than a couple of millions records, maybe it doesn't - but you'll only know after benchmarking it).

Related

java.time: the simplest way to get a difference in hours between two LocalTime objects

I want to get a difference in hours between a current time in a specific timezone and UTC time. I tried this:
LocalTime time = LocalTime.now();
System.out.println(time); //21:05:42:764
LocalTime utcTime = LocalTime.now(ZoneId.of("UTC"));
System.out.println(utcTime); //18:05:42:769
System.out.println(Duration.between(utcTime, time).getSeconds()/3600); //2
System.out.println(Duration.between(time, utcTime).getSeconds()/3600); //-3
Why is the difference between the last two results and are there better ways to do it? I need to get the number 3.
Why is the difference between the last two results
The reason that you're getting different results for the two computed durations is a combination of the fact that there is some tiny amount of time elapsed between the two recordings and the fact that the duration start time is included in the range but the duration end time is not.
Consider these times instead: 6:00:00:001 vs 8:00:00:000. Here it is very obvious that we're only exactly one millisecond off of two hours, but when we think about seconds we're either going to get 7199 or -7200. When we then do integer math (i.e. divide by 3600), we're going to get 1 or -2.
If it weren't for the one extra millisecond on the first timestamp, the absolute value of the two would be identical.
Duration is the wrong class. There is zero duration between "now" in one time zone and "now" in another. For a fun but memorable way to think about this, see here.
You appear to be seeking to know the current offset from UTC for a given time zone. You can use the ZonedDateTime class for that:
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));
ZoneOffset offset = zdt.getOffset();
int offsetMinutes = offset.getTotalSeconds() / 60;
double offsetHours = ((double) offsetMinutes) / 60;
System.out.println(offsetHours); // 5.5
You could also just use ZonedDateTime.now() on the first line, if you want to use the computer's current time zone.
With regard to LocalTime - that is just the time portion (hours, minutes, seconds, and smaller). Since there is no date associated, you can't necessarily determine which time zone offset it belongs to. There is more than one date that "today" going on at any given moment. Time zone offsets range from UTC-12 to UTC+14, so there are indeed values where the same time of day is happening on two different dates somewhere on the planet.
As an example, 08:00:00 in Hawaii (Pacific/Honolulu) on 2019-01-01 is also 08:00:00 in Kiribati (Pacific/Kiritimati), but on 2019-01-02 - the following date! (Reference here.) Thus, if you had a LocalTime object with 08:00:00 and it was 08:00:00 in one of those two zones, you'd not be able to tell which one it was, or what the corresponding UTC offset should be.
Also, keep in mind that time zone offsets are not limited to whole hours. There are present-day time zones with half-hour and 45-minute offset. Historically, there have been others.
Lastly, keep in mind that an offset is not necessarily enough to identify a time zone. Many time zones share offsets at some points in time, but differ in others. See "Time Zone != Offset" in the timezone tag wiki.
Oh, and about your results getting 2 in one direction and -3 in the other - this is a rounding error due to your integer division. If you print out the seconds value, you'll notice they are one second apart (10799, vs -10800). Dig closer and you'll find that "now" included fractional seconds that were truncated with the getSeconds call. (You called .now() twice, so they were at slightly different times.)

No. of days between two timestamps

I have two timestamp values. like
Timestamp date1;
Timestamp date2;
Now I want to find no. of days between these two timeStamps like in java
int no_of_days = difference(date2 - date1)
the above codes are just for clarification of the question. just ignore the coding mistakes.
You can use Duration for this (it works for java 8) :
Duration between = Duration.between(date1.toInstant(), date2.toInstant());
int no_of_days = between.get(ChronoUnit.DAYS);
If you are using Java8, it will be much easier. Here's a one way of doing it.
Duration.between(date1.toLocalDateTime(), date2.toLocalDateTime()).toDays();
Other answers given here so far only handle a day unit as fixed amount of 24 hours by using the class java.time.Duration. If you consider your timestamps in the context of daylight saving switching time zones then this is probably not what you want.
During the change from winter to summer time, a calendar day can last only 23 hours (or even 23:30 in some rare cases). And in autumn when switching back to winter time, the calendar days can be more than 24 hours long. So you also need a time zone to handle this situation in order to calculate durations in the correct way.
Suggested solution if your timestamps have been stored in the database coming from instants:
ZonedDateTime zdt1 = date1.toInstant().atZone(ZoneId.systemDefault());
ZonedDateTime zdt2 = date2.toInstant().atZone(ZoneId.systemDefault());
long days = java.time.temporal.ChronoUnit.DAYS.between(zdt1, zdt2);
Of course, you are free to specify any other zone than the system time zone. The result can vary depending on the chosen zone.
Suggested solution if your timestamps have been stored coming from LocalDateTime without any zone context:
LocalDateTime ldt1 = date1.toLocalDateTime();
LocalDateTime ldt2 = date2.toLocalDateTime();
long days = java.time.temporal.ChronoUnit.DAYS.between(ldt1, ldt2);
Here I still prefer the enum ChronoUnit.DAYS because the type java.time.Duration internally stores seconds and nanoseconds only and is hence rather designed for machine-like timestamps. And another remark: The second solution implicitly uses your system time zone for the conversion from java.sql.Timestamp to LocalDateTime. This is only correct if you also have stored your timestamps that way.
By the way, with a JDBC-4.2-compliant driver, you could also retrieve your java.time-type directly, see also a related post here on SO.

Create company specific timezone for 'working day' in java

I work at a company where part of the work for a day is done in the early hours of the next day (i.e. shipping orders). Now for several processes (mainly reporting), we want to let the 'working day' end at 04:00 the next morning so we get more consistent reporting values per day.
We want this to always be at 04:00 the next morning and since we are affected by daylight saving times in our area (Europe - Netherlands) we effectively want a 4 hour shifted variant of our normal timezone 'Europe/Amsterdam' (in our case).
To make this as easy to use for all applications in our company I would like to create a small library that simply contains the code to provide my coworkers to get a modified instance of TimeZone that does this. That way all normal time/date manipulation methods can be used in conjunction with this special time zone.
I did a deep dive into the standard Java 8 code/Javadoc related to the TimeZone/ZoneInfo instances and at this moment I do not understand what the correct field is to change in the returned TimeZone/ZoneInfo instance.
At this point, my best guess is setting the RawOffset to 4 hours, but I'm not sure.
What is the correct way to achieve my goal?
Update:
I had a look at the suggested LocalTime and as I expected: It needs a timezone definition as being what it should use as "Local" when converting an existing timestamp (usually epoch milliseconds) into the "Local" timezone.
Looking at all these classes seems like I'll be using the LocalDate more often than the LocalTime.
Effectively the code I expect to have is something like this:
long epoch = 1525033875230L; // Obtained from some dataset
LocalDate localDate = LocalDateTime
.ofInstant(Instant.ofEpochMilli(epoch),
ZoneId.of("Europe/Amsterdam"))
.toLocalDate();
Where I expect that I need to change that Zone into the 'right one'.
If I have got that correctly, what you really need is a way to convert a milliseconds value since the epoch to a date in a way where days don’t change a 00:00 but not until 04:00.
static ZoneId zone = ZoneId.of("Europe/Amsterdam");
static LocalTime lastShiftEnds = LocalTime.of(4, 0);
public static LocalDate epochMilliToDate(long epoch) {
ZonedDateTime dateTime = Instant.ofEpochMilli(epoch)
.atZone(zone);
if (dateTime.toLocalTime().isAfter(lastShiftEnds)) { // normal date-time
return dateTime.toLocalDate();
} else { // belonging to previous day’s night shift
return dateTime.toLocalDate().minusDays(1);
}
}
Use for example like this:
long epoch = 1_525_050_875_230L;
System.out.println(Instant.ofEpochMilli(epoch));
LocalDate date = epochMilliToDate(epoch);
System.out.println(date);
Output is:
2018-04-30T01:14:35.230Z
2018-04-29
From printing the Instant you can see that the time is after midnight (really 03:14:35.230 in Amsterdam time zone). And the method has correctly deemed that this time belongs to April 29 rather than April 30.
Perhaps I am missing something? On the other hand, if that were me I’d go quite a long way to avoid inventing a time zone that doesn’t exist in real life. Such a time zone would be bound to confuse your coworkers.

Cannot convert from java Double to java Date

I am having an issue with converting a double to a Java date object in the format: yyyy-MM-dd HH:mm:ss
I have tried to convert this double value to a long value then instanciate a Date object with the result, but I got errors saying that I cannot convert from double to long.
my timestamp double values are in this format:
1.511554592277516E9
Can anybody help me with this issue please, thanks in advance.
Assuming that this floating point value is seconds past the Unix Epoch of 1 Jan 1970 0:00 GMT, this will provide a conversion to a LocalDateTime with that offset:
LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(
Double.valueOf(1.511554592277516E9).longValue(), 0, ZoneOffset.UTC);
System.out.println(localDateTime);
I leave converting this to a Date as an exercise for the reader.
Your floating-point value, 1.511554592277516E9, no doubt denotes seconds since the epoch of January 1, 1970 at midnight UTC, with microsecond precision: 1 511 554 592 seconds and 277 516 microseconds (millionths of a second).
I suggest using java.time, the modern Java date and time API also known as JSR-310, for this. It is much nicer to work with than the outdated Date class and friends, and also offers nanosecond precision (Date only has millisecond precision, so you would lose precision if converting into one). More specifically I will first create a java.time.Instant object (conversion to other date-time types will be easy, I’ll touch on one example at the end).
Getting the full precision through into an Instant requires a little thought. I played a little with double and long, but realized (1) double doesn’t have the full precision required, the nanoseconds will not be right (2) converting to a long holding the nanoseconds (not the only way, but certainly the easiest) will create a “year 2262 problem”, so if you are handling dates in a far future, it will not work. In any case, I think that the easy and safe solution is to use BigDecimal for the math required before feeding the numbers into an Instant.
String secondsSinceEpoch = "1.511554592277516E9";
BigDecimal decimalSeconds = new BigDecimal(secondsSinceEpoch);
long seconds = decimalSeconds.longValue();
long nanos = decimalSeconds.subtract(BigDecimal.valueOf(seconds))
.movePointRight(9)
.longValueExact();
Instant inst = Instant.ofEpochSecond(seconds, nanos);
System.out.println(inst);
This prints:
2017-11-24T20:16:32.277516Z
The printed date-time is in UTC. If the value is as expected, I should say it confirms that your floating-point value was indeed seconds since the epoch.
You requested a date-time in the format yyyy-MM-dd HH:mm:ss. You will need to decide in which time zone you want the date-time. Date objects don’t have a format, so you will also need to get the format in a string. For example:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(inst.atZone(ZoneId.of("America/Vancouver")).format(formatter));
This prints
2017-11-24 12:16:32
You substitute your desired time zone, of course. atZone() converts the Instant into a ZonedDateTime, another often used class of java.time.
EDIT: In case you don’t want to bother with BigDecimal, you accept a slight inaccuracy and/or your receive your value as a double rather than a string, here’s an alternative in fewer lines of code:
double secondsSinceEpoch = 1.511554592277516E9;
long longSeconds = (long) secondsSinceEpoch;
long micros = Math.round((secondsSinceEpoch - longSeconds) * 1_000_000);
Instant inst = Instant.ofEpochSecond(longSeconds).plus(micros , ChronoUnit.MICROS);
In this particular case it gives exactly the same result, down to the nanosecond. I’m not sure whether with other inputs, the microseconds may end up inaccurate, but on the other hand, if you receive a double (not a string), there’s nothing you could do about that anyway.

Timer - How do I calculate the difference between two dates using Joda Time?

I want to get the difference between two times P (start time) and Q (end time) using Joda Time. P and Q could be times on different days or even on the same day. I want to get the difference in format HH-MM-SS, where H=hours, M=minutes, S=seconds.
I want to use this functionality in a timer. I assume that no one will use my timer to measure more than 24 hours.
Please guide me to do this.
Take a look at the Joda time FAQ
http://joda-time.sourceforge.net/faq.html#datediff
And you can use a PeriodFormatter to get the format of your choice. Try the following sample code.
DateTime dt = new DateTime();
DateTime twoHoursLater = dt.plusHours(2).plusMinutes(10).plusSeconds(5);
Period period = new Period(dt, twoHoursLater);
PeriodFormatter HHMMSSFormater = new PeriodFormatterBuilder()
.printZeroAlways()
.minimumPrintedDigits(2)
.appendHours().appendSeparator("-")
.appendMinutes().appendSeparator("-")
.appendSeconds()
.toFormatter(); // produce thread-safe formatter
System.out.println(HHMMSSFormater.print(period));
I admire the Joda date/time API. It offers some cool functionality and if I needed an immutable calendar or some of the esoteric calendar options it offers, I'd be all over it.
It is still an external API though.
So ... why use it when you don't have to. In the Joda API, the "Instant" is the exact same thing as a Java API "Date" (or pretty close to it). These are both thin wrappers around a long that represents an instant in POSIX EPOCH UTC time (which is the number of milliseconds that have elapsed since 00:00am Jan 1, 1970 UTC.
If you have either two Instants or two Dates, computing the days between them is trivial, and the Joda library is absolutely not needed for this purpose alone:
public double computeDaysBetweenDates(Date earlier, Date later) {
long diff;
diff = later.getTime() - earlier.getTime();
return ((double) diff) / (86400.0 * 1000.0);
}
This assumes that the number of seconds in a day is 86400 ... and this is mostly true.
Once you have a difference as a double, it is trivial to convert the fractional portion of the answer (which is the fraction of one day) into HH:MM:SS.

Categories