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.
This question already has answers here:
How to set time zone of a java.util.Date?
(12 answers)
Closed 5 years ago.
I have a problem whereby the Calendar.getTime() method changes the timezone (probably to be in line with the JVM).
Calendar cal = javax.xml.bind.DatatypeConverter.parseDateTime("2017-10-20T07:10:08.123458Z");
Date datrFromCal = cal.getTime();//Adds two hours(GMT+2:00)
Is there any way to stop this movement from GMT+0 to GMT+2 when calling cal.getTime()?
P.s. We use Java 7 at my company.
Another thing related to this is the support for microseconds. I have read a lot about Java 7 and below not supporting microSeconds (when parsing a String), but is there any suggestions to get around this?
It's not the Date that has the timezone. Dates are simply a number of milliseconds since the "epoch" (1 Jan 1970 GMT). They do not contain timezone information. You only see time zones when you format a date for display. By default, it uses the timezone for your locale. You can use SimpleDateFormat to print a date with a different Locale or TimeZone.
Make sure your Calendar instance has the right TimeZone (e.g. you might have to explicitly set it: cal.setTimeZone(TimeZone.getTimeZone("GMT"));).
If I were you, I'd switch to the Joda time library or the ThreeTen Backport and use the far better designed java.time. The built-in java.util.Date/Calendar classes are a nightmare to work with. They are poorly designed and mutable, which means you can't trust them as value objects to be passed around.
Though i agree with #ELEVATE, i have to add that the iso 8601 date format uses a time zone.
We can see that you are effectively setting the timezone with the 'Z' indicator but not specify it. Therefore, the calendar is built using the your local time zone but with the time defined as UTC (or GMT if you prefer, therefore as GMT+2h00 in your case).
If, however, you remove the 'Z' indicator, you'll the time set with your timezone as it is.
See https://en.wikipedia.org/wiki/ISO_8601#Time_zone_designators for instance
When you look at the javadoc of the java.util.Date class, most of the methods are deprecated. Why was this done?
Well, for two related reasons. It was a very poor implementation of the concept of Dates and Times and it was replaced by the Calendar class.
The Calendar class, although an improvement, leaves a lot to be desired as well, so for serious Date/Time work, everyone recommends Joda-Time. Java 8 brings the new java.time.* package, inspired by Joda-Time, defined by JSR-310, and intended to supplant the old Date/Calendar classes.
Edit: In response to the specific question of why the implementation is poor, there are many reasons. The JavaDoc sums it up as follows:
Unfortunately, the API for these functions was not amenable to internationalization.
In addition to this general deficiency (which covers issues like the lack of a Time Zone component as well as the date formatting which is better handled in DateFormat and the inability to have a non-Gregorian calendar representation), there are specific issues which really hurt the Date class, including the fact that year is presented in an offset of 1900 from Common Era year.
Calendar has its own problems, but even as early as JDK 1.1 it was obvious that java.util.Date was not going to cut it. Even though Calendar is arguable the worst JDK API, it has taken until version 7 to attempt to address it.
Date is mutable
Date doesn't have support for time zones
The latter led to it being replaced by Calendar. And the former, combined with the ease-of-use, lead to both being replaced by Joda-Time / JSR-310 (java.time.* package)
They're deprecated because Date was written as fast as possible back in the day when they wanted to rush the JDK out the door.
It turns out the Dates and Calendars are Hard. So, they created the Calendar class, which much more thought, in order to handle the Hard Parts of working with calendars.
They deprecated the Date methods and delegated to Calendar because they didn't want to change the behavior of the existing Date methods, and possibly break existing applications.
Here's a good answer straight from Oracle: http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html
A long-standing bugbear of Java developers has been the inadequate support for the date and time use cases of ordinary developers.
For example, the existing classes (such as java.util.Date and SimpleDateFormatter) aren’t thread-safe, leading to potential concurrency issues for users—not something the average developer would expect to deal with when writing date-handling code.
Some of the date and time classes also exhibit quite poor API design. For example, years in java.util.Date start at 1900, months start at 1, and days start at 0—not very intuitive.
... java.util.Date represents an instant on the timeline—a wrapper around the number of milli-seconds since the UNIX epoch—but if you call toString(), the result suggests that it has a time zone, causing confusion among developers.
I don't know the official reason why it has been deprecated, but as far as I can tell GregorianCalendarand Joda-Time support operations on dates, meaning that you can add, for instance, a day to a date and have its month and year updated accordingly.
For instance, say you want to compute the day after the current date and today is May 31st; with java.util.Date, you just have getDays() +1, which returns 32, and you have to handle the knowledge that the current month doesn't have 32 days by yourself; with GregorianCalendaror Joda.time, adding a day to May 31st results in an object representing June 1st, hiding the complexity from your sight.
Could someone please advise the current "best practice" around Date and Calendar types.
When writing new code, is it best to always favour Calendar over Date, or are there circumstances where Date is the more appropriate datatype?
Date is a simpler class and is mainly there for backward compatibility reasons. If you need to set particular dates or do date arithmetic, use a Calendar. Calendars also handle localization. The previous date manipulation functions of Date have since been deprecated.
Personally I tend to use either time in milliseconds as a long (or Long, as appropriate) or Calendar when there is a choice.
Both Date and Calendar are mutable, which tends to present issues when using either in an API.
The best way for new code (if your policy allows third-party code) is to use the Joda Time library.
Both, Date and Calendar, have so many design problems that neither are good solutions for new code.
Date and Calendar are really the same fundamental concept (both represent an instant in time and are wrappers around an underlying long value).
One could argue that Calendar is actually even more broken than Date is, as it seems to offer concrete facts about things like day of the week and time of day, whereas if you change its timeZone property, the concrete turns into blancmange! Neither objects are really useful as a store of year-month-day or time-of-day for this reason.
Use Calendar only as a calculator which, when given Date and TimeZone objects, will do calculations for you. Avoid its use for property typing in an application.
Use SimpleDateFormat together with TimeZone and Date to generate display Strings.
If you're feeling adventurous use Joda-Time, although it is unnecessarily complicated IMHO and is soon to be superceded by the JSR-310 date API in any event.
I have answered before that it is not difficult to roll your own YearMonthDay class, which uses Calendar under the hood for date calculations. I was downvoted for the suggestion but I still believe it is a valid one because Joda-Time (and JSR-310) are really so over-complicated for most use-cases.
tl;dr
advise the current "best practice" around Date and Calendar
is it best to always favour Calendar over Date
Avoid these legacy classes entirely. Use java.time classes instead.
For a moment in UTC, use Instant(the modern equivalent of Date)
For a moment in a particular time zone, use ZonedDateTime(the modern equivalent of GregorianCalendar)
For a moment in a particular offset-from-UTC, use OffsetDateTime(no equivalent in legacy classes)
For a date-time (not a moment) with unknown time zone or offset, use LocalDateTime(no equivalent in legacy classes)
Details
The Answer by Ortomala Lokni is right to suggest using the modern java.time classes rather than the troublesome old legacy date-time classes (Date, Calendar, etc.). But that Answer suggests the wrong class as equivalent (see my comment on that Answer).
Using java.time
The java.time classes are a vast improvement over the legacy date-time classes, night-and-day difference. The old classes are poorly-designed, confusing, and troublesome. You should avoid the old classes whenever possible. But when you need to convert to/from the old/new, you can do so by calling new methods add to the old classes.
For much more information on conversion, see my Answer and nifty diagram to another Question, Convert java.util.Date to what “java.time” type?.
Searching Stack Overflow gives many hundreds of example Questions and Answers on using java.time. But here is a quick synopsis.
Instant
Get the current moment with an Instant. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = Instant.now();
ZonedDateTime
To see that same simultaneous moment through the lens of some particular region’s wall-clock time, apply a time zone (ZoneId) to get a ZonedDateTime.
Time zone
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone();
Offset
A time zone is a region’s history of changes in its offset-from-UTC. But sometimes you are given only an offset without the full zone. In that case, use the OffsetDateTime class.
ZoneOffset offset = ZoneOffset.parse( "+05:30" );
OffsetDateTime odt = instant.atOffset( offset );
Use of a time zone is preferable over use of a mere offset.
LocalDateTime
The “Local” in the Local… classes means any locality, not a particular locality. So the name can be counter-intuitive.
LocalDateTime, LocalDate, and LocalTime purposely lack any information about offset or time zone. So they do not represent actual moments, they are not points on the timeline. When in doubt or in confusion, use ZonedDateTime rather than LocalDateTime. Search Stack Overflow for much more discussion.
Strings
Do not conflate date-time objects with strings that represent their value. You can parse a string to get a date-time object, and you can generate a string from a date-time object. But the string is never the date-time itself.
Learn about standard ISO 8601 formats, used by default in the java.time classes.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Using a JDBC driver compliant with JDBC 4.2 or later, you may exchange java.time objects directly with your database. No need for strings nor java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
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.
Date is best for storing a date object. It is the persisted one, the Serialized one ...
Calendar is best for manipulating Dates.
Note: we also sometimes favor java.lang.Long over Date, because Date is mutable and therefore not thread-safe. On a Date object, use setTime() and getTime() to switch between the two. For example, a constant Date in the application (examples: the zero 1970/01/01, or an applicative END_OF_TIME that you set to 2099/12/31 ; those are very useful to replace null values as start time and end time, especially when you persist them in the database, as SQL is so peculiar with nulls).
I generally use Date if possible. Although it is mutable, the mutators are actually deprecated. In the end it basically wraps a long that would represent the date/time. Conversely, I would use Calendars if I have to manipulate the values.
You can think of it this way: you only use StringBuffer only when you need to have Strings that you can easily manipulate and then convert them into Strings using toString() method. In the same way, I only use Calendar if I need to manipulate temporal data.
For best practice, I tend to use immutable objects as much as possible outside of the domain model. It significantly reduces the chances of any side effects and it is done for you by the compiler, rather than a JUnit test. You use this technique by creating private final fields in your class.
And coming back to the StringBuffer analogy. Here is some code that shows you how to convert between Calendar and Date
String s = "someString"; // immutable string
StringBuffer buf = new StringBuffer(s); // mutable "string" via StringBuffer
buf.append("x");
assertEquals("someStringx", buf.toString()); // convert to immutable String
// immutable date with hard coded format. If you are hard
// coding the format, best practice is to hard code the locale
// of the format string, otherwise people in some parts of Europe
// are going to be mad at you.
Date date = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH).parse("2001-01-02");
// Convert Date to a Calendar
Calendar cal = Calendar.getInstance();
cal.setTime(date);
// mutate the value
cal.add(Calendar.YEAR, 1);
// convert back to Date
Date newDate = cal.getTime();
//
assertEquals(new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH).parse("2002-01-02"), newDate);
Dates should be used as immutable points in time; Calendars are mutable, and can be passed around and modified if you need to collaborate with other classes to come up with a final date. Consider them analogous to String and StringBuilder and you'll understand how I consider they should be used.
(And yes, I know Date isn't actually technically immutable, but the intention is that it should not be mutable, and if nothing calls the deprecated methods then it is so.)
With Java 8, the new java.time package should be used.
Objects are immutable, time zones and day light saving are taken into account.
You can create a ZonedDateTime object from an old java.util.Date object like this:
Date date = new Date();
ZonedDateTime zonedDateTime = date.toInstant().atZone(ZoneId.systemDefault());
I always advocate Joda-time. Here's why.
the API is consistent and intuitive. Unlike the java.util.Date/Calendar APIs
it doesn't suffer from threading issues, unlike java.text.SimpleDateFormat etc. (I've seen numerous client issues relating to not realising that the standard date/time formatting is not thread-safe)
it's the basis of the new Java date/time APIs (JSR310, scheduled for Java 8. So you'll be using APIs that will become core Java APIs.
EDIT: The Java date/time classes introduced with Java 8 are now the preferred solution, if you can migrate to Java 8
A little bit late at party, but Java has a new Date Time API in JDK 8. You may want to upgrade your JDK version and embrace the standard. No more messy date/calendar, no more 3rd party jars.
Date should be re-developed. Instead of being a long interger, it should hold year, month, date, hour, minute, second, as separate fields. It might be even good to store the calendar and time zone this date is associated with.
In our natural conversation, if setup an appointment at Nov. 1, 2013 1pm NY Time, this is a DateTime. It is NOT a Calendar. So we should be able to converse like this in Java as well.
When Date is stored as a long integer (of mili seconds since Jan 1 1970 or something), calculating its current date depends on the calendar. Different calendars will give different date. This is from the prospective of giving an absolute time (eg 1 trillion seconds after Big Bang). But often we also need a convenient way of conversation, like an object encapsulating year, month etc.
I wonder if there are new advances in Java to reconcile these 2 objectives. Maybe my java knowledge is too old.
Btw "date" is usually tagged as "obsolete / deprecated" (I dont know exactly why) - something about it is wrote there
Java: Why is the Date constructor deprecated, and what do I use instead?
It looks like it's a problem of the constructor only- way via new Date(int year, int month, int day), recommended way is via Calendar and set params separately .. (Calendar cal = Calendar.getInstance();
)
I use Calendar when I need some specific operations over the dates like moving in time, but Date I find it helpful when you need to format the date to adapt your needs, recently I discovered that Locale has a lot of useful operations and methods.So I'm using Locale right now!