As most people are painfully aware of by now, the Java API for handling calendar dates (specifically the classes java.util.Date and java.util.Calendar) are a terrible mess.
Off the top of my head:
Date is mutable
Date represents a timestamp, not a date
no easy way to convert between date components (day, month, year...) and Date
Calendar is clunky to use, and tries to combine different calendar systems into one class
This post sums it up quite well, and JSR-310 also expains these problems.
Now my question is:
How did these classes make it into the Java SDK? Most of these problems seem fairly obvious (especially Date being mutable) and should have been easy to avoid. So how did it happen? Time pressure? Or are the problems obvious in retrospect only?
I realize this is not strictly a programming question, but I'd find it interesting to understand how API design could go so wrong. After all, mistakes are always a good learning opportunity (and I'm curious).
Someone put it better than I could ever say it:
Class Date represents a specific instant in time, with millisecond
precision. The design of this class is a very bad joke - a sobering
example of how even good programmers screw up. Most of the methods in
Date are now deprecated, replaced by methods in the classes below.
Class Calendar is an abstract class for converting between a Date
object and a set of integer fields such as year, month, day, and hour.
Class GregorianCalendar is the only subclass of Calendar in the JDK.
It does the Date-to-fields conversions for the calendar system in
common use. Sun licensed this overengineered junk from Taligent - a
sobering example of how average programmers screw up.
from Java Programmers FAQ, version from 07.X.1998, by Peter van der Linden - this part was removed from later versions though.
As for mutability, a lot of the early JDK classes suffer from it (Point, Rectangle, Dimension, ...). Misdirected optimizations, I've heard some say.
The idea is that you want to be able to reuse objects (o.getPosition().x += 5) rather than creating copies (o.setPosition(o.getPosition().add(5, 0))) as you have to do with immutables. This may even have been a good idea with the early VMs, while it's most likely isn't with modern VMs.
Java's early APIs are nothing more than a product of their time. Immutability only became a popular concept years after that. You say that immutability is "obvious". That might be true now but it wasn't then. Just like dependency injection is now "obvious" but it wasn't 10 years ago.
It was also at one time expensive to create Calendar objects.
They remain that way for backwards compatibility reasons. What is perhaps more unfortunate was that once the mistake was realized the old class wasn't deprecated and new date/time classes were created for all APIs going forward. This has to some degree occurred with the JDK 8 adoption of a JodaTime like API (java.time, JSR 310) but really it's too little too late.
Time is itself not easy to measure and to handle with. Just look at the length of the wikipedia article about time. And then, there are different understandings about time itself: a absoulte time point (as a constant), a time point at a certain place, a time range, the resolution of time....
I remember, when i saw java.util.Date the first time (JDK 1.0?) i was really happy about it. The languages i knew of didn't had such a feature. I didn't have think about time conversion etc.
I think it's mess, because everything that changes leaves a mess if you evolve from one level of understanding (XMLGregorianCaldender vs. Date) and requirements (Nanoseconds, past 2030) to higher level, but keeping the old untouched. And java.util.Date is not a Exception. Just look at the I/O subsystem or the transition from AWT to Swing...
And because of that, "we should sometimes press the reset button." (who said that, btw.?)
Related
First, please note that this question is not a duplicate of this Question:
Java Date vs Calendar. My question is much more specific. The referenced question asks "what" (or "which"), but I already know the "what" and am asking the "why".
I am on a team working on enhancements to an existing Java project for a client. This Java project uses java 6, and does not have Joda Time as a dependency. After inquiring, it looks like adding Joda Time or upgrading to Java 8 are not options.
So, when it comes to representing date/time as a field in an object, we have to use either Calendar or Date for property typing. The legacy code of this project is littered with Objects that use Calendar to represent date/time fields -- fields that we would never have cause to manipulate (as in add or subtract units of time, etc). I know that this is bad practice, as Calendar is a more complex object, while Date is simpler and would work just as well. (And granted, I know that both are fundamentally wrappers for a long of epoch millis, are mutable, and are poorly designed, but again these are our only two options.)
In other words, an object like this:
public class Reservation {
private Guest guest;
// Set only once, never used for calculations
private Calendar dateReserved;
...
}
Should be this instead:
public class Reservation {
private Guest guest;
// Set only once, never used for calculations
private Date dateReserved;
...
}
I then noticed that when adding new Objects for new features, my team was following the same convention of using Calendar instead of Date. When I brought this up, the reply was that it's better to use Calendar because it can do more and doesn't have all these deprecated methods like Date does.
I know that this reasoning is oversimplified. I also see that this answer to the broader question of usage expresses the same view, namely that Calendar should not be used for property typing. However, the answer doesn't contain much explanation as to why Calendar should not be preferred.
So I already know the "What". But I'm trying to make the case to my team, so my question is, "Why"? Why, when property typing, should Date be preferred to Calendar? What are the disadvantages of using Calendar instead of Date for property typing?
I agree with Jon Skeet's comment regarding calendar systems and time zones, and I think your premise is fundamentally flawed. Dates aren't better than Calendars. If you're never ever ever going to compare times, or never ever ever have two dates in different time zones, then sure, the smaller footprint can be nice, I guess, but at that point, just use longs and Unix timestamps. Calendars are by far the better object model, and after all, if you absolutely need it, you can get a Date object from it.
If you are stuck having to choose between Date and Calendar when property typing:
Use Calendar if either one of these is true:
You need to be able to adjust the date/time after it is initially set
(such as changing the month while leaving the day and hour the same).
You need to be aware of timezone.
Otherwise, use Date for the following reasons:
Expressing your intentions accurately. If you use Calendar, you are implying that you want a certain functionality that you don't actually intend to use (timezones, changing the day or month, etc).
Less hassle with String representations. For example, consider this class:
public class Reservation {
private Guest guest;
private Calendar dateReserved;
#Override
public String toString() {
return String.format("Reservation{guest=%s,dateReserved=\"%s\"}",
guest, dateReserved);
}
}
Now if you print out an instance of this class, you'll get something hideous:
Reservation{guest=Guest{id=17,name="John Smith"},dateReserved="java.util.GregorianCalendar[time=1426707020619,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=2,WEEK_OF_YEAR=12,WEEK_OF_MONTH=3,DAY_OF_MONTH=18,DAY_OF_YEAR=77,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=0,HOUR_OF_DAY=12,MINUTE=30,SECOND=20,MILLISECOND=619,ZONE_OFFSET=-28800000,DST_OFFSET=3600000]"}
Whereas if you had used Date instead, you'd get this:
Reservation{guest=Guest{id=17,name="John Smith"},dateReserved="Wed Mar 18 12:34:26 PDT 2015"}
So if you use Calendar and you want your toString() to be usable, you would need to call dateReserved.getTime() -- which means you'd need to add a null check. This goes for whether or not you end up using a DateFormat object.
Date is a smaller object, quicker to instantiate and with less overhead.
Date is practically immutable -- meaning that the only way to change a date object is to use deprecated methods. So, as said in point 1, expressing your intentions matters. If your date field should be immutable, don't confuse developers who will touch your code in the future by using Calendar (unless of course you need timezone awareness).
"Date" is a more intuitive name than "Calendar" for the type of a field that represents a single point in time.
Date object has fewer fields and occupies less memory than Calendar object and is also faster to instantiate.
We have an existing product that is very time sensitive. For example, statistics are published on a 5 min, 1 hour, 24 hour basis. This means testing is very slow in real world conditions (we actually don't test the 1 hour and 24 hour statistics because of this).
What I want is to speed up the JVM clock.... I know I could create a dummy time class as I've seen others do. But the code base is huge so this would be a lot of retrofit work + third party libs would still use the normal Date Class/System.getCurrentTimeInMillis...
What I'm really looking for is a way to override the Java time behavior (monkey patching in the ruby world). I'm guessing it should be possible to use byte code manipulation to do this. But I'm not sure where to start.... I can't find any libraries that do this or anyone who has solved this problem via byte code manipulation.
question 1: is it possible to override the Date behavior?
question 2: how would one do this? Perhaps an example of overriding another core java class?
thanks for any thoughts
Modify the system time (disable ntpd if in use), Java time will reflect accordingly.
There are two main approaches. Modify the Date class itself, or modify all references to it.
The Date class is part of the standard library, so it's given a privileged status. The only way I can think of to modify it is to actually modify the system jar. You can either put your modified jar in the normal location or tell the JVM to look elsewhere. Note that this is not officially supported by Oracle and is only really feasible for personal use.
The other approach is to modify all your classes to reference a mocked version of the class. Even in a large codebase, you can do this with a simple script. If you only care about normal references you can do this either statically or dynamically (as classes are loaded).
If you're worried about accessing Date via reflection, doing it dynamically is the only option. Typically you would use a custom class loader with ASM to do bytecode rewriting. In order to capture reflection, you would have to mock out all the reflection classes and replace references to those too. This gets pretty tricky, especially when you consider the possibility of using reflection to access reflection to access Date, but it's possible.
Also, note that bytecode rewriting won't let you mess with the system classes because they use the hardcoded system classloader. So if another system class references Date, it will use the unmodified version.
I believe Java Agents can also be used to do bytecode rewriting without the need for a custom classloader, but I don't have experience with this.
Java 8
Java 8 includes a new java.time.* package, defined by JSR 310, inspired by Joda-Time (but re-architected).
These new classes supplant the notoriously troublesome classes of java.util.Date, java.util.Calendar, and java.text.SimpleDateFormat.
Mock Clock
Amongst the new classes is the abstract class, Clock.
The designers had exactly your need in mind, a pluggable replacement for the system clock to create fake "mock" date-time values to facilitate testing. The Clock class is meant to be extended by you (or others) for whatever unnatural behavior you desire. Pass an instance of that altered clock to the other java.time.* classes. Those other classes will use your Clock as a source of date-time data rather than the computer/JVM's real clock.
Joda-Time
Looks like Joda-Time may have something similar. Read this discussion about a "MillisProvider". Apparently referring to DateTimeUtils.MillisProvider.
I am a college student.
In our college we have to develop a simple Date class in Java similar to the one available in java.util package.
If we do that then what are the methods we can implement in that class,
Since most of the methods are deprecated in original Date class.
I saw the original Date class definitions in java/util/Date.java. Being a beginner to java, I could not understand the concepts of Serializable, Cloneable, Comparable ,and many variables like fasttime.
It will be good if we can implement this date class simply (since we have to develop this code as a test with in 3 hours at lab.)
Whether it is necessary to implement those concepts in a simple Date class.
If I take it as a Project and start developing the whole Date utilities,
then that code will run to many pages
and I cannot finish it with 3 hours for our lab session.
So someone please guide me....
I have doubts about....
Whether it is possible to create a utility class similar to Date class with a simpler implementation.
If we do that, then what are the methods we can implement in that class , since most of the useful methods are deprecated in the original date class.
Thanks in advance.
Serializable means the class can be Serialized to a transportable binary form.
Clonable means the class can be cloned, this is very tricky to get right, mainly because of inheritance and mutability concerns.
Comparable means the class supports being compared to other classes of the same type.
Of these Comparable is the only one that requires any code to function, it is also probably the only one that is any use in your scenario.
Serializable is what is called a Marker Interface it doesn't require any code to cause anything to happen since it doesn't have any methods to implement. It just exists to mark the object as supporting something and another class and check for this interface and do things based on its existence.
Cloneable is something you want to stay away from if at all possible. Cloning objects in Java is not straight forward, has lots of gotchas and generally behaves in the most non-intuitive ways imaginable. If you really want to know about this, learn about this, read this article.
Comparable is very valuable, it lets you compare to like objects to see if one is less than, equal or greater than another. This is a requirement for sorting and Collections classes that support Comparators. Comparators can be thought of as stand alone implementations of Comparable that can be plugged into other classes to control how objects are compared.
java.util.Date is a tricky class, it appears to be a straight forward struct type class with some mutators and convenience methods, but the underlying problem domain for calendar data isn't that simple. Calendar math has lots of exceptional cases. That is why there are so many methods on java.util.Date that are deprecated, they produced wrong behavior in many cases.
For some more code to study, look at this temporal package I developed to wrap the standard Java Calendar with very basic Date, Time and TimeStamp classes, they all just delegate to an instance of Calendar for the actual calcuations.
NOTE: this library code pre-dates JodaTime for those of you that might complain about just not using that library.
You don't have to implement all the interfaces to have a simple working Date class.
I would suggest that you forget about Java's Date class and consider what you think is needed for a date class. For example:
Get/set day of month
Get/set month
Get/set year
Get day of week
toString()
Would give you a pretty good basic date class.
For the sake of completeness, I'll tell you what the interfaces are for. You can decide whether to implement them based on how much you have learned and the assignment's requirements:
Serializable is for saving your object to a stream. You actually don't need to do much work to implement it.
Comparable is for comparing objects (date1.compareTo(date2) should return an integer indicating whether date1 is before, after, or the same as date2).
Clonable is for creating a deep copy of the object.
Since this is a lab project, why don't you list down what kind of function a 'simple' Date class needs and should have. For example, toString() -- returns a string representation of a Data or toMilliSecond() -- returns the number of milliSecond from the reference time.
What is really meant when using Java Date utilities and something has been deprecated. Does this mean that it is discouraged to use, or does it imply that it is forbidden?
I am guessing it is bad practice to use deprecated methods, but am not sure and wanted to find out.
For example, I am trying to use code such as the following
String date = request.getParameter("date");
model.setDate(new Date(date));
Of course...this is a high level example, but in this situation, my model uses type Date and I need to pull the date off the request as a String and create a date with it.
It works fine how I have it, but it is using a deprecated method.
EDIT - I have gone back and used
SimpleDateFormat formatter = new SimpleDateFormat();
model.setDate(formatter.parse(request.getParameter("date");
The date is in the format MM/DD/YYY like 07/23/2010 but I am getting a ParseException
What could this be from?
You're right that this is bad practice. In almost all cases, deprecated methods tell you what to use instead, and this is no exception (see the Javadocs).
You're trying to create a Date out of a String. But what format is the String in? How should it be parsed? Is it UK or US date format?
The "proper" way to do this is to create an instance of SimpleDateFormat, and call its parse() method passing in your text string. This is guaranteed to work in future, and will be more robust now.
A lot of people have mentioned what Deprecated means, but I don't see any explanation of why these methods are deprecated:
Sun (before they were part of Oracle) deprecated a number of methods in Date to get people to use the Calendar/GregorianCalendar classes for date manipulation instead.
Deprecated objects or methods merely means that if you want to use it in current project, rather use what is recommended. The reason why they still have it is for legacy codes who have used the deprecated method before it was deprecated. Typical example is StringTokenizer vs String.split() method.
For your Date example use SimpleDateFormat to do conversion from String to Date. This allows you to create a date format from which your string date can be parsed to create a Date object.
For your EDIT do this
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
model.setDate(formatter.parse(request.getParameter("date")));
ParseException is caused since you didn't provide a date format structure so the SimpleDateFormat didn't know how your date was structured.
What "Deprecated" Means
You may have heard the term,
"self-deprecating humor," or humor
that minimizes the speaker's
importance. A deprecated class or
method is like that. It is no longer
important. It is so unimportant, in
fact, that you should no longer use
it, since it has been superseded and
may cease to exist in the future.
Java provides a way to express
deprecation because, as a class
evolves, its API (application
programming interface) inevitably
changes: methods are renamed for
consistency, new and better methods
are added, and fields change. But such
changes introduce a problem. You need
to keep the old API around until
developers make the transition to the
new one, but you don't want them to
continue programming to the old API.
The ability to deprecate a class,
method, or member field solves the
problem. Java supports two mechanisms
for deprecation: and an annotation,
(supported starting with J2SE 5.0) and
a Javadoc tag (supported since 1.1).
Existing calls to the old API continue
to work, but the annotation causes the
compiler to issue a warning when it
finds references to deprecated program
elements. The Javadoc tag and
associated comments warn users against
using the deprecated item and tell
them what to use instead.them what to use instead.
http://download-llnw.oracle.com/javase/1.5.0/docs/guide/javadoc/deprecation/deprecation.html
You are right, Its discouraged to use deprecated methods.
This is because these methods may have issues in some situation or have been replaced with more optimistic solutions And also future versions may not support these.
Deprecated means it is planned for removal, because it is buggy or some other bad reason.
It is better to use SimpleDateFormat.parse(); to parse your strings.
In general, when Sun (Oracle, whatever) declares a Java method deprecated, it means that they changed their minds about including it, they discourage you from using it, and they may remove it in some future version. Of course it's likely to be a long time before it gets removed as who knows how much existing code there is out there using it, and what's the point of breaking existing programs just because the inventors of Java think they now have a better idea about how to do something?
Presumably they had a good reason for deprecating something, so you should investigate WHY they say that some newer function is better.
In the case of deprecated Date methods, usually this means that they suggest you now use the Calendar or SimpleDateFormat classes. In your case, probably the latter.
deprecated: something that exists in the current version of Java, but will be removed from future versions at some point.
For your edit, you need to properly initialize the SimpleDateFormat, so it knows what format is coming in. For 07/22/1978 format:
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
As other said, the java.util.Date methods were deprecated because the Java team believed they had a better solution in the java.util.Calendar.
Unfortunately that class also proved to be confusing, troublesome, and poorly designed.
So, yes, you should avoid deprecated methods in deference to their replacements. But now those replacements (.Calendar) have a replacement (java.time).
java.time
All the old date-time classes have been supplanted by the java.time framework built into Java 8. The new classes are inspired by the highly successful Joda-Time framework, intended as its successor, similar in concept but re-architected. Defined by JSR 310. Extended by the ThreeTen-Extra project. See the Tutorial.
Use the java.time.format package for parsing and generating String representations of date-time values.
String input = "07/23/2010";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "MM/dd/yyyy" );
The new classes include LocalDate for representing a date-only value without time-of-day.
LocalDate localDate = LocalDate.parse( input , formatter );
If you call toString on a LocalDate you get a String representation of the date value in the standard ISO 8601 format, YYYY-MM-DD. To generate a String in other formats, define another formatter. Or call the 'localize' methods to let java.time do the heavy lifting in determining a particular localized format.
Nothing will break if you use them...yet.
But they may well be removed in future versions.
Is it confusing to design an API with multiple ways of achieving the same outcome? For example, I have my own Date library (which is a simple wrapper around the Java Date/Calendar classes to distinguish a year-month-day, Date, from an instant-in-time, Instant and provide mechanisms to convert between the two). I started off with one method to create an instance of a Date:
Date.valueOfYearMonthDay(int year, int month, int day);
But then I found that the resultant code using the API was not very readable. So I added:
Date.yearMonthDay(int year, int month, int day)
Date.ymd(int year, int month, int day)
Date.date(int year, int month, int day)
Then I started getting fluent:
Date.january().the(int day).in(int year);
(I find that the fluent version is really useful for making readable tests). All these methods do identical things and have accurate JavaDoc. I think I've read that a strength of perl is that each programmer can choose exactly which method he/she prefers to solve something. And a strength of Java is that there is usually only one way of doing things :-)
What are people's opinions?
I've been doing academic research for the past 10 years on different issues that have to do with API usability in Java.
I can tell you that the statement about having one way to do things in Java is fairly incorrect. There are often many ways to do the same thing in Java. And unfortunately, they are often not consistent or documented.
One problem with bloating the interface of a class with convenience methods is that you are making it more difficult to understand the class and how to use it. The more choices you have, things become more complex.
In an analysis of some open-source libraries, I've found instances of redundant functionality, added by different individuals using different terms. Clearly a bad idea.
A greater problem is that the information carried by a name is no longer meaningful. For example, things like putLayer vs. setLayer in swing, where one just updates the layer and the other also refreshes (guess which one?) are a problem. Similarly, getComponentAt and findComponentAt. In other ways, the more ways to do something, the more you obfuscate everything else and reduce the "entropy" of your existing functionality.
Here is a good example. Suppose you want in Java to replace a substring inside a string with another string. You can use String.replace(CharSequence, CharSequence) which works perfectly as you'd expect, literal for literal. Now suppose you wanted to do a regular expression replacement. You could use Java's Matcher and do a regular expression based replacement, and any maintainer would understand what you did. However, you could just write String.replaceAll(String, String), which calls the Matcher version. However, many of your maintainers might not be familiar with this, and not realize the consequences, including the fact that the replacement string cannot contains "$"s. So, the replacement of "USD" with "$" signs would work well with replace(), but would cause crazy things with replaceAll().
Perhaps the greatest problem, however, is that "doing the same thing" is rarely an issue of using the same method. In many places in Java APIs (and I am sure that also in other languages) you would find ways of doing "almost the same thing", but with differences in performance, synchronization, state changes, exception handling, etc. For instance, one call would work straight, while another would establish locks, and another will change the exception type, etc. This is a recipe for trouble.
So bottom line: Multiple ways to do the same thing are not a good idea, unless they are unambiguous and very simple and you take a lot of care to ensure consistency.
I'd echo what some others said in that convenience methods are great, but will take it a step further - all "convenience" methods should eventually call the same underlying method. The only thing that the convenience methods should do other than proxy the request is to take or return variables differently.
No calculations or processing allowed in the convenience methods. If you need to add additional functionality in one of them, go the extra mile and make it happen in the "main" / "real" one.
Its fine to provide convenience methods, the real problem is if each entry point begins to do behave in subtly different ways. Thats when the api isn't convenient anymore. Its just a pain to remember which way is "right," and documentation starts saying "the recommended way is..."
If Date.yearMonthDay() began to validate the date while Date.ymd() didn't, that'd be a problem. The same goes for if each begins supporting different "features" - Date.yearMonthDay() could take non-gregorian dates, and Date.date() could take a non-gregorian dates so long as a 4th object is given that tells the calendar type.
First, please don't invent your own date library. It's too hard to get right. If you absolutely have nothing better to do, be sure to read -- and understand -- Calendrical Calculations. Without understanding Calendrical Calculations you run a big risk of doing things wrong in obscure corner and edge cases.
Second, multiple access to a common underlying method is typical. Lots of Java library API methods state that they are simply a "wrapper" around some other method of class.
Also, because of the Java language limitations, you often have overloaded method names as a way to provide "optional" arguments to a method.
Multiple access methods is a fine design.
If these do the exact same thing:
Date.yearMonthDay(int year, int month, int day)
Date.ymd(int year, int month, int day)
Date.date(int year, int month, int day)
I think that is bad form. When I am reading your code, I have no clue which one to use.
Things like
canvas.setClipRegion (int left, int top, int right, int bottom);
canvas.setClipRegion (Rect r);
are different in that it allows the caller to access the functionality without having to figure out how to format the data.
My personal opinion is that you should stick with one method to do something. It all 4 methods ultimatly call the same method then you only need on of them. If however they do something in addition to calling them method then they should exist.
so:
// This method should not exist
Data yearMonthDay(final int year, final int month, final int day)
{
return (valueOfYearMonthDay(year, month, day));
}
The first methid in addition to the fluent version would make more sense. But the yearMonthDay, ymd, and date methods should go.
Also, differnt langauges have different goals. Just because it makse "sense" in Perl doesn't mean it makes sense in Java (or C#, or C++, or C, or Basic, or...)
I find that the fluent version is really useful for making readable tests.
This is a little bit troublesome because I worry that you might only be testing the fluent version. If the only reason methodX() exists is so you can have a readable test for methodY() then there is no reason for one of methodX() or methodY() to exist. You still need to test them in isolation. You're repeating yourself needlessly.
One of the guiding principles of TDD is that you force yourself into thinking about your API while you're writing your code. Decide which method you want clients of your API to use and get rid of the redundant ones. Users won't thank you providing convenience methods, they'll curse you for cluttering your API with seemingly useless redundant methods.