7.4. Date Operations (Joda Time)¶
Table of Contents
7.4.1. Overview¶
java.util.Date
, java.util.Calendar
class is poorly built to perform complex date calculations.org.joda.time.DateTime
, org.joda.time.LocalDate
, or org.joda.time.LocalTime
object instead of java.util.Date
.org.joda.time.DateTime
, org.joda.time.LocalDate
, or org.joda.time.LocalTime
object is immutable (the result of date related calculations, etc. is returned in a new object).7.4.2. How to use¶
The usage of Joda Time, Joda Time JSP tags is explained below.
7.4.2.1. Fetching date¶
7.4.2.1.1. Fetching current time¶
org.joda.time.DateTime
, org.joda.time.LocalDate
and org.joda.time.LocalTime
. The usage method is shown below.- Use
org.joda.time.DateTime
to fetch time up to milliseconds.
DateTime dateTime = new DateTime();
- Use
org.joda.time.LocalDate
when only date, which does not include time and TimeZone, is required.
LocalDate localDate = new LocalDate();
- Use
org.joda.time.LocalTime
when only time, which does not include date and TimeZone, is required.
LocalTime localTime = new LocalTime();
- Use
org.joda.time.DateTime.withTimeAtStartOfDay()
to fetch the current date with the time set to start of day.
DateTime dateTimeAtStartOfDay = new DateTime().withTimeAtStartOfDay();
Note
LocalDate and LocalTime do not have the TimeZone information.
Note
It is recommended that you use
org.terasoluna.gfw.common.date.jodatime.JodaTimeDateFactory
, for fetching instance of DateTime, LocalDate and LocalTime at the time of fetching current time.DateTime dateTime = dataFactory.newDateTime();Refer to System Date for using
JodaTimeDateFactory
.LocalDate and LocalTime can be generated in the following way.
LocalDate localDate = dataFactory.newDateTime().toLocalDate(); LocalTime localTime = dataFactory.newDateTime().toLocalTime();
7.4.2.1.2. Fetching current time by specifying the time zone¶
org.joda.time.DateTimeZone
class indicates time zone.DateTime dateTime = new DateTime(DateTimeZone.forID("Asia/Tokyo"));
org.terasoluna.gfw.common.date.jodatime.JodaTimeDateFactory
is used as follows:
// Fetching current system date using default TimeZone
DateTime dateTime = dataFactory.newDateTime();
// Changing to TimeZone of Tokyo
DateTime dateTimeTokyo = dateTime.withZone(DateTimeZone.forID("Asia/Tokyo"));
For the list of other available Time zone ID strings, refer to Available Time Zones.
7.4.2.1.3. Fetching the date and time by specifying Year Month Day Hour Minute and Second¶
Specific time can be specified in the constructor. An example is given below.
- Fetching DateTime by specifying time up to milliseconds
DateTime dateTime = new DateTime(year, month, day, hour, minute, second, millisecond);
- Fetching LocalDate by specifying Year Month and Day
LocalDate localDate = new LocalDate(year, month, day);
- Fetching LocalDate by specifying Hour Minute and Second
LocalTime localTime = new LocalTime(hour, minutes, seconds, milliseconds);
7.4.2.1.4. Fetching Year Month Day individually¶
DateTime dateTime = new DateTime(2013, 1, 10, 2, 30, 22, 123);
int year = dateTime.getYear(); // (1)
int month = dateTime.getMonthOfYear(); // (2)
int day = dateTime.getDayOfMonth(); // (3)
int week = dateTime.getDayOfWeek(); // (4)
int hour = dateTime.getHourOfDay(); // (5)
int min = dateTime.getMinuteOfHour(); // (6)
int sec = dateTime.getSecondOfMinute(); // (7)
int millis = dateTime.getMillisOfSecond(); // (8)
Sr. No. | Description |
---|---|
(1)
|
Get Year. In this example,
2013 is returned. |
(2)
|
Get Month. In this example, “
1 ” is returned. |
(3)
|
Get Day. In this example,
10 is returned. |
(4)
|
Get Day of Week. In this example, “
4 ” is returned.The mapping of returned values and days of week is as follows:
[1:Monday, 2:Tuesday:, 3:Wednesday, 4:Thursday, 5:Friday, 6:Saturday, 7:Sunday]
|
(5)
|
Get Hour. In this example, “
2 ” is returned. |
(6)
|
Get Minute. In this example,
30 is returned. |
(7)
|
Get Second. In this example,
22 is returned. |
(8)
|
Get Millisecond. In this example,
123 is returned. |
Note
getDayOfMonth() starts with 1, differing from the specifications of
java.util.Calendar
.
7.4.2.2. Type conversion¶
7.4.2.2.1. Interoperability with java.util.Date¶
java.util.Date
can be easily performed.Date date = new Date();
DateTime dateTime = new DateTime(date); // (1)
Date convertDate = dateTime.toDate(); // (2)
Sr. No. | Description |
---|---|
(1)
|
Convert
java.util.Date to DateTime by passing java.util.Date as an argument to DateTime constructor. |
(2)
|
Convert DateTime to
java.util.Date using DateTime#toDate method. |
7.4.2.2.2. String format¶
DateTime dateTime = new DateTime();
dateTime.toString("yyyy-MM-dd HH:mm:ss"); // (1)
Sr. No. | Description |
---|---|
(1)
|
String of “yyyy-MM-dd HH:mm:ss” format is fetched.
For values that can be specified as arguments of toString, refer to Input and Output .
|
7.4.2.2.3. Parsing from string¶
LocalDate localDate = DateTimeFormat.forPattern("yyyy-MM-dd").parseLocalDate("2012-08-09"); // (1)
Sr. No. | Description |
---|---|
(1)
|
Convert “yyyy-MM-dd” string format to LocalDate type.
For values that can be specified as arguments of DateTimeFormat#forPattern, refer to Formatters.
|
7.4.2.3. Date operations¶
7.4.2.3.1. Date calculations¶
LocalDate localDate = new LocalDate(); // localDate is 2013-01-10
LocalDate yesterday = localDate.minusDays(1); // (1)
LocalDate tomorrow = localDate.plusDays(1); // (2)
LocalDate afterThreeMonth = localDate.plusMonths(3); // (3)
LocalDate nextYear = localDate.plusYears(1); // (4)
Sr. No. | Description |
---|---|
(1)
|
The value specified in argument of LocalDate#minusDays is subtracted from the date. In this example, it becomes
2013-01-09 . |
(2)
|
The value specified in argument of LocalDate#plusDays is added to the date. In this example, it becomes
2013-01-11 . |
(3)
|
The value specified in argument of LocalDate#plusMonths is added to the number of months. In this example, it becomes
2013-04-10 . |
(4)
|
The value specified in argument of LocalDate#plusYears is added to the number of years. In this example, it becomes
2014-01-10 . |
For methods other than those mentioned above, refer to LocalDate JavaDoc .
7.4.2.3.2. Fetching first and last day of the month¶
LocalDate localDate = new LocalDate(); // dateTime is 2013-01-10
Property dayOfMonth = localDate.dayOfMonth(); // (1)
LocalDate firstDayOfMonth = dayOfMonth.withMinimumValue(); // (2)
LocalDate lastDayOfMonth = dayOfMonth.withMaximumValue(); // (3)
Sr. No. | Description |
---|---|
(1)
|
Get Property object that holds the attribute values related to day of current month.
|
(2)
|
Get first day of the month by fetching the minimum value from Property object. In this example, it becomes
2013-01-01 . |
(3)
|
Get last day of the month by fetching the maximum value from Property object. In this example, it becomes
2013-01-31 . |
7.4.2.3.3. Fetching the first and the last day of the week¶
LocalDate localDate = new LocalDate(); // dateTime is 2013-01-10
Property dayOfWeek = localDate.dayOfWeek(); // (1)
LocalDate firstDayOfWeek = dayOfWeek.withMinimumValue(); // (2)
LocalDate lastDayOfWeek = dayOfWeek.withMaximumValue(); // (3)
Sr. No. | Description |
---|---|
(1)
|
Get Property object that holds attribute values related to the day of current week.
|
(2)
|
Get first day of the week (Monday) by fetching the minimum value from Property object. In this example, it becomes
2013-01-07 . |
(3)
|
Get last day of the week (Sunday) by fetching the maximum value from Property object. In this example, it becomes
2013-01-13 . |
7.4.2.3.4. Comparison of date time¶
By comparing the date and time, it can be determined whether it is a past or future date.
DateTime dt1 = new DateTime();
DateTime dt2 = dt1.plusHours(1);
DateTime dt3 = dt1.minusHours(1);
System.out.println(dt1.isAfter(dt1)); // false
System.out.println(dt1.isAfter(dt2)); // false
System.out.println(dt1.isAfter(dt3)); // true
System.out.println(dt1.isBefore(dt1)); // false
System.out.println(dt1.isBefore(dt2)); // true
System.out.println(dt1.isBefore(dt3)); // false
System.out.println(dt1.isEqual(dt1)); // true
System.out.println(dt1.isEqual(dt2)); // false
System.out.println(dt1.isEqual(dt3)); // false
Sr. No. | Description |
---|---|
(1)
|
isAfter method returns true when the target date and time is later than the date and time of argument. |
(2)
|
isBefore method returns true when the target date and time is prior to the date and time of argument. |
(3)
|
isEqual method returns true when the target date and time is same as the date and time of argument. |
7.4.2.4. Fetching the duration¶
Joda-Time provides several classes related to duration. The following 2 classes are explained here.
org.joda.time.Interval
org.joda.time.Period
7.4.2.4.1. Interval¶
Class indicating the interval between two instances (DateTime).
The following 4 are checked using the Interval class.
- Checking whether the interval includes the specified date or interval.
- Checking whether the 2 intervals are consecutive.
- Fetching the difference between 2 intervals in an interval
- Fetching the overlapping interval between 2 intervals
For implementation, refer to the following example.
DateTime start1 = new DateTime(2013,8,14,0,0,0);
DateTime end1 = new DateTime(2013,8,16,0,0,0);
DateTime start2 = new DateTime(2013,8,16,0,0,0);
DateTime end2 = new DateTime(2013,8,18,0,0,0);
DateTime anyDate = new DateTime(2013, 8, 15, 0, 0, 0);
Interval interval1 = new Interval(start1, end1);
Interval interval2 = new Interval(start2, end2);
interval1.contains(anyDate); // (1)
interval1.abuts(interval2); // (2)
DateTime start3 = new DateTime(2013,8,18,0,0,0);
DateTime end3 = new DateTime(2013,8,20,0,0,0);
Interval interval3 = new Interval(start3, end3);
interval1.gap(interval3); // (3)
DateTime start4 = new DateTime(2013,8,15,0,0,0);
DateTime end4 = new DateTime(2013,8,17,0,0,0);
Interval interval4 = new Interval(start4, end4);
interval1.overlap(interval4); // (4)
Sr. No. | Description |
---|---|
(1)
|
Check whether the interval includes the specified date and interval, using Interval#contains method.
If the interval includes the specified date and interval, return “true”. If not, return “false”.
|
(2)
|
Check whether the 2 intervals are consecutive, using Interval#abuts method.
If the 2 intervals are consecutive, return “true”. If not, return “false”.
|
(3)
|
Fetch the difference between 2 intervals in an interval, using Interval#gap method.
In this example, the interval between “2013-08-16~2013-08-18” is fetched.
When there is no difference between intervals, return null.
|
(4)
|
Fetch the overlapping interval between 2 intervals, using Interval#overlap method.
In this example, the interval between “2013-08-15~2013-08-16” is fetched.
When there is no overlapping interval, return null.
|
Intervals can be compared by converting into Period.
- Convert the intervals into Period when comparing them from abstract perspectives such as Month or Day.
// Convert to Period
interval1.toPeriod();
7.4.2.4.2. Period¶
Period is a class indicates duration in terms of Year, Month and Week.
- Number of days is “31” when a Period of “1 month” is added to “March 1”.
- Number of days is “30” when a Period of “1 month” is added to “April 1”.
The result of adding a Period of “1 month” differs depending on the target DateTime.
- Single field Period (Example:Type having values of single unit such as “1 Day” or “1 month”)
- Any field Period (Example:Type indicating the period and having values of multiple units such as “1 month 2 days 4 hours”)
For details, refer to Period.
7.4.2.5. JSP Tag Library¶
7.4.2.5.1. How to set¶
The following taglib definition is required to use the tag library.
<%@ taglib uri="http://www.joda.org/joda/time/tags" prefix="joda"%>
7.4.2.5.2. joda:format tag¶
joda:format tag is used to format the objects of DateTime, LocalDateTime, LocalDate and LocalTime.
<% pageContext.setAttribute("now", new org.joda.time.DateTime()); %>
<span>Using pattern="yyyyMMdd" to format the current system date</span><br/>
<joda:format value="${now}" pattern="yyyyMMdd" />
<br/>
<span>Using style="SM" to format the current system date</span><br/>
<joda:format value="${now}" style="SM" />
Output result
The list of attributes of joda:format tag is as follows:
No. | Attributes | Description |
---|---|---|
value
|
Set the instance of ReadableInstant or ReadablePartial.
|
|
var
|
Variable name
|
|
scope
|
Scope of variables
|
|
locale
|
Locale information
|
|
style
|
Style information for doing formatting (2 digits. Set the style for date and time. Values that can be entered are S=Short, M=Medium, L=Long, F=Full, -=None)
|
|
pattern
|
Pattern for doing formatting (yyyyMMdd etc.). For patterns that can be entered, refer to Input and Output.
|
|
dateTimeZone
|
Time zone
|
For other Joda-Time tags, refer to Joda Time JSP tags User guide.
Note
When the date and time is displayed by specifying style attributes, the displayed contents differ depending on browser locale. Locale of the format displayed in the above style attribute is “en”.
7.4.2.6. Example (display of calendar)¶
Using Spring MVC, sample for displaying a month wise calendar, is shown below.
Process name | URL | Handler method |
---|---|---|
Display of current month’s calendar | /calendar | today |
Display of specified month’s calendar | /calendar/month?year=yyyy&month=m | month |
The controller is implemented as follows:
@Controller
@RequestMapping("calendar")
public class CalendarController {
@RequestMapping
public String today(Model model) {
LocalDate today = new LocalDate();
int year = today.getYear();
int month = today.getMonthOfYear();
return month(year, month, model);
}
@RequestMapping(value = "month")
public String month(@RequestParam("year") int year,
@RequestParam("month") int month, Model model) {
LocalDate firstDayOfMonth = new LocalDate(year, month, 1);
LocalDate lastDayOfMonth = firstDayOfMonth.dayOfMonth()
.withMaximumValue();
LocalDate firstDayOfCalendar = firstDayOfMonth.dayOfWeek()
.withMinimumValue();
LocalDate lastDayOfCalendar = lastDayOfMonth.dayOfWeek()
.withMaximumValue();
List<List<LocalDate>> calendar = new ArrayList<List<LocalDate>>();
List<LocalDate> weekList = null;
for (int i = 0; i < 100; i++) {
LocalDate d = firstDayOfCalendar.plusDays(i);
if (d.isAfter(lastDayOfCalendar)) {
break;
}
if (weekList == null) {
weekList = new ArrayList<LocalDate>();
calendar.add(weekList);
}
if (d.isBefore(firstDayOfMonth) || d.isAfter(lastDayOfMonth)) {
// skip if the day is not in this month
weekList.add(null);
} else {
weekList.add(d);
}
int week = d.getDayOfWeek();
if (week == DateTimeConstants.SUNDAY) {
weekList = null;
}
}
LocalDate nextMonth = firstDayOfMonth.plusMonths(1);
LocalDate prevMonth = firstDayOfMonth.minusMonths(1);
CalendarOutput output = new CalendarOutput();
output.setCalendar(calendar);
output.setFirstDayOfMonth(firstDayOfMonth);
output.setYearOfNextMonth(nextMonth.getYear());
output.setMonthOfNextMonth(nextMonth.getMonthOfYear());
output.setYearOfPrevMonth(prevMonth.getYear());
output.setMonthOfPrevMonth(prevMonth.getMonthOfYear());
model.addAttribute("output", output);
return "calendar";
}
}
The CalendarOutput
class mentioned below is JavaBean having the consolidated information to be output on the screen.
public class CalendarOutput {
private List<List<LocalDate>> calendar;
private LocalDate firstDayOfMonth;
private int yearOfNextMonth;
private int monthOfNextMonth;
private int yearOfPrevMonth;
private int monthOfPrevMonth;
// omitted getter/setter
}
Warning
For the sake of simplicity, this sample code includes all the logic in the handler method of Controller, but in real scenario, this logic should be delegated to Helper classes to improve maintainability.
In JSP(calendar.jsp), it is output as follows:
<p> <a href="${pageContext.request.contextPath}/calendar/month?year=${f:h(output.yearOfPrevMonth)}&month=${f:h(output.monthOfPrevMonth)}">← Prev</a> <a href="${pageContext.request.contextPath}/calendar/month?year=${f:h(output.yearOfNextMonth)}&month=${f:h(output.monthOfNextMonth)}">Next →</a> <br> <joda:format value="${output.firstDayOfMonth}" pattern="yyyy-M" /> </p> <table> <tr> <th>Mon.</th> <th>Tue.</th> <th>Wed.</th> <th>Thu.</th> <th>Fri.</th> <th>Sat.</th> <th>Sun.</th> </tr> <c:forEach var="week" items="${output.calendar}"> <tr> <c:forEach var="day" items="${week}"> <td><c:choose> <c:when test="${day != null}"> <joda:format value="${day}" pattern="d" /> </c:when> <c:otherwise> </c:otherwise> </c:choose></td> </c:forEach> </tr> </c:forEach> </table>
Access {contextPath}/calendar to display the calendar below (showing result of November 2012).
Access {contextPath}/calendar/month?year=2012&month=12 to display the calendar below.
7.4.3. Appendix¶
7.4.3.1. Japanese calendar operation before Java8¶
java.time.chrono.JapaneseDate
is offered in Java8 for Japanese calendar operation however it is possible to deal with the Japanese calendar by java.util.Calendar
class in older Java version.java.util.Locale
in the java.util.Calendar
class and java.text.DateFormat
class.Locale locale = new Locale("ja", "JP", "JP");
Calendar
class.Locale locale = new Locale("ja", "JP", "JP");
Calendar cal = Calendar.getInstance(locale); // Ex, 2015-06-05
String format1 = "Gy.MM.dd";
String format2 = "GGGGyy/MM/dd";
DateFormat df1 = new SimpleDateFormat(format1, locale);
DateFormat df2 = new SimpleDateFormat(format2, locale);
df1.format(cal.getTime()); // "H27.06.05"
df2.format(cal.getTime()); // "平成27/06/05"
Locale locale = new Locale("ja", "JP", "JP");
String format1 = "Gy.MM.dd";
String format2 = "GGGGyy/MM/dd";
DateFormat df1 = new SimpleDateFormat(format1, locale);
DateFormat df2 = new SimpleDateFormat(format2, locale);
Calendar cal1 = Calendar.getInstance(locale);
Calendar cal2 = Calendar.getInstance(locale);
cal1.setTime(df1.parse("H27.06.05"));
cal2.setTime(df2.parse("平成27/06/05"));
Note
Thejava.util.JapaneseImperialCalendar
object corresponding to the Japanese calendar is created by specifying thenew Locale("ja", "JP", "JP")
into thegetInstance
method.If you specify the other,java.util.GregorianCalendar
object gets created therefore it should be noted.