Wednesday, December 12, 2012

Groovy JDK (GDK): Date and Calendar

I have looked at some highly useful methods available in Groovy GDK's extensions to the Java JDK in blog posts such as Groovy JDK (GDK): File.deleteDir(), Groovy JDK (GDK): Text File to String, Groovy JDK (GDK): More File Fun, Groovy JDK (GDK): String Support, and Groovy JDK (GDK): Number Support. In this post, I look at some of the endearing features of Groovy's GDK extensions to the Java JDK java.util.Date and java.util.Calendar classes.

Java's current standard support for dates and times is generally disliked in the Java development community. Many of us look forward to JSR-310 and/or already use Joda Time to get around the shortcomings of Java's treatment of dates and times. Groovy makes working with dates and times a little easier when third-party frameworks are not available or cannot be used.

The Groovy GDK extension of Date provides several new and highly useful methods as shown in the screen snapshot of its documentation.

Some of these useful mehtods that I will highlight in this post are clearTime(), format(String), getDateString(), getTimeString(), parse(String, String), parseToStringDate(String), toCalendar(), toTimestamp(), and updated(Map). Many of the other methods listed in the API support Groovy operator overloading and are not highlighted in this post.

Date.clearTime() and Calendar.clearTime()

There are times when one wishes to represent a date only and the time portion of a Date or Calendar is not important (which is exactly why JSR 310 is bringing date-only constructs such as LocalDate to JDK 8). In such cases, Groovy's extension to Date and Calendar make it easy to "clear" the time component. The next code listing demonstrates use of Date.clearTime() followed by a screen snapshot showing that code executed. Note that the clearTime() method mutates the object it acts upon.

/**
 * Demonstrates Groovy's GDK Date.clearTime() method. Note that the clearTime()
 * method acts upon the Date object upon which it is called, mutating its value
 * in addition to returning a reference to that changed object.
 */
def demoClearTime()
{
   printTitle("Groovy GDK Date.clearTime()")
   def now = new Date()
   println "Now: ${now}"
   def timelessNow = now.clearTime()
   println "Now sans Time: ${timelessNow}"
   println "Mutated Time:  ${now}"
}

Calendar's clearTime() works similarly as shown in the next code snippet and its accompanying screen snapshot of its execution.

/**
 * Demonstrates Groovy's GDK Calendar.clearTime() method. Note that the
 * clearTime() method acts upon the Calendar object upon which it is called,
 * mutating its value in addition to returning a reference to that changed object.
 */
def demoCalendarClearTime()
{
   printTitle("Groovy GDK Calendar.clearTime()")
   def now = Calendar.getInstance()
   println "Now: ${now}"
   now.clearTime()
   println "Now is Timeless: ${now}"
}
Date.format and Calendar.format

It is common in Java development to need to display a Date or Calendar in a specific user-friendly format and this is typically accomplished using instances of SimpleDateFormat. Groovy simplifies this process of applying a format to a Date or String with the respective methods Date.format(String) and Calendar.format(String). Code listings demonstrating each are shown next with each code listing followed by a screen snapshot displaying the executed code.

/**
 * Demonstrate how much more easily a formatted String representation of a Date
 * can be acquired in Groovy using GDK Date.format(String). No need for an
 * explicit instance of SimpleDateFormat or any other DateFormat implementation
 * here!
 */
def demoFormat()
{
   printTitle("Groovy GDK Date.format(String)")
   def now = new Date()
   println "Now: ${now}"
   def dateString = now.format("yyyy-MMM-dd HH:mm:ss a")
   println "Formatted Now: ${dateString}"
}
/**
 * Demonstrate how much more easily a formatted String representation of a
 * Calendar can be acquired in Groovy using GDK Calendar.format(String). No need
 * for an explicit instance of SimpleDateFormat or any other DateFormat
 * implementation here!
 */
def demoCalendarFormat()
{
   printTitle("Groovy GDK Calendar.format(String)")
   def now = Calendar.getInstance()
   println "Now: ${now}"
   def calendarString = now.format("yyyy-MMM-dd HH:mm:ss a")
   println "Formatted Now: ${calendarString}"
}
Date.getDateString(), Date.getTimeString(), and Date.getDateTimeString()

The format methods shown previously allow customized representation of a Date or Calendar and the clearTime methods shown previously allow the time element to be removed from an instance of a Date or Calendar. Groovy provides some convenience methods on Date for displaying a user-friendly date only, time only, or date and time without specifying a format or clearing the time component. These methods print dates and times in the predefined format specified by DateFormat.SHORT (for date portions) and DateFormat.MEDIUM (for time portions). Code listings of each of these methods are shown next and are each followed by screen snapshots of that code being executed.

/**
 * Demonstrates Groovy's GDK Date.getDateString() method. Note that this
 * method doesn't change the underlying date, but simply presents only the date
 * portion (no time portion is presented) using the JDK's DateFormat.SHORT
 * constant (which defines the locale-specific "short style pattern" for
 * formatting a Date).
 */
def demoGetDateString()
{
   printTitle("Groovy GDK Date.getDateString()")
   def now = new Date()
   println "Now: ${now}"
   println "Date Only: ${now.getDateString()}"
   println "Now Unchanged: ${now}"
}
/**
 * Demonstrates Groovy's GDK Date.getTimeString() method. Note that this
 * method doesn't change the underlying date, but simply presents only the time
 * portion (no date portion is presented) using the JDK's DateFormat.MEDIUM
 * constant (which defines the locale-specific "medium style pattern" for
 * formatting a Date).
 */
def demoGetTimeString()
{
   printTitle("Groovy GDK Date.getTimeString()")
   def now = new Date()
   println "Now: ${now}"
   println "Time Only: ${now.getTimeString()}"
   println "Now Unchanged: ${now}"
}
/**
 * Demonstrates Groovy's GDK Date.getDateTimeString() method. Note that this
 * method doesn't change the underlying date, but simply presents the date and
 * time portions as a String. The date is presented with locale-specific format
 * as defined by DateFormat.SHORT and the time is presented with locale-specific
 * format as defined by DateFormat.MEDIUM.
 */
def demoGetDateTimeString()
{
   printTitle("Groovy GDK Date.getDateTimeString()")
   def now = new Date()
   println "Now: ${now}"
   println "Date/Time String: ${now.getDateTimeString()}"
   println "Now Unchanged: ${now}"
}
Date.parse(String, String)

The GDK Date class provides a method Date.parse(String, String) that is a "convenience method" that "acts as a wrapper for SimpleDateFormat." A code snippet and corresponding screen snapshot of the code's output follow and demonstrate this method's usefulness.

/**
 * Demonstrate Groovy GDK's Date.parse(String, String) method which parses a
 * String (second parameter) based on its provided format (first parameter).
 */
def demoParse()
{
   printTitle("Groovy GDK Date.parse(String, String)")
   def nowString = "2012-Nov-26 11:45:23 PM"
   println "Now String: ${nowString}"
   def now = Date.parse("yyyy-MMM-dd hh:mm:ss a", nowString)
   println "Now from String: ${now}"
}
Date.parseToStringDate(String)

The GDK Date.parseToStringDate(String) method can be used to obtain an instance of Date from a String matching the exact format put out by the Date.toString() method. In other words, this method can be useful for converting back to a Date from a String that was generated from a Date's toString() method.

Use of this method is demonstrated with the following code snippet and screen snapshot of the corresponding output.

/**
 * Demonstrate Groovy GDK's Date.parseToStringDate(String) method which parses
 * a String generated by a Date.toString() call, but assuming U.S. locale to
 * do this.
 */
def demoParseToStringDate()
{
   printTitle("Groovy GDK Date.parseToStringDate(String)")
   def now = new Date()
   println "Now: ${now}"
   def nowString = now.toString()
   def nowAgain = Date.parseToStringDate(nowString)
   println "From toString: ${nowAgain}"
}

There is one potentially significant downside to the GDK Date.parseToStringDate(String) method. As its documentation states, it relies on "US-locale-constants only."

Date.toCalendar() and Date.toTimestamp()

It is often useful to convert a java.util.Date to a java.util.Calendar or java.sql.Timestamp. Groovy makes these common conversions particularly easy with the GDK Date-provided methods Date.toCalendar and Date.toTimestamp(). These are demonstrated in the following code snippets with their output displayed in corresponding screen snapshots.

/**
 * Demonstrates how easy it is to get a Calendar instance from a Date instance
 * using Groovy's GDK Date.toCalendar() method.
 */
def demoToCalendar()
{
   printTitle("Groovy GDK Date.toCalendar()")
   def now = new Date()
   println "Now: ${now}"
   def calendarNow = now.toCalendar()
   println "Now: ${calendarNow} [${calendarNow.class}]"
}
/**
 * Demonstrates how easy it is to get a Timestamp instance from a Date instance
 * using Groovy's GDK Date.toTimestamp() method.
 */
def demoToTimestamp()
{
   printTitle("Groovy GDK Date.toTimestamp()")
   def now = new Date()
   println "Now: ${now}"
   def timestampNow = now.toTimestamp()
   println "Now: ${timestampNow} [${timestampNow.class}]"
}

Date.updated(Map) [and Calendar.updated(Map)]

The final convenience method provided by the GDK Date that I'm going to discuss in this post is Date.updated(Map), which its documentation describes as "Support creating a new Date having similar properties to an existing Date (which remains unaltered) but with some fields updated according to a Map of changes." In other words, this method allows one to start with a certain Date instance and acquire another Date instance with the same properties other than changes specified in the provided Map.

The next code listing acquires a new Date instance from an existing Date instance with a few fields updated using the Date.updated(Map) method. The code listing is followed by a screen snapshot of its execution.

/**
 * Demonstrate Groovy GDK's Date.updated(Map) with adaptation of the example
 * provided for that method in that method's Javadoc-based GDK documentation.
 * Note that the original Date upon which updated is invoked is NOT mutated and
 * the updates are on the returned instance of Date only.
 */
def demoUpdated()
{
   printTitle("Groovy GDK Date.updated(Map)")
   def now = new Date()
   def nextYear = now[YEAR] + 1
   def nextDate = now[DATE] + 1
   def prevMonth = now[MONTH] - 1
   def oneYearFromNow = now.updated(year: nextYear, date: nextDate, month: prevMonth)
   println "Now: ${now}"
   println "1 Year from Now: ${oneYearFromNow}"
}

The demonstration shows that the original Date instance does remain unaltered and that a copy with specified fields changed is provided. There is also an equivalent for the GDK Calendar called Calendar.updated(Map).

Conclusion

One of the things I like about Groovy is the GDK extensions to SDK classes. In this post, I looked at how the GDK Date extension of the JDK's Date provides many useful convenience methods that lead to more concise and more readable code.

No comments: