' Date formatting: ISO 8601, US, EU, custom format strings, parsing
#COMPILE EXE
#DIM ALL

TYPE DateComponents
    year AS INTEGER
    month AS INTEGER
    day AS INTEGER
    hour AS INTEGER
    minute AS INTEGER
    second AS INTEGER
END TYPE

FUNCTION IsLeapYear(BYVAL year AS LONG) AS LONG
    IF year MOD 400 = 0 THEN FUNCTION = 1: EXIT FUNCTION
    IF year MOD 100 = 0 THEN FUNCTION = 0: EXIT FUNCTION
    IF year MOD 4 = 0 THEN FUNCTION = 1 ELSE FUNCTION = 0
END FUNCTION

FUNCTION DaysInMonth(BYVAL year AS LONG, BYVAL month AS LONG) AS LONG
    SELECT CASE month
        CASE 1, 3, 5, 7, 8, 10, 12: FUNCTION = 31
        CASE 4, 6, 9, 11: FUNCTION = 30
        CASE 2: FUNCTION = IFF(IsLeapYear(year), 29, 28)
        CASE ELSE: FUNCTION = 0
    END SELECT
END FUNCTION

FUNCTION FormatISO8601(BYVAL year AS INTEGER, BYVAL month AS INTEGER, BYVAL day AS INTEGER) AS STRING
    FUNCTION = FORMAT$(year, "0000") + "-" + RIGHT$("00" + FORMAT$(month), 2) + "-" + RIGHT$("00" + FORMAT$(day), 2)
END FUNCTION

FUNCTION FormatUS(BYVAL year AS INTEGER, BYVAL month AS INTEGER, BYVAL day AS INTEGER) AS STRING
    FUNCTION = RIGHT$("00" + FORMAT$(month), 2) + "/" + RIGHT$("00" + FORMAT$(day), 2) + "/" + FORMAT$(year, "0000")
END FUNCTION

FUNCTION FormatEU(BYVAL year AS INTEGER, BYVAL month AS INTEGER, BYVAL day AS INTEGER) AS STRING
    FUNCTION = RIGHT$("00" + FORMAT$(day), 2) + "." + RIGHT$("00" + FORMAT$(month), 2) + "." + FORMAT$(year, "0000")
END FUNCTION

FUNCTION FormatDateTimeISO(ByVal year AS INTEGER, ByVal month AS INTEGER, ByVal day AS INTEGER, ByVal hour AS INTEGER, ByVal minute AS INTEGER, ByVal second AS INTEGER) AS STRING
    LOCAL result AS STRING
    result = FormatISO8601(year, month, day)
    result = result + "T" + RIGHT$("00" + FORMAT$(hour), 2) + ":" + RIGHT$("00" + FORMAT$(minute), 2) + ":" + RIGHT$("00" + FORMAT$(second), 2)
    FUNCTION = result
END FUNCTION

FUNCTION FormatCustom(BYVAL year AS INTEGER, BYVAL month AS INTEGER, BYVAL day AS INTEGER, BYVAL fmt AS STRING) AS STRING
    LOCAL result AS STRING
    LOCAL monthNames(1 TO 12) AS STRING
    monthNames(1) = "Jan": monthNames(2) = "Feb": monthNames(3) = "Mar": monthNames(4) = "Apr"
    monthNames(5) = "May": monthNames(6) = "Jun": monthNames(7) = "Jul": monthNames(8) = "Aug"
    monthNames(9) = "Sep": monthNames(10) = "Oct": monthNames(11) = "Nov": monthNames(12) = "Dec"
    result = fmt
    result = REPLACE$(result, "YYYY", FORMAT$(year, "0000"))
    result = REPLACE$(result, "YY", RIGHT$(FORMAT$(year), 2))
    result = REPLACE$(result, "MMM", monthNames(month))
    result = REPLACE$(result, "MM", RIGHT$("00" + FORMAT$(month), 2))
    result = REPLACE$(result, "DD", RIGHT$("00" + FORMAT$(day), 2))
    FUNCTION = result
END FUNCTION

FUNCTION FormatOrdinal(BYVAL day AS LONG) AS STRING
    LOCAL suffix AS STRING
    SELECT CASE day MOD 10
        CASE 1: suffix = IFF(day MOD 100 = 11, "th", "st")
        CASE 2: suffix = IFF(day MOD 100 = 12, "th", "nd")
        CASE 3: suffix = IFF(day MOD 100 = 13, "th", "rd")
        CASE ELSE: suffix = "th"
    END SELECT
    FUNCTION = FORMAT$(day) + suffix
END FUNCTION

FUNCTION MonthName(BYVAL month AS LONG) AS STRING
    SELECT CASE month
        CASE 1: FUNCTION = "January": CASE 2: FUNCTION = "February"
        CASE 3: FUNCTION = "March": CASE 4: FUNCTION = "April"
        CASE 5: FUNCTION = "May": CASE 6: FUNCTION = "June"
        CASE 7: FUNCTION = "July": CASE 8: FUNCTION = "August"
        CASE 9: FUNCTION = "September": CASE 10: FUNCTION = "October"
        CASE 11: FUNCTION = "November": CASE 12: FUNCTION = "December"
    END SELECT
END FUNCTION

FUNCTION ParseISODate(BYVAL isoStr AS STRING, y AS INTEGER, m AS INTEGER, d AS INTEGER) AS LONG
    LOCAL parts() AS STRING
    LOCAL pcount AS LONG
    LOCAL cleaned AS STRING
    cleaned = REPLACE$(isoStr, "-", "")
    IF LEN(cleaned) < 8 THEN FUNCTION = 0: EXIT FUNCTION
    y = VAL(LEFT$(cleaned, 4)): m = VAL(MID$(cleaned, 5, 2)): d = VAL(MID$(cleaned, 7, 2))
    IF y < 1 OR y > 9999 OR m < 1 OR m > 12 OR d < 1 OR d > DaysInMonth(y, m) THEN FUNCTION = 0: EXIT FUNCTION
    FUNCTION = 1
END FUNCTION

FUNCTION PBMAIN() AS LONG
    LOCAL y  AS INTEGER
    LOCAL m  AS INTEGER
    LOCAL d  AS INTEGER
    LOCAL h  AS INTEGER
    LOCAL mi  AS INTEGER
    LOCAL s  AS INTEGER
    LOCAL parsedY  AS INTEGER
    LOCAL parsedM  AS INTEGER
    LOCAL parsedD  AS INTEGER
    y = 2024: m = 7: d = 4: h = 14: mi = 30: s = 15
    PRINT "=== Date Formatting ==="
    PRINT
    PRINT "Input date: Year=" + FORMAT$(y) + " Month=" + FORMAT$(m) + " Day=" + FORMAT$(d)
    PRINT
    PRINT "ISO 8601:     " + FormatISO8601(y, m, d)
    PRINT "US format:    " + FormatUS(y, m, d)
    PRINT "EU format:    " + FormatEU(y, m, d)
    PRINT "ISO datetime: " + FormatDateTimeISO(y, m, d, h, mi, s)
    PRINT
    PRINT "Custom formats:"
    PRINT "  YYYY-MM-DD:      " + FormatCustom(y, m, d, "YYYY-MM-DD")
    PRINT "  DD/MM/YYYY:      " + FormatCustom(y, m, d, "DD/MM/YYYY")
    PRINT "  MMM DD, YYYY:    " + FormatCustom(y, m, d, "MMM DD, YYYY")
    PRINT "  MM/DD/YY:        " + FormatCustom(y, m, d, "MM/DD/YY")
    PRINT
    PRINT "Ordinal dates:"
    PRINT "  " + FormatOrdinal(1) + " of " + MonthName(m) + ", " + FORMAT$(y)
    PRINT "  " + FormatOrdinal(2) + " of July"
    PRINT "  " + FormatOrdinal(11) + " of July"
    PRINT "  " + FormatOrdinal(12) + " of July"
    PRINT "  " + FormatOrdinal(13) + " of July"
    PRINT "  " + FormatOrdinal(21) + " of July"
    PRINT "  " + FormatOrdinal(23) + " of July"
    PRINT
    PRINT "ISO date parsing:"
    IF ParseISODate("2024-07-04", parsedY, parsedM, parsedD) THEN
        PRINT "  '2024-07-04' -> Y:" + FORMAT$(parsedY) + " M:" + FORMAT$(parsedM) + " D:" + FORMAT$(parsedD)
    END IF
    IF ParseISODate("1999-12-31", parsedY, parsedM, parsedD) THEN
        PRINT "  '1999-12-31' -> Y:" + FORMAT$(parsedY) + " M:" + FORMAT$(parsedM) + " D:" + FORMAT$(parsedD)
    END IF
    IF ParseISODate("invalid", parsedY, parsedM, parsedD) = 0 THEN
        PRINT "  'invalid' -> parse failed (expected)"
    END IF
    PRINT
    PRINT "Date formatting tests complete."
    FUNCTION = 0
END FUNCTION
