Original - https://mestachs.wordpress.com/tag/jodatime/
Don’t use java built-in classes, use jodatime and enforce this rule with sonar !
Don’t use java built-in classes
How many bugs in 5 lines of code ?
Date date = new Date(2007, 12, 13, 16, 40); TimeZone zone = TimeZone.getInstance("Europe/Bruxelles"); Calendar cal = new GregorianCalendar(date, zone); DateFormat fm = new SimpleDateFormat("HH:mm Z"); String str = fm.format(cal);
Just 6 bugs !
int year = 2007 - 1900; int month = 12 - 1; Date date = new Date(year, month, 13, 16, 40); TimeZone zone = TimeZone.getInstance("Europe/Brussels"); Calendar cal = new GregorianCalendar(zone); cal.setTime(date); DateFormat fm = new SimpleDateFormat("HH:mm Z"); fm.setTimeZone(zone); Date calDate = cal.getTime(); String str = fm.format(calDate);
If you want deeper explanations see this [presentation]
java.util.Date issues
- From JDK1.0
- Uses two digit years (from 1900)
- January is 0, December is 11
- Should have been immutable
- Most methods deprecated in JDK1.1
- Uses milliseconds from 1970 representation
java.util.Calendar issues
- From JDK1.1
- Uses subclasses for different calendar systems
- January is 0, December is 11
- Should have been immutable
- Uses dual representation internally
- value for each field
- milliseconds from 1970 representation
- Odd performance and bugs
java.util.DateFormat issues
- Pattern based date formatting
- “dd MMM yyyy”
- Requires Date object
- Not thread-safe : see rule findbugs : Multithreaded correctness – Call to static DateFormat
- Not especially fast
- Sun RFE to make thread-safe ignored
SQL – java.util.sql.Date, Time, Timestamp issues
Subclass java.util.Date
- Date extends Date (!)
- Time extends Date (!)
- Override superclass to block methods (throws Exception)
- Timestamp adds nanoseconds
- equals() broken
- All the problems of java.util.Date and more
- timezone problem new Time(long)
Avoid millis manipulation and let’s use Jodatime !
when playing with java.util.Date you end up doing calculation in millis
int
days =
40
;
Date now =
new
Date();
long
nowMillis = now.getTime();
Timestamp nowTimestamp =
new
Timestamp(nowMillis);
long
future =
3600
*
24
* days *
1000
;
Timestamp expiryTimestamp =
new
Timestamp(nowMillis + future);
System.out.println(
"nowTimestamp "
+ nowTimestamp);
System.out.println(
"expiryTimestamp "
+ expiryTimestamp);
this last code sample contains a bug… int vs long for days !
see this explaination the expiryTimestamp is before the nowTimestamp for “large days count”
nowTimestamp 2011-02-04 12:45:40.381
expiryTimestamp 2011-01-25 19:42:53.085
now let’s write the same code with joda time
DateTime nowTimestamp2 =
new
DateTime();
System.out.println(
"nowTimestamp "
+ nowTimestamp2);
System.out.println(
"expiryTimestamp "
+ nowTimestamp2.plusDays(days));
it’s more readable… and most important it return the correct value ;)
nowTimestamp 2011-02-04T12:45:40.443+01:00
expiryTimestamp 2011-03-16T12:45:40.443+01:00
Sonar to the rescue
sonar can detect these issues :
- Multithreaded correctness – Call to static Calendar
- Multithreaded correctness – Call to static DateFormat
to fix them with jodatime let’s use the DateTimeFormat.
fmt
=DateTimeFormat.forPattern("MMMM, yyyy");DateTime datetime = fmt.parseDateTime(duedate);
this one is threadsafe and can be static and final field. or use the toString
datetime.toString("dd:MM:yy");
but the step further is to banish jdk date from your code base !
To do so, let’s define sonar architectural constraint like
To do so, let’s define sonar architectural constraint like
- Arch : avoid java.util.GregorianCalendar
- Arch : avoid java.util.Date
- Arch : avoid java.text.SimpleDateFormat
- Arch : avoid java.sql.Timestamp
- Arch : avoid java.sql.Date
to banish jdk dates from your model, you may implement hibernate usertype, jaxb adapter,…
No comments:
Post a Comment