diff --git a/src/csl/mod.rs b/src/csl/mod.rs index 4a3297c..0263ad8 100644 --- a/src/csl/mod.rs +++ b/src/csl/mod.rs @@ -638,19 +638,9 @@ fn date_replacement( ElemChildren(vec![ElemChild::Text(Formatted { text: if let Some(date) = date { - format!( - "{}{}", - if date.year > 0 { date.year } else { date.year.abs() + 1 }, - if date.year < 1000 { - if date.year < 0 { - "BC" - } else { - "AD" - } - } else { - "" - } - ) + let mut s = String::with_capacity(4); + write_year(date.year, false, &mut s).unwrap(); + s } else if let Some(no_date) = ctx .ctx(entry, cite_props.clone(), locale, term_locale, false) .term(Term::Other(OtherTerm::NoDate), TermForm::default(), false) @@ -663,6 +653,33 @@ fn date_replacement( })]) } +pub fn write_year( + year: i32, + short: bool, + w: &mut W, +) -> std::fmt::Result { + if short && year >= 1000 { + return write!(w, "{:02}", year % 100); + } + + write!( + w, + "{}{}", + if year > 0 { year } else { year.abs() + 1 }, + if year < 1000 { + if year <= 0 { + "BC" + } else { + // AD is used as a postfix, see + // https://docs.citationstyles.org/en/stable/specification.html?#ad-and-bc + "AD" + } + } else { + "" + } + ) +} + fn last_purpose_render( ctx: &StyleContext<'_>, item: &SpeculativeItemRender, @@ -2923,6 +2940,24 @@ mod tests { } } + #[test] + fn low_year_test() { + let yield_year = |year, short| { + let mut s = String::new(); + write_year(year, short, &mut s).unwrap(); + s + }; + + assert_eq!(yield_year(2021, false), "2021"); + assert_eq!(yield_year(2021, true), "21"); + assert_eq!(yield_year(0, false), "1BC"); + assert_eq!(yield_year(-1, false), "2BC"); + assert_eq!(yield_year(1, false), "1AD"); + assert_eq!(yield_year(0, true), "1BC"); + assert_eq!(yield_year(-1, true), "2BC"); + assert_eq!(yield_year(1, true), "1AD"); + } + #[test] #[cfg(feature = "archive")] fn test_alphanumeric_disambiguation() { diff --git a/src/csl/rendering/mod.rs b/src/csl/rendering/mod.rs index 867f569..8b982bc 100644 --- a/src/csl/rendering/mod.rs +++ b/src/csl/rendering/mod.rs @@ -20,7 +20,7 @@ use crate::types::{ChunkedString, Date, MaybeTyped, Numeric}; use crate::PageRanges; use super::taxonomy::EntryLike; -use super::{Context, ElemMeta, IbidState, SpecialForm, UsageInfo}; +use super::{write_year, Context, ElemMeta, IbidState, SpecialForm, UsageInfo}; pub mod names; @@ -778,17 +778,8 @@ fn render_date_part( write!(ctx, "{}", val).unwrap(); } } - DateStrongAnyForm::Year(LongShortForm::Short) => { - write!(ctx, "{:02}", (val % 100).abs()).unwrap(); - } - DateStrongAnyForm::Year(LongShortForm::Long) => { - write!(ctx, "{}", val.abs()).unwrap(); - } - } - - if let DateStrongAnyForm::Year(_) = form { - if date.year < 1000 { - ctx.push_str(if date.year < 0 { "BC" } else { "AD" }); + DateStrongAnyForm::Year(brevity) => { + write_year(val, brevity == LongShortForm::Short, ctx).unwrap(); } } }