Showing posts with label temporal. Show all posts
Showing posts with label temporal. Show all posts

Saturday, February 14, 2009

Java Date/Time Objects with JSR-310

I spent the day experimenting with the proposed javax.time API's from JSR-310. Modeled after the popular joda-time the new javax.time objects offer a rich set of date and time manipulation, basic math, durations, matching and time zone awareness.

At the top level, the concept of a TimeSource is introduced. This is a replacement for using System.currentTimeMillis() or System.timeNanos() or the standard java.util.Date and Calendar contructors. It can be spring loaded, so getting a source that has an offset is possible, which makes testing very easy.

The abstract Clock class is similar to TimeSource, but it is time zone aware. It's easy to get the local time (Clock.systemDefaultZone()) or to create a clock from a different time zone (Clock.system(TimeZone.UTC)).

Once the Clock object is created you can access today(), tomorrow(), yesterday(), the current DateTime and other methods for creating complex DateTime objects. You can also get the current instant which represents the current date/time as an instant since the epoch in nanoseconds.

The first step to using javax.time is to checkout the latest version from subversion. (I'm at build version 702).

Converting Date and Calendar to javax.time

A common way to construct both Date and Calendar from the java.util package is to use milliseconds. The Instant class from javax.time includes a method called toEpochMillis() that makes this easy: (groovy syntax)

utilDate = new java.util.Date()
instant = Instant.millisInstant( utilDate.time )
zone = ZoneOffset.zoneOffset( -8 )
dateTime = OffsetDateTime.dateTime( instant, zone )


Converting javax.time to Date/Calendar

To convert a javax.time object back to either Date or Calendar is just as simple:

ts = TimeSource.system()
dt = new Date( ts.instant().toEpochMillis() )

cal = Calendar.instance
cal.timeInMillis = ts.instant().toEpochMillis()

Date Math

Here is a quick example of what you can do with a DateTime object. In this case, I'll use an OffsetDateTime to express the full ISO8601 date. (again, groovy syntax)

clock = Clock.systemDefaultZone()
assert clock.today().toString() == "2009-02-14"
assert clock.tomorrow().toString() == "2009-02-14"

dt = clock.offsetDateTime()
assert dt.toString() == "2009-02-14T20:31:50.896-08:00"

dt.plusYears(3).toString() == "2012-02-14T20:31:50.896-08:00

assert dt.year == 2009
year = dt.toYear() // get the object, not the int

assert year.lengthInDays() == 365
assert year.next().value == 2010
assert year.nextLeap().value == 2012
assert year.nextLeap().lengthInDays() == 366

yearMonth = YearMonth.yearMonth( dt )
assert yearMonth.toString() == "2009-02"
assert yearMonth.plusMonths(5).toString() == "2009-07"
assert yearMonth.minusMonths(5).toString() == "2008-09"


So you get the point. Many useful methods currently missing from the java JDK. So, hopefully this will be complete in time to make it into java 7. In the mean time, though not ready for production, it's available for bleeding-edge use.

Tuesday, March 4, 2008

Groovy DateTime Comparisons

Those of you following my posts know that I'm working on enhanced date/time support for groovy. In a previous post, I discussed a new DateTime class that leverages the power of java's Calendar and updates the interface in a groovy way. Which brings me to after(), before(), and compareTo() methods.

Calendar implements compareTo() that accepts only a calendar object. If you pass it a Date and exception is thrown. How restrictively lame.

DateTime's compareTo() accepts multiple types that can be evaluated to the millisecond level. So, DateTime, Date, Calendar, even long types are accepted. This approach enables this to work:

now = [ new Date(), Calendar.getInstance(), System.currentTimeMillis, new DateTime() ]

yesterday = new DateTime() - 1
tomorrow = new DateTime() + 1
later = new DateTime(hours:23, minutes:59)

now.each {
assert yesterday.before( it )
assert tomorrow.after( it )
assert laster.after( it )
}
DateTime. How groovy is that!

Groovy Temporal Support Continued

One area where ruby outshines groovy is in date and date time support. Ruby's date time support isn't more capable, just easier to use. Java, and groovy have Calendar--very complete, and very clunky. There is also joda time. Very complete, but a bit of an overkill for my tastes.

So here are the objectives:
  • ability to add/subtract dates with integers representing days, months, etc.
  • ability to subtract two dates to yield intervals representing days, hours, etc.
  • ability to compare two dates
  • ability to parse any type of date input, and most easily parse the standards
  • ability to format dates in a wide variety of ways
  • ability to create dates with integers and words like 10.days.ago or nextweek
Java's calendar class supports the first three requirements, parsing is available through SimpleDateFormat class. Groovy's TimeCategory enables some of the most common integer enhancements to create dates based on common temporal works. So, the pieces are in place, the next step is to wrap this in a groovy way.

DateTime, the Groovy Calendar: The first step is to create an easy (easier) to use, full featured DateTime class. One approach is to simply extend GregorianCalanedar and add the new methods, mostly getters and setters. That's were I started but soon discovered a better solution was to contain the calendar object, and use invokeMethod() to pass methods and arguments. Then, rather than define properties of the DateTime object, define getters and setters in bean style making the variables appear as if they were members. For example, I define these two methods:
int getYear() { return calendar.get(Calendar.YEAR) }
void setYear(int yr) { calendar.set(Calendar.YEAR, yr) }
Now, I can use short hand to access "year" like this:
def dateTime = new DateTime(year:2008)
assert dateTime.year == 2008
dateTime.year = 2020
assert dateTime.year == 2020
dateTime.year++
assert dateTime.year == 2021
Groovy, right? And adding other methods like setSeconds(), clearTime(), isWednesday() were easy one-liners to implement. I also took the liberty of shifting the month to 1..12 rather than 0..11, the calendar default.

Constructors: This was also very groovy. After I added the getter/setters, constructing with the virtual member vars was easy. So the DateTime class can be constructed like this:
dt = new DateTime(year:2020, month:4, day:1)
dt = new DateTime(hours:0, minutes:0, seconds:0)
dt = new DateTime().clearTime()
dt = new DateTime() + 5

Date/Time Formatting: The Rails group enhanced ruby's DateTime.to_s() method by adding formatting like this: to_s(format). This makes a lot of sense. To implement this I use a format hash that stores formatting strings by name. The map is static to enable use application wide (I may regret this later) and there is a default format member variable that controls how toString() is formatted. I also added toString(format) to display a date in multiple formats like this:
// default format is a modified ISO-8601 called 'db'
dt = new DateTime(year:2010, day:25, month:4, hours:15, minutes:35, seconds:29)
assert dt.toString() == '2010-04-25 15:35:29'
assert dt.toString(8601) == '2010-04-25T15:35:29-0800'
assert dt.toString('mdy') == '04/25/2010'
assert dt.toString('dmy') == '25.04.2010'
assert dt.toString('dMony') == '25-Apr-2010'
Other formats are available in the formats hash. The actual formatting is done using SimpleDateFormat that supplies a wide range of date formats and is compatible with Java 1.4, important in the groovy community.

A Groovy Date Parser: I've seen many approaches to this, usually making use of java's SimpleDateFormat class with a boat load of formats. I think there may be a better way. First, extracting the date parts, usually the numbers and constructing date time objects based on the numeric values. I'll discuss this implementation later...

Sunday, March 2, 2008

Groovy Temporal Support Classes

Groovy adds a rich set of features to java including closures, ranges, dynamic metaClass, access to existing java code base, etc. What is missing is full featured date processing. One approach to fixing this is to use existing libraries of java date extensions, such as joda time. Today I decided to implement some basic date/time features without a third party dependency. I may end up using joda as the project unfolds, but for now I'm trying to resist the temptation.

HourMinuteSecond: This class acts as a container and formatter of hours, minutes and seconds similar to groovy's TimeDuration class. In native form it's completely mutable, so not thread safe. But it can be made immutable by invoking asImmutable(). I'll explain how groovy makes this easy to implement later on.

The class API is as follows:
static HourMinuteSecond now()
static HourMinuteSecond fromDate(date)

def parse('hh:mm:ss')
def set(hours, minutes, seconds)
def set(Date dt)
def next() // enables ++
def previous() // enables --
def add(hours, minutes, seconds)
def sub(hours, minutes, seconds)
boolean isZero()
String toString() // formatted as hh:mm:ss
void addZeroEventListener(closure)
def asImmutable()
The object can be used as a simple up/down timer measured in seconds. When attached to a one second ticker thread, only the next() method needs to be invoked to tally up the seconds. Used as a count down timer, an event is triggered when zero is reached.

The methods are actually closures, so creating the immutable clone was as simple as pointing all the mutators to a single closure that throws an UnsupportedOperationException when a mutating method is called. This mimics how groovy's List.asImmutable() method works.

I will post more as the temporal project unfolds, and supply a link once the library is ready for distribution.