{"record":{"id":"3c58094eaeecc2ce","repo":"databendlabs/databend","slug":"invalid-month-month","errorCode":null,"errorMessage":"invalid month: {month}","messagePattern":"invalid month: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/common/timezone/src/civil.rs","lineNumber":132,"sourceCode":"    let mut ordinal = CUMULATIVE_DAYS[(month - 1) as usize] + day as u16;\n    if month > 2 && is_leap_year(year) {\n        ordinal += 1;\n    }\n    ordinal\n}\n\npub(crate) fn last_day_of_month(year: i32, month: u8) -> u8 {\n    match month {\n        1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,\n        4 | 6 | 9 | 11 => 30,\n        2 => {\n            if is_leap_year(year) {\n                29\n            } else {\n                28\n            }\n        }\n        _ => unreachable!(\"invalid month: {month}\"),\n    }\n}\n\npub(crate) fn is_leap_year(year: i32) -> bool {\n    (year % 4 == 0 && year % 100 != 0) || year % 400 == 0\n}\n\nfn weeks_in_year(year: i32) -> u32 {\n    let Some(first_day) = NaiveDate::from_ymd_opt(year, 1, 1) else {\n        return 52;\n    };\n    match first_day.weekday() {\n        Weekday::Thu => 53,\n        Weekday::Wed if is_leap_year(year) => 53,\n        _ => 52,\n    }\n}\n","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/common/timezone/src/civil.rs#L114-L150","documentation":"`last_day_of_month` in the timezone crate panics with `unreachable!(\"invalid month: {month}\")` (civil.rs:132) when given a month outside 1..=12. The function computes the last calendar day (28/29/30/31) and treats any other month value as an internal caller bug, so it aborts with a panic carrying the offending month in the message.","triggerScenarios":"Calling `last_day_of_month(year, month)` (directly or via day_index_for_date / date constructors) with `month == 0`, `month > 12`, or a month computed from unchecked arithmetic (e.g. month + offset wrapping) instead of validating the date first.","commonSituations":"Date arithmetic that produces month 0 or 13+ (manual `month +/- n` without normalization), parsing dates from untrusted data where month bounds were not checked before calling this internal helper, or integer overflow in custom calendar code.","solutions":["Validate the month is in 1..=12 before calling `last_day_of_month`, or normalize with `((month - 1).rem_euclid(12) + 1)` plus year adjustment.","Prefer constructing dates via checked constructors (e.g. `NaiveDate::from_ymd_opt`) which reject invalid months before reaching this helper.","If the input comes from user data, parse with a strict date format (TO_DATE / strptime) so invalid months error out at parse time.","When extending calendar arithmetic, use month-add helpers that carry overflow into the year instead of raw addition."],"exampleFix":"// before\nlet last_day = last_day_of_month(year, month); // month may be 0 or 13\n\n// after\nassert!((1..=12).contains(&month), \"month out of range: {month}\");\nlet last_day = last_day_of_month(year, month);\n\n// or normalize overflowing arithmetic\nlet (year, month) = if month > 12 { (year + 1, month - 12) } else { (year, month) };","handlingStrategy":"validation","validationCode":"fn is_valid_month(month: u8) -> bool { (1..=12).contains(&month) }\n// call site:\nif !is_valid_month(month) { return Err(format!(\"invalid month: {month}\")); }\nlet last = last_day_of_month(year, month);","typeGuard":"fn valid_month(m: u8) -> Option<u8> { matches!(m, 1..=12).then_some(m) }","tryCatchPattern":"// Rust panic; contain at task boundary\nlet result = std::panic::catch_unwind(|| last_day_of_month(year, month));","preventionTips":["Always normalize month arithmetic (month+/-n) into 1..=12 with year carry","Build dates via checked constructors like NaiveDate::from_ymd_opt","Parse user-supplied dates with strict format validation before calendar math","Range-check month in any struct that stores it before use"],"tags":["rust","timezone","date","panic","month-out-of-range"],"backgroundTag":"invalid-argument-value","analyzedSha":"288d84d76e20a2f8f7173bda9691eb6ece301aa9","analyzedAt":"2026-09-11T11:29:36.208Z","contentChangedAt":"2026-09-11T11:29:36.208Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}