use chrono::prelude::*;
use kana::*;
use regex::Regex;
const START_YEAR_OF_MEIJI: i32 = 1868;
const START_YEAR_OF_TAISHO: i32 = 1912;
const START_YEAR_OF_SHOWA: i32 = 1926;
const START_YEAR_OF_HEISEI: i32 = 1989;
const START_YEAR_OF_REIWA: i32 = 2019;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Date {
pub year: i32,
pub month: u32,
pub day: u32,
}
impl Date {
pub fn new(year: i32, month: u32, day: u32) -> Self {
Self { year, month, day }
}
pub fn year(&self) -> i32 {
self.year
}
pub fn month(&self) -> u32 {
self.month
}
pub fn day(&self) -> u32 {
self.day
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Gengo {
Meiji,
Taisho,
Showa,
Heisei,
Reiwa,
}
impl Gengo {
pub const fn first_year(&self) -> i32 {
match *self {
Gengo::Meiji => START_YEAR_OF_MEIJI,
Gengo::Taisho => START_YEAR_OF_TAISHO,
Gengo::Showa => START_YEAR_OF_SHOWA,
Gengo::Heisei => START_YEAR_OF_HEISEI,
Gengo::Reiwa => START_YEAR_OF_REIWA,
}
}
pub const fn name(&self) -> &'static str {
match *self {
Gengo::Meiji => "Meiji",
Gengo::Taisho => "Taisho",
Gengo::Showa => "Showa",
Gengo::Heisei => "Heisei",
Gengo::Reiwa => "Reiwa",
}
}
}
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord)]
pub enum DateType {
JisX0301Basic,
JisX0301Extended,
JisX0301ExtendedWithKanji,
SeparatedWithKanji,
}
pub fn to_half_width(input: &str) -> String {
wide2ascii(input)
}
pub fn find_type(wareki: &str) -> Result<Option<DateType>, regex::Error> {
let wareki_half = to_half_width(wareki);
let elm: Vec<&str> = wareki_half.split('.').collect();
let re_begin_with_digit = Regex::new(r"^\d")?;
let re_begin_with_char = Regex::new(r"^(M|T|S|H|R)")?;
let re_begin_with_kanji = Regex::new(r"^(明|大|昭|平|令)")?;
let re_separated_with_kanji = Regex::new(r"^(明治|大正|昭和|平成|令和)\d+年\d+月\d+日")?;
if elm.len() == 1 {
assert!(re_separated_with_kanji.is_match(elm.get(0).unwrap()));
return Ok(Some(DateType::SeparatedWithKanji));
}
assert_eq!(elm.len(), 3);
let date_type = match elm.get(0) {
Some(x) if re_begin_with_digit.is_match(x) => Some(DateType::JisX0301Basic),
Some(x) if re_begin_with_char.is_match(x) => Some(DateType::JisX0301Extended),
Some(x) if re_begin_with_kanji.is_match(x) => Some(DateType::JisX0301ExtendedWithKanji),
_ => None,
};
Ok(date_type)
}
pub fn gengo_resolve(wareki: &str) -> Option<Gengo> {
let meiji = vec!['M', '明'];
let taisho = vec!['T', '大'];
let showa = vec!['S', '昭'];
let heisei = vec!['H', '平'];
#[allow(unused_variables)]
let reiwa = vec!['R', '令'];
let wareki_half = to_half_width(wareki);
let first_char = wareki_half.chars().nth(0);
let gengo = match first_char {
Some(x) if meiji.contains(&x) => Some(Gengo::Meiji),
Some(x) if taisho.contains(&x) => Some(Gengo::Taisho),
Some(x) if showa.contains(&x) => Some(Gengo::Showa),
Some(x) if heisei.contains(&x) => Some(Gengo::Heisei),
_ => Some(Gengo::Reiwa),
};
gengo
}
pub fn convert(wareki: &str) -> Result<Option<DateTime<Utc>>, regex::Error> {
let mut wareki_half = to_half_width(wareki);
wareki_half = wareki_half.replace("元", "1");
let date_type = match find_type(&wareki_half) {
Ok(x) => x,
Err(e) => return Err(e),
};
let gengo = gengo_resolve(&wareki_half);
let ymd_elements: Vec<u32>;
match date_type {
Some(DateType::SeparatedWithKanji) => {
let tmp: String = wareki_half
.chars()
.skip(2)
.filter(|x| x != &'日')
.map(|x| if x.is_ascii_digit() { x } else { '.' })
.collect();
ymd_elements = tmp
.split('.')
.into_iter()
.map(|x| x.parse().unwrap())
.collect();
assert_eq!(ymd_elements.len(), 3);
}
Some(DateType::JisX0301Basic) => {
ymd_elements = wareki_half
.split('.')
.into_iter()
.map(|x| x.parse().unwrap())
.collect();
assert_eq!(ymd_elements.len(), 3);
}
Some(DateType::JisX0301Extended) | Some(DateType::JisX0301ExtendedWithKanji) => {
ymd_elements = wareki_half
.chars()
.skip(1)
.collect::<String>()
.split('.')
.into_iter()
.map(|x| x.parse().unwrap())
.collect();
assert_eq!(ymd_elements.len(), 3);
}
None => return Ok(None),
}
let year = match gengo {
Some(Gengo::Meiji) => {
ymd_elements.get(0).unwrap().clone() as i32 + Gengo::first_year(&Gengo::Meiji) - 1
}
Some(Gengo::Taisho) => {
ymd_elements.get(0).unwrap().clone() as i32 + Gengo::first_year(&Gengo::Taisho) - 1
}
Some(Gengo::Showa) => {
ymd_elements.get(0).unwrap().clone() as i32 + Gengo::first_year(&Gengo::Showa) - 1
}
Some(Gengo::Heisei) => {
ymd_elements.get(0).unwrap().clone() as i32 + Gengo::first_year(&Gengo::Heisei) - 1
}
Some(Gengo::Reiwa) => {
ymd_elements.get(0).unwrap().clone() as i32 + Gengo::first_year(&Gengo::Reiwa) - 1
}
None => return Ok(None),
};
let date = Date::new(
year,
*ymd_elements.get(1).unwrap(),
*ymd_elements.get(2).unwrap(),
);
let date_time: DateTime<Utc> = Utc
.with_ymd_and_hms(date.year(), date.month(), date.day(), 00, 00, 00)
.unwrap();
Ok(Some(date_time))
}