jbe/bsw@0: local function map_2digit_year(y2) jbe/bsw@0: local current_year = atom.date:get_current().year jbe/bsw@0: local guess2 = math.floor(current_year / 100) * 100 + tonumber(y2) jbe/bsw@0: local guess1 = guess2 - 100 jbe/bsw@0: local guess3 = guess2 + 100 jbe/bsw@0: if guess1 >= current_year - 80 and guess1 <= current_year + 10 then jbe/bsw@0: return guess1 jbe/bsw@0: elseif guess2 >= current_year - 80 and guess2 <= current_year + 10 then jbe/bsw@0: return guess2 jbe/bsw@0: elseif guess3 >= current_year - 80 and guess3 <= current_year + 10 then jbe/bsw@0: return guess3 jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: jbe/bsw@0: function parse.date(str, dest_type, options) jbe/bsw@0: if dest_type ~= atom.date then jbe/bsw@0: error("parse.date(...) can only return dates, but a different destination type than atom.date was given.") jbe/bsw@0: end jbe/bsw@0: local date_format = locale.get("date_format") jbe/bsw@0: if date_format and string.find(date_format, "Y+%-D+%-M+") then jbe/bsw@0: error("Date format collision with ISO standard.") jbe/bsw@0: end jbe/bsw@0: if string.match(str, "^%s*$") then jbe/bsw@0: return nil jbe/bsw@0: end jbe/bsw@0: -- first try ISO format jbe/bsw@0: local year, month, day = string.match( jbe/bsw@0: str, "^%s*([0-9][0-9][0-9][0-9])%-([0-9][0-9])%-([0-9][0-9])%s*$" jbe/bsw@0: ) jbe/bsw@0: if year then jbe/bsw@0: return atom.date{ jbe/bsw@0: year = tonumber(year), jbe/bsw@0: month = tonumber(month), jbe/bsw@0: day = tonumber(day) jbe/bsw@0: } jbe/bsw@0: end jbe/bsw@0: if not date_format then jbe/bsw@0: return atom.date.invalid jbe/bsw@0: end jbe/bsw@0: local format_parts = {} jbe/bsw@0: local numeric_parts = {} jbe/bsw@0: for part in string.gmatch(date_format, "[YMD]+") do jbe/bsw@0: format_parts[#format_parts+1] = part jbe/bsw@0: end jbe/bsw@0: for part in string.gmatch(str, "[0-9]+") do jbe/bsw@0: numeric_parts[#numeric_parts+1] = part jbe/bsw@0: end jbe/bsw@0: if #format_parts ~= #numeric_parts then jbe/bsw@0: return atom.date.invalid jbe/bsw@0: end jbe/bsw@0: local year, month, day jbe/bsw@0: local function process_part(format_part, numeric_part) jbe/bsw@0: if string.find(format_part, "^Y+$") then jbe/bsw@0: if #numeric_part == 4 then jbe/bsw@0: year = tonumber(numeric_part) jbe/bsw@0: elseif #numeric_part == 2 then jbe/bsw@0: year = map_2digit_year(numeric_part) jbe/bsw@0: else jbe/bsw@0: return atom.date.invalid jbe/bsw@0: end jbe/bsw@0: elseif string.find(format_part, "^M+$") then jbe/bsw@0: month = tonumber(numeric_part) jbe/bsw@0: elseif string.find(format_part, "^D+$") then jbe/bsw@0: day = tonumber(numeric_part) jbe/bsw@0: else jbe/bsw@0: if not #format_part == #numeric_part then jbe/bsw@0: return atom.date.invalid jbe/bsw@0: end jbe/bsw@0: local year_str = "" jbe/bsw@0: local month_str = "" jbe/bsw@0: local day_str = "" jbe/bsw@0: for i = 1, #format_part do jbe/bsw@0: local format_char = string.sub(format_part, i, i) jbe/bsw@0: local number_char = string.sub(numeric_part, i, i) jbe/bsw@0: if format_char == "Y" then jbe/bsw@0: year_str = year_str .. number_char jbe/bsw@0: elseif format_char == "M" then jbe/bsw@0: month_str = month_str .. number_char jbe/bsw@0: elseif format_char == "D" then jbe/bsw@0: day_str = day_str .. number_char jbe/bsw@0: else jbe/bsw@0: error("Assertion failed.") jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: if #year_str == 2 then jbe/bsw@0: year = map_2digit_year(year_str) jbe/bsw@0: else jbe/bsw@0: year = tonumber(year_str) jbe/bsw@0: end jbe/bsw@0: month = tonumber(month_str) jbe/bsw@0: day = tonumber(day_str) jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: for i = 1, #format_parts do jbe/bsw@0: process_part(format_parts[i], numeric_parts[i]) jbe/bsw@0: end jbe/bsw@0: if not year or not month or not day then jbe/bsw@0: error("Date parser did not determine year, month and day. Maybe the 'date_format' locale is erroneous?") jbe/bsw@0: end jbe/bsw@0: return atom.date{ year = year, month = month, day = day } jbe/bsw@0: end