I have two Java instances of java.util.Date and I have to find out if they refer to the same day.
I can do this the hard way, taking the dates apart and compare the days, making sure the years match too.
Since this is such a common problem, I expect there to be an easier solution to this problem.
Thanks!
Instances of java.util.Date refer to instants in time. Which day they fall on depends on which time zone you're using. You could use a java.util.Calendar to represent an instant in a particular time zone...
... or you could use Joda Time instead, which is a much, much better API. Either way, you'll have to know what time zone you're interested in.
In Joda Time, once you've got a relevant time zone, you can convert both instants to LocalDate objects and compare those. (That also means you can compare whether instant X in time zone A is on the same day as instant Y in time zone B, should you wish to...)
I use the DateUtils class by Apache Commons Lang 2, it provides the isSameDay(Date,Date) method.
Update:
Here the link to Apache Commons Lang 3.
From elsewhen on StackOverflow
SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
return fmt.format(date1).equals(fmt.format(date2));
Related
In particular, why does the constructor for a timezone-aware LocalTime require a Chronology instead of a DateTimeZone?
public LocalTime(
int hourOfDay,
int minuteOfHour,
int secondOfMinute,
int millisOfSecond,
Chronology chronology) // <--- this
Why can we not just pass a DateTimeZone to specify the timezone?
What is the difference between Chronology and DateTimeZone -- they both seem to just be used to specify timezone here?
It just means I end up having to wrap any timezone with a call to ISOChronology.getInstance() which seems cumbersome:
new LocalTime(18, 0, 0, 0, ISOChronology.getInstance(DateTimeZone.forID("America/New_York")))
TL;DR: In your particular example there is no difference. I could not find an example where there is any difference, though with serialization of a LocalTime there may be in corner cases. The time zone of the chronology is ignored.
Skimming through the source code of LocalTime revealed a couple of observations:
LocalTime always uses UTC time zone internally. So no matter which chronology or time zone you pass, you are not getting what I would call a timezone-aware LocalTime. So providing a LocalTime(int, int, int, int, DateTimeZone) constructor would have been meaingless.
The chronology is used by the readResolve method, a method used by deserialization. There’s a Javadoc comment stating Handle broken serialization from other tools. I don’t know what it means. It seems to me that all the code does is make sure that the chronology stored in the object uses UTC time zone. Any advantage over not storing any chronology at all is slipping me.
It just means I end up having to wrap any timezone with a call to
ISOChronology.getInstance() which seems cumbersome:
new LocalTime(18, 0, 0, 0, ISOChronology.getInstance(DateTimeZone.forID("America/New_York")))
The LocalTime object ignores the time zone anyway. The documentation of the constructor you are using says:
Constructs an instance set to the specified time using the specified
chronology, whose zone is ignored.
So I think you can just as well use:
new LocalTime(18, 0)
You may provide three or four arguments if you need to specify second and/or millisecond. The LocalTime class has a total of 13 constructors to choose from.
To demonstrate (and disappoint?):
LocalTime timeInNewYork = new LocalTime(18, 0, 0, 0,
ISOChronology.getInstance(DateTimeZone.forID("America/New_York")));
System.out.println(timeInNewYork.getChronology().getZone());
Output:
UTC
To answer the question in your title more directly, the Chronology class is for handling different calendar systems like Islamic or Buddhist calendar, which is something different from what a DateTimeZone does. And also something that doesn’t immediately seem to be needed in the context of a LocalTime, so there’s good reason to ask your question.
A further observation is that java.time, the modern Java date and time API inspired by Joda-Time, completely separates the concepts of chronology and time zone. A java.time chronology does not include a time zone. Also a java.time LocalTime objects neither uses a chronology nor a time zone (except that its static now method very reasonably accepts a ZoneId argument; I consider this a different story).
How to solve?
So how do we create a timezone-aware LocalTime with Joda then?
Edit: How much sense would that make? For events that happen at a certain time every day in some time zone? Of course, if you are designing a calendar system where you need to store, say, an event that occurs every day at 16:00 in Santiago de Chile and another event that happens every day at 18:00 in New York, NY. You know, I don’t.
To answer, as far as I can figure out, you can’t. The local in LocalTime means without time zone (which isn’t obvious, but it’s so).
If it were me, I would store the time of day in a LocalTime and the time zone in a DateTimeZone separately. That would give you the data you require. You may wrap the two pieces into a class of your own, a Time or ZonedTime class, for example.
java.time offers an OffsetTime class, a time with an offset. So it’s not a time-zone aware time, only a UTC offset aware time. Don’t know if that would be of any help to you, just thought it was worth mentioning in case.
Links
Joda-Time Chronology documentation
Joda-Time LocalTime documentation
java.time OffsetTime documentation
I should preface this with I use Apache Spark, which uses java.sql.Date, in case anyone suggests I should use dates from java.time. The example below is in Scala.
The API that I use (which is deprecated) to get the month for a date is as follows:
val date: java.sql.Date = ???
val month = date.getMonth()
However if I look at how it appears I should do this based on the deprecation, the above code would be re-written as follows:
val date: java.sql.Date = ???
val cal = Calendar.getInstance()
cal.setTime(date)
cal.get(Calendar.MONTH)
The simplicity and readability of the code is significantly different, and the date being a side effect on the calendar is not terribly nice from a functional programming point of view. Can someone explain why they think this change was made?
Prior to JDK 1.1, the class Date had two additional functions. It
allowed the interpretation of dates as year, month, day, hour, minute,
and second values. It also allowed the formatting and parsing of date
strings. Unfortunately, the API for these functions was not amenable
to internationalization. As of JDK 1.1, the Calendar class should be
used to convert between dates and time fields and the DateFormat class
should be used to format and parse date strings. The corresponding
methods in Date are deprecated.
The JavaDoc explains. Internationalization.
"in case anyone suggests I should use dates from java.time"
There is nothing to stop you from converting to java.time classes as soon as possible, performing whatever calculations/modifications you need and, if you need to re-insert, converting back to java.sql.Date again.
val date: java.sql.Date = ???
val month = date.toLocalDate().getMonthValue()
You said it yourself, and I still think: You should use java.time, the modern Java date and time API. When you get an old-fashioned java.sql.Date from a legacy API not yet upgraded to java.time, convert it to a modern LocalDate and enjoy the natural code writing with java.time.
Why were getMonth() and the other getXxx methods deprecated?
While Michael has already answered the question with respect to java.util.Date, I have something to add when it comes to java.sql.Date. For this class the situation is quite a bit worse than what Michael reported.
What is left undeprecated (apprecated?) of java.util.Date after the deprecations is that a Date is a point in time. java.sql.Date on the other hand was never meant to be a point in time. One way to illustrate this fact is that its toInstant method — which should convert it to an Instant, a point in time — unconditionally throws an UnsupportedOperationException. A java.sql.Date was meant to be a calendar date to be used with an SQL database and its date datatype, which in most cases is also a date, defined by year, month and day of month. Since a Date is no longer year, month and day of month, they have virtually deprecated everything that a java.sql.Date was supposed to be. And they didn’t give us a replacement until with JDBC 4.2 we can exchange LocalDate objects with SQL databases.
The observations that lead to deprecation have got very practical consequences. Let’s try this (in Java because it is what I can write):
void foo(java.sql.Date sqlDate) {
System.out.println(sqlDate);
TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("Pacific/Samoa")));
System.out.println(sqlDate.getMonth());
}
In one call the method printed:
2020-11-02
9
So we had the 2nd day of the 11th month, and month prints as 9? There are two things going on:
Confusingly the month number that getMonth() returns is 0-based, so 9 means October.
The Date is internally represented as a count of milliseconds since the epoch to the start of the day in the default time zone of the JVM. 2020-11-02 00:00:00 in my original time zone (set to Pacific/Kiritimati for this demonstration) is the same point in time as 2020-10-31 23:00:00 in Samoa. Therefore we get October.
You don’t have to change the time zone yourself for this to happen. Situations where it can happen include:
The default time zone of the JVM can be changed from any part of your program and from any other program running in the same JVM.
The date may be serialized in a program running in one JVM and deserialized in a different JVM with a different time zone setting.
BTW the first snippet I presented at the top often won’t help against unexpected results in these situations. If things go off track before you convert from java.sql.Date to LocalDate, the conversion too will give you the wrong date. If you can make it, convert to LocalDate before anyone messes with the JVM time zone setting and be on the safe side.
I'm working on some legacy code where java.util.Calendar is used for date related calculations (basically adding months). Now I want to replace java.util.Calendar by java.time.LocalDate in the code but I don't want to change the behavior. So, I've been looking for a source which clarifies that they yield same result for the same calculation for any case but I can't find any.
Especially I want to know if is there a date that makes them yield a different result between:
Calendar#add(Calendar.MONTH, months)
and
LocalDate#plusMonth(months)
I've tested some corner cases (e.g. a leap year related dates) and they seem to yield the same result but I can't be 100% sure with that. Isn't there any official information about that or some known difference between them?
TL;DR
If:
You are sure that your Calendar is really a GregorianCalendar (by far the most commonly used subclass), and…
Your dates don’t go more than 100 years back, then…
…you can safely use LocalDate#plusMonth(months) instead of Calendar#add(Calendar.MONTH, months).
Details
Congratulations on the decision to migrate from the old and poorly designed Calendar class to LocalDate from java.time, the modern Java date and time API. This will be an improvement to your code base.
You are correct, the methods you mention are used for the same purpose and generally work the same. So when you migrate from Calendar to java.time, if you find that LocalDate is the right new class to use, then you will use LocalDate#plusMonth(months) where you used Calendar#add(Calendar.MONTH, months) before.
Differences include:
The Calendar class is an abstract superclass for classes representing dates (and times) in many different calendar systems (Gregorian, Buddhist and more), a LocalDate is always in the proleptic Gregorian calendar. Since each calendar system has its own definition of what a month is, adding a number of months in a calendar other than the Gregorian calendar can give quite different results from what LocalDate.plusMonths gives you.
If your dates go back in history it will also make a minor difference that LocalDate uses the proleptic Gregorian calendar. This means that it doesn’t use the Julian calendar for dates where it was in use centuries ago.
While Calendar.add modifies the Calendar object that you call it on, LocalDate.plusMonths returns a new LocalDate object with the new date.
While for going backward in the calendar you need to pass a negative number of months to Calendar::add, LocalDate has a convenient minusMonths method that you will typically want to use instead of plusMonths (both work, though).
The range of dates that each class can represent is different. I don’t readily remember the minimum and maximum date for each. On Calendar/GregorianCalendar, see their various methods such as getGreatestMinimum & getLeastMaximum. For LocalDate, see the constants: MAX & MIN.
I want to format time like 19:19:00 to different time zones. If I use SimpleDateFormat it always takes into account the start of the epoch: 1970.01.01.
Some timezones have different offsets on the start of the epoch and now. For example, the default offset from Europe/Kiev now is UTC+0200 but in 1970 it was UTC+0300. That means if I run my server under Europe/Kiev the client which login under Europe/Berlin(UTC+0100) will see three hours different instead of two.
I can solve this problem by writing a custom formatter for java.sql.Time. But I want to ask maybe there are some common approach or Java tools/libraries which can solve it.
Another solution can be using joda-time:
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Kiev"));
DateTimeZone.setDefault(DateTimeZone.forID("Europe/Kiev"));
DateTimeFormat.forPattern("HH:mm:ss.SSS")
.withZone(DateTimeZone.forID("Europe/Berlin"))
.print(Time.valueOf("19:00:00").getTime());
You can't format just a time to different time zones. You need a date.
If you want to assume that the date of that time is today, you can try this code:
ZoneId originalZone = ZoneId.of("Europe/Kiev");
ZoneId targetZone = ZoneId.of("Europe/Berlin");
LocalTime originalTime = LocalTime.parse("19:19:00");
LocalTime convertedTime = LocalDate.now(originalZone)
.atTime(originalTime)
.atZone(originalZone)
.withZoneSameInstant(targetZone)
.toLocalTime();
System.out.println(convertedTime);
Is java.time.instant an alternative for you? It handles all Timestamps internally as UTC-Time.
One way to parse it from a string is Instant.parse("2018-05-30T19:00:00")
If you want to have the time for a specific timezone you can get it with myInstant.atZone("Zone")
ZoneId originalZone = ZoneId.of("Europe/Kiev");
ZoneId targetZone = ZoneId.of("Europe/Berlin");
LocalDate assumedDate = LocalDate.now(originalZone);
String formattedTime = assumedDate.atTime(LocalTime.parse("19:19:00"))
.atZone(originalZone)
.withZoneSameInstant(targetZone)
.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
System.out.println(formattedTime);
Today this printed:
18:19:00
When you know the date, you should of course use that instead of just today. In the case of Kyiv and Berlin I think they follow the same rules for summer time (DST), so the precise date may not be important. If converting between zones that don’t use the same transitions, or between a time zone that uses summer time and one that doesn’t, it’s suddenly crucial. And who knows in which of those two countries the politicians will change the rules next year? Better be safe.
Link: Oracle tutorial: Date Time explaining how to use java.time.
Previously, when I first design a stock application related software, I decide to use java.util.Date to represent date/time information of a stock.
Later, I realize most of the methods in java.util.Date is deprecated. Hence, very soon, I refactor all my code to make use of java.util.Calendar
However, there is 2 shortcomings I encounter.
Construct java.util.Calendar is comparative slower than java.util.Date
Within the accessors getCalendar method of Stock class, I need to clone a copy, as Calendar is a mutable class
Here is the current source code for Stock.java
Recently, I discover Joda-Time. I do the following benchmarking, by creating 1,000,000 java.util.Date, java.util.Calendar and org.joda.time.DateTime. I found org.joda.time.DateTime performs better than java.util.Calendar, during instantiation.
Here is the benchmarking result.
This instantiation speed is important, especially many instance of Stocks will be created, to represent a long price history of a stock.
Do you think is it worth to migrate from Java Calendar to Joda Date Time, to gain application speed performance? Is there any trap I need to pay attention to?
Note that java.util.Date is mutable too - so if it's a problem now you're using Calendar, it would have been a problem using Date too. That said, using Joda Time is definitely worth doing just because it's a much, much better API.
How certain are you that performance is actually an issue in your particular app? You say there will be "many instances" of Stock created... how many? Have you profiled it? I wouldn't expect it to actually make a significant difference in most situations. It's not obvious from your benchmarking graph what you're actually measuring.
Moving to Joda Time is a good idea in general, but I would measure the performance to see how much difference it really makes for you.
Why do you need a Calendar in your Stock class? I think using a Date to represent a point in time is fine. This seems to be what you want, because you want the Calendar object in the a stock to be immutable, which the Date class should be, if you ignore the deprecated methods.
You can use a temporary Calendar when you need to do time operations on a Date outside the Stock class:
Calendar calendar = Calendar.getInstance();
calendar.setTime(stock.getDate());
System.out.println(calendar.getYear());
Like this you can still store a Date in your Stock class, which should have the best performance when only store and retrieve Stock objects from a data storage. If you do several operations at once you can also reuse the same Calendar object.
If you don't like the Calendar interface you could still use Joda Time to do the time operations. You can probably convert dates to and from Joda Time if needed, to do time operations, and still store Date objects in your Stock class.
If java.util.Calendar instances to be replaced with org.joda.time.DateTime are parsed and/or formatted to a particular pattern, e.g.
String format = "YYYY-MM-dd HH:mm:ss";
Within method signatures for parameter and return types, as well as variable declarations
and instantiations, repace whole word occurrences of the class names Calendar (with
DateTime) and SimpleDateFormat (with DateTimeFormatter), respectively
Replace the formatter instantiation statement, e.g.
formatter = new SimpleDateFormat(format);
with
formatter = DateTimeFormat.forPattern(format);
Replace calls to methods of `SimpleDateFormat', e.g.
String dateStr = formatter.format(Calendar.getInstance().getTime());
with
String dateStr = DateTime.now().toString(formatter);
and
formatter.parse(dateStr);
with
DateTime.parse(dateStr, formatter);
I used Joda in the past, and it is an awesome library.
In terms of performance, you'll have to test it, unfortunately. But refactoring your code seems too much. Personally, I used Date throughout my whole application (including DB store and retrieve), and used Joda only when I needed data manipulation. Joda calculates fields only when needed, so I guess the overhead will be much lower than using Java API classes; furthermore, you won't have object version issues to transfer and handle Date objects in your DB, serialize objects and such. I don't expect Joda to break such compatibility, but it is less likely using Date.
It is always better to move to joda-time in general. But it it is really worth to move to the joda-time for your project is based on your use-cases pertaining to the date usage. Look at slide number 46 in the presentation for performance related numbers for some of the operationslink text