Friday, September 23, 2011

Calculating Dates with Java Calendar

Found a tricky problem with Java Calendars today. Essentially, I discovered that you can't reuse Calendar objects in a calling function, because they are singletons. Take this code, for example:

How NOT to do it

// give me the date X days from the day represented by cal
public static String getOffsetDate(Calendar cal, int daysOffset){
    cal.add(Calendar.DATE, daysOffset);
    return dateFormat.format(cal.getTime());
}

This function appears to work until you call it a second and third time, and find out (like I did) that the new calendar value is being retained each time, and so increasing the daysOffset incrementally results in dates that are additively further out:

Today :2011-01-15
1 days ago     : 2011-01-14
2 days ago     : 2011-01-12 
3 days ago     : 2011-01-09
4 days ago     : 2011-01-05
5 days ago     : 2010-12-31 // wait. what?!

Apparently the Calendar.getInstance() gives you a singleton. Try passing it around to other methods, expecting it to make a new instance of itself and you'll run into problems. I found this to be extremely annoying and non-intuitive, and the Java Doc does not warn you about this, either.

Solution

So what's the solution? You have to create a new instance, or clone() the instance of the calendar before making the calculation (see sample code below).

package com.test.calendar;

import java.text.SimpleDateFormat;
import java.util.Calendar;

public class CalendarTestDaysAgo {

    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
 
    /**
     * @param args
     */
    public static void main(String[] args) {
        Calendar cal = Calendar.getInstance(); // today
        
        // set to January 15
        cal.set(Calendar.MONTH, Calendar.JANUARY);
        cal.set(Calendar.DAY_OF_MONTH, 15);
        
        System.out.print("Today :"+getOffsetDate(cal,0) + "\n");
        for (int d = 1; d < 32; d++){
            System.out.print(d + " days ago: " + getOffsetDate(cal, d * -1) + "\n");
        }
    }

    // give me the date X days hence from the day represented by cal
    public static String getOffsetDate(Calendar cal, int daysOffset){
                                          // this clone() call is required
                                          // otherwise subsequent changes are additive 
        Calendar newCal = (Calendar) cal.clone();
  
        newCal.add(Calendar.DATE, daysOffset);
        return dateFormat.format(newCal.getTime());
    }

}
Results in:
Today :2011-01-15
1 days ago     : 2011-01-14
2 days ago     : 2011-01-13
3 days ago     : 2011-01-12
4 days ago     : 2011-01-11
5 days ago     : 2011-01-10 // that's better.
...

Tuesday, September 20, 2011

Arduino Remote Control Irrigation Rig - Prototype 2

Last night I finished Prototype 2 of my Arduino irrigation remote control rig. Added the screw terminal headers so I can connect the irrigation control wires. The very last screw terminal on the right with the white wire is my power bus. It's wired into the COM terminals of all the relay boards.



For those of you who may be wondering, this iteration is mounted on an old IKEA cutting board. I needed the extra physical strength, because the 18 gauge wire used for the irrigation wires is stiff and heavy.

I drilled holes in the cutting board that are just big enough for 4-40 machine screws to poke through (7/32 drill bit, I think). The relay boards have long pins on the bottom, so they need be elevated off the board. I found some 1-inch spacer nuts at Fry's, and that's what the relay boards are mounted on top of. It worked so well, I will be doing the same for the Arduino ADK and Ethernet Shield.

Next step: Wire it up to my irrigation control box and try real hard not the burn the house down. The real trick will be to tap into my irrigation controller's 24-volt power supply without disabling the controller. Fun times!