Monday, 16 November 2015

Why yoda time?



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
  • 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