vendredi, novembre 02, 2012

Managing dates with a DSL implemented in Scala

Following Paulo "JCranky" Siqueira's post who himself built it based on Sergio Lopes's post, I also enhanced this class by adding the ability to set dates and to compare them.

Another good link: Baysick: A Scala DSL Implementing BASIC



package kebra

import java.util.Calendar
import java.text.SimpleDateFormat
import java.text.ParsePosition

class DateDSL(val cal: Calendar) {
 cal.clear(Calendar.HOUR)
 import DateDSL.Conjunction
 
 def this(d: DateDSL) = this(d.cal)

 private var last = 0;

 def plus(num: Int) = { last = num; this }
 def minus(num: Int) = { last = -num; this }

 def +(num: Int) = plus(num)
 def -(num: Int) = minus(num)

 def months = { cal.add(Calendar.MONTH, last); this }
 def months(and: Conjunction): DateDSL = months
 def month = months
 def month(and: Conjunction): DateDSL = months

 def years = { cal.add(Calendar.YEAR, last); this }
 def years(and: Conjunction): DateDSL = years
 def year = years
 def year(and: Conjunction): DateDSL = years

 def days = { cal.add(Calendar.DAY_OF_MONTH, last); this }
 def days(and: Conjunction): DateDSL = days
 def day = days
 def day(and: Conjunction): DateDSL = days

 def is(then: Calendar) = {
  cal.setTimeInMillis(then.getTimeInMillis)
  cal.clear(Calendar.HOUR)
  this
 }

 def is(then: String) = {
  val cthen = ParseDate(then,"MM/dd/yyyy")
  cal.setTimeInMillis(cthen.getTimeInMillis)
  cal.clear(Calendar.HOUR)
  this
 }

 def ParseDate(s_date: String, s_format: String): Calendar = {
  var cal = Calendar.getInstance()
  cal.setTime(new SimpleDateFormat(s_format).parse(s_date, new ParsePosition(0)))
  cal
 }

 def before(d: DateDSL): Boolean = cal.before(d.cal)
 def after(d: DateDSL): Boolean = cal.after(d.cal)

 override def toString = new String(new SimpleDateFormat("ddMMMyy").format(cal.getTime()))
}

object DateDSL {
 class Conjunction
 val and = new Conjunction

 def Today = new DateDSL(Calendar.getInstance)
 def Tomorrow = Today + 1 day
 def Yesterday = Today - 1 day

 def today = Today
 def tomorrow = Tomorrow
 def yesterday = Yesterday

 def Now = Today
 def now = Today
}
I also added a testcase below:
package kebra

import java.util.Calendar
import java.text.SimpleDateFormat
import kebra.DateDSL._


object TestKebra extends App {

 println("Hello World!")
 println(Tomorrow minus 1 month and plus 10 years and plus 1 day)
 println(Today + 2 months and plus 9 years and minus 1 day)
 println(Today - 9 years and minus 1 day)
 println(((Today + 2 months and) + 9 years and) - 1 day)
 println(now is "10/1/2011" plus 3 days)
 println("\n"+printZisday((now is "10/1/2011" plus 3 days).cal,"ddMMMyy"))
 assert(printZisday((now is "10/1/2011" plus 3 days).cal,"MM/dd/yyyy").indexOf("10/04/2011")==0)
 assert((now is "10/1/2011" plus 3 days).before(now is "10/1/2011" plus 4 days))
 assert((now is "10/1/2011" plus 4 days).after(now is "10/2/2011" plus 2 days))
 
 val zendDate = new DateDSL(now is "2/2/2011")
 var ago = new DateDSL(now is "2/2/2011" minus (9*7) days)
 println("***** ago: "+ago+", zendDate: "+zendDate)
 while(ago.before(zendDate)) {
  println("      ago: "+ago)
  ago plus 7 days
 }
 println("***** ago: "+ago+", zendDate: "+zendDate)
 assert((ago plus 1 day).after(zendDate))
 println("***** ago: "+ago+", zendDate: "+zendDate)
 assert((ago minus 2 days).before(zendDate))
 println("***** ago: "+ago+", zendDate: "+zendDate)
 
 def printZisday(zisday:  Calendar, fmt: String): String = new String(new SimpleDateFormat(fmt).format(zisday.getTime()))
}