Handling time using the standard Java libraries can be a real pain. I am writing a webapp that uses pretty complicated date arithmetic, and while Java's Calendar object works, clients of the library are forced to work with a mutable object, thus being rather verbose to accomplish a simple task.
Enter ScalaCalendar to save the day! I began writing a Scala wrapper for the Java Calendar object, and I'm mostly happy how it turned out. Here's an example of how it works:
// Importing the base ScalaCalendar object like so // allows you to do some pretty powerful things. import com.philipcali.calendar.ScalaCalendar._ // Let's work with the simple things val rightNow = java.util.Date() // or createTime() or "now" val tomorrow = rightNow + 1 day val nextMonth = rightNow + 1 month val yesterday = rightNow - 1 day // Generate a duration is rather simple too val span = rightNow to (rightNow + 1 week) rightNow isIn span // returns true rightNow isWeekend rightNow isWeekday span.delta.days // returns 7 span.delta.months // returns 0 // Calendar fields // Your standard field have a name method // which tries to define the field in English rightNow.time == rightNow.millisecond rightNow.second.value rightNow.minute.value rightNow.hour.value rightNow.day.value rightNow.day.inWeek rightNow.day.inYear rightNow.week.value rightNow.week.InYear rightNow.month.value rightNow.year.value // Work with strings val allFeb = "2/1/2011" to ("3/1/2011" - 1 day) // This is where it gets good: // A duration is traversable by any other duration span.by(1 day) foreach { dayDuration println(dayDuration.day.name) } // Only work with MWF days val mwf = span.by(1 day) filter(_.day.name match { case "Monday" | "Wednesday" | "Friday" => true case _ => false }) // Only weekends span.by(1 day) filter(_.isWeekend) foreach { time => println("Partying it up on %s!" format(time.day.name)) } // Using this logic, building a calendar table is painless val calHtml = <table> { "2/1/2011".calendarMonth.traverse(1 week) { weekD => <tr> { weekD.traverse(1 day) { dayD => <td>{ dayD.day.value }</td> } }</tr> } }</table> // There's convenience methods for generating a Calendar month // week, and day "2/1/2011".calendarMonth // contains 1/30/2011 - 3/5/2011 "2/1/2011".calendarWeek // contains 1/30/2011 - 2/5/2011 "2/1/2011".calendarDay // contains 00:00:00 - 23:59:59 // You can even traverse a timespan in reverse val countdown = ("2/28/2011" to "2/1/2011").by(1 day) map(_.day.value) countdown.take(5).mkString(",") // "28,27,26,25,24" // Traverse a duration at your own space val february = "2/1/2011" to "2/1/2011" + (1 month) while(february.hasNext) { february.next(1 day) // do something } // Construct something wacky // This gets the weekdays every other week in the month for(days <- february.by(1 week); if(days.week.value % 2 == 0) day <- weeks.by(1 day); if day.isWeekDay) { println(day.day) }
The by method splits the durations into a lists of durations, while the traverse method splits and applies a function you give to transform that duration into something meaningful. The ScalaCalendar integrates with java.util.Date and java.util.Calendar seamlessly.
Most people will probably look at this and face palm, but it satisfied my needs for this particular project quite nicely.
I'm going to release the calendar api on my github account very soon.
-- Philip Cali
Wow, very nice Phil! Impressive. You came up with an absolutely great API!
ReplyDeleterightNow + 1 day // fantastic stuff here!!
Two quick questions:
1) Why does February 1st's calendarMonth contain January 30th and March 5th?
2) Have you considered calling it Scalendar? :-)
Ha, thanks Brad!
ReplyDelete1) It was important for my Implementation that the "Calendar month" was a month representation like you would see on Google calendar, yet I suppose I could pass some argument in there to make it more flexible. (Whether or not to contain days from other months in there.)
2) No I haven't, but that sounds like a winner to me!
sweet!
ReplyDelete