Monday, October 26, 2009

Flex 4 BitmapImage

Flex 4, and specifically the new spark framework has a BitmapImage object in the spark.primitives package. It's associated mxml tag is . I found this after trying to add a Bitmap object to a VGroup only to get errors. So, after searching a bit, I found the BitmapImage and simply set the source property to my BitmapData. Very easy.

My code problem was to read a remote image then scale it to as close to a target width & height as possible without changing the original aspect ratio. From there, the user can rescale the image and drag a focus rectangle over the image to select the desired image section. Then, with the click of a button, the image is scaled and cropped to the optimum size. I used JPEGEncoder to write the data back out to a new image file.

Tuesday, October 20, 2009

Flex 4/Flash Builder Beta

Adobe released Flash 4 Beta 2 a few weeks ago with many changes and improvements over Flex 3. The new lightweight spark framework has many advantages over the older mx components and separates skinning in an attempt to facility and advocate better separation of concerns and improved maintainability.

There are also some significant improvements to the Flash Builder IDE that take much better advantage of the eclipse framework. It was always a big mystery to my why Flex 2 and 3 Builders seemed to strip so many of the basic features out of eclipse when they created their IDE. Many of those features, although not all, are now included.

One basic but vital feature is the ability to create your own standard templates for AS3 classes and MXML applications and components. The approach follows the eclipse use of internal variables, like ${user}, ${date}, ${year}, etc.

Another feature is conditional breakpoints and watchpoints. There is also a way to do remote debugging.

So it looks like the new Flash Builder comes closer to providing the basic eclipse functionality that has been missing since Flex 1.x was introduced. I'll post later with other features that have been included while I work my way through the latest Max 2009 presentations and the on-line "Flex 4 in a week" training series. And, in subsequent posts, I will share what is offered by the new Spark platform.

Friday, September 4, 2009

Hotlinks External Page Viewer

I first heard about twitter in 2007 while listening to a Ruby On Rails podcast. It took a while, but I finally got on board, mainly as a result of the mandatory SEO stuff required for my current project. So, I'm now all a twitter.

Twitter's 140 character limit leads to a mandatory link for almost every post. Some links are huge, but with the help of tinyurl.com or bit.ly you can hash down your URL to a very manageable size. At the same time, the link reduction sites allow you to track the number of hits the link has received.

There are a couple of companies that have mashed up the URL reducers with the ability to display a personalized reference at the top of the target page. So, If I tweet about Hillary Duff's new utube video I would be able to display a header that includes a link to my site at the top of the page. Rather than use one of the off the shelf products, I decided to build my own, mainly because 1) it was trivial to implement, and 2) I (and my customer) wanted custom graphics displayed on the banner without the third party interference.

So, here is a sample of what I mean. The original tweet would look a bit like this: "check out Hillary Duff's new utube thingy here: http://bit.ly/30YbdB". When the tweet recipient clicks on the link, they see the video as described, but they also see the Rain City Software banner at the top of the page, complete with links to my web site and twitter account. Go ahead, give it a try.



As it turns out, this is very simple to do with a simple iframe to display the target URL. The banner is small and hopefully not offensive and can be closed by clicking on the 'X' icon. Or, the user can click on the logo to get to the Rain City site or click on the tweety birds to get to Rain City's twitter account. All sorts of possibilities (including incorporating swf's, but lets keep it simple for now). And, the clicks are tracked not only by bit.ly but for google analytics as well.

Creating the Hotlink: I made the hotlink creation easy with a small web based UI. The user simply enters a title and pastes in the link then clicks create (after selecting the correct server). The hotlink is created and can be copied to bit.ly, tinyurl or your favorite URL shortener. Here is what we get for the Hillary Duff link:



The user can test the link by clicking 'Test Link' to see if all is in order. If this looks ok, then copy the link to the shortener, make the tweet and that's it.

Adobe/Flex UI Code: As you can see from the code below, the code is quite simple. Click handlers for create and test, and the use of escape to get the title and target URL (hotlink) to work correctly as http parameters. I thought about using he open APIs provided by many URL shorteners, but decided to just let the user copy/paste the target URL. Here is the Flex xml code:



The Hotlink Service Code: I decided to implement the service code in ruby but php or java would have worked just as well. The objective is to pull the title and link from the URL parameters and use those inside the standard template code. The template code should probably be separated out, but it was easy enough to keep everything all in one file. The banner can be any combination of backgrounds, links, whatever. Anyway, here is the ruby code:



So that's it. Easy to use and all the tracking that an SEO could hope for. And, very customizable to suite the customer's requirements. Let me know if you want help setting up your own. I would be happy to help.

Thursday, August 27, 2009

Social Network Common Login in Flex

JanRain provides a free version of their industry-leading OpenID solution. RPX Basic enables websites to accelerate user registration and login success by allowing visitors to easily sign in with one of their existing third party accounts from AOL, Facebook, Google, MySpace, Yahoo! and more.

Their product includes a drop-in widget written in Javascript that does all the communications to their open id server and third party providers. But, for Flex applications (or Java Applets) they provide a set of API calls that do the job.

Here is what our custom login dialog looks like:



The user simply clicks the preferred provider, and the appropriate API call is made to RPX to open a new window. For example, if the user clicks the facebook icon, the following page is displayed.



If the users successfully logs into facebook, a token is returned to the Flex application. The next step is to use this token to access the user's profile, email, photo, etc. Here's what the user see's after they log in.



The same thing happens if the user logs in with any of the other providers. Subsequent logins don't require the approval, but simply log in without the intermediate page. So, if I'm already logged into facebook and have approved access from my Flex application, the click skips the facebook login and returns the token. Simple.

The Code: The code adheres to the MVC paradigm and is short and sweet. A single controller talks to the RPX API and a login dialog mxml handles the display and clicks. The controller publishes two events loginComplete and accessFailed. The dialog processes the provider click through the controller while listening for the response event.

Configuration: When you sign up with JanRain for RPX Basic, there are a few configurations that need to be made to get facebook and other third party authorization platforms to work. The process is simple and only takes a few minutes.

Testing was easy for all six of the providers that our application uses. And, there are more coming on line all the time. It's a great example of distributed processing that eliminates user resistance to signing up for your latest social network application.

Friday, July 17, 2009

Simple Digest to Test File Uniqueness

Here is a simple way to use the groovy Digest class from org.raincity.glib.crypto package. It's limited to reasonably small files, but could be used with chunked sections of files. In any case, here is the test script:

void testStringDigest() {
def algorithm = 'SHA-512'
def s1 = 'this is test 1'
def s2 = 'this is test 2'

def hash1 = new Digest().createHashString( s1, algorithm )
def hash2 = new Digest().createHashString( s2, algorithm )

println "h1 = ${hash1}"
println "h2 = ${hash2}"

assert hash1 != hash2
}

s1 = the byte stream from the first file, s2 is the byte stream from the second file. If the hashes match, then the files are identical, i.e., not unique.

Digest's default algorithm is SHA-256, but SHA-512 is available in java > 1.4 and is much stronger. The 256 default was designed to match the SHA-256 limit for Adobe/Flex.

Thanks to Brad Rhoads for inspiring this post...

Thursday, June 4, 2009

Amazon S3 Access with JetS3t

Back in my ruby/rails days, I always accessed Amazon S3 from the command line. No that I'm coding in groovy/grails, I thought I would search for a groovy solution. Well, I found one, but it's not ready for prime-time. But, I did come across JetS3t, written in java with examples in groovy, so I thought I would give it a try.

After downloading, I opened the README and found that JetS3t comes with a nifty set of stand-alone and web based UI application/applets for accessing, browsing and uploading files. The application is called Cockpit and I was able to easily use it inside Firefox and as a standalone Java application.

Find out more about JetS3t suite of applications and be sure to read Andrew Glover's tutorial.

Wednesday, April 22, 2009

Lame Flex IDE

Ok, Adobe Flex, or AS3 is an OK language. But their commercial Flex IDE sucks. Adobe uses (abuses) the eclipse framework for their commercial IDE. They charge big bucks for a somewhat visual editor that amounts to a bucket of shit.

After wrestling with it for over two years I'm tempted to go back to vi and the command line. Anyone else feel this way?

Monday, March 23, 2009

Parsing ISO 8601 Dates in Flex

Flex has many capabilities, but parsing dates is not one of them. As a member of the JSR-310 team I am focused on date formatting and parsing across many formats, so it comes as a bit of a surprise that Flex has very little support for this. I guess it's time to roll my own.

Unlike Java date formatters, Flex doesn't let you set the parse format to accept custom inputs. The formats must match one of the seven standard parse formats that Flex supports. Some third party examples try to use Date.parse() after massaging the input string, but they are a bit lame.

A good way to parse dates in Flex is to use RegExp to extract the numbers. Then, the numbers need to be validated prior to assignment. First, lets look at a few code snippets to see how to pull the numbers out of a local date (no time zone offset).

// ISO 8601 date time as YYYY-MM-DDThh:mm:ss.SSS
var dateTimeArray:Array = inputDate.split('T')
var dateString:String = dateTimeArray[0]
var timeString:String = dateTimeArray[1]

// parse the date from YYYY-MM-DD
var pattern:RegExp = /(^\d{4})-(\d{2})-(\d{2}$)/
var result:Object = pattern.exec( dateString )
if (result == null)
throw(new Error("invalid date format"))

var year:uint = new uint( result[1] )
var month:uint = new uint( result[2] )
var day:uint = new uint( result[3] )

if (!validateDate(year, month, day))
throw(new Error("invalid date format"))

var date:Date = new Date(year, month - 1, day, 0, 0, 0, 0)

// now parse the time from HH:MM:SS.SSS
pattern = /(^\d{2}):(\d{2}):(\d{2}).(\d{3})/
result = pattern.exec( timeString )
if (result == null)
throw(new Error("invalid time format"))

var hour:uint = new uint( result[1] )
var minute:uint = new uint( result[2] )
var second:uint = new uint( result[3] )
var milli:uint = new uint( result[4] )

if (!validateTime(hour, minute, second, milli))
throw(new Error("invalid time format"))

date.setHours(hour, minute, second, milli)

Lenient Dates and Times: It appears that in Flex world, March 32nd == April 1st. And April 2nd at 25 hours == April 3rd at 1am. Is this an April fools joke? No, it's just the lenient way that Flex handles date parameters. No problem with that until you start parsing, then you need to be strict.

Strict Dates: Lets say a vendor sends an electronic invoice to you with a payment due date of 2009-May-31. Does he want his money on June 1st? Probably not. The best response is to reply that his date is invalid and he must submit a valid date.

That's what being strict means. Unfortunately, Flex does not have a way of controlling strict dates, so you need to do this manually.

Validation: Without validation constraints a Flex date can range from 0100-01-01T00:00:00.000 to beyond the year 10,000. Dates earlier than the year 100 are coerced to 1900. This is clearly, not what we want. For parsing purposes there should be a range of minimum/maximum acceptable dates. For discussion purposes, I'll set the range to start at the recognized Gregorian start date of 1582-10-17T00:00:00.000 and end at an arbitrary future date of 2999-12-31:23:59:59.999.

Now that we have a date range, the years are easy, 1582..2999. Months for Flex follow the old C convention of zero indexing, so months range from 0..11 but we will use 1..12 for validation then decrement the month prior to creating the flex Date object.

Days are a bit trickier, but lets use 31 as the default, then 30 for Apr, Jun, Sep, Nov and 28/29 for Feb, after determining the leap year. Here is the standard calculation for leap year lifted from the white book:

var leap = ((year % 4 == 0) && ( !(year % 100 == 0) || (year % 400 == 0)))

Hours, minutes, seconds and milliseconds are bound by 0..23, 0..59, 0..59, and 0..999. This ignores time zone changes and the occasional leap-second, but otherwise works fine.

The Final Code: I created a demo application that allows you to plug in and parse dates and run the unit tests. The demo and source code are available here. I'm still working on a parser for time-zoned dates, but this is a good start.

Saturday, February 28, 2009

JSR-310 javax.time Periods

The proposed JSR-310 date/time API comes with many representations of date and time including Instant, Duration, LocalDate, LocalTime, LocalDateTime, OffsetDate, OffsetTime, OffsetDateTime, ZonedDateTime, MonthDay, YearMonth and others. One of the more interesting classes is Period. It represents a quantity of time, not fixed to any time in space--just a quantity. This entry will discuss periods, how they are parsed, and examples of use.

Parsing: Periods are parsed from string using formats that conform to the ISO-8601 duration format PnYnMnDTnHnMn.nS. Variations of this format are parsed to create Period objects. Parsing is done in PeriodParser, a standalone helper class that is easily accessed through the static method Period.parse(). Here are a few examples:

assert Period.parse("PT0S") == Period.ZERO
assert Period.parse("P1Y") == Period.years(1)
assert Period.parse("P10Y8M22DT3M") == Period.period(10, 8, 22, 3)
assert Period.parse("PT1M") == Period.minutes(1)
assert Period.parse("P-4Y") == Period.years(-4)

As you can see, the parsing scheme is robust and flexible. The main thing to keep in mind is that the Period object is more of a value container as opposed to a process unit. So, 60 minutes won't directly translate to 1 hour, and 24 months does not directly equal 2 years. But this aside, there are many uses for the Period class.

For example, lets say I want to periodically archive old temp files from a system. I could set the period to minutes, hours, days, whatever, then based on an arbitrary start time, begin the archive sweep. In the same moment, I could compute the next archive sweep by adding my pre-defined period object to the current time.

A more elaborate implementation would be to self-adjust the scheduled periods based on some criteria. Sticking with the archive sweep, lets say I set the initial period to 2 hours, or better yet, 120 minutes to give a finer resolution. Then, at the end of the sweep, I tally archived files. The smaller the number, the longer the period, on a sliding scale, up to 240 minutes. A large number would decrease the period to zero minutes, or a continual sweep, so I want to make sure that this is the very worst case scenario.

The Period class makes this easy to implement and maintain. To keep it simple, lets just use a linear equation. The formula for calculating the number of minutes between sweeps is reduced to period in minutes = mx + b, where b = 240 minutes (our maximum amount), x = the number of files, and m = the slope. Lets say that the maximum number anticipated required archives were 2 per minute, so after 240 minutes we would have 480 files that needed to be archived. If that is our worst case then m = -0.5 (delta y divide delta x, or -240 / 480). So the curve looks like this:

Now each time I do an archive sweep, I tally the files and calculate the next sweep interval using:

maxMinutes = 240
slope = -0.5
periodInMinutes = slope * fileCount + maxMinutes

Or, as a single groovy closure:

def periodToNextSweep = { fileCount, slope = -0.5, maxMinutes = 240 ->
Period.minutes( slope * fileCount + maxMinutes )
}

You might think there is a danger in allowing the returned period to be a negative number of minutes. But in all practicality this is acceptable because the objective is to determine the next instant when a sweep should occur. If this time is in the past, simply do it now. Of coarse you would design the slope to target the worst case, so a negative time should seldom if ever occur. And the good part is that the parameters are easy to modify to fit changing environments.

Testing with a Fixed Time Source: My previous entries have demonstrated using TimeSource and Clock tied to the System clock. But, for these tests, I think a fixed time source would be more appropriate. The syntax is like this:

millis = 1234920035991L // 2009-02-17T17:20:35.991-08:00 Tuesday...
timeSource = TimeSource.fixed( Instant.millisInstant( millis ) )
clock = Clock.clockDefaultZone( timeSource )

So now when I create a date, time or date/time object from the clock, the time is always the same. Not very meaningful for real life, but great for testing.

If I create a class that uses clock, I can inject the TimeSource based on the system clock. And for testing, I can inject a fixed TimeSource, run tests, and not have to worry about the specific time, but simply base my tests on a static source. Here is the class:

class SweepController {
def clock = Clock.systemDefaultZone()
def slope = -0.5
def maxMinutes = 240

def periodToNextSweep = { fileCount ->
int x = (int)(slope * fileCount + maxMinutes)
x < 0 ? Period.ZERO : Period.minutes( x )
}

def nextSweepTime = { fileCount ->
def period = periodToNextSweep( fileCount )

clock.offsetDateTime() + period
}
}

And here is the test script:

millis = 1235721600000L // 2009-02-27T00:00-08:00
fixed = TimeSource.fixed( Instant.millisInstant( millis ) )
clock = Clock.clockDefaultZone( fixed )

sweep = new SweepController( clock:clock )
println "now -> ${clock.offsetDateTime()}"
source = [
[ 480, '2009-02-27T00:00-08:00' ],
[ 0, '2009-02-27T04:00-08:00' ],
[ 240, '2009-02-27T02:00-08:00' ],
[ 120, '2009-02-27T03:00-08:00' ],
]
source.each { count, value ->
println "count: ${count} -> ${sweep.periodToNextSweep( count )}, ${sweep.nextSweepTime( count )}"
assert value == sweep.nextSweepTime( count ).toString()
}

When I run the script, here is what I get:

now -> 2009-02-27T00:00-08:00
count: 480 -> PT0S, 2009-02-27T00:00-08:00
count: 0 -> PT240M, 2009-02-27T04:00-08:00
count: 240 -> PT120M, 2009-02-27T02:00-08:00
count: 120 -> PT180M, 2009-02-27T03:00-08:00

The main advantage is that I can run this independent of the current date, but still use the clock object without changing anything inside the class.

Conclusion: This quick look at Period and PeriodParser to see how it fits into the JSR-310 from the groovy coder's perspective. Next time well look closer at Date, Time and DateTime math capabilities and how they work with groovy plus/minus operator overloading.

Sunday, February 22, 2009

Adjusting Date and Time with javax.time

Java's JSR-310 date and time API, co-lead by Michael Nascimento Santos and Stephen Colebourne is a natural spinoff from the venerable joda time. The implementation has many advantages over java util's Date and Calendar. Compared to java.util.Date the API has many types of Date and Time including LocalDate, LocalTime, LocalDateTime, OffsetDate, OffsetTime, OffsetDateTime, and ZonedDateTime.

The new JSR-310 date, time, and date/time classes are immutable and thread safe, unlike java.util.Calendar, but at the same time they offer many basic math, adjusters, and matchers that enable date and time calculations missing in java.util.Date. They are created from a TimeSource that can be tied to the System clock, fixed, or offset in time. This makes the classes extremely test friendly.

This entry discusses how the basic date/time math work and how the date and time adjusters can be used to solve common problems. Lets look first at the basic math.

Look Ma, no Setters: The JSR-310 date/time classes are immutable and thread safe. To accomplish this, the API doesn't include any setXX methods. Days, Hours, Years, etc are manipulated through "plus", "minus", and "with" methods that return new objects of the same type. Here are a few examples, first with date then time:

// tomorrow and 5 years from now
clock = Clock.systemDefaultZone()
today = clock.today()
assert clock.tomorrow() == today.plusDays(1)
fiveYearsFromNow = today.plusYears( 5 )
assert today.plusMonths( 60 ).year == fiveYearsFromNow.year

// two hours ago
now = clock.timeToSecond()
twoHoursAgo = now.minusHours( 2 )
assert now.minusMinutes( 120 ) == twoHoursAgo

// use offset datetime to get today at noon
noon = clock.offsetDateTime().withTime( 12, 0, 0 )
assert noon.hourOfDay == 12
assert noon.minuteOfHour == 0
assert noon.secondOfMinute == 0

The Adjusters: Here is a tricky problem: how do you compute the specific date of a week numbered day of the month, for example the 3rd friday or 4th tuesday. Bay area residents know that not being able to calculate these simple problems can cost real money (street sweep adys). So here is how JSR-310 handles this:

dt = clock.offsetDate()

thirdFridayAdjuster = DateAdjusters.dayOfWeekInMonth( 3, DayOfWeek.FRIDAY )
fourthTuesdayAdjuster = DateAdjusters.dayOfWeekInMonth( 4, DayOfWeek.TUESDAY )

thirdFriday = dt.with( thirdFridayAdjuster )
println "third friday -> ${thirdFriday}, ${thirdFriday.toDayOfWeek()}, ${thirdFridayAdjuster}"
assert DayOfWeek.FRIDAY == thirdFriday.toDayOfWeek()

fourthTuesday = dt.with( fourthTuesdayAdjuster )
println "fourth tuesday -> ${fourthTuesday}, ${fourthTuesday.toDayOfWeek()}, ${fourthTuesdayAdjuster}"
assert DayOfWeek.TUESDAY == fourthTuesday.toDayOfWeek()

These examples just scratch the surface of the many date/time manipluation methods for JSR-310's javax.time package. The next entry will discuss how JSR-310 matchers help determine if a date lands on a leap year, leap day, last day of the month, etc. and how to use this in groovy.

Saturday, February 21, 2009

JSR-310 javax.time and Groovy Tests

The development and implementation of JSR-310 is continuing to move ahead at a good pace. Once delivered, (hopefully by JDK 7) the java community will have a solid replacement for the outdated and comparatively buggy Date and Calendar classes. At the same time, I have created a series of groovy tests that exercise the JSR-310 capabilities.

Formal unit tests for JSR-310 are written in testng and cover all the low level details including TimeZone resolving, serialization, etc. My tests simply verify a subset of the current API and demonstrate use with groovy. The tests cover the basic API, adjusters, matchers, peroids, formatters and parsers. This post demonstrates the basic tests.

Basic API Tests: These tests take advantage of groovy's invokeMethod() to test methods that take zero or more parameters. They demonstrate typical use of TimeSource, Clock, LocalDate, and LocalTime classes. With these basics it's easy to extend to LocalDateTime, OffsetDate, OffsetTime, or ZonedDateTime. Here is the output trace generated from running BasicTests.groovy:

TimeSource to Date/Calendar conversion tests ---------------------------------------
Timesource.system().instant() -> {ts.instant()}
Clock Method Tests ----------------------------------------------------------------
clock = Clock.systemDefaultZone() -> TimeSourceClock[SystemTimeSource, America/Los_Angeles]
Clock methods...
method: clock.getSource() -> SystemTimeSource
method: clock.getZone() -> America/Los_Angeles
method: clock.today() -> 2009-02-21
method: clock.tomorrow() -> 2009-02-22
method: clock.yesterday() -> 2009-02-20
method: clock.dateTime() -> 2009-02-21T08:56:35.512
method: clock.dateTimeToMinute() -> 2009-02-21T08:56
method: clock.dateTimeToSecond() -> 2009-02-21T08:56:35
method: clock.time() -> 08:56:35.515
method: clock.timeToMinute() -> 08:56
method: clock.timeToSecond() -> 08:56:35
method: clock.offsetDate() -> 2009-02-21-08:00
method: clock.offsetTime() -> 08:56:35.517-08:00
method: clock.offsetTimeToMinute() -> 08:56-08:00
method: clock.offsetTimeToSecond() -> 08:56:35-08:00
method: clock.offsetDateTime() -> 2009-02-21T08:56:35.518-08:00
method: clock.zonedDateTime() -> 2009-02-21T08:56:35.521-08:00 America/Los_Angeles
method: clock.zonedDateTimeToMinute() -> 2009-02-21T08:56-08:00 America/Los_Angeles
method: clock.zonedDateTimeToSecond() -> 2009-02-21T08:56:35-08:00 America/Los_Angeles
method: clock.year() -> Year=2009
method: clock.yearMonth() -> 2009-02

OffsetDate methods from offsetDate = clock.offsetDate() -> 2009-02-21-08:00
method: offsetDate.atMidnight() -> 2009-02-21T00:00-08:00
method: offsetDate.getDayOfMonth() -> 21
method: offsetDate.getDayOfWeek() -> DayOfWeek=SATURDAY
method: offsetDate.getDayOfYear() -> 52
method: offsetDate.getMonthOfYear() -> MonthOfYear=FEBRUARY
method: offsetDate.getYear() -> 2009
method: offsetDate.toDayOfMonth() -> DayOfMonth=21
method: offsetDate.toDayOfWeek() -> DayOfWeek=SATURDAY
method: offsetDate.toDayOfYear() -> DayOfYear=52
method: offsetDate.toLocalDate() -> 2009-02-21
method: offsetDate.toMonthOfYear() -> MonthOfYear=FEBRUARY
method: offsetDate.toYear() -> Year=2009
method: offsetDate.getDate() -> 2009-02-21

LocalDate methods from localDate = LocalDate.date(2019, 03, 01) -> 2019-03-01
method: localDate.atMidnight() -> 2019-03-01T00:00
method: localDate.getDayOfMonth() -> 1
method: localDate.getDayOfWeek() -> DayOfWeek=FRIDAY
method: localDate.getDayOfYear() -> 60
method: localDate.getMonthOfYear() -> MonthOfYear=MARCH
method: localDate.getYear() -> 2019
method: localDate.toDayOfMonth() -> DayOfMonth=1
method: localDate.toDayOfWeek() -> DayOfWeek=FRIDAY
method: localDate.toDayOfYear() -> DayOfYear=60
method: localDate.toLocalDate() -> 2019-03-01
method: localDate.toMonthOfYear() -> MonthOfYear=MARCH
method: localDate.toYear() -> Year=2019
method: localDate.getMonthDay() -> --03-01
method: localDate.getYearMonth() -> 2019-03
method: localDate.isLeapYear() -> false
method: localDate.toEpochDays() -> 17956
method: localDate.toModifiedJulianDays() -> 58543
method: localDate.toDateTimeFields() -> {ISO.Year=2019, ISO.MonthOfYear=3, ISO.DayOfMonth=1}

LocalTime methods, from localTime = clock.time() -> 08:56:35.549
method: localTime.getHourOfDay() -> 8
method: localTime.getMinuteOfHour() -> 56
method: localTime.getSecondOfMinute() -> 35
method: localTime.toDateTimeFields() -> {ISO.HourOfDay=8, ISO.MinuteOfHour=56, ISO.SecondOfMinute=35, ISO.NanoOfSecond=549000000}
method: localTime.toHourOfDay() -> HourOfDay=8
method: localTime.toLocalTime() -> 08:56:35.549
method: localTime.toMinuteOfHour() -> MinuteOfHour=56
method: localTime.toSecondOfMinute() -> SecondOfMinute=35
method: localTime.getNanoOfSecond() -> 549000000
method: localTime.toNanoOfSecond() -> NanoOfSecond=549000000
method: localTime.toNanoOfDay() -> 32195549000000
End basic tests...
Close inspection reveals that some methods in LocalDate are missing in OffsetDate. This isn't a huge problem, because OffsetDate can easily create a LocalDate object. But, it would be nice to have isLeapYear(), toEpochDays(), toModifiedJulianDays() and other methods in both classes. (This may already be in the works)...

Here is a simple way to access some of the missing methods:

offsetDate = clock.offsetDate()
offsetDate.toLocalDate().toEpochDays()
offsetDate.toLocalDate().toModifiedJulianDays()
offsetDate.toLocalDate().isLeapYear()
// or, even groovyer
offsetDate.toLocalDate().leapYear
Conclusion: What these tests really do is to demonstrate the basic functionality of the new javax.time classes. It's obvious that JSR-310 offers the horsepower missing from the current java implementations. And, it will be a very useful addition to the groovy toolkit as well.

Future posts will demonstrate the Adjusters, Matchers, Periods, Formatters and Parsers. Once the tests are cleaned up a bit, I'll post them on line.

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.