///|
fn parser_slice(
text : String,
first : Int32,
last : Int32,
) -> String raise DateError {
let chars = text.to_array()
if first < 0 || first > last || last > chars.length() {
raise DateError::InvalidDate(message="incomplete numeric date token")
}
String::from_array(chars[first:last])
}
///|
fn parser_token(
tokens : Array[String],
index : Int32,
) -> String raise DateError {
if index < 0 || index >= tokens.length() {
raise DateError::InvalidDate(message="incomplete date string")
}
tokens[index]
}
///|
fn parser_hms_index(tokens : Array[String], index : Int32) -> Int32? {
let count = tokens.length()
if index + 1 < count && parser_hms(tokens[index + 1]) is Some(_) {
Some(index + 1)
} else if index + 2 < count &&
tokens[index + 1] == " " &&
parser_hms(tokens[index + 2]) is Some(_) {
Some(index + 2)
} else if index > 0 && parser_hms(tokens[index - 1]) is Some(_) {
Some(index - 1)
} else if index > 1 &&
index == count - 1 &&
tokens[index - 1] == " " &&
parser_hms(tokens[index - 2]) is Some(_) {
Some(index - 2)
} else {
None
}
}
///|
fn ParserFields::assign_hms(
self : ParserFields,
text : String,
hms : Int32,
) -> Unit raise DateError {
let number = parser_number(text).unwrap_or_else(() => {
raise DateError::InvalidDate(message="invalid time token")
})
match hms {
0 => {
self.hour = Some(number.integer())
if number.fractional() {
self.minute = Some(parser_fraction_times60(number.fraction))
}
}
1 => {
let (minute, second) = parser_min_sec(number)
self.minute = Some(minute)
self.second = second
}
2 => {
let (second, microsecond) = parser_seconds(text)
self.second = Some(second)
self.microsecond = Some(microsecond)
}
_ => ()
}
}
///|
fn parser_numeric_token(
tokens : Array[String],
position : Int32,
ymd : ParserYmd,
fields : ParserFields,
) -> Int32 raise DateError {
let text = tokens[position]
let number = parser_number(text).unwrap_or_else(() => {
raise DateError::InvalidDate(message="invalid numeric date token")
})
let length = text.to_array().length()
let count = tokens.length()
let mut index = position
if ymd.values.length() == 3 &&
(length == 2 || length == 4) &&
fields.hour is None &&
(
index + 1 >= count ||
(tokens[index + 1] != ":" && parser_hms(tokens[index + 1]) is None)
) {
fields.hour = Some(parser_integer(parser_slice(text, 0, 2)))
if length == 4 {
fields.minute = Some(parser_integer(parser_slice(text, 2, 4)))
}
} else if length == 6 ||
(length > 6 && parser_slice(text, 0, 7).to_array()[6] == '.') {
if ymd.values.is_empty() && !text.contains(".") {
ymd.append_text(parser_slice(text, 0, 2))
ymd.append_text(parser_slice(text, 2, 4))
ymd.append_text(parser_slice(text, 4, length))
} else {
fields.hour = Some(parser_integer(parser_slice(text, 0, 2)))
fields.minute = Some(parser_integer(parser_slice(text, 2, 4)))
let (second, microsecond) = parser_seconds(parser_slice(text, 4, length))
fields.second = Some(second)
fields.microsecond = Some(microsecond)
}
} else if length == 8 || length == 12 || length == 14 {
ymd.append_text(parser_slice(text, 0, 4), label=Y)
ymd.append_text(parser_slice(text, 4, 6))
ymd.append_text(parser_slice(text, 6, 8))
if length > 8 {
fields.hour = Some(parser_integer(parser_slice(text, 8, 10)))
fields.minute = Some(parser_integer(parser_slice(text, 10, 12)))
if length > 12 {
fields.second = Some(parser_integer(parser_slice(text, 12, length)))
}
}
} else if parser_hms_index(tokens, index) is Some(hms_index) {
let hms = parser_hms(tokens[hms_index]).unwrap() +
(if hms_index < index { 1 } else { 0 })
if hms_index > index {
index = hms_index
}
fields.assign_hms(text, hms)
} else if index + 2 < count && tokens[index + 1] == ":" {
fields.hour = Some(number.integer())
let minute = parser_number(tokens[index + 2]).unwrap_or_else(() => {
raise DateError::InvalidDate(message="invalid minute token")
})
let (minute, second) = parser_min_sec(minute)
fields.minute = Some(minute)
fields.second = second
if index + 4 < count && tokens[index + 3] == ":" {
let (second, microsecond) = parser_seconds(tokens[index + 4])
fields.second = Some(second)
fields.microsecond = Some(microsecond)
index += 2
}
index += 2
} else if index + 1 < count && ["-", "/", "."].contains(tokens[index + 1]) {
let separator = tokens[index + 1]
ymd.append_text(text)
if index + 2 < count && !parser_jump(tokens[index + 2]) {
let next = tokens[index + 2]
if !next.to_array().is_empty() &&
next.to_array().iter().all(parser_is_digit) {
ymd.append_text(next)
} else if parser_month(next) is Some(month) {
ymd.append_number(month, label=M)
} else {
raise DateError::InvalidDate(message="invalid month token")
}
if index + 3 < count && tokens[index + 3] == separator {
let last = parser_token(tokens, index + 4)
if parser_month(last) is Some(month) {
ymd.append_number(month, label=M)
} else {
ymd.append_text(last)
}
index += 2
}
index += 1
}
index += 1
} else if index + 1 >= count || parser_jump(tokens[index + 1]) {
if index + 2 < count && parser_ampm(tokens[index + 2]) is Some(ampm) {
fields.hour = Some(parser_adjust_ampm(number.integer(), ampm))
index += 1
} else {
ymd.append_numeric(number)
}
index += 1
} else if parser_ampm(tokens[index + 1]) is Some(ampm) &&
number.integer() < 24 {
fields.hour = Some(parser_adjust_ampm(number.integer(), ampm))
index += 1
} else if ymd.could_be_numeric_day(number) {
ymd.append_numeric(number)
} else {
raise DateError::InvalidDate(message="unrecognized numeric date token")
}
index
}