///|
fn format_cell_value(
  value_type : CellValueType,
  raw : String,
  style : Style?,
  options : Options,
  use_1904_format? : Bool = false,
) -> String raise XlsxError {
  format_cell_value_limited(value_type, raw, style, options, use_1904_format~)
}

///|
/// Formats a cell while optionally bounding the materialized UTF-16 output.
/// The text-pattern path checks each append before it reaches `StringBuilder`,
/// so repeated `@` placeholders cannot amplify a small cell into an unbounded
/// intermediate string.
fn format_cell_value_limited(
  value_type : CellValueType,
  raw : String,
  style : Style?,
  options : Options,
  use_1904_format? : Bool = false,
  max_output_chars? : Int,
) -> String raise XlsxError {
  let output = match value_type {
    String =>
      match style {
        Some(style) =>
          match style.number_format {
            Some(format) => format_text_value(raw, format, max_output_chars?)
            None => raw
          }
        None => raw
      }
    Error => raw
    Bool => if parse_cell_bool(raw) { "TRUE" } else { "FALSE" }
    Number =>
      match style {
        Some(style) =>
          match style.number_format {
            Some(format) =>
              format_number_with_format(
                raw,
                format,
                options,
                use_1904_format,
                max_output_chars?,
              )
            None => raw
          }
        None => raw
      }
  }
  check_formatted_output_length(output, max_output_chars)
  output
}

///|
// Bounded formatting is used from cooperative async commands. Keeping every
// synchronous format program below half of the command's 64-KiB work quantum
// ensures one cell cannot monopolize the scheduler before the caller reaches
// its next suspension point.
let max_bounded_format_program_chars : Int = 32 * 1024

///|
fn check_bounded_format_program(
  program : StringView,
  maximum : Int?,
) -> Unit raise XlsxError {
  match maximum {
    None => ()
    Some(_) =>
      if program.length() > max_bounded_format_program_chars {
        raise ResourceLimitExceeded(
          kind="formatted_number_format_work_units",
          limit=max_bounded_format_program_chars,
          actual=program.length(),
        )
      }
  }
}

///|
// Proves a conservative upper bound before any formatted result is allocated.
// `program_multiplier` covers literal prefixes/suffixes, grouping separators,
// fraction placeholders, and date-token expansion. Callers select a tighter
// multiplier for the format family they are about to execute.
fn check_bounded_numeric_output(
  fallback_chars : Int,
  program_chars : Int,
  program_multiplier : Int,
  fixed_overhead : Int,
  maximum : Int?,
) -> Unit raise XlsxError {
  match maximum {
    None => ()
    Some(limit) => {
      if limit < 0 {
        raise InvalidOptions(msg="formatted output limit must be non-negative")
      }
      if fallback_chars > limit ||
        program_chars < 0 ||
        program_multiplier < 0 ||
        fixed_overhead < 0 ||
        fixed_overhead > limit ||
        program_multiplier == 0 ||
        program_chars > (limit - fixed_overhead) / program_multiplier {
        raise ResourceLimitExceeded(
          kind="formatted_cell_value_chars",
          limit~,
          actual=if limit < 0x7fffffff { limit + 1 } else { limit },
        )
      }
    }
  }
}

///|
fn number_format_ascii_fold(unit : UInt16) -> Int {
  let value = unit.to_int()
  if value >= 65 && value <= 90 {
    value + 32
  } else {
    value
  }
}

///|
fn number_format_ascii_case_equal(
  value : StringView,
  expected : StringView,
) -> Bool {
  if value.length() != expected.length() {
    return false
  }
  for i in 0.. Bool {
  if expected.length() == 0 {
    return true
  }
  if value.length() < expected.length() {
    return false
  }
  for start in 0..<=(value.length() - expected.length()) {
    let mut matches = true
    for offset in 0.. String? {
  if number_format_ascii_case_equal(content, "h") {
    Some("h")
  } else if number_format_ascii_case_equal(content, "hh") {
    Some("hh")
  } else if number_format_ascii_case_equal(content, "m") {
    Some("m")
  } else if number_format_ascii_case_equal(content, "mm") {
    Some("mm")
  } else if number_format_ascii_case_equal(content, "s") {
    Some("s")
  } else if number_format_ascii_case_equal(content, "ss") {
    Some("ss")
  } else {
    None
  }
}

///|
fn format_text_value(
  raw : String,
  format : NumberFormat,
  max_output_chars? : Int,
) -> String raise XlsxError {
  match format {
    Builtin(_id) => raw
    Custom(code) =>
      if number_format_ascii_case_equal(code.trim(), "general") {
        raw
      } else {
        check_bounded_format_program(code, max_output_chars)
        format_text_pattern(raw, code, max_output_chars?)
      }
  }
}

///|
fn checked_formatted_output_growth(
  current : Int,
  amount : Int,
  maximum : Int?,
) -> Int raise XlsxError {
  match maximum {
    None => current + amount
    Some(limit) => {
      if limit < 0 {
        raise InvalidOptions(msg="formatted output limit must be non-negative")
      }
      if amount < 0 || current < 0 || amount > limit - current {
        raise ResourceLimitExceeded(
          kind="formatted_cell_value_chars",
          limit~,
          actual=if limit < 0x7fffffff { limit + 1 } else { limit },
        )
      }
      current + amount
    }
  }
}

///|
fn check_formatted_output_length(
  output : String,
  maximum : Int?,
) -> Unit raise XlsxError {
  ignore(checked_formatted_output_growth(0, output.length(), maximum))
}

///|
fn format_number_with_format(
  raw : String,
  format : NumberFormat,
  options : Options,
  use_1904_format : Bool,
  max_output_chars? : Int,
) -> String raise XlsxError {
  match format {
    Builtin(id) =>
      format_number_builtin(
        raw,
        id,
        options,
        use_1904_format,
        max_output_chars?,
      )
    Custom(code) =>
      format_number_code(
        raw,
        code,
        options,
        use_1904_format~,
        max_output_chars?,
      )
  }
}

///|
fn format_number_builtin(
  raw : String,
  id : Int,
  options : Options,
  use_1904_format : Bool,
  max_output_chars? : Int,
) -> String raise XlsxError {
  let short_date = options.short_date_pattern
  let long_date = options.long_date_pattern
  let long_time = options.long_time_pattern
  match id {
    0 => raw
    1 => {
      check_bounded_numeric_output(raw.length(), 0, 1, 64, max_output_chars)
      format_number_simple(raw, 0, false, false)
    }
    2 => {
      check_bounded_numeric_output(raw.length(), 0, 1, 64, max_output_chars)
      format_number_simple(raw, 2, false, false)
    }
    3 => {
      check_bounded_numeric_output(raw.length(), 0, 1, 64, max_output_chars)
      format_number_simple(raw, 0, true, false)
    }
    4 => {
      check_bounded_numeric_output(raw.length(), 0, 1, 64, max_output_chars)
      format_number_simple(raw, 2, true, false)
    }
    9 => {
      check_bounded_numeric_output(raw.length(), 0, 1, 65, max_output_chars)
      format_number_simple(raw, 0, false, true)
    }
    10 => {
      check_bounded_numeric_output(raw.length(), 0, 1, 65, max_output_chars)
      format_number_simple(raw, 2, false, true)
    }
    14 =>
      if short_date != "" {
        format_excel_date(raw, short_date, use_1904_format~, max_output_chars?)
      } else {
        format_excel_date(raw, "mm-dd-yy", use_1904_format~, max_output_chars?)
      }
    15 =>
      if long_date != "" {
        format_excel_date(raw, long_date, use_1904_format~, max_output_chars?)
      } else {
        format_excel_date(raw, "d-mmm-yy", use_1904_format~, max_output_chars?)
      }
    16 => format_excel_date(raw, "d-mmm", use_1904_format~, max_output_chars?)
    17 => format_excel_date(raw, "mmm-yy", use_1904_format~, max_output_chars?)
    18 =>
      format_excel_date(raw, "h:mm AM/PM", use_1904_format~, max_output_chars?)
    19 =>
      format_excel_date(
        raw,
        "h:mm:ss AM/PM",
        use_1904_format~,
        max_output_chars?,
      )
    20 =>
      if long_time != "" {
        format_excel_date(raw, long_time, use_1904_format~, max_output_chars?)
      } else {
        format_excel_date(raw, "hh:mm", use_1904_format~, max_output_chars?)
      }
    21 =>
      if long_time != "" {
        format_excel_date(raw, long_time, use_1904_format~, max_output_chars?)
      } else {
        format_excel_date(raw, "hh:mm:ss", use_1904_format~, max_output_chars?)
      }
    22 =>
      if short_date != "" {
        format_excel_date(
          raw,
          "\{short_date} hh:mm",
          use_1904_format~,
          max_output_chars?,
        )
      } else {
        format_excel_date(
          raw,
          "m/d/yy hh:mm",
          use_1904_format~,
          max_output_chars?,
        )
      }
    // language-glyph builtin IDs resolve through the workbook culture
    27..=36 | 50..=62 | 67..=81 =>
      match lang_num_fmt_code(options, id) {
        Some(code) =>
          if code == "" {
            raw
          } else {
            format_number_code(
              raw,
              code,
              options,
              use_1904_format~,
              max_output_chars?,
            )
          }
        None => raw
      }
    _ => raw
  }
}

///|
fn format_number_code(
  raw : String,
  code : String,
  options : Options,
  use_1904_format? : Bool = false,
  max_output_chars? : Int,
) -> String raise XlsxError {
  let _ = options
  check_bounded_format_program(code, max_output_chars)
  let sections = split_format_sections(code)
  if sections.length() == 0 {
    return raw
  }
  let parsed : Result[Double, Error] = Ok(@string.parse_double(raw)) catch {
    e => Err(e)
  }
  match parsed {
    Err(_) => format_text_pattern(raw, code, max_output_chars?)
    Ok(value) => {
      let (section, add_minus) = select_format_section(sections, value)
      let (pattern, locale, currency_prefix) = parse_section_metadata(section)
      if pattern == "" {
        return ""
      }
      if number_format_ascii_case_equal(pattern, "general") {
        check_bounded_numeric_output(raw.length(), 0, 1, 0, max_output_chars)
        return format_general_number(raw)
      }
      if has_date_tokens(pattern) {
        match locale {
          Some(tag) => if !is_supported_locale(tag) { return raw }
          None => ()
        }
        check_bounded_numeric_output(
          raw.length(),
          code.length(),
          3,
          32,
          max_output_chars,
        )
        let formatted = format_excel_date(raw, pattern, use_1904_format~)
        let output = currency_prefix + formatted
        return if add_minus { "-" + output } else { output }
      }
      if !pattern_has_number_placeholders(pattern) {
        check_bounded_numeric_output(0, code.length(), 2, 2, max_output_chars)
        let literal = render_literal_segment(pattern)
        let output = currency_prefix + literal
        return if add_minus { "-" + output } else { output }
      }
      check_bounded_numeric_output(0, code.length(), 4, 64, max_output_chars)
      format_number_pattern(raw, pattern, add_minus, currency_prefix)
    }
  }
}

///|
fn split_format_sections(code : String) -> Array[String] {
  let sections : Array[String] = []
  let mut current = StringBuilder::new()
  let mut in_quote = false
  let mut escape_next = false
  let mut bracket_depth = 0
  for c in code {
    if escape_next {
      current.write_char(c)
      escape_next = false
      continue
    }
    match c {
      '\\' => {
        escape_next = true
        current.write_char(c)
      }
      '"' => {
        in_quote = !in_quote
        current.write_char(c)
      }
      '[' => {
        if !in_quote {
          bracket_depth = bracket_depth + 1
        }
        current.write_char(c)
      }
      ']' => {
        if !in_quote && bracket_depth > 0 {
          bracket_depth = bracket_depth - 1
        }
        current.write_char(c)
      }
      ';' =>
        if !in_quote && bracket_depth == 0 {
          sections.push(current.to_string())
          current = StringBuilder::new()
        } else {
          current.write_char(c)
        }
      _ => current.write_char(c)
    }
  }
  sections.push(current.to_string())
  sections
}

///|
fn select_format_section(
  sections : ArrayView[String],
  value : Double,
) -> (String, Bool) {
  let mut idx = 0
  let mut add_minus = false
  if value < 0.0 {
    if sections.length() >= 2 {
      idx = 1
    } else {
      add_minus = true
    }
  } else if value == 0.0 && sections.length() >= 3 {
    idx = 2
  }
  (sections[idx], add_minus)
}

///|
fn number_format_scalar_width_at(text : StringView, at : Int) -> Int {
  if text[at].is_leading_surrogate() &&
    at + 1 < text.length() &&
    text[at + 1].is_trailing_surrogate() {
    2
  } else {
    1
  }
}

///|
fn parse_section_metadata(section : String) -> (String, String?, String) {
  let sb = StringBuilder::new()
  let mut in_quote = false
  let mut escape_next = false
  let mut skip_next = false
  let mut locale : String? = None
  let mut currency_prefix = ""
  let len = section.length()
  let mut i = 0
  while i < len {
    let c = section[i]
    if escape_next {
      let width = number_format_scalar_width_at(section, i)
      sb.write_view(section[i:i + width])
      escape_next = false
      i = i + width
      continue
    }
    if skip_next {
      let width = number_format_scalar_width_at(section, i)
      skip_next = false
      i = i + width
      continue
    }
    match c {
      '\\' => {
        escape_next = true
        sb.write_char('\\')
      }
      '"' => {
        in_quote = !in_quote
        sb.write_char('"')
      }
      '_' => if !in_quote { skip_next = true } else { sb.write_char('_') }
      '*' => if !in_quote { skip_next = true } else { sb.write_char('*') }
      '[' =>
        if !in_quote {
          let mut j = i + 1
          while j < len && section[j] != ']' {
            j = j + 1
          }
          if j < len {
            let content = section[i + 1:j]
            match number_format_elapsed_token(content) {
              Some(elapsed) => {
                sb.write_char('[')
                sb.write_string(elapsed)
                sb.write_char(']')
              }
              None =>
                if content.has_prefix("$") {
                  let trimmed = content[1:]
                  match trimmed.rev_find("-") {
                    Some(pos) => {
                      let prefix = trimmed[:pos].to_owned()
                      let tag = trimmed[pos + 1:].to_owned()
                      if prefix != "" {
                        currency_prefix = prefix
                      }
                      if tag != "" {
                        locale = Some(tag)
                      }
                    }
                    None => ()
                  }
                }
            }
            i = j
          }
        } else {
          sb.write_char('[')
        }
      _ => {
        let width = number_format_scalar_width_at(section, i)
        sb.write_view(section[i:i + width])
        i = i + width
        continue
      }
    }
    i = i + 1
  }
  (sb.to_string(), locale, currency_prefix)
}

///|
fn is_supported_locale(tag : String) -> Bool {
  let normalized = tag.trim()
  number_format_ascii_case_equal(normalized, "409") ||
  number_format_ascii_case_equal(normalized, "0409") ||
  number_format_ascii_case_equal(normalized, "en-us") ||
  number_format_ascii_case_equal(normalized, "en_us")
}

///|
fn render_literal_segment(segment : String) -> String {
  let sb = StringBuilder::new()
  let mut in_quote = false
  let mut escape_next = false
  let mut skip_next = false
  for c in segment {
    if escape_next {
      sb.write_char(c)
      escape_next = false
      continue
    }
    if skip_next {
      skip_next = false
      continue
    }
    match c {
      '\\' => escape_next = true
      '"' => in_quote = !in_quote
      '_' => if !in_quote { skip_next = true } else { sb.write_char('_') }
      '*' => if !in_quote { skip_next = true } else { sb.write_char('*') }
      _ => sb.write_char(c)
    }
  }
  sb.to_string()
}

///|
fn text_format_section_has_placeholder(section : StringView) -> Bool {
  let mut in_quote = false
  let mut escape_next = false
  let mut skip_next = false
  let mut bracket_depth = 0
  for c in section {
    if escape_next {
      escape_next = false
      continue
    }
    if skip_next {
      skip_next = false
      continue
    }
    match c {
      '\\' => escape_next = true
      '"' => in_quote = !in_quote
      '[' => if !in_quote { bracket_depth = bracket_depth + 1 }
      ']' =>
        if !in_quote && bracket_depth > 0 {
          bracket_depth = bracket_depth - 1
        }
      '_' | '*' => if !in_quote && bracket_depth == 0 { skip_next = true }
      '@' => if !in_quote && bracket_depth == 0 { return true }
      _ => ()
    }
  }
  false
}

///|
fn select_text_format_section(sections : ArrayView[String]) -> String? {
  if sections.length() >= 4 {
    return Some(sections[3])
  }
  guard sections.length() > 0 else { return None }
  let final_section = sections[sections.length() - 1]
  if text_format_section_has_placeholder(final_section) {
    Some(final_section)
  } else {
    None
  }
}

///|
fn format_text_pattern(
  raw : String,
  code : String,
  max_output_chars? : Int,
) -> String raise XlsxError {
  let sections = split_format_sections(code)
  let section = match select_text_format_section(sections) {
    Some(section) => section
    None => return raw
  }
  let (pattern, _locale, currency_prefix) = parse_section_metadata(section)
  let sb = StringBuilder::new()
  let mut output_chars = checked_formatted_output_growth(
    0,
    currency_prefix.length(),
    max_output_chars,
  )
  sb.write_view(currency_prefix)
  let mut in_quote = false
  let mut escape_next = false
  let mut skip_next = false
  for c in pattern {
    if escape_next {
      output_chars = checked_formatted_output_growth(
        output_chars,
        if c.to_int() > 0xffff {
          2
        } else {
          1
        },
        max_output_chars,
      )
      sb.write_char(c)
      escape_next = false
      continue
    }
    if skip_next {
      skip_next = false
      continue
    }
    match c {
      '\\' => escape_next = true
      '"' => in_quote = !in_quote
      '_' =>
        if !in_quote {
          skip_next = true
        } else {
          output_chars = checked_formatted_output_growth(
            output_chars, 1, max_output_chars,
          )
          sb.write_char('_')
        }
      '*' =>
        if !in_quote {
          skip_next = true
        } else {
          output_chars = checked_formatted_output_growth(
            output_chars, 1, max_output_chars,
          )
          sb.write_char('*')
        }
      '@' =>
        if !in_quote {
          output_chars = checked_formatted_output_growth(
            output_chars,
            raw.length(),
            max_output_chars,
          )
          sb.write_view(raw)
        } else {
          output_chars = checked_formatted_output_growth(
            output_chars, 1, max_output_chars,
          )
          sb.write_char('@')
        }
      _ => {
        output_chars = checked_formatted_output_growth(
          output_chars,
          if c.to_int() > 0xffff {
            2
          } else {
            1
          },
          max_output_chars,
        )
        sb.write_char(c)
      }
    }
  }
  sb.to_string()
}

///|
fn has_date_tokens(code : StringView) -> Bool {
  let mut in_quote = false
  let mut escape_next = false
  let mut i = 0
  let len = code.length()
  while i < len {
    let c = code[i]
    if escape_next {
      escape_next = false
      i = i + number_format_scalar_width_at(code, i)
      continue
    }
    match c {
      '\\' => escape_next = true
      '"' => in_quote = !in_quote
      '[' =>
        if !in_quote {
          let mut j = i + 1
          while j < len && code[j] != ']' {
            j = j + 1
          }
          if j < len {
            if number_format_elapsed_token(code[i + 1:j]) is Some(_) {
              return true
            }
            i = j
          }
        }
      _ =>
        if !in_quote &&
          (
            c == 'y' ||
            c == 'Y' ||
            c == 'm' ||
            c == 'M' ||
            c == 'd' ||
            c == 'D' ||
            c == 'h' ||
            c == 'H' ||
            c == 's' ||
            c == 'S'
          ) {
          return true
        }
    }
    i = i + number_format_scalar_width_at(code, i)
  }
  number_format_ascii_case_contains(code, "am/pm") ||
  number_format_ascii_case_contains(code, "a/p")
}

///|
fn pattern_has_number_placeholders(code : StringView) -> Bool {
  let mut in_quote = false
  let mut escape_next = false
  for c in code {
    if escape_next {
      escape_next = false
      continue
    }
    match c {
      '\\' => escape_next = true
      '"' => in_quote = !in_quote
      _ => if !in_quote && (c == '0' || c == '#' || c == '?') { return true }
    }
  }
  false
}

///|
fn format_number_pattern(
  raw : String,
  pattern : String,
  add_minus : Bool,
  currency_prefix : String,
) -> String raise XlsxError {
  let value = @string.parse_double(raw) catch { _ => return raw }
  if value.is_nan() || value.is_inf() {
    raise InvalidXml(msg="cell number must be finite")
  }
  let negative = value < 0.0
  let abs_value = if negative { -value } else { value }
  let (prefix, suffix, info) = parse_number_pattern(pattern)
  let scaled = scale_number_for_percent_format(abs_value, info.percent_count)
  let formatted = if info.has_fraction {
    format_fraction_number(scaled, info)
  } else if info.has_scientific {
    format_scientific_number(scaled, info)
  } else {
    format_decimal_number(scaled, info)
  }
  let output = StringBuilder::new()
  if add_minus {
    output.write_char('-')
  }
  output.write_view(currency_prefix)
  output.write_view(prefix)
  output.write_view(formatted)
  output.write_view(suffix)
  output.to_string()
}

///|
fn scale_number_for_percent_format(
  value : Double,
  percent_count : Int,
) -> Double raise XlsxError {
  // Applying the entire power first can overflow even when the final product
  // is representable (for example, 1e-300 * 100^155). Scaling one marker at a
  // time preserves that finite result and lets us fail at the first genuinely
  // non-finite intermediate. Keeping zero on an explicit fast path also avoids
  // manufacturing NaN from IEEE-754's 0 * infinity rule.
  if value == 0.0 || percent_count == 0 {
    return value
  }
  let mut scaled = value
  for _ in 0.. (String, String, NumberPatternInfo) {
  let len = pattern.length()
  let mut in_quote = false
  let mut escape_next = false
  let mut first_placeholder : Int? = None
  let mut last_placeholder : Int? = None
  let mut percent_count = 0
  let mut has_scientific = false
  let mut exp_digits = 0
  let mut exp_upper = false
  let mut has_fraction = false
  let mut i = 0
  while i < len {
    let c = pattern[i]
    if escape_next {
      escape_next = false
      i = i + 1
      continue
    }
    match c {
      '\\' => escape_next = true
      '"' => in_quote = !in_quote
      '%' => if !in_quote { percent_count = percent_count + 1 }
      'E' | 'e' =>
        if !in_quote {
          has_scientific = true
          exp_upper = c == 'E'
          let mut j = i + 1
          if j < len && (pattern[j] == '+' || pattern[j] == '-') {
            j = j + 1
          }
          let mut digits = 0
          while j < len && (pattern[j] == '0' || pattern[j] == '#') {
            digits = digits + 1
            j = j + 1
          }
          exp_digits = if digits > 0 { digits } else { exp_digits }
        }
      '0' | '#' | '?' =>
        if !in_quote {
          if first_placeholder is None {
            first_placeholder = Some(i)
          }
          last_placeholder = Some(i)
        }
      _ => ()
    }
    i = i + 1
  }
  let (prefix, suffix) = match (first_placeholder, last_placeholder) {
    (Some(start), Some(end)) => {
      let prefix = render_literal_segment(pattern[:start].to_owned())
      let suffix = render_literal_segment(pattern[end + 1:].to_owned())
      (prefix, suffix)
    }
    _ => (render_literal_segment(pattern), "")
  }
  let placeholder_body = match (first_placeholder, last_placeholder) {
    (Some(start), Some(end)) => pattern[start:end + 1].to_owned()
    _ => ""
  }
  let mut min_int_digits = 0
  let mut required_decimals = 0
  let mut max_decimals = 0
  let mut use_group = false
  let mut numerator_digits = 0
  let mut denominator_digits = 0
  let slash_pos = placeholder_body.find("/")
  has_fraction = slash_pos is Some(_)
  match slash_pos {
    Some(pos) => {
      let left = placeholder_body[:pos].to_owned()
      let right = placeholder_body[pos + 1:].to_owned()
      let mut j = left.length() - 1
      while j >= 0 {
        let ch = left[j]
        if ch == '0' || ch == '#' || ch == '?' {
          numerator_digits = numerator_digits + 1
          j = j - 1
        } else {
          break
        }
      }
      let int_part = if j >= 0 { left[:j + 1].to_owned() } else { "" }
      use_group = int_part.contains(",")
      for c in int_part {
        if c == '0' {
          min_int_digits = min_int_digits + 1
        }
      }
      let mut k = 0
      while k < right.length() {
        let ch = right[k]
        if ch == '0' || ch == '#' || ch == '?' {
          denominator_digits = denominator_digits + 1
          k = k + 1
        } else {
          break
        }
      }
    }
    None => {
      let dot_pos = placeholder_body.find(".")
      let (int_part, dec_part) = match dot_pos {
        Some(pos) =>
          (
            placeholder_body[:pos].to_owned(),
            placeholder_body[pos + 1:].to_owned(),
          )
        None => (placeholder_body, "")
      }
      use_group = int_part.contains(",")
      for c in int_part {
        if c == '0' {
          min_int_digits = min_int_digits + 1
        }
      }
      for c in dec_part {
        match c {
          '0' => {
            required_decimals = required_decimals + 1
            max_decimals = max_decimals + 1
          }
          '#' | '?' => max_decimals = max_decimals + 1
          'E' | 'e' => break
          _ => ()
        }
      }
    }
  }
  let info = {
    min_int_digits,
    required_decimals,
    max_decimals,
    use_group,
    percent_count,
    has_scientific,
    exp_digits,
    exp_upper,
    has_fraction,
    numerator_digits,
    denominator_digits,
  }
  (prefix, suffix, info)
}

///|
fn format_general_number(raw : String) -> String {
  let value = @string.parse_double(raw) catch { _ => return raw }
  let negative = value < 0.0
  let abs_value = if negative { -value } else { value }
  let int_part = Double::to_int64(Double::floor(abs_value))
  let int_len = int_part.to_string().length()
  if int_len >= 11 {
    return raw
  }
  let decimals = if int_len >= 10 { 0 } else { 10 - int_len }
  let formatted = format_decimal_number(abs_value, {
    min_int_digits: 1,
    required_decimals: 0,
    max_decimals: decimals,
    use_group: false,
    percent_count: 0,
    has_scientific: false,
    exp_digits: 0,
    exp_upper: false,
    has_fraction: false,
    numerator_digits: 0,
    denominator_digits: 0,
  })
  if negative {
    "-" + formatted
  } else {
    formatted
  }
}

///|
fn format_scientific_number(value : Double, info : NumberPatternInfo) -> String {
  if value == 0.0 {
    let mantissa = format_fixed_number(0.0, info.max_decimals, false, 1)
    let exp = pad_left_int(
      0,
      if info.exp_digits > 0 {
        info.exp_digits
      } else {
        2
      },
    )
    let marker = if info.exp_upper { "E" } else { "e" }
    return mantissa + marker + "+" + exp
  }
  let abs_value = value.abs()
  let mut exp = Double::to_int(@math.log10(abs_value))
  let mut mantissa = abs_value / @math.pow(10.0, Double::from_int(exp))
  let mut mantissa_text = format_fixed_number(
    mantissa,
    info.max_decimals,
    false,
    1,
  )
  if mantissa_text.has_prefix("10") {
    exp = exp + 1
    mantissa = mantissa / 10.0
    mantissa_text = format_fixed_number(mantissa, info.max_decimals, false, 1)
  }
  let marker = if info.exp_upper { "E" } else { "e" }
  let sign = if exp < 0 { "-" } else { "+" }
  let exp_abs = if exp < 0 { -exp } else { exp }
  let exp_text = pad_left_int(
    exp_abs,
    if info.exp_digits > 0 {
      info.exp_digits
    } else {
      2
    },
  )
  let result = mantissa_text + marker + sign + exp_text
  if value < 0.0 {
    "-" + result
  } else {
    result
  }
}

///|
fn format_fraction_number(value : Double, info : NumberPatternInfo) -> String {
  // The integer part is non-negative here. Keep the full UInt64 range instead
  // of passing through Double::to_int64, whose saturating conversion would
  // manufacture Int64::MAX at the signed boundary.
  if value >= uint64_exclusive_upper_bound_double {
    return value.to_string()
  }
  let integer_double = Double::floor(value)
  let int_part = nonnegative_double_to_uint64(integer_double)
  let frac = value - integer_double
  let integer_text = format_uint64_padded(
    int_part,
    info.min_int_digits,
    info.use_group,
  )
  if info.numerator_digits <= 0 || info.denominator_digits <= 0 {
    return integer_text
  }
  let fraction = float_to_fraction(
    frac,
    info.numerator_digits,
    info.denominator_digits,
  )
  integer_text + " " + fraction
}

///|
fn float_to_fraction(
  value : Double,
  numerator_placeholders : Int,
  denominator_placeholders : Int,
) -> String {
  if denominator_placeholders <= 0 {
    return ""
  }
  let limit = pow10_int64(denominator_placeholders)
  let (num, den) = float_to_frac_use_continued_fraction(value, limit)
  if num == 0L {
    return String::repeat(
      " ",
      numerator_placeholders + denominator_placeholders + 1,
    )
  }
  let num_str = num.to_string()
  let den_str = den.to_string()
  let num_pad = if numerator_placeholders > num_str.length() {
    numerator_placeholders - num_str.length()
  } else {
    0
  }
  let den_pad = if denominator_placeholders > den_str.length() {
    denominator_placeholders - den_str.length()
  } else {
    0
  }
  String::repeat(" ", num_pad) +
  num_str +
  "/" +
  den_str +
  String::repeat(" ", den_pad)
}

///|
fn float_to_frac_use_continued_fraction(
  value : Double,
  denominator_limit : Int64,
) -> (Int64, Int64) {
  if value.is_nan() || value.is_inf() || value <= 0.0 || denominator_limit <= 1L {
    return (0L, 1L)
  }
  let maximum_int64 = 9_223_372_036_854_775_807L
  let mut p1 : Int64 = 1L
  let mut q1 : Int64 = 0L
  let mut p2 : Int64 = 0L
  let mut q2 : Int64 = 1L
  let mut lasta : Int64 = 0L
  let mut lastb : Int64 = 0L
  let mut r = value
  // A finite Double has at most 53 significant binary digits, so 128
  // continued-fraction steps are more than enough to reach its exact rational
  // representation. The hard bound also makes this helper total if a future
  // backend changes floating-point corner-case behavior.
  for _ in 0..<128 {
    if r.is_nan() || r.is_inf() || r < 0.0 {
      return if lastb > 0L { (lasta, lastb) } else { (0L, 1L) }
    }
    let floored = Double::floor(r)
    if floored >= int64_exclusive_upper_bound_double {
      return if lastb > 0L { (lasta, lastb) } else { (0L, 1L) }
    }
    let a = Double::to_int64(floored)
    if a < 0L ||
      (p1 > 0L && a > (maximum_int64 - p2) / p1) ||
      (q1 > 0L && a > (maximum_int64 - q2) / q1) {
      return if lastb > 0L { (lasta, lastb) } else { (0L, 1L) }
    }
    let curra = a * p1 + p2
    let currb = a * q1 + q2
    if currb <= 0L || currb >= denominator_limit {
      return if lastb > 0L { (lasta, lastb) } else { (0L, 1L) }
    }
    p2 = p1
    q2 = q1
    p1 = curra
    q1 = currb
    let frac = r - a.to_double()
    if frac.abs() < 0.000000000001 {
      return (curra, currb)
    }
    lasta = curra
    lastb = currb
    r = 1.0 / frac
  }
  if lastb > 0L {
    (lasta, lastb)
  } else {
    (0L, 1L)
  }
}

///|
fn pow10_int64(exp : Int) -> Int64 {
  let mut value : Int64 = 1L
  let mut i = 0
  let limit = if exp > 18 { 18 } else { exp }
  while i < limit {
    value = value * 10L
    i = i + 1
  }
  value
}

///|
fn format_number_simple(
  raw : String,
  decimals : Int,
  use_group : Bool,
  percent : Bool,
) -> String raise XlsxError {
  let value = @string.parse_double(raw) catch { _ => return raw }
  if value.is_nan() || value.is_inf() {
    raise InvalidXml(msg="cell number must be finite")
  }
  let scaled = if percent { value * 100.0 } else { value }
  if scaled.is_nan() || scaled.is_inf() {
    raise InvalidXml(msg="number format scale must remain finite")
  }
  let info = {
    min_int_digits: 1,
    required_decimals: decimals,
    max_decimals: decimals,
    use_group,
    percent_count: if percent {
      1
    } else {
      0
    },
    has_scientific: false,
    exp_digits: 0,
    exp_upper: false,
    has_fraction: false,
    numerator_digits: 0,
    denominator_digits: 0,
  }
  let formatted = format_decimal_number(scaled, info)
  if percent {
    formatted + "%"
  } else {
    formatted
  }
}

///|
fn format_decimal_number(value : Double, info : NumberPatternInfo) -> String {
  let negative = value < 0.0
  let abs_value = if negative { -value } else { value }
  let formatted = format_fixed_number(
    abs_value,
    info.max_decimals,
    info.use_group,
    if info.min_int_digits > 0 {
      info.min_int_digits
    } else {
      1
    },
  )
  let trimmed = if info.max_decimals > info.required_decimals {
    trim_optional_decimals(formatted, info.required_decimals)
  } else {
    formatted
  }
  if negative {
    "-" + trimmed
  } else {
    trimmed
  }
}

///|
fn trim_optional_decimals(text : String, required_decimals : Int) -> String {
  let dot = text.find(".")
  match dot {
    None => text
    Some(pos) => {
      let exponent_pos = match text.find("e") {
        Some(value) => value
        None => text.find("E").unwrap_or(text.length())
      }
      // A fallback from fixed-point formatting can be scientific notation.
      // Trim only the significand's fractional zeros and retain the exponent
      // verbatim (especially its trailing zero).
      if exponent_pos < pos {
        return text
      }
      let int_part = text[:pos].to_owned()
      let frac_part = text[pos + 1:exponent_pos].to_owned()
      let exponent = text[exponent_pos:].to_owned()
      let mut end = frac_part.length()
      while end > required_decimals {
        if frac_part[end - 1] == '0' {
          end = end - 1
        } else {
          break
        }
      }
      if end == 0 && required_decimals == 0 {
        int_part + exponent
      } else {
        int_part + "." + frac_part[:end].to_owned() + exponent
      }
    }
  }
}

///|
let int64_exclusive_upper_bound_double : Double = 9_223_372_036_854_775_808.0

///|
let uint64_exclusive_upper_bound_double : Double = 18_446_744_073_709_551_616.0

///|
fn nonnegative_double_to_uint64(value : Double) -> UInt64 {
  // Some MoonBit backends currently saturate Double::to_uint64 at the signed
  // boundary. Split off bit 63 explicitly so behavior is identical across
  // backends for every finite integral Double in [0, 2^64).
  if value >= int64_exclusive_upper_bound_double {
    0x8000_0000_0000_0000UL +
    Double::to_int64(value - int64_exclusive_upper_bound_double).reinterpret_as_uint64()
  } else {
    Double::to_int64(value).reinterpret_as_uint64()
  }
}

///|
fn format_fixed_number(
  value : Double,
  decimals : Int,
  use_group : Bool,
  min_int_digits : Int,
) -> String {
  let negative = value < 0.0
  let abs_value = if negative { -value } else { value }
  let places = if decimals < 0 {
    0
  } else if decimals > 9 {
    9
  } else {
    decimals
  }
  let scale = pow10_int(places)
  let integer_double = Double::floor(abs_value)
  if integer_double >= uint64_exclusive_upper_bound_double {
    return abs_value.to_string()
  }
  let scale64 = scale.to_uint64()
  let mut int_part = nonnegative_double_to_uint64(integer_double)
  let mut frac_part = nonnegative_double_to_uint64(
    Double::round((abs_value - integer_double) * Double::from_int(scale)),
  )
  if frac_part >= scale64 {
    if int_part == 0xffff_ffff_ffff_ffffUL {
      return abs_value.to_string()
    }
    int_part = int_part + 1UL
    frac_part = 0UL
  }
  let result = StringBuilder::new()
  if negative {
    result.write_char('-')
  }
  result.write_view(format_uint64_padded(int_part, min_int_digits, use_group))
  if places > 0 {
    result.write_char('.')
    result.write_view(pad_left_uint64(frac_part, places))
  }
  result.to_string()
}

///|
fn pow10_int(exp : Int) -> Int {
  let mut value = 1
  let mut i = 0
  let limit = if exp > 9 { 9 } else { exp }
  while i < limit {
    value = value * 10
    i = i + 1
  }
  value
}

///|
fn pad_left_int(value : Int, width : Int) -> String {
  let text = value.to_string()
  if text.length() >= width {
    return text
  }
  let sb = StringBuilder::new()
  let mut i = text.length()
  while i < width {
    sb.write_char('0')
    i = i + 1
  }
  sb.write_view(text)
  sb.to_string()
}

///|
fn pad_left_int64(value : Int64, width : Int) -> String {
  let text = value.to_string()
  if text.length() >= width {
    return text
  }
  let sb = StringBuilder::new()
  let mut i = text.length()
  while i < width {
    sb.write_char('0')
    i = i + 1
  }
  sb.write_view(text)
  sb.to_string()
}

///|
fn pad_left_uint64(value : UInt64, width : Int) -> String {
  let text = value.to_string()
  if text.length() >= width {
    return text
  }
  let sb = StringBuilder::new()
  let mut i = text.length()
  while i < width {
    sb.write_char('0')
    i = i + 1
  }
  sb.write_view(text)
  sb.to_string()
}

///|
fn format_uint64_padded(
  value : UInt64,
  min_digits : Int,
  use_group : Bool,
) -> String {
  let text = value.to_string()
  let padding = if min_digits > text.length() {
    min_digits - text.length()
  } else {
    0
  }
  let padded = if padding > 0 {
    String::repeat("0", padding) + text
  } else {
    text
  }
  if use_group {
    format_int_grouped_string(padded)
  } else {
    padded
  }
}

///|
fn format_int_grouped_string(text : String) -> String {
  let len = text.length()
  let sb = StringBuilder::new()
  let mut i = 0
  for c in text {
    if i > 0 && (len - i) % 3 == 0 {
      sb.write_char(',')
    }
    sb.write_char(c)
    i = i + 1
  }
  sb.to_string()
}

///|
fn format_excel_date(
  raw : String,
  pattern : String,
  use_1904_format? : Bool = false,
  max_output_chars? : Int,
) -> String raise XlsxError {
  check_bounded_format_program(pattern, max_output_chars)
  check_bounded_numeric_output(
    raw.length(),
    pattern.length(),
    2,
    32,
    max_output_chars,
  )
  let value = @string.parse_double(raw) catch { _ => return raw }
  let parts = if use_1904_format {
    excel_serial_to_parts_1904(value)
  } else {
    excel_serial_to_parts(value)
  }
  match parts {
    None => raw
    Some((year, month, day, hour, minute, second)) =>
      format_date_pattern(
        pattern, year, month, day, hour, minute, second, value,
      )
  }
}

///|
fn is_leap_year(year : Int) -> Bool {
  if year % 400 == 0 {
    true
  } else if year % 100 == 0 {
    false
  } else {
    year % 4 == 0
  }
}

///|
fn days_in_month(year : Int, month : Int) -> Int {
  match month {
    1 | 3 | 5 | 7 | 8 | 10 | 12 => 31
    4 | 6 | 9 | 11 => 30
    2 => if is_leap_year(year) { 29 } else { 28 }
    _ => 30
  }
}

///|
fn weekday_index(year : Int, month : Int, day : Int) -> Int {
  if year == 1900 && month == 2 && day == 29 {
    return 4
  }
  let mut y = year
  let mut m = month
  if m < 3 {
    m = m + 12
    y = y - 1
  }
  let k = y % 100
  let j = y / 100
  let h = (day + 13 * (m + 1) / 5 + k + k / 4 + j / 4 + 5 * j) % 7
  match h {
    0 => 6
    1 => 0
    2 => 1
    3 => 2
    4 => 3
    5 => 4
    _ => 5
  }
}

///|
fn format_date_pattern(
  pattern : String,
  year : Int,
  month : Int,
  day : Int,
  hour : Int,
  minute : Int,
  second : Int,
  serial_value : Double,
) -> String {
  format_date_pattern_tokens(
    pattern, year, month, day, hour, minute, second, serial_value,
  )
}

///|
priv enum DateToken {
  Literal(String)
  Year(Int)
  Month(Int)
  Day(Int)
  Hour(Int)
  Second(Int)
  AmPm(Bool)
  Ap(Bool)
  ElapsedHour(Int)
  ElapsedMinute(Int)
  ElapsedSecond(Int)
}

///|
fn tokenize_date_pattern(pattern : String) -> Array[DateToken] {
  let tokens : Array[DateToken] = []
  let mut sb = StringBuilder::new()
  let mut has_literal = false
  let mut in_quote = false
  let mut escape_next = false
  let len = pattern.length()
  let mut i = 0
  while i < len {
    let scalar_width = number_format_scalar_width_at(pattern, i)
    if escape_next {
      sb.write_view(pattern[i:i + scalar_width])
      has_literal = true
      escape_next = false
      i = i + scalar_width
      continue
    }
    let c = pattern[i]
    match c {
      '\\' => escape_next = true
      '"' => in_quote = !in_quote
      '[' =>
        if !in_quote {
          let mut j = i + 1
          while j < len && pattern[j] != ']' {
            j = j + 1
          }
          if j < len {
            if has_literal {
              tokens.push(Literal(sb.to_string()))
              sb = StringBuilder::new()
              has_literal = false
            }
            match number_format_elapsed_token(pattern[i + 1:j]) {
              Some(content) =>
                match content {
                  "h" | "hh" => tokens.push(ElapsedHour(content.length()))
                  "m" | "mm" => tokens.push(ElapsedMinute(content.length()))
                  "s" | "ss" => tokens.push(ElapsedSecond(content.length()))
                  _ => ()
                }
              _ => ()
            }
            i = j
          }
        } else {
          sb.write_char('[')
          has_literal = true
        }
      _ =>
        if !in_quote {
          let is_ampm = i + 5 <= len &&
            (c == 'a' || c == 'A') &&
            (pattern[i + 1] == 'm' || pattern[i + 1] == 'M') &&
            pattern[i + 2] == '/' &&
            (pattern[i + 3] == 'p' || pattern[i + 3] == 'P') &&
            (pattern[i + 4] == 'm' || pattern[i + 4] == 'M')
          if is_ampm {
            if has_literal {
              tokens.push(Literal(sb.to_string()))
              sb = StringBuilder::new()
              has_literal = false
            }
            let upper = c == 'A' || pattern[i + 3] == 'P'
            tokens.push(AmPm(upper))
            i = i + 5
            continue
          }
          let is_ap = i + 3 <= len &&
            (c == 'a' || c == 'A') &&
            pattern[i + 1] == '/' &&
            (pattern[i + 2] == 'p' || pattern[i + 2] == 'P')
          if is_ap {
            if has_literal {
              tokens.push(Literal(sb.to_string()))
              sb = StringBuilder::new()
              has_literal = false
            }
            let upper = c == 'A' || pattern[i + 2] == 'P'
            tokens.push(Ap(upper))
            i = i + 3
            continue
          }
          if c == 'y' ||
            c == 'Y' ||
            c == 'm' ||
            c == 'M' ||
            c == 'd' ||
            c == 'D' ||
            c == 'h' ||
            c == 'H' ||
            c == 's' ||
            c == 'S' {
            if has_literal {
              tokens.push(Literal(sb.to_string()))
              sb = StringBuilder::new()
              has_literal = false
            }
            let mut j = i
            while j < len {
              let repeated = match c {
                'y' | 'Y' => pattern[j] == 'y' || pattern[j] == 'Y'
                'm' | 'M' => pattern[j] == 'm' || pattern[j] == 'M'
                'd' | 'D' => pattern[j] == 'd' || pattern[j] == 'D'
                'h' | 'H' => pattern[j] == 'h' || pattern[j] == 'H'
                's' | 'S' => pattern[j] == 's' || pattern[j] == 'S'
                _ => false
              }
              if !repeated {
                break
              }
              j = j + 1
            }
            let count = j - i
            let kind = match c {
              'y' | 'Y' => Year(count)
              'm' | 'M' => Month(count)
              'd' | 'D' => Day(count)
              'h' | 'H' => Hour(count)
              's' | 'S' => Second(count)
              _ => Literal(pattern[i:j].to_owned())
            }
            tokens.push(kind)
            i = j
            continue
          }
          sb.write_view(pattern[i:i + scalar_width])
          has_literal = true
        } else {
          sb.write_view(pattern[i:i + scalar_width])
          has_literal = true
        }
    }
    i = i + scalar_width
  }
  if has_literal {
    tokens.push(Literal(sb.to_string()))
  }
  tokens
}

///|
fn format_date_pattern_tokens(
  pattern : String,
  year : Int,
  month : Int,
  day : Int,
  hour : Int,
  minute : Int,
  second : Int,
  serial_value : Double,
) -> String {
  let tokens = tokenize_date_pattern(pattern)
  let mut use_ampm = false
  for token in tokens {
    match token {
      AmPm(_) | Ap(_) => {
        use_ampm = true
        break
      }
      _ => ()
    }
  }
  let weekday = weekday_index(year, month, day)
  let total_seconds = Double::to_int64(Double::round(serial_value * 86400.0))
  let elapsed_hours = total_seconds / 3600L
  let elapsed_minutes = total_seconds / 60L
  let elapsed_seconds = total_seconds
  let output = StringBuilder::new()
  for idx, token in tokens {
    match token {
      Literal(text) => output.write_view(text)
      Year(count) =>
        if count == 2 {
          output.write_view(pad_left_int(year % 100, 2))
        } else if count >= 4 {
          output.write_view(pad_left_int(year, 4))
        } else {
          output.write_view(year.to_string())
        }
      Month(count) => {
        let mut treat_as_minute = false
        if count <= 2 {
          let mut prev_idx = idx - 1
          while prev_idx >= 0 {
            match tokens[prev_idx] {
              Literal(_) => prev_idx = prev_idx - 1
              Hour(_) | ElapsedHour(_) => {
                treat_as_minute = true
                break
              }
              _ => break
            }
          }
          let mut next_idx = idx + 1
          while next_idx < tokens.length() {
            match tokens[next_idx] {
              Literal(_) => next_idx = next_idx + 1
              Second(_) | ElapsedSecond(_) => {
                treat_as_minute = true
                break
              }
              _ => break
            }
          }
        }
        if treat_as_minute {
          if count >= 2 {
            output.write_view(pad_left_int(minute, 2))
          } else {
            output.write_view(minute.to_string())
          }
        } else if count >= 6 {
          output.write_view(month_long_name(month))
        } else if count == 5 {
          output.write_view(month_initial_name(month))
        } else if count == 4 {
          output.write_view(month_long_name(month))
        } else if count == 3 {
          output.write_view(month_short_name(month))
        } else if count == 2 {
          output.write_view(pad_left_int(month, 2))
        } else {
          output.write_view(month.to_string())
        }
      }
      Day(count) =>
        if count >= 4 {
          output.write_view(weekday_long_name(weekday))
        } else if count == 3 {
          output.write_view(weekday_short_name(weekday))
        } else if count == 2 {
          output.write_view(pad_left_int(day, 2))
        } else {
          output.write_view(day.to_string())
        }
      Hour(count) => {
        let hour_value = if use_ampm { hour12(hour) } else { hour }
        if count >= 2 {
          output.write_view(pad_left_int(hour_value, 2))
        } else {
          output.write_view(hour_value.to_string())
        }
      }
      Second(count) =>
        if count >= 2 {
          output.write_view(pad_left_int(second, 2))
        } else {
          output.write_view(second.to_string())
        }
      AmPm(upper) =>
        if upper {
          output.write_view(if hour >= 12 { "PM" } else { "AM" })
        } else {
          output.write_view(if hour >= 12 { "pm" } else { "am" })
        }
      Ap(upper) =>
        if upper {
          output.write_view(if hour >= 12 { "P" } else { "A" })
        } else {
          output.write_view(if hour >= 12 { "p" } else { "a" })
        }
      ElapsedHour(count) =>
        if count >= 2 {
          output.write_view(pad_left_int64(elapsed_hours, 2))
        } else {
          output.write_view(elapsed_hours.to_string())
        }
      ElapsedMinute(count) =>
        if count >= 2 {
          output.write_view(pad_left_int64(elapsed_minutes, 2))
        } else {
          output.write_view(elapsed_minutes.to_string())
        }
      ElapsedSecond(count) =>
        if count >= 2 {
          output.write_view(pad_left_int64(elapsed_seconds, 2))
        } else {
          output.write_view(elapsed_seconds.to_string())
        }
    }
  }
  output.to_string()
}

///|
fn month_short_name(month : Int) -> String {
  match month {
    1 => "Jan"
    2 => "Feb"
    3 => "Mar"
    4 => "Apr"
    5 => "May"
    6 => "Jun"
    7 => "Jul"
    8 => "Aug"
    9 => "Sep"
    10 => "Oct"
    11 => "Nov"
    12 => "Dec"
    _ => ""
  }
}

///|
fn month_long_name(month : Int) -> String {
  match month {
    1 => "January"
    2 => "February"
    3 => "March"
    4 => "April"
    5 => "May"
    6 => "June"
    7 => "July"
    8 => "August"
    9 => "September"
    10 => "October"
    11 => "November"
    12 => "December"
    _ => ""
  }
}

///|
fn month_initial_name(month : Int) -> String {
  let name = month_short_name(month)
  if name.length() == 0 {
    ""
  } else {
    name[:1].to_owned()
  }
}

///|
fn weekday_short_name(index : Int) -> String {
  match index {
    0 => "Sun"
    1 => "Mon"
    2 => "Tue"
    3 => "Wed"
    4 => "Thu"
    5 => "Fri"
    6 => "Sat"
    _ => ""
  }
}

///|
fn weekday_long_name(index : Int) -> String {
  match index {
    0 => "Sunday"
    1 => "Monday"
    2 => "Tuesday"
    3 => "Wednesday"
    4 => "Thursday"
    5 => "Friday"
    6 => "Saturday"
    _ => ""
  }
}

///|
fn hour12(hour : Int) -> Int {
  let h = hour % 12
  if h == 0 {
    12
  } else {
    h
  }
}