Upload
others
View
25
Download
0
Embed Size (px)
Citation preview
Joda-Time & JSR-310 as an alternative to Java Date API
Fevzi Anifieiev ([email protected])
Kyiv, Epam Systems
Agenda
• JDK Date API Pitfalls
• Joda-Time
• A bit of JSR-310
JDK 1.6 and prior Date & Time API • Date and its subclasses :
• java.util.Date • java.sql.Date • java.sql.Timestamp • java.sql.Time
• The calendar and time zone classes : • java.util.Calendar • java.util.GregorianCalendar • java.util.TimeZone • java.util.SimpleTimeZone (for use with the Gregorian
calendar only) • The formatting and parsing classes :
• java.text.DateFormat • java.text.SimpleDateFormat
Date, Calendar & DateFormat
• Mutable
• Jan is 0, Dec is 11
• Date is not actually date
• Date uses years from 1900
• Calendar cannot be formatted
• DateFormat is not thread-safe
• Broken equals() symmetric contract for Date and Timestamp
Date, Calendar & DateFormat in action • Mutable Map<Date, String> map = new HashMap<Date, String>();
• February is 1, December is 11
Calendar.getInstance().get(Calendar.MONTH); //returns 1 (it is FEB)
• Date is not actually date Represents date and time
• Date uses years from 1900 new Date(2013, 1, 23); //Sun Feb 23 00:00:00 EET 3913
• Calendar cannot be formatted Calendar calendar = Calendar.getInstance();
new SimpleDateFormat("dd-MM-yyyy").format(calendar.getTime());
• Broken equals() symmetric contract Date date = new Date(); Timestamp timestamp = new Timestamp(date.getTime()); date.equals(timestamp) //true timestamp.equals(date) //false - equals symmetric violation
DateFormat demo • DateFormat is not thread-safe
public final class DateUtils {
private DateUtils(){
} private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd"); public static Date parse (String s) throws ParseException { return DATE_FORMAT.parse(s);
} } TimeZone Pitfalls TimeZone.getTimeZone("WRONG_TIME_ZONE") // GMT
How to overcome all the problems with current JDK Date & Time API?
• Understand all the pitfalls of the API to better write quality code, but still….
• Use Alternative Date & Time library
Moving to better alternatives
• JodaTime library
• JSR-310 (JDK8)
Joda-Time to rescue • Immutable & Threadsafe No changes after creation
Classes and fields are final
• Easy to Use Has straightforward field accessors such as getYear() or getDayOfWeek()
• Comprehensive Feature Set Almost everything you need is there for date manipulations
• Up-to-date Time Zone calculations Based on the public Olson time zone database http://www.iana.org/time-zones
• Calendar support ISO8601, Buddhist, Coptic, Ethiopic, Gregorian, GregorianJulian, Islamic, Julian
• Open Source
Joda-Time API Overview • Instant
Milliseconds from 1970-01-01T00:00Z
• Partial Only partially represent a date or time in the datetime continuum.
• Interval Time between two instants (TimeZone aware)
• Duration No TimeZone aware or Chronology, just millis
• Period No TimeZone aware or Chronology, defined in terms of fields, for ex: 1 day 5 hrs
Differs from Duration in that it is inexact in terms of millis.
• Chronology Pluggable chronologies. A Chronology is obtained and used in Joda-Time as a singleton
• TimeZone Bazed on TZ(Olson). Frquently updatable. You can also update whenever required.
Joda-Time Instant • Instant
Interface: ReadableInstant
Common classes: Instant , DateTime , DateMidnight, MutableDateTime import static org.joda.time .DateTimeFieldType.year; import static org.joda.time .DateTimeFieldType.monthOfYear;
... public void showInstants() {
Instant instant = Instant.now();
instant.get(year())); //2013
instant.plus(1000L * 60 * 60 * 24).get(monthOfYear())); //
DateTime dateTime = DateTime.now();
dateTime.getYear(); //2013
dateTime.plusDays(1).getMonthOfYear(); //2 (it is Feb)
}
Joda-Time Partial • Partial
Interface: ReadablePartial Common classes: LocalDate, LocalTime, LocalDateTime, YearMonth, MonthDay, Partial import static org.joda.time .DateTimeFieldType.dayOfMonth; import static org.joda.time .DateTimeFieldType.dayOfWeek;
… private static void showPartials() {
LocalDate.now(); // 2013-02-21
LocalTime.now()); // 21:16:16.965
LocalDateTime.now()); // 2013-02-21T21:16:16.971 YearMonth.now()); // 2013-02 MonthDay.now()); // --02-21
LocalDate locDate = LocalDate.now();
//DayOfMonthDayOfWeek
Partial partial = new Partial( //--02-22
new DateTimeFieldType[] { dayOfMonth(), dayOfWeek() }, //static import
new int[] {locDate .dayOfMonth().get(), locDate .dayOfWeek().get()}));
}
Joda-Time Interval • Interval
Interface: ReadableInterval Common classes: Interval, MutableInterval DateTime today = new DateTime(DateTimeZone.UTC);
Interval workBeforeLunch = new Interval( new LocalTime(10, 0).toDateTime(today), new LocalTime(13, 0).toDateTime(today)); Interval lunch = new Interval( new LocalTime(12, 30).toDateTime(today), new LocalTime(14, 0).toDateTime(today)); Interval workAfterLunch= new Interval( new LocalTime(14, 0).toDateTime(today), new LocalTime(19, 0).toDateTime(today)); workBeforeLunch.overlaps(lunch));//true workAfterLunch.overlaps(lunch)); //false
Joda-Time Duration
• Duration Interface: ReadableDuration Common classes: Duration private static void showDurations() {
DateTime from = DateTime.now();
DateTime to = from.plusMonths(3);
Duration probabDurationInEpam = new Duration(from, to);
probabDurationInEpam.getStandardDays());
}
Joda-Time Period • Period
Interface: ReadablePeriod Common classes: Period, MutablePeriod, Years, Months, Weeks, Days, Hours, Minutes, Seconds private static void showPeriods() {
DateMidnight firstOfFebruary = new DateMidnight(2012, 2, 1);
DateMidnight firstOfMarch = new DateMidnight(2012, 3, 1);
DateTimeFormatter fmt =ISODateTimeFormat.yearMonthDay();
fmt.print(firstOfFebruary.plus(Months.ONE)); // 2012-03-01
fmt.print(firstOfMarch.plus(Weeks.ONE)); // 2012-03-08
}
Joda-Time Chronology • Chronology
The default chronology in Joda-Time is ISO Abstarct class: Chronology Common Classes: BuddhistChronology, CopticChronology, EthiopicChronology, GJChronology, GregorianChronology, IslamicChronology, ISOChronology, JulianChronology private static void showChronologies() {
Set<? extends Chronology> chronos = ImmutableSet.of( ISOChronology.getInstance(), CopticChronology.getInstance(), IslamicChronology.getInstance(), GregorianChronology.getInstance()); for(Chronology chrono: chronos) { DateTime dt = DateTime.now(chrono); "Current year in " + chrono+ ": " + dt.getYear());
} } RESULT:
Current year in ISOChronology[Europe/Helsinki]: 2013 Current year in CopticChronology[Europe/Helsinki]: 1729 Current year in IslamicChronology[Europe/Helsinki]: 1434 Current year in GregorianChronology[Europe/Helsinki]: 2013
Joda-Time Time Zone • Time Zones
Abstract class: DateTimeZone Common classes: CachedDateTimeZone, FixedDateTimeZone private static void showTimeZones() {
DateTimeZone kievZone = DateTimeZone.forID("Europe/Kiev");
kievZone);
DateTimeZone invalidZone = DateTimeZone.forID("WRONG_TIME_ZONE");
//IllegalArgumentException
invalidZone);
}
Joda-Time Date Formatting • DateTimeFormatter(thread safe)
Common classes: DateTimeFormat, DateTimeFormatter, DateTimeFormatterBuilder DateTimeFormatter ddMMyyyyFormat = DateTimeFormat.forPattern("dd-MM-yyyy"); DateTime dt = ddMMyyyyFormat.parseDateTime("23-02-2013"));
• DateTimeFormatterBuilder DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendDayOfWeekShortText().appendLiteral(", ") .appendDayOfMonth(2).appendLiteral('-') .appendMonthOfYearShortText().appendLiteral('-') .appendYear(4, 4).appendLiteral(", ").appendEraText() .toFormatter();
formatter.print(DateTime.now()); // Fri, 22-Feb-2013, AD
Examples(Days to NewYear) public static int daysToNewYearJDK(Date fromDate) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(fromDate);
calendar.add(Calendar.YEAR, 1);
calendar.set(Calendar.DAY_OF_YEAR, 1);
return (int)(Math.abs((calendar.getTimeInMillis()
- fromDate.getTime()) / (24 * 60 * 60 * 1000L)));
}
public int daysToNewYearJoda(Date fromDate) {
LocalDate localDate = LocalDate.fromDateFields(fromDate);
LocalDate newYear = localDate.plusYears(1).withDayOfYear(1);
return Days.daysBetween(localDate, newYear).getDays();
}
Examples(adding days)
• public static Date addDaysUsingCalendar(Date fromDate,int days) {
Calendar cal = Calendar.getInstance();
cal.setTime(fromDate);
cal.add(Calendar.DATE, days);
return cal.getTime();
}
• public static Date addDaysUsingJoda(Date fromDate, int days) {
return new LocalDate(fromDate).plusDays(days).toDate();
}
Examples(formatting)
• Date formatting directly import static org.joda.time.format.DateTimeFormat.forPattern;
…..
DateTime dt = DateTime.now();
dt.toString();
dt.toString("hh:mm:ss a");
dt.toString(forPattern("dd-MM-yyyy")); dt.toString(ISODateTimeFormat.yearMonthDay()); dt.toString(ISODateTimeFormat. hourMinute());
Examples (formatting & parsing) • import static org.joda.time.format.DateTimeFormat.forPattern; • … • private static String fromJdkDate(Date jdkDate) { return new DateTime(jdkDate).toString("dd-MM-yyyyy"); } • private static String fromJdkCalendar(Calendar jdkCalendar) {
return new DateTime(jdkCalendar).toString("dd-MM-yyyyy"); } • private static Date fromStringDate(String dateAsString,
String datePattern) { DateTimeFormatter fmt = forPattern(datePattern); return DateTime.parse(dateAsString, fmt).toDate(); }
Design flaws of Joda-Time and why is not JSR-310 RI • Human/Machine timelines
Two principle views of the timeline - Human and Machine. DateTime is a human-timeline view of the world, not a machine-timeline view and should not implement ReadableInstant as it now does.
• Pluggable chronology Each date/time class in Joda-Time has a pluggable chronology. A better solution would be to keep the date/time classes restricted to a single calendar system.
• Nulls Joda-Time defines a null instant as the current time.
• When a method is defined as taking a ReadableInstant, • passing null in will be the same as passing in an instant set to the
current time.
• Nevertheless Joda-Time isn't broken and is currently the best alternative!
JSR-310 RI(ThreeTen) to the rescue
JSR-310 Design Principles • Well designed, based on immutable classes
• Thread safe
• Two models of time: “machine” and “human”
• Construction typically by factory method
• ‘with’ methods instead of ‘set’
• Enums for Months, Weeks, Era, ChronoFields, etc
• ISO based model by default
• Possibility to create date and time for different chronogies
JSR-310 Machine time • Machine time - Instant
An instantaneous point on the time-line, starting from EPOCH, representing epoch-seconds and nanos-of-second
- Duration
Difference in seconds and nanos-of-second between two instants
JSR-310 Human time • Human time LocalDate – just date w/o tz, e.g. 2013-02-22
LocalTime – just time w/o tz, e.g. 10:15:30
LocalDateTime – date and time w/o offset,
e.g. 2013-02-22 T10:15:30
ZoneOffset – offset against UTC (positive or negative),
e.g. +05:00, +01:00, -02:00, +04:30, Z, CEST, UTC, GMT
OffsetDate – date w/o time but with offset,
e.g. 2013-12-03+02:00
OffsetTime – time w/o date but with offset,
e.g. 10:15:30+02:00
OffsetDateTime – date with time and offset,
e.g. 2013-12-03T10:15:30+02:00
Examples of date & time
• Date without time component - e.g. 1 Mar 2012
LocalDate firstMarch2012 = LocalDate.of(2012, 03, 01);
LocalDate firstMarch2012 = LocalDate.parse("2012-03-01");
• Time without date component - e.g. 11:00
LocalTime elevenAm= LocalTime.of(11, 00);
LocalTime elevenAm = LocalTime.parse("11:00");
• Time with vs time without offset, e.g. 11:00 vs 11:00 CEST
LocalTime local = LocalTime.of(11, 0);
OffsetTime cest1 = OffsetTime.of(local, ZoneOffset.ofHours(2));
OffsetTime cest2 = OffsetTime.of(local, ZoneOffset.of("+02:00"));
JSR-310 RI(ThreeTen) & Joda-Time Comparison
• Joda-Time is mature and stable, recommended for production usage.
• ThreeTen is not yet stable enough.
Questions
Learn in details
• JodaTime links http://joda-time.sourceforge.net/index.html
• http://joda-time.sourceforge.net/faq.html http://jodatime.sourceforge.net/contrib/hibernate/
• JSR-310 and RI(ThreeTen project) http://java.net/projects/jsr-310 http://openjdk.java.net/projects/threeten/ http://hg.openjdk.java.net/threeten/threeten/jdk http://cr.openjdk.java.net/~rriggs/threeten-javadoc/index.html