5
\$\begingroup\$

I'm looking to define the Rebol date format in BNF notation (as adapted for Rebol Parse). I'd like as best as possible to only define valid dates—at least those that are valid in Rebol at the moment:

Rebol [
    Title: "A Formal Description of Rebol Dates"
    Date: 23-Apr-2015
    File: %date-grammar.reb
    Author: "Christopher Ross-Gill"
]

Date: [DateDate opt ["/" Time opt DateZone] end]

DateDate: [
      DateDay31 ["-" DateMonth31 "-" | "/" DateMonth31 "/"] DateYear
    | DateDay30 ["-" DateMonth30 "-" | "/" DateMonth30 "/"] DateYear
    | DateDay28 ["-" DateMonthFebruary "-" | "/" DateMonthFebruary "/"] DateYear
    | "29" ["-" DateMonthFebruary "-" | "/" DateMonthFebruary "/"] DateYearLeap
    | DateYear ["-" DateMonth31 "-" | "/" DateMonth31 "/"] DateDay31
    | DateYear ["-" DateMonth30 "-" | "/" DateMonth30 "/"] DateDay30
    | DateYearLeap ["-" DateMonthFebruary "-" | "/" DateMonthFebruary "/"] "29"
    | DateYear ["-" DateMonthFebruary "-" | "/" DateMonthFebruary "/"] DateDay28
]

; Currently years cannot be negative and have a maximum value of 16383
; So the following two values are shortcuts.
DateYear: [Digit opt [Digit opt [Digit opt [Digit opt Digit]]]]

DateYearLeap: [
      "1" Digit Digit DateYearLeapEnd
    | Digit Digit DateYearLeapEnd
    | Digit DateYearLeapEnd
    | DateYearLeapEnd
    | Digit048
]

DateYearLeapEnd: [Digit02468 Digit048 | Digit13579 Digit26]

DateDay31: ["30" | "31" | "29" | DateDay28]

DateDay30: ["30" | "29" | DateDay28]

DateDay28: ["2" Digit08 | "1" Digit | opt "0" Digit19]

DateMonth: [DateMonth31 | DateMonth30 | DateMonthFebruary]

DateMonth31: [
    DateMonthJanuary |
    DateMonthMarch |
    DateMonthMay |
    DateMonthJuly |
    DateMonthAugust |
    DateMonthOctober |
    DateMonthDecember
]

DateMonth30: [
    DateMonthApril |
    DateMonthJune |
    DateMonthSeptember |
    DateMonthNovember
]

; Currently only English month names are valid
DateMonthJanuary: ["Jan" opt ["u" opt ["a" opt ["r" opt "y"]]] | opt "0" "1"]

DateMonthFebruary: ["Feb" opt ["r" opt ["u" opt ["a" opt ["r" opt "y"]]]] | opt "0" "2"]

DateMonthMarch: ["Mar" opt ["c" opt "h"] | opt "0" "3"]

DateMonthApril: ["Apr" opt ["i" opt "l"] | opt "0" "4"]

DateMonthMay: ["May" | opt "0" "5"]

DateMonthJune: ["Jun" opt "e" | opt "0" "6"]

DateMonthJuly: ["Jul" opt "y" | opt "0" "7"]

DateMonthAugust: ["Aug" opt ["u" opt ["s" opt "t"]] | opt "0" "8"]

DateMonthSeptember: ["Sep" opt ["t" opt ["e" opt ["m" opt ["b" opt ["e" opt "r"]]]]] | opt "0" "9"]

DateMonthOctober: ["Oct" opt ["o" opt ["b" opt ["e" opt "r"]]] | "10"]

DateMonthNovember: ["Nov" opt ["e" opt ["m" opt ["b" opt ["e" opt "r"]]]] | "11"]

DateMonthDecember: ["Dec" opt ["e" opt ["m" opt ["b" opt ["e" opt "r"]]]] | "12"]

; Zone Hours are currently -15 - 15, the following is a shortcut:
DateZone: [Sign Digit opt Digit ":" [["0" | "3"] "0" | ["1" | "4"] "5"]]

Time: [TimeHour ":" TimeMinute opt [":" TimeSecond]]

; Need to constrain to valid hours
TimeHour: [Sign any Digit | opt Sign some Digit]

; Need to constrain to 0-59
TimeMinute: [opt Sign Digit opt Digit]

; Need to constrain to 0-59.999999
TimeSecond: [opt Sign [opt [Digit opt Digit] "." 1 6 Digit | Digit opt Digit opt "."]]

Digit: charset "0123456789"

Digit02468: charset "02468"

Digit048: charset "048"

Digit08: charset "012345678"

Digit13579: charset "13579"

Digit19: charset "123456789"

Digit26: charset "26"

Sign: charset "+-"

Notes:

  • Partial matches are bad: this rule should match a whole string—a partial match is failure, e.g. the following would only partially match 22 and would thus fail. ["2" | "22"]

  • I've taken shortcuts on some values (see source comments), I would expect to flesh these out.

  • Although Rebol will interpret 12-010-2014 as a date (12-Oct-2014), I don't see any reason to support this.

To test this code, save as a file and load as a parse rule to use against samples:

date-grammar: get in context load %date-grammar.reb 'date

foreach test [
    "28-Feb-2016"
    "29-Feb-2016"
    "1-April-2015/12:00"
    "1-4-2015/12:00+5:00"
][
    print [either parse test date-grammar ["*"][" "] mold test]
]
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.