---- -*- Mode: rpl; -*-                                                                             
---- vim:syn=rosie
----
---- time.rpl    Common time patterns in Rosie Pattern Language
----
---- © Copyright IBM Corporation 2016, 2017.
---- LICENSE: MIT License (https://opensource.org/licenses/mit-license.html)
---- AUTHORS: Jamie A. Jennings, Kevin Zander

rpl 1.1

package time

local alias d = [0-9]

-- RFC3339
--
--   time-hour       = 2DIGIT  ; 00-23
--   time-minute     = 2DIGIT  ; 00-59
--   time-second     = 2DIGIT  ; 00-58, 00-59, 00-60 based on leap second
--                             ; rules
--   time-secfrac    = "." 1*DIGIT
--   time-numoffset  = ("+" / "-") time-hour ":" time-minute
--   time-offset     = "Z" / time-numoffset
--
--   partial-time    = time-hour ":" time-minute ":" time-second
--                     [time-secfrac]
--   full-time       = partial-time time-offset
--
-- Note: The "Z" separator may be lowercase.

hour =  { [01] [0-9] } /
        { "2"  [0-3] } 
-- test hour accepts "00", "15", "20", "23"
-- test hour rejects "", "0", "5", "24", "99"

minute = { [0-5] [0-9] }
-- test minute accepts "00", "25", "39", "59"
-- test minute rejects "", "0", "60", "99"

second =  { [0-5] [0-9] } / "60"          -- time_second must match 60 for leap second
-- test second accepts "00", "24", "47", "59", "60"
-- test second rejects "", "0", "9", "61", "99"

secfrac = { [:,.] [0-9]+ }
-- test secfrac accepts ".00000", ".12435", ".99999999", ".0"
-- test secfrac accepts ",0", ",12435"
-- test secfrac rejects "", "0", ".", ".now", ".0r", ".never"

numoffset = { [+\-] hour ":" minute }
-- test numoffset accepts "+00:10", "-23:59"
-- test numoffset rejects "@12:34", "-55:00", "+2:30"

offset = [Zz] / numoffset
-- test offset accepts "Z", "z", "+00:10", "-23:59"
-- test offset rejects "", "2", "*00:00", "+00:60"

rfc3339_time = { hour ":" minute ":" second secfrac? }
-- test rfc3339_time accepts "00:00:00", "00:00:00.00000", "10:30:02.125", "23:59:60"
-- test rfc3339_time rejects "", "24:10:10", "17:28:33.0r"

rfc3339_strict = { rfc3339_time offset }


-- RFC2822
--     time            =       time-of-day FWS zone
--     time-of-day     =       hour ":" minute [ ":" second ]
--     hour            =       2DIGIT
--     minute          =       2DIGIT
--     second          =       2DIGIT
--     zone            =       (( "+" / "-" ) 4DIGIT) / obs-zone
--     obs-zone        =       "UT" / "GMT" /                      ; Universal Time
--                             "EST" / "EDT" / "CST" / "CDT" /     ; North American
--                             "MST" / "MDT" / "PST" / "PDT" 
-- Notes:
--   No support for military time zones (which are marked obsolete)
--   Not restricting 'day' value based on the values of month and year.  0-31 is always valid.

alias rfc2822_numeric_zone = { [+\-] d{4} }
-- test rfc2822_numeric_zone accepts "+0000", "-4321", "+9999", "-9870"
-- test rfc2822_numeric_zone rejects "", "@0100", "-1", "+50", "1000"

alias rfc2822_obsolete_zone = "UTC" / "UT" / "GMT" / "CET" / "EST" / "EDT" / 
                              "CST" / "CDT" / "MST" / "MDT" / "PST" / "PDT" 
-- test rfc2822_obsolete_zone accepts "UT", "UTC", "PDT"
-- test rfc2822_obsolete_zone rejects "-0500"

rfc2822_zone = rfc2822_numeric_zone / rfc2822_obsolete_zone

rfc2822_time = { hour ":" minute {":" second}? }
-- test rfc2822_time accepts "00:00:00", "12:00", "23:59:60", "16:08:33", "01:55", 
-- test rfc2822_time rejects "", "24:10:10", "33:40", "17:28:33.0", "00:00:00.00000", "10:30:02.125"

rfc2822_strict = { rfc2822_time rfc2822_zone }

rfc2822_frac = { hour ":" minute {":" second secfrac?}? }   -- Extension to the standard

-- Misc formats

ampm = "AM" / "am" / "PM" / "pm"
sequence = d+
short_hour = {[0]? [0-9]} / { [1] [0-9] } / { [2] [0-3] }

simple = { short_hour ":" minute {":" second secfrac?}? }  ampm? 
websphere = { rfc2822_time ":" sequence } rfc2822_zone
db2 = { hour "." minute "." second "." sequence }


-- Useful top-level patterns

rfc2822 = rfc2822_time rfc2822_zone?
rfc3339 = { rfc3339_time [:space:]* offset? }
any = rfc3339 / rfc2822_frac / websphere / simple / db2

