/**
 * This module offers strategies to format a ComponentTime to a String.
 *
 * You have to specify the output with a list of patterns. The
 * patterns are the ones used in the SimpleDateFormat class of Java.
 * Not all of the patterns are however implemented in this module.
 * 
 * Supported patterns: yyyy, yy, M, MM, MMM, MMMM, d, dd, D, DD, EEE, EEEE,
 * a, HH, kk, hh, KK, mm, ss. For documentation of the meaning of the
 * patterns please visit:
 * http://java.sun.com/j2se/1.4.1/docs/api/java/text/SimpleDateFormat.html
 *
 * Limitations (and future work):
 *   - No way of using i18n and locales. Introduction of locales might
 *     change the type of pattern-to rules. It's wise to use the 
 *     date-format strategy as long as possible: this strategy is stable
 *     and will use the default locale in the future.
 *   - This modules doesn't parse a String containing patterns like 
 *     Java's SimpleDateFormat. This simplifies the implementation
 *     and is not a big deal because of the list syntax in Stratego.
 *
 * Examples:
 *   - ["EEEE", " ", "dd", " ", "MMMM", " ", "yyyy"]
 *     "Thursday 02 January 2003"
 *   - ["EEE", " ", "HH", ":", "mm", ":", "ss"]
 *     "Thu 21:35:30"
 *   - ["EEE", " ", "hh", ":", "mm", ":", "ss", " ", "a"]
 *     "Thu 09:38:46 p.m."
 *
 * @author Martin Bravenboer <martin@mbravenboer.org>
 */
module util/date-format
imports 
  util/time 
  term/string 
  term/integer
  collection/list/common

strategies

  /**
   * @type List(String) * ComponentTime -> String
   */
  date-format:
    (ps, t) -> <map(<date-pattern-to-string <+ Fst> (<id>, t)); concat-strings> ps

strategies

  /**
   * @type String * ComponentTime -> String
   */
  date-pattern-to-string =
      ?(p, _)
    ; date-pattern-to-int
    ; <align-right> ('0', <int-to-string>, <string-length> p)

rules

  /**
   * @type String * ComponentTime -> Int
   */

  /* year */
  date-pattern-to-int : ("yyyy", ComponentTime(Date(y, _, _), _, _)) -> y
  date-pattern-to-int : ("yy",   ComponentTime(Date(y, _, _), _, _)) -> <<mod> (<id>, 100)> y

  /* month */
  date-pattern-to-int    : ("M",    ComponentTime(Date(_, m, _), _, _)) -> <month2index; inc> m
  date-pattern-to-int    : ("MM",   ComponentTime(Date(_, m, _), _, _)) -> <month2index; inc> m

  /**
   * @type String * ComponentTime -> String
   */

  /* month */
  date-pattern-to-string : ("MMM",  ComponentTime(Date(_, m, _), _, _)) -> <month2abbr> m
  date-pattern-to-string : ("MMMM", ComponentTime(Date(_, m, _), _, _)) -> <month2text> m

  /**
   * @type String * ComponentTime -> Int
   */
  /* day of */
  date-pattern-to-int    : ("d",    ComponentTime(Date(_, _, d), _, _)) -> d
  date-pattern-to-int    : ("dd",   ComponentTime(Date(_, _, d), _, _)) -> d
  date-pattern-to-int    : ("D",    ComponentTime(_, _, Dupl(_, dy)))   -> dy
  date-pattern-to-int    : ("DD",   ComponentTime(_, _, Dupl(_, dy)))   -> dy

  /**
   * @type String * ComponentTime -> String
   */
  /* day of */
  date-pattern-to-string : ("EEE",  ComponentTime(_, _, Dupl(dw, _)))   -> <day-of-week2abbr> dw
  date-pattern-to-string : ("EEEE", ComponentTime(_, _, Dupl(dw, _)))   -> <day-of-week2text> dw

  /* am/pm marker */
  date-pattern-to-string : ("a", ComponentTime(_, DayTime(h, _, _), _)) -> "a.m."
      where <leq-lt> (0, h, 12)

  date-pattern-to-string : ("a", ComponentTime(_, DayTime(h, _, _), _)) -> "p.m."
      where <leq-leq> (12, h, 23)

  /**
   * @type String * ComponentTime -> Int
   */
  /* hour */
  date-pattern-to-int : ("HH", ComponentTime(_, DayTime(h, _, _), _)) -> h
  date-pattern-to-int : ("kk", ComponentTime(_, DayTime(h, _, _), _)) -> <inc> h
  date-pattern-to-int : ("hh", ComponentTime(_, DayTime(0, _, _), _)) -> 12
  date-pattern-to-int : ("hh", ComponentTime(_, DayTime(h, _, _), _)) -> h
      where <leq-leq> (1, h, 12)

  date-pattern-to-int : ("hh", ComponentTime(_, DayTime(h, _, _), _)) -> <subt> (h, 12)
      where <leq-leq> (12, h, 23)

  date-pattern-to-int : ("KK", ComponentTime(_, DayTime(h, _, _), _)) -> h
      where <leq-lt> (0, h, 12)

  date-pattern-to-int : ("KK", ComponentTime(_, DayTime(h, _, _), _)) -> <subt> (h, 12)
      where <leq-leq> (12, h, 23)

  /* minutes */
  date-pattern-to-int : ("mm", ComponentTime(_, DayTime(_, m, _), _)) -> m

  /* seconds */
  date-pattern-to-int : ("ss", ComponentTime(_, DayTime(_, _, s), _)) -> s

/**
 * Translate a day constructor to corresponding abbreviation.
 *
 * E.g. <day-of-week2abbr>Monday() => "Mon"      
 *
 * @type DayOfWeek -> String
 */
rules
  day-of-week2abbr : Sunday()    -> "Sun"
  day-of-week2abbr : Monday()    -> "Mon"
  day-of-week2abbr : Tuesday()   -> "Tue"
  day-of-week2abbr : Wednesday() -> "Wed"
  day-of-week2abbr : Thursday()  -> "Thu"
  day-of-week2abbr : Friday()    -> "Fri"
  day-of-week2abbr : Saturday()  -> "Sat"

/**
 * Translate a day constructor to a full text representation.
 *
 * E.g. <day-of-week2text>Tuesday() => "Tuesday"
 *
 * @type DayOfWeek -> String
 */
rules
  day-of-week2text : Sunday()    -> "Sunday"
  day-of-week2text : Monday()    -> "Monday"
  day-of-week2text : Tuesday()   -> "Tuesday"
  day-of-week2text : Wednesday() -> "Wednesday"
  day-of-week2text : Thursday()  -> "Thursday"
  day-of-week2text : Friday()    -> "Friday"
  day-of-week2text : Saturday()  -> "Saturday"


/**
 * Translate a month constructor to corresponding abbreviation.
 *
 * E.g. <month2abbr>September() => "Sep"
 *
 * @type Month -> String
 */
rules
  month2abbr : January()     -> "Jan"
  month2abbr : February()    -> "Feb"
  month2abbr : March()       -> "Mar"
  month2abbr : April()       -> "Apr"
  month2abbr : May()         -> "May"
  month2abbr : June()        -> "Jun"
  month2abbr : July()        -> "Jul"
  month2abbr : August()      -> "Aug"
  month2abbr : September()   -> "Sep"
  month2abbr : October()     -> "Oct"
  month2abbr : November()    -> "Nov"
  month2abbr : December()    -> "Dec"


/**
 * Translate a month constructor to a full text representation.
 *
 * E.g. <month2text>September() => "September" 
 *
 * @type Month -> String
 */
rules
  month2text : January()     -> "January"
  month2text : February()    -> "February"
  month2text : March()       -> "March"
  month2text : April()       -> "April"
  month2text : May()         -> "May"
  month2text : June()        -> "June"
  month2text : July()        -> "July"
  month2text : August()      -> "August"
  month2text : September()   -> "September"
  month2text : October()     -> "October"
  month2text : November()    -> "November"
  month2text : December()    -> "December"