2

For consistency's sake I need to abbreviate all number-ranges in my document.

While biblatex can easily be made to abbreviate page ranges the equivalent command for the year ranges breaks compilation with Argument of \@secondoftwo has an extra }

\documentclass{article}
\usepackage{filecontents}

\begin{filecontents*}{mylibrary.bib}
@book{somebook1769,
    Address = {Place},
    Author = {Name, Given},
    Title = {Some title of this publication},
    Date = {1769/1783}}
}
\end{filecontents*}

\usepackage[style=authoryear,backend=biber]{biblatex}
\usepackage{csquotes}
\addbibresource{mylibrary.bib}

\DeclareFieldFormat{postnote}{\mkcomprange[{\mkpageprefix[pagination]}]{#1}}
\DeclareFieldFormat{pages}{\mkcomprange{#1}}

%%doesn't work:
%\DeclareFieldFormat{date}{\mkcomprange{#1}}


%%works (just for comparison)
%\DeclareFieldFormat{date}{\textbf{#1}}


\begin{document}
“some quote” from \cite[1348--1368]{somebook1769}

\printbibliography
\end{document}

(I tried to use

    Year = {1769--83}}

which looks right but biblatex warns

   WARN - year field '1769--83' in entry 'somebook1769' is not an integer - this will probably not sort properly.

and indeed the order of publications in the bibliography for several works by the same author is not correct.)

1

Most of the functions are already there, so we only need to piece them together. Unfortunately, the date handling code in biblatex is a bit complex due to the many EDTF/ISO 8601 features that are supported.

The American date format used by the default language english needs special handling. So the number of lines needed to get this done essentially double.

\documentclass[american]{article}
\usepackage{babel}
\usepackage{csquotes}
\usepackage[style=authoryear, backend=biber]{biblatex}

\DeclareFieldFormat{postnote}{\mkcomprange[{\mkpageprefix[pagination]}]{#1}}
\DeclareFieldFormat{pages}{\mkcomprange{#1}}

\makeatletter
\newcommand*{\ifcompyear}[1]{%
  \ifboolexpr{        test {\iffieldundef{#1month}}
              and     test {\iffieldundef{#1endmonth}}
              and not test {\iffieldundef{#1endyear}}
              and not test {\iffieldequalstr{#1endyear}{}}
              and     test {\iffieldsequal{#1dateera}{#1enddateera}}
              and test     {\iffieldint{#1year}}
              and test     {\iffieldint{#1endyear}}}}

\newrobustcmd*{\compyear}[1]{%
  \begingroup
  \edef\blx@compyear@tempa{\thefield{#1year}-\thefield{#1endyear}}%
  \expandafter\mkcomprange\expandafter{\blx@compyear@tempa}%
  \endgroup}

\renewrobustcmd*{\mkdaterangetrunc}[2]{%
  \begingroup
    \blx@metadateinfo{#2}%
    \iffieldundef{#2year}
      {\blx@nounit}
      {\printtext[#2date]{%
         \datecircaprint
         % Such a season component can only come from an ISO8601 season which replaces
         % a normal month so if it exists, we know that a normal date print is ruled out
         \iffieldundef{#2season}
           {\ifdateyearsequal{#2}{#2end}
              {\iffieldsequal{#2month}{#2endmonth}
                 {\csuse{mkbibdate#1}{}{}{#2day}}
                 {\csuse{mkbibdate#1}{}{#2month}{#2day}}}
              {\ifcompyear{#2}
                 {}
                 {\csuse{mkbibdate#1}{#2year}{#2month}{#2day}%
                  \iffieldsequal{#2dateera}{#2enddateera}{}
                    {\dateeraprint{#2year}}}}}
           {\ifdateyearsequal{#2}{#2end}
             {\csuse{mkbibseasondate#1}{}{#2season}}
             {\csuse{mkbibseasondate#1}{#2year}{#2season}%
              \iffieldsequal{#2dateera}{#2enddateera}{}
                {\dateeraprint{#2year}}}}%
         \dateuncertainprint
         \iffieldundef{#2endyear}
           {}
           {\iffieldequalstr{#2endyear}{}
              {\mbox{\bibdaterangesep}}
              {\ifcompyear{#2}
                 {\dateeraprintpre{#2endyear}%
                  \compyear{#2}%
                  \dateeraprint{#2endyear}}
                 {\bibdaterangesep
                  \enddatecircaprint
                  \iffieldundef{#2season}
                    {\csuse{mkbibdate#1}{#2endyear}{#2endmonth}{#2endday}}
                    {\csuse{mkbibseasondate#1}{#2endyear}{#2endseason}}%
                  \enddateuncertainprint
                  \dateeraprint{#2endyear}}}}}}%
  \endgroup}

\renewrobustcmd*{\mkdaterangetruncextra}[2]{%
  \begingroup
    \blx@metadateinfo{#2}%
    \iffieldundef{#2year}
      {\blx@nounit}
      {\printtext[#2date]{%
         \datecircaprint
         % Such a season component can only come from an ISO8601 season which replaces
         % a normal month so if it exists, we know that a normal date print is ruled out
         \iffieldundef{#2season}
           {\ifdateyearsequal{#2}{#2end}
              {\iffieldsequal{#2month}{#2endmonth}
                 {\csuse{mkbibdate#1}{}{}{#2day}}
                 {\csuse{mkbibdate#1}{}{#2month}{#2day}}}
              {\ifcompyear{#2}
                 {}
                 {\csuse{mkbibdate#1}{#2year}{#2month}{#2day}%
                  \iffieldsequal{#2dateera}{#2enddateera}{}
                    {\dateeraprint{#2year}}}}}
           {\ifdateyearsequal{#2}{#2end}
             {\csuse{mkbibseasondate#1}{}{#2season}}
             {\csuse{mkbibseasondate#1}{#2year}{#2season}%
              \iffieldsequal{#2dateera}{#2enddateera}{}
                {\dateeraprint{#2year}}}}%
         \dateuncertainprint
         \iffieldundef{#2endyear}
           {\printfield{extradate}}
           {\iffieldequalstr{#2endyear}{}
              {\printfield{extradate}%
               \printtext{\mbox{\bibdaterangesep}}}
              {\ifcompyear{#2}
                 {\dateeraprintpre{#2endyear}%
                  \compyear{#2}%
                  \printfield{extradate}%
                  \dateeraprint{#2endyear}}
                 {\printtext{%
                    \bibdaterangesep
                    \enddatecircaprint
                    \iffieldundef{#2season}
                      {\csuse{mkbibdate#1}{#2endyear}{#2endmonth}{#2endday}}
                      {\csuse{mkbibseasondate#1}{#2endyear}{#2endseason}}}%
                    \printfield{extradate}%
                    \enddateuncertainprint
                    \dateeraprint{#2endyear}}}}}}%
  \endgroup}

\newrobustcmd*{\mkdaterangecompyear}[1]{%
  \blx@imc@clearfield{#1season}%
  \blx@imc@clearfield{#1month}%
  \blx@imc@clearfield{#1day}%
  \blx@imc@clearfield{#1hour}%
  \blx@imc@clearfield{#1minute}%
  \blx@imc@clearfield{#1second}%
  \blx@imc@clearfield{#1timezone}%
  \blx@imc@clearfield{#1endseason}%
  \blx@imc@clearfield{#1endmonth}%
  \blx@imc@clearfield{#1endday}%
  \blx@imc@clearfield{#1endhour}%
  \blx@imc@clearfield{#1endminute}%
  \blx@imc@clearfield{#1endsecond}%
  \blx@imc@clearfield{#1endtimezone}%
  \ifdateyearsequal{#1}{#1end}
    {\blx@imc@clearfield{#1endyear}}
    {}%
  \mkdaterangetrunc{short}{#1}}

\newrobustcmd*{\mkdaterangecompyearextra}[1]{%
  \blx@imc@clearfield{#1season}%
  \blx@imc@clearfield{#1month}%
  \blx@imc@clearfield{#1day}%
  \blx@imc@clearfield{#1hour}%
  \blx@imc@clearfield{#1minute}%
  \blx@imc@clearfield{#1second}%
  \blx@imc@clearfield{#1timezone}%
  \blx@imc@clearfield{#1endseason}%
  \blx@imc@clearfield{#1endmonth}%
  \blx@imc@clearfield{#1endday}%
  \blx@imc@clearfield{#1endhour}%
  \blx@imc@clearfield{#1endminute}%
  \blx@imc@clearfield{#1endsecond}%
  \blx@imc@clearfield{#1endtimezone}%
  \ifdateyearsequal{#1}{#1end}
    {\blx@imc@clearfield{#1endyear}}
    {}%
  \mkdaterangetruncextra{short}{#1}}

\DefineBibliographyExtras{english}{%
\protected\gdef\lbx@us@mkdaterangetrunc@long#1#2{%
  \begingroup
    \blx@metadateinfo{#2}%
    \iffieldundef{#2year}
      {\blx@nounit}
      {\printtext[#2date]{%
         \datecircaprint
         \iffieldundef{#2season}
           {\ifdateyearsequal{#2}{#2end}
             {\csuse{mkbibdate#1}{}{#2month}{#2day}}
             {\ifcompyear{#2}
                {}
                {\csuse{mkbibdate#1}{#2year}{#2month}{#2day}%
                 \dateeraprint{#2year}}}}
           {\ifdateyearsequal{#2}{#2end}
             {\csuse{mkbibseasondate#1}{}{#2season}}
             {\csuse{mkbibseasondate#1}{#2year}{#2season}%
              \dateeraprint{#2year}}}%
         \dateuncertainprint
         \iffieldundef{#2endyear}
           {}
           {\iffieldequalstr{#2endyear}{}
             {\mbox{\bibdaterangesep}}
             {\ifcompyear{#2}
                {\dateeraprintpre{#2endyear}%
                 \compyear{#2}%
                 \dateeraprint{#2endyear}}
                {\bibdaterangesep
                 \enddatecircaprint
                 \iffieldundef{#2season}
                   {\ifdateyearsequal{#2}{#2end}
                     {\iffieldsequal{#2month}{#2endmonth}
                       {\csuse{mkbibdate#1}{#2endyear}{}{#2endday}}
                       {\csuse{mkbibdate#1}{#2endyear}{#2endmonth}{#2endday}}}
                     {\csuse{mkbibdate#1}{#2endyear}{#2endmonth}{#2endday}}}
                   {\csuse{mkbibseasondate#1}{#2endyear}{#2endseason}}%
                 \enddateuncertainprint
                 \dateeraprint{#2endyear}}}}}}%
  \endgroup}%
\protected\gdef\lbx@us@mkdaterangetrunc@short#1#2{%
  \begingroup
    \blx@metadateinfo{#2}%
    \iffieldundef{#2year}
      {\blx@nounit}
      {\printtext[#2date]{%
         \datecircaprint
         \iffieldundef{#2season}
           {\ifdateyearsequal{#2}{#2end}
             {\csuse{mkbibdate#1}{}{#2month}{#2day}}
             {\ifcompyear{#2}
                {}
                {\csuse{mkbibdate#1}{#2year}{#2month}{#2day}%
                 \dateeraprint{#2year}}}}
           {\ifdateyearsequal{#2}{#2end}
             {\csuse{mkbibseasondate#1}{}{#2season}}
             {\csuse{mkbibseasondate#1}{#2year}{#2season}%
              \dateeraprint{#2year}}}%
         \dateuncertainprint
         \iffieldundef{#2endyear}
           {}
           {\iffieldequalstr{#2endyear}{}
              {\mbox{\bibdaterangesep}}
              {\ifcompyear{#2}
                {\dateeraprintpre{#2endyear}%
                 \compyear{#2}%
                 \dateeraprint{#2endyear}}
                {\bibdaterangesep
                  \enddatecircaprint
                  \iffieldundef{#2season}
                    {\csuse{mkbibdate#1}{#2endyear}{#2endmonth}{#2endday}}
                    {\csuse{mkbibseasondate#1}{#2endyear}{#2endseason}}%
                  \enddateuncertainprint
                  \dateeraprint{#2endyear}}}}}}%
  \endngroup}%
\protected\gdef\lbx@us@mkdaterangetruncextra@long#1#2{%
  \begingroup
    \blx@metadateinfo{#2}%
    \iffieldundef{#2year}
      {\blx@nounit}
      {\printtext[#2date]{%
         \datecircaprint
         \iffieldundef{#2season}
           {\ifdateyearsequal{#2}{#2end}
             {\csuse{mkbibdate#1}{}{#2month}{#2day}}
             {\ifcompyear{#2}
                {}
                {\csuse{mkbibdate#1}{#2year}{#2month}{#2day}%
                 \dateeraprint{#2year}}}}
           {\ifdateyearsequal{#2}{#2end}
             {\csuse{mkbibseasondate#1}{}{#2season}}
             {\csuse{mkbibseasondate#1}{#2year}{#2season}%
              \dateeraprint{#2year}}}%
         \dateuncertainprint
         \iffieldundef{#2endyear}
           {\printfield{extradate}}
           {\iffieldequalstr{#2endyear}{}
              {\printfield{extradate}%
               \mbox{\bibdaterangesep}}
              {\ifcompyear{#2}
                {\dateeraprintpre{#2endyear}%
                 \compyear{#2}%
                 \printfield{extradate}%
                 \dateeraprint{#2endyear}}
                {\bibdaterangesep
                  \enddatecircaprint
                  \iffieldundef{#2season}
                    {\ifdateyearsequal{#2}{#2end}
                      {\iffieldsequal{#2month}{#2endmonth}
                         {\csuse{mkbibdate#1}{#2endyear}{}{#2endday}}
                         {\csuse{mkbibdate#1}{#2endyear}{#2endmonth}{#2endday}}}
                      {\csuse{mkbibdate#1}{#2endyear}{#2endmonth}{#2endday}}}
                    {\csuse{mkbibseasondate#1}{#2endyear}{#2endseason}}%
                  \printfield{extradate}%
                  \enddateuncertainprint
                  \dateeraprint{#2endyear}}}}}}%
  \endgroup}%
\protected\gdef\lbx@us@mkdaterangetruncextra@short#1#2{%
  \begingroup
    \blx@metadateinfo{#2}%
    \iffieldundef{#2year}
      {\blx@nounit}
      {\printtext[#2date]{%
         \datecircaprint
         \iffieldundef{#2season}
           {\ifdateyearsequal{#2}{#2end}
             {\csuse{mkbibdate#1}{}{#2month}{#2day}}
             {\ifcompyear{#2}
                {}
                {\csuse{mkbibdate#1}{#2year}{#2month}{#2day}%
                 \dateeraprint{#2year}}}}
           {\ifdateyearsequal{#2}{#2end}
             {\csuse{mkbibseasondate#1}{}{#2season}}
             {\csuse{mkbibseasondate#1}{#2year}{#2season}%
              \dateeraprint{#2year}}}%
         \dateuncertainprint
         \iffieldundef{#2endyear}
           {\printfield{extradate}}
           {\iffieldequalstr{#2endyear}{}
              {\printfield{extradate}%
            \mbox{\bibdaterangesep}}
           {\ifcompyear{#2}
                {\dateeraprintpre{#2endyear}%
                 \compyear{#2}%
                 \printfield{extradate}%
                 \dateeraprint{#2endyear}}
                {\bibdaterangesep
               \enddatecircaprint
               \iffieldundef{#2season}
                 {\csuse{mkbibdate#1}{#2endyear}{#2endmonth}{#2endday}}
                 {\csuse{mkbibseasondate#1}{#2endyear}{#2endseason}}%
               \printfield{extradate}%
               \enddateuncertainprint
               \dateeraprint{#2endyear}}}}}}%
  \endgroup}%
}
\makeatother

\ExecuteBibliographyOptions{labeldate=compyear}


\usepackage{filecontents}
\begin{filecontents*}{\jobname.bib}
@book{somebook1769,
  address = {Place},
  author  = {Name, Given},
  title   = {Some title of this publication},
  date    = {1769/1783},
}
\end{filecontents*}
\addbibresource{\jobname.bib}

\begin{document}
\enquote{some quote} from \cite[1348--1368]{somebook1769}

\printbibliography
\end{document}

"1769–83"

  • This is just stellar! Your huge effort is greatly appreciated! I hope this can be included into biblatex at some point. – Florian May 26 '19 at 20:04
  • My MWE being minimal I didn't mention that I am using polyglossia and xelatex. There seems to be a direct conflict between polyglossia's english and your solution: It compiles all right but adds a space (quad?) before "Name". (There doesn't seem to be a problem with other languages.) If it is difficult to fix or you don't have the time right now, I should be able to switch to babel, though. – Florian May 26 '19 at 20:14
  • 1
    @Florian Oh, sorry I forgot a few %. Should be fixed in the edit just now. The issue can occur with both polyglossia and babel, but due to some deficiencies with biblatex's polyglossia module (see tex.stackexchange.com/q/464281/35864) you may only have noticed it with polyglossia. Generally I would advise to use babel if at all possible and to only use polyglossia if it gives better results. polyglossia development has effectively stalled and many posts suggesting polyglossia are outdated. See also tex.stackexchange.com/q/482396/35864. – moewe May 26 '19 at 20:25
  • Again, thanks a lot! It seems to work fine now! (I am gradually abandoning polyglossia but I have still quite some long-term projects using older code. Obviously it's about time to make an effort to cut the ties...) – Florian May 26 '19 at 20:56
  • sorry to bother again … leading zeros in the second part of the range are suppressed (I haven't found it in the documentation). Even though I know some styles enforce it, I am generally not too happy about this. But for years it seems really odd to me: "2000--7". Is there a switch for the gobbling of leading zeros? – Florian May 27 '19 at 12:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.