Search code examples
context-free-grammarebnfcontext-free-language

Write EBNF form for a year/month/day


I want to find solution to the next problem, but I need to write EBNF of time in two formats, year-month-day and month-day-year to see the differences:

Identify one advantage of writing dates as a structured-integer in the form: year, month, day (1954-02-10) instead of in the normal order (02-10-1954).

Format: year-month-day. Here is what I came up with:

<NonZeroDigit> ::= ("1" | "2" | ... | "9")
<Month> ::= ( "0" <NonZeroDigit> ) | ( "1" ( "0" | "1" | "2" ) ) 
<Day> ::= ( "0" <NonZeroDigit> ) | ( ("1" | "2") <NonZeroDigit> ) | ("3" ( "0" | "1" ) )
<Year> ::= ( "000" <NonZeroDigit> ) | 
            ( "00" <NonZeroDigit> <NonZeroDigit> ) | 
            ( "0" <NonZeroDigit> <NonZeroDigit> <NonZeroDigit> ) | 
            ( "1"  <NonZeroDigit> <NonZeroDigit> <NonZeroDigit>) |
            ( "20" <NonZeroDigit> <NonZeroDigit> ) )

The year goes to 2099 which I guess its ok and these rules work but is there a better way to write EBNF of the time? Do I miss something?


Solution

  • So the solution I came up with is this:

    <FebruaryNum> ::= "02"
    <Dash> ::= "-"
    <NonLeapDigit> ::= "1" | "2" | "3" | "5" | "6" | "7"
    <LeapDigit> ::= "4" | "8"
    <NonZeroOrNineDigit> ::= <NonLeapDigit> | <LeapDigit>
    <Digit> ::= "0" | <NonZeroOrNineDigit> | "9"
    <ThreeDigits> ::= <Digit> <Digit> <Digit>
    <DaysFebNonLeap> ::= ( "0" <Digit> ) | ( ("1" | "2" ) ( <NonZeroOrNineDigit> | "0")
    <DaysFebLeap> ::= <DaysFebNonLeap> | "29"
    <Days30> ::= <DaysFebLeap> | "30"
    <Days31> ::= <Days30> | "31" 
    <LeapYear> ::= <ThreeDigits> ( <LeapDigit> | "0" )
    <NonLeapYear> ::= <ThreeDigits> ( "0" | <NonLeapDigit> | "9" )
    <Month31Days> ::= ( ( "0" ( "1" | "3" | "5" | "7" | "8") ) | ( "1" ( "0" | "2" ) ) ) <Dash> <Days31>
    <Month30Days> ::= ( ( "0" ( "4" | "6" | "9" ) ) | "11" ) <Dash> <Days30> 
    <FebruaryLeap> ::= <FebruaryNum> <Dash> <DaysFebLeap>
    <FebruaryNonLeap> ::= <FebruaryNum> <Dash> <DaysFebNonLeap>
    
    <DateYearMonthDay> ::= ( <LeapYear> <Dash> ( <FebruaryLeap> | <Month30Days> | <Month31Days> ) ) | 
                            ( <NonLeapYear> <Dash> ( <FebruaryNonLeap> | <Month30Days> | <Month31Days> ))
    

    And the advantage you get when writing year-month-day instead of day-month-year or month-day-year is, day is dependent on the month and month is dependent on the year so it is easier to program automata that can print date in format year-month-day.