%\iffalse % datatool.dtx generated using makedtx version 1.2 (c) Nicola Talbot % Command line args: % -codetitle "" % -macrocode "databib\.bst" % -author "Nicola Talbot" % -src "datatool-base.sty\Z=>datatool-base.sty" % -src "datatool-undetermined.ldf\Z=>datatool-undetermined.ldf" % -src "datatool-(utf8|latin1).ldf\Z=>datatool-\1.ldf" % -src "datatool-l3fp.def\Z=>datatool-l3fp.def" % -src "datatool-lua.def\Z=>datatool-lua.def" % -src "datatool-fp.def\Z=>datatool-fp.def" % -src "datatool-fp.sty\Z=>datatool-fp.sty" % -src "datatool-pgfmath.def\Z=>datatool-pgfmath.def" % -src "datatool-pgfmath.sty\Z=>datatool-pgfmath.sty" % -src "datatool.sty\Z=>datatool.sty" % -src "datagidx.sty\Z=>datagidx.sty" % -src "databib.sty\Z=>databib.sty" % -src "databar.sty\Z=>databar.sty" % -src "datapie.sty\Z=>datapie.sty" % -src "dataplot.sty\Z=>dataplot.sty" % -src "person.sty\Z=>person.sty" % -src "databib.bst\Z=>databib.bst" % -src "(.*-2019-09-27.sty\Z)=>\1" % -doc "datatool-manual.tex" % -setambles "databib\.bst=>\nopreamble\nopostamble" % -comment "databib\.bst" % datatool % Created on 2025/3/25 11:38 %\fi %\iffalse %<*package> %% \CharacterTable %% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z %% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z %% Digits \0\1\2\3\4\5\6\7\8\9 %% Exclamation \! Double quote \" Hash (number) \# %% Dollar \$ Percent \% Ampersand \& %% Acute accent \' Left paren \( Right paren \) %% Asterisk \* Plus \+ Comma \, %% Minus \- Point \. Solidus \/ %% Colon \: Semicolon \; Less than \< %% Equals \= Greater than \> Question mark \? %% Commercial at \@ Left bracket \[ Backslash \\ %% Right bracket \] Circumflex \^ Underscore \_ %% Grave accent \` Left brace \{ Vertical bar \| %% Right brace \} Tilde \~} % %\fi % \iffalse % Doc-Source file to use with LaTeX2e % Copyright (C) 2025 Nicola Talbot, all rights reserved. % \fi % \iffalse %<*driver> \iffalse datatool-manual.tex is a stub file used by makedtx to create datatool.dtx \fi \documentclass{article} \usepackage{doc} \CheckSum{61922} \RecordChanges \PageIndex \SetupDoc{reportchangedates} \setcounter{IndexColumns}{2} \usepackage{fontspec} \usepackage{tcolorbox} \usepackage{datatool-base} \usepackage{hologo} \setmainfont{Liberation Serif} \setsansfont{Liberation Sans} \setmonofont{Liberation Mono} \newcommand*{\LuaLaTeX}{\hologo{LuaLaTeX}} \newcommand*{\XeLaTeX}{\hologo{XeLaTeX}} \newcommand*{\pdfLaTeX}{\hologo{pdfLaTeX}} \definecolor{defbackground}{rgb}{1,1,0.75} \newtcolorbox{definition}{halign=flush left, colframe=black,colback=defbackground, fontupper=\ttfamily, before upper={\frenchspacing\obeylines}, after={\par\noindent\ignorespacesafterend} } \newtcolorbox{important}[1][]{ coltitle=red,colbacktitle=red!20!white, colframe=red,colback=red!5!white,#1} \NewDocElement{Option}{option} \NewDocElement{Counter}{counter} \newcommand{\ics}[1]{\cs{#1}} \newcommand{\sty}[1]{\textsf{#1}} \newcommand{\isty}[1]{\textsf{#1}} \newcommand{\cls}[1]{\textsf{#1}} \newcommand{\app}[1]{\texttt{#1}} \newcommand{\ctr}[1]{\textsf{#1}} \newcommand{\env}[1]{\textsf{#1}} \newcommand{\ctrfmt}[1]{\textsf{#1}} \newcommand{\pkgoptfmt}[1]{\textsf{#1}} \newcommand{\pkgopt}[2][]{\pkgoptfmt{#2\ifstrempty{#1}{}{=#1}}} \newcommand{\filetype}[1]{\texttt{#1}} \newcommand{\term}[1]{\emph{#1}} \newcommand{\qt}[1]{``#1''} \providecommand\marg[1]{% \texorpdfstring{\allowbreak{\ttfamily\char`\{}\meta{#1}{\ttfamily\char`\}}} {\{#1\}}} \providecommand\oarg[1]{% \texorpdfstring{\allowbreak{\ttfamily[}\meta{#1}{\ttfamily]}} {[#1]}} \providecommand\parg[1]{\texorpdfstring{(\meta{#1})}{(#1)}} \begin{document} \DocInput{datatool.dtx} \end{document} % %\fi %\DeleteShortVerb{|} %\MakeShortVerb{"} % %\title{Documented Code for datatool v3.3} %\author{Nicola L. C. Talbot\\ %\url{http://www.dickimaw-books.com/}} % %\date{2025-03-25} %\maketitle % %\section{Introduction} % %This is the documented code for the \sty{datatool} bundle. See %\url{datatool-user.pdf} for the main user manual. % %\StopEventually{% % \clearpage % \PrintChanges % \clearpage % \PrintIndex %} % % % %\iffalse % \begin{macrocode} %<*datatool-base.sty> % \end{macrocode} %\fi %\section{datatool-base.sty} %\label{sec:code:datatool-base} % This package provides all the basic commands needed by various % packages in the \sty{datatool} bundle. %The \sty{datatool} package was first released in 2007. The %\LaTeX\ kernel has changed significantly since then. I've started %switching over to using \LaTeX3, but it's still in a hybrid state. %I have too many large packages and not enough time to do a %complete rewrite. % % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} % \end{macrocode} % Rollback releases: % \begin{macrocode} \DeclareRelease{v2.32}{2019-09-27}{datatool-base-2019-09-27.sty} \DeclareCurrentRelease{v3.3}{2025-03-25} % \end{macrocode} % Declare package: % \begin{macrocode} \ProvidesPackage{datatool-base}[2025/03/25 v3.3 (NLCT)] % \end{macrocode} % Required packages: % \begin{macrocode} \RequirePackage{etoolbox} \RequirePackage{amsmath} \RequirePackage{xfor} \RequirePackage{ifthen} % \end{macrocode} %\sty{tracklang} now loaded as from v3.0. Ideally it needs to be %v1.6.3+ but doesn't matter so much if no localisation support %requested. % \begin{macrocode} \RequirePackage{tracklang}[2022/12/13] % \end{macrocode} %Removed substr.sty. % %\subsection{Package Options} % %Version 3.0 has switched to \LaTeX3 key=value interface. % %\begin{macro}{\ifdtlverbose} %Switch to govern verbose mode. % \begin{macrocode} \newif\ifdtlverbose % \end{macrocode} %\end{macro} % %\begin{macro}{\if@dtl@utf8} %Switch to determine UTF-8 setting. Deprecated. % \begin{macrocode} \expandafter\newif\csname if@dtl@utf8\endcsname % \end{macrocode} %\end{macro} % %\begin{macro}{\TrackLangEncodingName} %Check for \cs{TrackLangEncodingName}. This is used to input the %appropriate encoding file, if available. If this command isn't %available then it's likely that an old version of \sty{tracklang} %is installed. % \begin{macrocode} \ExplSyntaxOn \cs_if_exist:NF \TrackLangEncodingName { \cs_if_exist:NTF \inputencodingname { \tl_set:NN \TrackLangEncodingName \inputencodingname } { \tl_set:Nn \TrackLangEncodingName { utf8 } } } % \end{macrocode} %\end{macro} %Are we using a Unicode engine? (Affects regular expression matches %with \c{x}\marg{hex} for sort handler functions.) % \begin{macrocode} \bool_lazy_any:nTF { { \sys_if_engine_luatex_p: } { \sys_if_engine_xetex_p: } { \sys_if_engine_uptex_p: } } { \cs_set_eq:NN \datatool_if_unicode_engine:TF \use_i:nn \cs_set_eq:NN \datatool_if_unicode_engine:T \use:n \cs_set_eq:NN \datatool_if_unicode_engine:F \use_none:n } { \cs_set_eq:NN \datatool_if_unicode_engine:TF \use_ii:nn \cs_set_eq:NN \datatool_if_unicode_engine:T \use_none:n \cs_set_eq:NN \datatool_if_unicode_engine:F \use:n } % \end{macrocode} % %\begin{macro}{\@dtl@mathprocessor} %As from v3.0, the default processor is lua if \cs{directlua} is %defined or l3fp otherwise. %\changes{3.0}{2025-03-03}{changed default processor to l3fp or lua} % \begin{macrocode} \ifdef\directlua {\providecommand*{\@dtl@mathprocessor}{lua}} {\providecommand*{\@dtl@mathprocessor}{l3fp}} % \end{macrocode} %\end{macro} % %Determine whether or not to load locale support. This command will %either expand to its argument or ignore it. % \begin{macrocode} \newcommand\datatool@load@locales[1]{#1} % \end{macrocode} %List of locales to track (in addition to any that might already be %tracked): % \begin{macrocode} \clist_new:N \l__datatool_extra_locales_clist % \end{macrocode} % %When to purify sort values: % \begin{macrocode} \bool_new:N \l__datatool_initial_purify_early_bool \bool_set_true:N \l__datatool_initial_purify_early_bool % \end{macrocode} % %List of types to reformat if auto-reformat settings are on: % \begin{macrocode} \seq_new:N \l__datatool_auto_reformat_types_seq \seq_set_from_clist:Nn \l__datatool_auto_reformat_types_seq { integer , decimal , si , currency , datetime , date , time } % \end{macrocode} % Append to sequence: % \begin{macrocode} \cs_new:Nn \__datatool_auto_reformat_types_do:n { \tl_if_eq:nnTF { #1 } { integer } { \seq_put_right:Nn \l__datatool_auto_reformat_types_seq { #1 } } { \tl_if_eq:nnTF { #1 } { decimal } { \seq_put_right:Nn \l__datatool_auto_reformat_types_seq { #1 } } { \tl_if_eq:nnTF { #1 } { si } { \seq_put_right:Nn \l__datatool_auto_reformat_types_seq { #1 } } { \tl_if_eq:nnTF { #1 } { datetime } { \seq_put_right:Nn \l__datatool_auto_reformat_types_seq { #1 } } { \tl_if_eq:nnTF { #1 } { date } { \seq_put_right:Nn \l__datatool_auto_reformat_types_seq { #1 } } { \tl_if_eq:nnTF { #1 } { time } { \seq_put_right:Nn \l__datatool_auto_reformat_types_seq { #1 } } { \PackageError { datatool-base } { Unsupported ~ auto-reformat ~ data ~ type ~ identifier ~ ` #1 ' } { Supported ~ identifiers: ~ `integer', ~ `decimal', ~ `si', ~ `datetime', ~ `date', ~ `time' } } } } } } } } % \end{macrocode} % % \begin{macrocode} \prg_new_conditional:Npnn \__datatool_if_auto_reformat_on:n #1 { T, F, TF } { \seq_if_in:NnTF \l__datatool_auto_reformat_types_seq { #1 } { \tl_if_eq:nnTF { #1 } { si } { \bool_if:NTF \l__datatool_reformat_numeric_bool { \prg_return_true: } { \prg_return_false: } } { \int_if_exist:cTF { c_datatool_ #1 _int } { \exp_args:Nc \datatool_if_temporal_datum_type:nTF { c_datatool_ #1 _int } { \bool_if:NTF \l__datatool_reformat_datetime_bool { \prg_return_true: } { \prg_return_false: } } { \exp_args:Nc \datatool_if_numeric_datum_type:nTF { c_datatool_ #1 _int } { \bool_if:NTF \l__datatool_reformat_numeric_bool { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } } { \prg_return_false: } } } { \prg_return_false: } } % \end{macrocode} % % Similarly, but data type under consideration is in % \cs{@dtl@datatype}: % \begin{macrocode} \prg_new_conditional:Npnn \__datatool_if_auto_reformat_on: { T, F, TF } { \int_case:nnF { \@dtl@datatype } { { \c_datatool_integer_int } { \seq_if_in:NnTF \l__datatool_auto_reformat_types_seq { integer } { \bool_if:NTF \l__datatool_reformat_numeric_bool { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } { \c_datatool_decimal_int } { \seq_if_in:NnTF \l__datatool_auto_reformat_types_seq { decimal } { \bool_if:NTF \l__datatool_reformat_numeric_bool { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } { \c_datatool_currency_int } { \seq_if_in:NnTF \l__datatool_auto_reformat_types_seq { currency } { \bool_if:NTF \l__datatool_reformat_numeric_bool { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } { \c_datatool_datetime_int } { \seq_if_in:NnTF \l__datatool_auto_reformat_types_seq { datetime } { \bool_if:NTF \l__datatool_reformat_datetime_bool { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } { \c_datatool_date_int } { \seq_if_in:NnTF \l__datatool_auto_reformat_types_seq { date } { \bool_if:NTF \l__datatool_reformat_datetime_bool { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } { \c_datatool_time_int } { \seq_if_in:NnTF \l__datatool_auto_reformat_types_seq { time } { \bool_if:NTF \l__datatool_reformat_datetime_bool { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } } { \prg_return_false: } } % \end{macrocode} % %Option definitions. Note that all packages in the \sty{datatool} %bundle now all use the same option group. % \begin{macrocode} \keys_define:nn { datatool } { verbose .legacy_if_set:n = dtlverbose, utf8 .legacy_if_set:n = @dtl@utf8, math .choices:nn = { fp, pgfmath, l3fp, lua } { \tl_set_eq:NN \@dtl@mathprocessor \l_keys_choice_tl }, math .usage:n = load, lang-warn .choice: , lang-warn / true .code:n = { \cs_set_eq:NN \datatool_locale_warn:nn \PackageWarning } , lang-warn / false .code:n = { \PackageInfo {datatool-base} { localisation ~ warnings ~ switched ~ off ~ (including ~ tracklang ~ warnings) } \cs_set_eq:NN \datatool_locale_warn:nn \use_none:nn \TrackLangShowWarningsfalse } , lang-warn .default:n = true , lang-warn .initial:n = true , lang-warn .usage:n = load , nolocale .code:n = { \cs_set_eq:NN \datatool@load@locales \use_none:n \clist_clear:N \l__datatool_extra_locales_clist }, nolocale .value_forbidden:n = true, nolocale .usage:n = load, locales .code:n = { \cs_set_eq:NN \datatool@load@locales \use:n \clist_set:Nn \l__datatool_extra_locales_clist { #1 } }, locales .usage:n = load, % \end{macrocode} %Synonym: % \begin{macrocode} lang .code:n = { \cs_set_eq:NN \datatool@load@locales \use:n \clist_set:Nn \l__datatool_extra_locales_clist { #1 } }, lang .usage:n = load, lists .code:n = { \keys_set:nn { datatool / lists } { #1 } }, initial-purify .choice:, initial-purify / early .code:n = { \bool_set_true:N \l__datatool_initial_purify_early_bool }, initial-purify / late .code:n = { \bool_set_false:N \l__datatool_initial_purify_early_bool }, auto-reformat-types .code:n = { \seq_clear:N \l__datatool_auto_reformat_types_seq \exp_args:Ne \clist_map_function:nN { #1 } \__datatool_auto_reformat_types_do:n } , compare .code:n = { \keys_set:nn { datatool / compare } { #1 } }, datetime .code:n = { \keys_set:nn { datatool / datetime } { #1 } }, numeric .code:n = { \keys_set:nn { datatool / numeric } { #1 } }, } % \end{macrocode} % % Numeric options (new to v3.0). %\changes{3.0}{2025-03-03}{added numeric option} % %Should numeric values be reformatted after parsing? % \begin{macrocode} \bool_new:N \l__datatool_reformat_numeric_bool \bool_set_false:N \l__datatool_reformat_numeric_bool % \end{macrocode} % %\begin{macro}{\DTLscinum} %\changes{3.0}{2025-03-03}{new} % Used with auto-reformat for scientific notation. % \begin{macrocode} \IfPackageLoadedTF { siunitx } { \newcommand \DTLscinum [ 1 ] { \num { #1 } } } { \newcommand \DTLscinum [ 1 ] { #1 } \AddToHook { package / siunitx / after } { \renewcommand \DTLscinum [ 1 ] { \num { #1 } } } } % \end{macrocode} %\end{macro} % %Should region files override the default number chars? % \begin{macrocode} \bool_new:N \l_datatool_region_set_numberchars_bool \bool_set_true:N \l_datatool_region_set_numberchars_bool % \end{macrocode} % %Should region files override the default currency? % \begin{macrocode} \bool_new:N \l_datatool_region_set_currency_bool \bool_set_true:N \l_datatool_region_set_currency_bool % \end{macrocode} % %\begin{macro}{\datatoolcurrencysymbolprefixfmt} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\datatoolcurrencysymbolprefixfmt}[1]{#1} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLcurrCodeOrSymOrChar} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLcurrCodeOrSymOrChar}[3]{#2} % \end{macrocode} %\end{macro} % % \begin{macrocode} \keys_define:nn { datatool / numeric } { auto-reformat .bool_set:N = \l__datatool_reformat_numeric_bool , % \end{macrocode} %This will also switch off any region number chars. %Value needs to be in the form \marg{grp-char}\marg{decimal-char} % \begin{macrocode} set-number-chars .code:n = { \DTLsetnumberchars #1 \bool_set_false:N \l_datatool_region_set_numberchars_bool } , % \end{macrocode} %Should the region set the number group and decimal characters: % \begin{macrocode} region-number-chars .choice: , region-number-chars / true .code:n = { \bool_set_true:N \l_datatool_region_set_numberchars_bool \tl_if_empty:NF \l_datatool_current_region_tl { \cs_if_exist_use:cF { datatool \l_datatool_current_region_tl SetNumberChars } { \PackageWarning { datatool-base } { No ~ numberchars ~ hook ~ found ~ for ~ region ~ ` \l_datatool_current_region_tl ' } } } } , region-number-chars / false .code:n = { \bool_set_false:N \l_datatool_region_set_numberchars_bool } , region-number-chars .default:n = true , % \end{macrocode} %Currency must already have been defined. This will also switch off %any region currency. % \begin{macrocode} set-currency .code:n = { \tl_if_exist:cTF { dtl@curr@ \tl_to_str:n { #1 } @sym } { \tl_set:Ne \@dtl@currency { \exp_not:c { DTLcurr #1 } } \tl_set:Ne \DTLCurrencyCode { #1 } \tl_set:Ne \DTLfmtcurrency { \exp_not:c { dtl@curr@ #1 @fmt } } \bool_false:N \l_datatool_region_set_currency_bool } { \PackageError { datatool-base } { Currency ~ ` #1 ' ~ has ~ not ~ been ~ defined } { Currency ~ must ~ first ~ be ~ defined ~ with ~ \tl_to_str:N \DTLdefcurrency } } } , region-currency .choice: , region-currency / true .code:n = { \bool_set_true:N \l_datatool_region_set_currency_bool \tl_if_empty:NF \l_datatool_current_region_tl { \cs_if_exist_use:cF { datatool \l_datatool_current_region_tl SetCurrency } { \PackageWarning { datatool-base } { No ~ currency ~ hook ~ found ~ for ~ region ~ ` \l_datatool_current_region_tl ' } } } } , region-currency / false .code:n = { \bool_set_false:N \l_datatool_region_set_currency_bool } , region-currency .default:n = true , % \end{macrocode} % Convenient method of redefining formatting command used by region % files, but will only have an effect if the region uses it. % \begin{macrocode} region-currency-prefix .choice: , region-currency-prefix / normal .code:n = { \cs_set_eq:NN \datatoolcurrencysymbolprefixfmt \use:n } , region-currency-prefix / smallcaps .code:n = { \renewcommand \datatoolcurrencysymbolprefixfmt [ 1 ] { \exp_args:Ne \textsc { \text_lowercase:n { ##1 } } } } , region-currency-prefix / smaller .code:n = { \renewcommand \datatoolcurrencysymbolprefixfmt [ 1 ] { \textsmaller { ##1 } } } , % \end{macrocode} %Redefine \cs{DTLcurrCodeOrSymOrChar}: % \begin{macrocode} currency-symbol-style .choice:, currency-symbol-style / iso .code:n = { \cs_set_eq:NN \DTLcurrCodeOrSymOrChar \use_i:nnn }, currency-symbol-style / symbol .code:n = { \cs_set_eq:NN \DTLcurrCodeOrSymOrChar \use_ii:nnn }, currency-symbol-style / string .code:n = { \cs_set_eq:NN \DTLcurrCodeOrSymOrChar \use_iii:nnn }, } % \end{macrocode} % % Date/time options (new to v3.0). %\changes{3.0}{2025-03-03}{added datetime option} % %Should date/time values be parsed? % \begin{macrocode} \bool_new:N \l__datatool_parse_datetime_bool \bool_set_false:N \l__datatool_parse_datetime_bool % \end{macrocode} % %The next two conditionals are only relevant if the above is true. %Should date/time values be parsed for ISO format? % \begin{macrocode} \bool_new:N \l__datatool_parse_datetime_iso_bool \bool_set_true:N \l__datatool_parse_datetime_iso_bool % \end{macrocode} %Should date/time values be parsed for regional format? %This will require the appropriate regional support. % \begin{macrocode} \bool_new:N \l__datatool_parse_datetime_regional_bool \bool_set_true:N \l__datatool_parse_datetime_regional_bool % \end{macrocode} % %Should parsed date/time values be reformatted? % \begin{macrocode} \bool_new:N \l__datatool_reformat_datetime_bool \bool_set_false:N \l__datatool_reformat_datetime_bool % \end{macrocode} % %\begin{macro}{\DTLCurrentLocaleFormatDate} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{DTLCurrentLocaleFormatDate}\marg{YYYY}\marg{MM}\marg{DD}\marg{DOW} %\end{definition} %Localisation support should redefine this. % \begin{macrocode} \newcommand \DTLCurrentLocaleFormatDate [4] { \datatool_default_date_fmt:nnnn { #1 } { #2 } { #3 } { #4 } } % \end{macrocode} %\end{macro} % % \begin{macrocode} \cs_new_nopar:Nn \datatool_default_date_fmt:nnnn { \cs_if_exist:NTF \DTMdisplaydate { \tl_if_empty:nTF { #4 } { \DTMdisplaydate { #1 } { #2 } { #3 } { -1 } } { \DTMdisplaydate { #1 } { #2 } { #3 } { #4 } } } { \datatool_date_iso_fmt:nnnn { #1 } { #2 } { #3 } { #4 } } } % \end{macrocode} % %\begin{macro}{\DataToolDateFmt} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{DataToolDateFmt}\marg{YYYY}\marg{MM}\marg{DD}\marg{DOW} %\end{definition} % \begin{macrocode} \newcommand \DataToolDateFmt [4] { \DTLCurrentLocaleFormatDate { #1 } { #2 } { #3 } { #4 } } % \end{macrocode} %\end{macro} % %The following has a fourth argument for consistency. % \begin{macrocode} \cs_new_nopar:Nn \datatool_date_iso_fmt:nnnn { \datatool_date_iso_fmt:nnn { #1 } { #2 } { #3 } } \cs_new_nopar:Nn \datatool_date_iso_fmt:nnn { \int_eval:n { #1 } - \datatool_two_digits:n { #2 } - \datatool_two_digits:n { #3 } } % \end{macrocode} % %\begin{macro}{\DTLCurrentLocaleFormatTime} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{DTLCurrentLocaleFormatDate}\marg{hh}\marg{mm}\marg{ss} %\end{definition} %Localisation support should redefine this. % \begin{macrocode} \newcommand \DTLCurrentLocaleFormatTime [3] { \datatool_default_time_fmt:nnn { #1 } { #2 } { #3 } } % \end{macrocode} %\end{macro} % \begin{macrocode} \cs_new_nopar:Nn \datatool_default_time_fmt:nnn { \cs_if_exist:NTF \DTMdisplaytime { \tl_if_empty:nTF { #3 } { \DTMdisplaytime { #1 } { #2 } { 0 } } { \DTMdisplaytime { #1 } { #2 } { #3 } } } { \datatool_time_iso_fmt:nnn { #1 } { #2 } { #3 } } } % \end{macrocode} % %\begin{macro}{\DataToolTimeFmt} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{DataToolTimeFmt}\marg{hh}\marg{mm}\marg{ss} %\end{definition} % \begin{macrocode} \newcommand \DataToolTimeFmt [3] { \DTLCurrentLocaleFormatTime { #1 } { #2 } { #3 } } % \end{macrocode} %\end{macro} % % \begin{macrocode} \cs_new_nopar:Nn \datatool_time_iso_fmt:nnn { \datatool_two_digits:n { #1 } \c_colon_str \datatool_two_digits:n { #2 } \tl_if_empty:nF { #3 } { \c_colon_str \datatool_two_digits:n { #3 } } } % \end{macrocode} % %\begin{macro}{\DTLCurrentLocaleFormatTimeZone} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{DTLCurrentLocaleFormatTimeZone}\marg{TZh}\marg{TZm} %\end{definition} % \begin{macrocode} \newcommand \DTLCurrentLocaleFormatTimeZone [2] { \datatool_default_timezone_fmt:nn { #1 } { #2 } } % \end{macrocode} %\end{macro} % \begin{macrocode} \cs_new:Nn \datatool_default_timezone_fmt:nn { \cs_if_exist:NTF \DTMdisplayzone { \DTMdisplayzone { #1 } { #2 } } { \datatool_timezone_iso_fmt:nn { #1 } { #2 } } } % \end{macrocode} % %\begin{macro}{\DataToolTimeZoneFmt} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{DataToolTimeZoneFmt}\marg{TZh}\marg{TZm} %\end{definition} % \begin{macrocode} \newcommand \DataToolTimeZoneFmt [2] { \DTLCurrentLocaleFormatTimeZone { #1 } { #2 } } % \end{macrocode} %\end{macro} % % \begin{macrocode} \cs_new_nopar:Nn \datatool_timezone_iso_fmt:nn { \datatool_signed_two_digits:n { #1 } \c_colon_str \datatool_two_digits:n { #2 } } % \end{macrocode} % %\begin{macro}{\DTLCurrentLocaleTimeStampFmtSep} %\changes{3.0}{2025-03-03}{new} %Localisation files may redefine this. % \begin{macrocode} \newcommand \DTLCurrentLocaleTimeStampFmtSep { ~ } % \end{macrocode} %\end{macro} % %\begin{macro}{\DataToolTimeStampFmtSep} %\changes{3.0}{2025-03-03}{new} %Separator used in \verb|\datatool_default_timestamp_fmt:nnnnnnn| between the date and %time. Defaults to a space. % \begin{macrocode} \newcommand \DataToolTimeStampFmtSep { \DTLCurrentLocaleTimeStampFmtSep } % \end{macrocode} %\end{macro} % %\begin{macro}{\DataToolDateTimeFmt} %\begin{definition} %\cs{DataToolDateTimeFmt}\marg{date-args}\marg{time-args}\marg{time-zone-args} %\end{definition} %\changes{3.0}{2025-03-03}{new} %The \meta{date-args} argument may either be empty or in the form %\marg{YYYY}\marg{MM}\marg{DD}\marg{DOW} appropriate for \cs{DataToolDateFmt}. %The \meta{time-args} argument may either be empty or in the form %\marg{hh}\marg{mm}\marg{ss} appropriate for \cs{DataToolTimeFmt}. %The \meta{time-zone-args} argument may either be empty or in the form %\marg{TZh}\marg{TZm} appropriate for \cs{DataToolTimeZoneFmt}. % \begin{macrocode} \newcommand* \DataToolDateTimeFmt [3] { \tl_if_empty:nTF { #1 } { % \end{macrocode} %No date arguments. % \begin{macrocode} \tl_if_empty:nF { #2 } { % \end{macrocode} %Time arguments provided. (There should be three.) % \begin{macrocode} \DataToolTimeFmt #2 } \tl_if_empty:nF { #3 } { % \end{macrocode} %Time zone arguments provided. (There should be two.) % \begin{macrocode} \DataToolTimeZoneFmt #3 } } { % \end{macrocode} %Date arguments provided. (There should be four.) % \begin{macrocode} \tl_if_empty:nTF { #2 } { % \end{macrocode} %No time arguments provided. % \begin{macrocode} \DataToolDateFmt #1 } { % \end{macrocode} %Time arguments provided. (There should be three.) % \begin{macrocode} \tl_if_empty:nTF { #3 } { % \end{macrocode} %No time zone arguments provided. % \begin{macrocode} \DataToolTimeStampNoZoneFmt #1 #2 } { % \end{macrocode} %Time zone arguments provided. (There should be two.) % \begin{macrocode} \DataToolTimeStampWithZoneFmt #1 #2 #3 } } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DataToolTimeStampWithZoneFmt} %\begin{definition} %\cs{DataToolTimeStampWithZoneFmt}\marg{YYYY}\marg{MM}\marg{DD}\marg{DOW}% %\marg{hh}\marg{mm}\marg{ss} %\end{definition} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand \DataToolTimeStampWithZoneFmt [9] { \DTLCurrentLocaleFormatTimeStampWithZone { #1 } { #2 } { #3 } { #4 } { #5 } { #6 } { #7 } { #8 } { #9 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DataToolTimeStampNoZoneFmt} %\begin{definition} %\cs{DataToolTimeStampNoZoneFmt}\marg{YYYY}\marg{MM}\marg{DD}\marg{DOW}% %\marg{hh}\marg{mm}\marg{ss} %\end{definition} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand \DataToolTimeStampNoZoneFmt [7] { \DTLCurrentLocaleFormatTimeStampNoZone { #1 } { #2 } { #3 } { #4 } { #5 } { #6 } { #7 } } % \end{macrocode} %\end{macro} % \begin{macrocode} \cs_new_nopar:Nn \datatool_default_timestamp_fmt:nnnnnnn { \cs_if_exist:NTF \DTMdisplay { % \end{macrocode} %The \sty{datetime2} style will need to have the zone suppressed %with showzone=false (if supported by the current style). % \begin{macrocode} \tl_if_empty:nTF { #4 } { \DTMdisplay { #1 } { #2 } { #3 } { -1 } { #5 } { #6 } { #7 } { 0 } { 0 } } { \DTMdisplay { #1 } { #2 } { #3 } { #4 } { #5 } { #6 } { #7 } { 0 } { 0 } } } { \datatool_default_date_fmt:nnnn { #1 } { #2 } { #3 } { #4 } \DataToolTimeStampFmtSep \datatool_default_time_fmt:nnn { #5 } { #6 } { #7 } } } % \end{macrocode} % % \begin{macrocode} \cs_new_nopar:Nn \datatool_default_timestamp_fmt:nnnnnnnnn { \cs_if_exist:NTF \DTMdisplay { \tl_if_empty:nTF { #4 } { \DTMdisplay { #1 } { #2 } { #3 } { -1 } { #5 } { #6 } { #7 } { #8 } { #9 } } { \DTMdisplay { #1 } { #2 } { #3 } { #4 } { #5 } { #6 } { #7 } { #8 } { #9 } } } { \datatool_default_timestamp_fmt:nnnnnnn { #1 } { #2 } { #3 } { #4 } { #5 } { #6 } { #7 } \datatool_default_timezone_fmt:nn { #8 } { #9 } } } % \end{macrocode} % % \begin{macrocode} \cs_new_nopar:Nn \datatool_timestamp_iso_fmt:nnnnnnn { \datatool_date_iso_fmt:nnnn { #1 } { #2 } { #3 } { #4 } T \datatool_time_iso_fmt:nnn { #5 } { #6 } { #7 } } % \end{macrocode} % % \begin{macrocode} \cs_new_nopar:Nn \datatool_timestamp_iso_fmt:nnnnnnnnn { \datatool_timestamp_iso_fmt:nnnnnnn { #1 } { #2 } { #3 } { #4 } { #5 } { #6 } { #7 } \datatool_timezone_iso_fmt:nn { #8 } { #9 } } % \end{macrocode} % %\begin{macro}{\DTLCurrentLocaleFormatTimeStampWithZone} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{DTLCurrentLocaleFormatTimeStampWithZone}\marg{YYYY}\marg{MM}\marg{DD}\marg{DOW}% %\marg{hh}\marg{mm}\marg{ss}\marg{tzh}\marg{tzm} %\end{definition} %Localisation support should redefine this. % \begin{macrocode} \newcommand \DTLCurrentLocaleFormatTimeStampWithZone [9] { \datatool_default_timestamp_fmt:nnnnnnnnn { #1 } { #2 } { #3 } { #4 } { #5 } { #6 } { #7 } { #8 } { #9 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLCurrentLocaleFormatTimeStampNoZone} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{DTLCurrentLocaleFormatTimeStampNoZone}\marg{YYYY}\marg{MM}\marg{DD}\marg{DOW}% %\marg{hh}\marg{mm}\marg{ss} %\end{definition} %Localisation support should redefine this. % \begin{macrocode} \newcommand \DTLCurrentLocaleFormatTimeStampNoZone [7] { \datatool_default_timestamp_fmt:nnnnnnn { #1 } { #2 } { #3 } { #4 } { #5 } { #6 } { #7 } } % \end{macrocode} %\end{macro} % % % \begin{macrocode} \keys_define:nn { datatool / datetime } { parse .choice:, parse / false .code:n = { \bool_set_false:N \l__datatool_parse_datetime_bool } , parse / true .code:n = { \bool_set_true:N \l__datatool_parse_datetime_bool } , parse / parse-only .code:n = { \bool_set_true:N \l__datatool_parse_datetime_bool \bool_set_false:N \l__datatool_reformat_datetime_bool } , parse / auto-reformat .code:n = { \bool_set_true:N \l__datatool_parse_datetime_bool \bool_set_true:N \l__datatool_reformat_datetime_bool } , parse / iso-only .code:n = { \bool_set_true:N \l__datatool_parse_datetime_bool \bool_set_true:N \l__datatool_parse_datetime_iso_bool \bool_set_false:N \l__datatool_parse_datetime_regional_bool } , parse / region-only .code:n = { \bool_set_true:N \l__datatool_parse_datetime_bool \bool_set_false:N \l__datatool_parse_datetime_iso_bool \bool_set_true:N \l__datatool_parse_datetime_regional_bool } , parse / iso + region .code:n = { \bool_set_true:N \l__datatool_parse_datetime_bool \bool_set_true:N \l__datatool_parse_datetime_iso_bool \bool_set_true:N \l__datatool_parse_datetime_regional_bool } , parse .default:n = true , auto-reformat .choice: , auto-reformat / false .code:n = { \bool_set_false:N \l__datatool_reformat_datetime_bool } , auto-reformat / true .code:n = { \bool_set_true:N \l__datatool_reformat_datetime_bool } , auto-reformat / region .code:n = { \bool_set_true:N \l__datatool_reformat_datetime_bool \renewcommand \DataToolDateFmt [ 4 ] { \DTLCurrentLocaleFormatDate { ##1 } { ##2 } { ##3 } { ##4 } } \renewcommand \DataToolTimeFmt [ 3 ] { \DTLCurrentLocaleFormatTime { ##1 } { ##2 } { ##3 } } \renewcommand \DataToolTimeZoneFmt [ 2 ] { \DTLCurrentLocaleFormatTimeZone { ##1 } { ##2 } } \renewcommand \DataToolTimeStampWithZoneFmt [ 9 ] { \DTLCurrentLocaleFormatTimeStampWithZone { ##1 } { ##2 } { ##3 } { ##4 } { ##5 } { ##6 } { ##7 } { ##8 } { ##9 } } \renewcommand \DataToolTimeStampNoZoneFmt [ 7 ] { \DTLCurrentLocaleFormatTimeStampNoZone { ##1 } { ##2 } { ##3 } { ##4 } { ##5 } { ##6 } { ##7 } } \renewcommand \DataToolTimeStampFmtSep { \DTLCurrentLocaleTimeStampFmtSep } } , auto-reformat / iso .code:n = { \bool_set_true:N \l__datatool_reformat_datetime_bool \renewcommand \DataToolDateFmt [ 4 ] { \datatool_date_iso_fmt:nnnn { ##1 } { ##2 } { ##3 } { ##4 } } \renewcommand \DataToolTimeFmt [ 3 ] { \datatool_time_iso_fmt:nnn { ##1 } { ##2 } { ##3 } } \renewcommand \DataToolTimeZoneFmt [ 2 ] { \datatool_timezone_iso_fmt:nn { ##1 } { ##2 } } \renewcommand \DataToolTimeStampWithZoneFmt [ 9 ] { \datatool_timestamp_iso_fmt:nnnnnnnnn { ##1 } { ##2 } { ##3 } { ##4 } { ##5 } { ##6 } { ##7 } { ##8 } { ##9 } } \renewcommand \DataToolTimeStampNoZoneFmt [ 7 ] { \datatool_timestamp_iso_fmt:nnnnnnn { ##1 } { ##2 } { ##3 } { ##4 } { ##5 } { ##6 } { ##7 } } } , auto-reformat / datetime2 .code:n = { \cs_if_exist:NTF \DTLdisplay { \bool_set_true:N \l__datatool_reformat_datetime_bool \renewcommand \DataToolDateFmt [ 4 ] { \tl_if_empty:nTF { ##4 } { \DTMdisplaydate { ##1 } { ##2 } { ##3 } { -1 } } { \DTMdisplaydate { ##1 } { ##2 } { ##3 } { ##4 } } } \renewcommand \DataToolTimeFmt [ 3 ] { \tl_if_empty:nTF { ##3 } { \DTMdisplaytime { ##1 } { ##2 } { 0 } } { \DTMdisplaytime { ##1 } { ##2 } { ##3 } } } \renewcommand \DataToolTimeZoneFmt [ 2 ] { \DTMdisplayzone { ##1 } { ##2 } } \renewcommand \DataToolTimeStampWithZoneFmt [ 9 ] { \tl_if_empty:nTF { ##4 } { \DTMdisplay { ##1 } { ##2 } { ##3 } {-1 } { ##5 } { ##6 } { ##7 } { ##8 } { ##9 } } { \DTMdisplay { ##1 } { ##2 } { ##3 } { ##4 } { ##5 } { ##6 } { ##7 } { ##8 } { ##9 } } } \renewcommand \DataToolTimeStampNoZoneFmt [ 7 ] { \tl_if_empty:nTF { ##4 } { \DTMdisplay { ##1 } { ##2 } { ##3 } {-1 } { ##5 } { ##6 } { ##7 } { 0 } { 0 } } { \DTMdisplay { ##1 } { ##2 } { ##3 } { ##4 } { ##5 } { ##6 } { ##7 } { 0 } { 0 } } } } { \PackageError { datatool-base } { Option ~ datetime ~ = ~ { ~ auto-reformat ~ = ~ datetime2 ~ } ~ requires ~ datetime2 ~ package } { You ~ need ~ to ~ load ~ datetime2.sty ~ before ~ you ~ set ~ this ~ option } } } , auto-reformat .default:n = false , } % \end{macrocode} % % List options (new to v3.0). %\changes{3.0}{2025-03-03}{added lists option} % %If true, trim leading and trailing space around elements in the list-related commands below. %Note the default changes the behaviour as from v3.0. % \begin{macrocode} \bool_new:N \l__datatool_list_trim_bool % \end{macrocode} %Reverse sort CSV lists: % \begin{macrocode} \bool_new:N \l__datatool_sort_reverse_bool % \end{macrocode} %Preprocess to create datum elements when sorting CSV lists: % \begin{macrocode} \bool_new:N \l__datatool_sort_datum_bool % \end{macrocode} % %\begin{macro}{\DTLlistand} %\changes{3.0}{2025-03-03}{new} %Use either \cs{DTLandname} or \verb|\&| in lists. %(No difference if no language support provided.) % \begin{macrocode} \bool_new:N \l_datatool_list_and_bool \bool_set_true:N \l_datatool_list_and_bool \newcommand \DTLlistand { \bool_if:NTF \l_datatool_list_and_bool { \DTLandname } { \& } } % \end{macrocode} %\end{macro} % %\begin{macro}{\ifDTLlistskipempty} %\changes{2.31}{2018-12-07}{new} %If true, skip empty elements in the list-related commands below. % \begin{macrocode} \newif\ifDTLlistskipempty \DTLlistskipemptytrue % \end{macrocode} %\end{macro} % % \begin{macrocode} \keys_define:nn { datatool / lists } { skip-empty .legacy_if_set:n = DTLlistskipempty , trim .bool_set:N = \l__datatool_list_trim_bool , trim .initial:n = true , sort-reverse .bool_set:N = \l__datatool_sort_reverse_bool , sort-reverse .initial:n = false , sort-datum .bool_set:N = \l__datatool_sort_datum_bool , sort-datum .initial:n = false , and .choice: , and / word .code:n = { \bool_true:N \l_datatool_list_and_bool } , and / symbol .code:n = { \bool_false:N \l_datatool_list_and_bool } , } % \end{macrocode} % % String comparison options. %\begin{macro}{\ifdtlcompareskipcs} %\changes{2.32}{2019-09-27}{new} %If true, \cs{dtlcompare} should skip control sequences. % \begin{macrocode} \newif\ifdtlcompareskipcs \dtlcompareskipcsfalse % \end{macrocode} %\end{macro} % %If the following boolean is true, \cs{dtlcompare} should expand control sequences. % \begin{macrocode} \bool_new:N \l__datatool_compare_expand_cs_bool % \end{macrocode} % % \begin{macrocode} \keys_define:nn { datatool / compare } { expand-cs .bool_set:N = \l__datatool_compare_expand_cs_bool, skip-cs .legacy_if_set:n = dtlcompareskipcs, } % \end{macrocode} % %\begin{macro}{\DTLsetup} %\changes{3.0}{2025-03-03}{new} %Provide user interface command to set options later. % \begin{macrocode} \DeclareDocumentCommand \DTLsetup { m } { \keys_set:nn { datatool } { #1 } } % \end{macrocode} %\end{macro} %Use \cs{DTLsetLocaleOptions} for options provided by localisation %files. % % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} % % Process options: % \begin{macrocode} \ProcessKeyOptions[datatool] % \end{macrocode} % % Load file dealing with numerical processes. As from %version 3.0, these are in def files not packages. % \begin{macrocode} \InputIfFileExists {datatool-\@dtl@mathprocessor.def} {} { \InputIfFileExists {datatool-l3fp.def} { \PackageError{datatool} {% Missing file `datatool-\@dtl@mathprocessor.def' for math=\@dtl@mathprocessor. Falling back on math=l3fp } {% Check that your TeX distribution contains the file `datatool-\@dtl@mathprocessor.def' } } { \PackageError{datatool} {% Missing file `datatool-\@dtl@mathprocessor.def' for math=\@dtl@mathprocessor. No math commands available! } {% Something is wrong with the datatool installation } } } % \end{macrocode} % %\subsection{Utilities} % %\begin{macro}{\dtl@message} %\begin{definition} %\cs{dtl@message}\marg{message string} %\end{definition} % Displays message only if the verbose option is set. % \begin{macrocode} \newcommand*{\dtl@message}[1]{% \ifdtlverbose\typeout{#1}\fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\@dtl@toks} % \begin{macrocode} \newtoks\@dtl@toks % \end{macrocode} %\end{macro} % %\begin{macro}{\@dtl@tmpcount} % Define temporary count register % \begin{macrocode} \newcount\@dtl@tmpcount % \end{macrocode} %\end{macro} %\begin{macro}{\dtl@tmplength} % Define temporary length register. % TODO: rename \cs{l\_\_datatool\_tmp\_dim}? % \begin{macrocode} \newlength\dtl@tmplength % \end{macrocode} %\end{macro} % %Provide a way of measuring the height of text with problematic %commands locally disabled: % \begin{macrocode} \ExplSyntaxOn \cs_new:Nn \datatool_measure_height:Nn { % \end{macrocode} %NB \cs{settoheight} automatically scopes the content. % \begin{macrocode} \settoheight #1 { \l_datatool_measure_hook_tl #2 } } \cs_new:Nn \datatool_measure_width:Nn { \settowidth #1 { \l_datatool_measure_hook_tl #2 } } \cs_new:Nn \datatool_measure_depth:Nn { \settodepth #1 { \l_datatool_measure_hook_tl #2 } } % \end{macrocode} %\begin{definition} %\cs{datatool\_measure:NNNn} \meta{wd-dim} \meta{ht-dim} %\meta{dp-dim} \marg{text} %\end{definition} %Measure all dimensions. % \begin{macrocode} \cs_new:Nn \datatool_measure:NNNn { \hbox_set:Nn \l__datatool_measure_box { \l_datatool_measure_hook_tl #4 } \dim_set:Nn #1 { \box_wd:N \l__datatool_measure_box } \dim_set:Nn #2 {\box_ht:N \l__datatool_measure_box } \dim_set:Nn #3 { \box_dp:N \l__datatool_measure_box } } % \end{macrocode} %\begin{definition} %\cs{datatool\_measure\_ht\_plus\_dp:Nn} \meta{dim} \marg{text} %\end{definition} %Measure the height plus depth. % \begin{macrocode} \cs_new:Nn \datatool_measure_ht_plus_dp:Nn { \hbox_set:Nn \l__datatool_measure_box { \l_datatool_measure_hook_tl #2 } \dim_set:Nn #1 { \box_ht:N \l__datatool_measure_box + \box_dp:N \l__datatool_measure_box } } % \end{macrocode} % % \begin{macrocode} \box_new:N \l__datatool_measure_box % \end{macrocode} %Hook: % \begin{macrocode} \tl_new:N \l_datatool_measure_hook_tl \tl_set:Nn \l_datatool_measure_hook_tl { \cs_set_eq:NN \label \use_none:n \cs_set_eq:NN \ref \use_none:n \cs_set_eq:NN \pageref \use_none:n \cs_set_eq:NN \refstepcounter \__datatool_local_stepcounter:n \cs_set_eq:NN \stepcounter \__datatool_local_stepcounter:n \cs_set_eq:NN \hypertarget \use_ii:nn \cs_set_eq:NN \hyperlink \use_ii:nn } % \end{macrocode} %Local step counter for use in the above. This doesn't trigger an %error if the counter is undefined nor does it reset dependent counter. % \begin{macrocode} \cs_new:Nn \__datatool_local_stepcounter:n { \int_if_exist:cT { c@ #1 } { \int_incr:c { c@ #1 } } } % \end{macrocode} %Swap values in integer variables: % \begin{macrocode} \cs_new:Nn \datatool_swap_ints:NN { \exp_args:Nee \__datatool_swap_ints:nnNN { \int_use:N #1 } { \int_use:N #2 } #1 #2 } \cs_new:Nn \__datatool_swap_ints:nnNN { \int_set:Nn #3 { #2 } \int_set:Nn #4 { #1 } } % \end{macrocode} % %\begin{macro}{\dtlifintopenbetween} %\begin{definition} %\cs{dtlifintopenbetween}\marg{num}\marg{min}\marg{max}\marg{true %part}\marg{false part} %\end{definition} % If the values may be decimal, use \cs{dtlifnumopenbetween} % instead. % %\changes{3.0}{2025-03-03}{switched to \LaTeX3} % Version 3.0 rewritten to use \LaTeX3 commands. This means it can % now fully expand. % \begin{macrocode} \newcommand{\dtlifintopenbetween}[5]{% \bool_lazy_and:nnTF { \int_compare_p:nNn { #1 } > { #2 } } { \int_compare_p:nNn { #1 } < { #3 } } { #4 } { #5 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlifintclosedbetween} %\begin{definition} %\cs{dtlifintclosedbetween}\marg{num}\marg{min}\marg{max}\marg{true %part}\marg{false part} %\end{definition} % If the values may be decimal, use \cs{dtlifnumclosedbetween} % instead. % %\changes{3.0}{2025-03-03}{switched to \LaTeX3} % Version 3.0 rewritten to use \LaTeX3 commands. This means it can % now fully expand. It's simpler to perform the reverse test (if % outside the range). % \begin{macrocode} \newcommand{\dtlifintclosedbetween}[5]{% \bool_lazy_or:nnTF { \int_compare_p:nNn { #1 } < { #2 } } { \int_compare_p:nNn { #1 } > { #3 } } { #5 } { #4 } } % \end{macrocode} %\end{macro} % %This is mainly for formatting time zone hours with a required %leading sign: % \begin{macrocode} \cs_new:Nn \datatool_signed_two_digits:n { \int_compare:nNnTF { #1 } < { \c_zero_int } { - \datatool_two_digits:e { \int_abs:n { #1 } } } { + \datatool_two_digits:n { #1 } } } % \end{macrocode} %Expanding first reduces computations if the argument is an %expression rather than just a number. % \begin{macrocode} \cs_generate_variant:Nn \datatool_signed_two_digits:n { e, V } % \end{macrocode} %Argument an integer variable: % \begin{macrocode} \cs_new:Nn \datatool_signed_two_digits:N { \int_compare:nNnTF { #1 } < { \c_zero_int } { - \datatool_two_digits:e { \int_abs:n { #1 } } } { + \datatool_two_digits:N #1 } } % \end{macrocode} %Similar but omit sign for positive numbers. %Note that \cs{two@digits} can discard a following space since it %uses \cs{number}. This is avoided here by using \cs{int\_eval:n}. % \begin{macrocode} \cs_new:Nn \datatool_two_digits:n { \int_compare:nNnTF { #1 } < { \c_zero_int } { - \datatool_two_digits:e { \int_abs:n { #1 } } } { \int_compare:nNnT { #1 } < { 10 } { 0 } \int_eval:n { #1 } } } \cs_generate_variant:Nn \datatool_two_digits:n { e, V } % \end{macrocode} %Argument an integer variable: % \begin{macrocode} \cs_new:Nn \datatool_two_digits:N { \int_compare:nNnTF { #1 } < { \c_zero_int } { - \datatool_two_digits:e { \int_abs:n { #1 } } } { \int_compare:nNnT { #1 } < { 10 } { 0 } \int_use:N #1 } } % \end{macrocode} % %\begin{macro}{\dtlpadleadingzeros} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{dtlpadleadingzeros}\marg{num-digits}\marg{value} %\end{definition} %Pad leading zeros. This is designed for sorting numbers by %character code (and so needs to be expandable). %Zero-padding helps place them in numeric order. %The \meta{num-digits} argument should be between 1 and 6. % \begin{macrocode} \newcommand{\dtlpadleadingzeros}[2]{ \fp_compare:nNnTF { #2 } < { \c_zero_fp } { \dtlpadleadingzerosminus } { \dtlpadleadingzerosplus } \__datatool_int_leading_zeros:ee { \int_eval:n { #1 } } { \fp_to_int:n { floor ( abs ( #2 ) ) } } \fp_to_decimal:n { abs ( #2 ) } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlpadleadingzerosminus} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtlpadleadingzerosminus}{-} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlpadleadingzerosplus} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtlpadleadingzerosplus}{} % \end{macrocode} %\end{macro} % % \begin{macrocode} \cs_new:Nn \__datatool_int_leading_zeros:nn { \bool_lazy_and:nnTF { \int_compare_p:nNn { #1 } > { \c_one_int } } { \int_compare_p:nNn { #2 } < { 10 } } { \prg_replicate:nn { #1 - \c_one_int } { 0 } } { \bool_lazy_and:nnTF { \int_compare_p:nNn { #1 } > { 2 } } { \int_compare_p:nNn { #2 } < { 100 } } { \prg_replicate:nn { #1 - 2 } { 0 } } { \bool_lazy_and:nnTF { \int_compare_p:nNn { #1 } > { 3 } } { \int_compare_p:nNn { #2 } < { 1000 } } { \prg_replicate:nn { #1 - 3 } { 0 } } { \bool_lazy_and:nnTF { \int_compare_p:nNn { #1 } > { 4 } } { \int_compare_p:nNn { #2 } < { 10000 } } { \prg_replicate:nn { #1 - 4 } { 0 } } { \bool_lazy_and:nnTF { \int_compare_p:nNn { #1 } > { 5 } } { \int_compare_p:nNn { #2 } < { 100000 } } { \prg_replicate:nn { #1 - 5 } { 0 } } { \bool_lazy_and:nnT { \int_compare_p:nNn { #1 } > { 6 } } { \int_compare_p:nNn { #2 } < { 1000000 } } { \prg_replicate:nn { #1 - 6 } { 0 } } } } } } } } \cs_generate_variant:Nn \__datatool_int_leading_zeros:nn { en, ee } % \end{macrocode} % % Pad trailing zeros for a plain decimal number (stored in a token list % variable): % \begin{macrocode} \cs_new:Nn \datatool_pad_trailing_zeros:Nn { \int_compare:nNnT { #2 } > { \c_zero_int } { \tl_if_in:NnTF #1 { . } { \exp_after:wN \__datatool_split_decimal:wNN #1 \q_stop \l__datatool_prefix_tl \l__datatool_suffix_tl \int_step_inline:nnn { \tl_count:N \l__datatool_suffix_tl + 1 } { #2 } { \tl_put_right:Nn #1 { 0 } } } { \tl_put_right:Nn #1 { . } \int_step_inline:nn { #2 } { \tl_put_right:Nn #1 { 0 } } } } } \cs_generate_variant:Nn \datatool_pad_trailing_zeros:Nn { cn } % \end{macrocode} % \begin{macrocode} \cs_new:Npn \__datatool_split_decimal:wNN #1 . #2 \q_stop #3 #4 { \tl_set:Nn #3 { #1 } \tl_set:Nn #4 { #2 } } % \end{macrocode} % %\subsubsection{General List Utilities} % %\begin{macro}{\@dtl@assigntmpseq} %\changes{3.0}{2025-03-03}{new} %Assign \cs{l\_\_datatool\_tmp\_seq} taking into account the trim %spaces and skip empty elements settings. The argument may be a single token %(a command whose definition is a comma-separated list), in which case it %will be expanded once, or the argument should be a comma-separated list. %Any nested use will need to be scoped. % \begin{macrocode} \newcommand{\@dtl@assigntmpseq}[1]{ \tl_if_single_token:nTF { #1 } { \exp_args:No \__datatool_create_tmp_list:n { #1 } } { \__datatool_create_tmp_list:n { #1 } } } \cs_new:Nn \__datatool_create_tmp_list:n { \bool_if:NTF \l__datatool_list_trim_bool { \seq_set_from_clist:Nn \l__datatool_tmp_seq { #1 } } { \seq_set_split_keep_spaces:Nnn \l__datatool_tmp_seq { , } { #1 } } \ifDTLlistskipempty \seq_remove_all:Nn \l__datatool_tmp_seq {} \fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifinlist} %\begin{definition} %\cs{DTLifinlist}\marg{element}\marg{list}\marg{true part}\marg{false part} %\end{definition} % If \meta{element} is contained in the comma-separated list given % by \meta{list}, then do \meta{true part} otherwise do false % part. The \meta{list} may be a command whose definition is a % comma-separated list. %Rewritten in v3.0 to use \LaTeX3. This is fractionally slower than %the old definition but more reliable. %\changes{3.0}{2025-03-03}{rewritten in \LaTeX3} % \begin{macrocode} \newcommand*{\DTLifinlist}[4]{ \@dtl@assigntmpseq{#2} \seq_if_in:NnTF \l__datatool_tmp_seq { #1 } { #3 } { #4 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLlistelement} %\begin{definition} %\cs{DTLlistelement}\marg{list}\marg{idx} %\end{definition} %Does the \meta{idx}th element in the list. The index %should start from 1 for the first element. %\changes{3.0}{2025-03-03}{rewritten in \LaTeX3} % \begin{macrocode} \newrobustcmd{\DTLlistelement}[2]{% \@dtl@assigntmpseq{#1} \seq_item:Nn \l__datatool_tmp_seq { #2 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLfetchlistelement} %\begin{definition} %\cs{DTLfetchlistelement}\marg{list}\marg{idx}\marg{cs} %\end{definition} %Fetches the \meta{idx}th element in the list and stores in \meta{cs}. The index %should start from 1 for the first element. %\changes{3.0}{2025-03-03}{rewritten in \LaTeX3} % \begin{macrocode} \newrobustcmd{\DTLfetchlistelement}[3]{% \@dtl@assigntmpseq{#1} \tl_set:Nx #3 { \seq_item:Nn \l__datatool_tmp_seq { #2 } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLnumitemsinlist} %\begin{definition} %\cs{DTLnumitemsinlist}\marg{list}\marg{cmd} %\end{definition} % Counts number of elements in list and stores result in control % sequence \meta{cmd}. %\changes{2.31}{2018-12-07}{made robust} %\changes{3.0}{2025-03-03}{rewritten in \LaTeX3} % \begin{macrocode} \newrobustcmd{\DTLnumitemsinlist}[2]{% \@dtl@assigntmpseq{#1} \tl_set:Nx #2 { \seq_count:N \l__datatool_tmp_seq } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLDefaultLocaleWordHandler} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLDefaultLocaleWordHandler}[1]{% \DTLCurrentLocaleWordHandler{#1}% \appto#1{\datatoolctrlboundary}% } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLCurrentLocaleWordHandler} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLCurrentLocaleWordHandler}[1]{} % \end{macrocode} %\end{macro} % % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} % %The original sorting algorithm required a comparison command. This %isn't a problem for a simple case-sensitive character code sort, %but it's less efficient if the sort value needs to be obtained by %processing the original value. If this is done by the comparison %command, then it has to be done every time a comparison is made %(for both values). It's more efficient to process the sort values %first and then sort, but this means keeping track of the original %value. % %Provide a convenient way of locally redefining commands while %obtaining the sort value. %\begin{macro}{\dtlSortWordCommands} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand*{\dtlSortWordCommands}{% \begingroup\makeatletter \dtl@SortWordCommands } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@SortWordCommands} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtl@SortWordCommands}[1]{% \gappto\dtl@SortWordCommands@hook{#1}% \endgroup } % \end{macrocode} %\end{macro} % %\begin{macro}{\datatoolasciistart} %\changes{3.0}{2025-03-03}{new} %Does nothing but influences sorting. % \begin{macrocode} \newcommand{\datatoolasciistart}{} % \end{macrocode} %\end{macro} % %\begin{macro}{\datatoolasciiend} %\changes{3.0}{2025-03-03}{new} %Does nothing but influences sorting. % \begin{macrocode} \newcommand{\datatoolasciiend}{} % \end{macrocode} %\end{macro} % %\begin{macro}{\datatoolctrlboundary} %\changes{3.0}{2025-03-03}{new} %Does nothing but influences sorting. % \begin{macrocode} \newcommand{\datatoolctrlboundary}{} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtltexorsort} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtltexorsort}[2]{#1} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@SortWordCommands@hook} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \begingroup \ExplSyntaxOn \char_set_catcode_other:n {0} \char_set_catcode_other:n {28} \char_set_catcode_other:n {29} \char_set_catcode_other:n {30} \char_set_catcode_other:n {31} \char_set_catcode_other:n {127} \ExplSyntaxOff \gdef\dtl@SortWordCommands@hook{% \def\datatoolasciistart{^^@}% \def\datatoolpersoncomma{^^1c}% \def\datatoolplacecomma{^^1d}% \def\datatoolsubjectcomma{^^1e}% \def\datatoolparenstart{^^1f}% \def\datatoolctrlboundary{^^1f}% \def\datatoolparen{\datatoolctrlboundary\@gobble}% \def\datatoolasciiend{^^7f}% \let\nobreakspace\space \let\ \space \edef\${\expandafter\@gobble\string\$}% \edef\_{\expandafter\@gobble\string\_}% \edef\#{\expandafter\@gobble\string\#}% \edef\%{\expandafter\@gobble\string\%}% \edef\&{\expandafter\@gobble\string\&}% \let\dtltexorsort\@secondoftwo \def\TeX{TeX}% \def\LaTeX{LaTeX}% \datatoolSetCurrencySort } \endgroup % \end{macrocode} %\end{macro} % % \begin{macrocode} \ExplSyntaxOn % \end{macrocode} % %Apply basic sort pre-processing (no locale): % \begin{macrocode} \cs_new:Nn \datatool_sort_preprocess:Nn { \datatool_sort_preprocess:NnN #1 { #2 } \l_datatool_sort_to_lowercase_bool } % \end{macrocode} %Version 3.2: added variant that takes boolean final argument to %determine whether or not to convert to lower case. % \begin{macrocode} \cs_new:Nn \datatool_sort_preprocess:NnN { \group_begin: \dtl@SortWordCommands@hook \bool_if:NTF #3 { \datatool_sort_handler_preprocess:Nn #1 { \text_lowercase:n { #2 } } } { \datatool_sort_handler_preprocess:Nn #1 { #2 } } \exp_args:NNNe \group_end: \tl_set:Nn #1 { \exp_args:NV \text_purify:n #1 } } % \end{macrocode} % %\begin{macro}{\DTLsortwordlist} %\begin{definition} %\cs{DTLsortwordlist}\marg{clist-var}\marg{handler-cs} %\end{definition} %\changes{3.0}{2025-03-03}{new} %The first argument is a macro containing the list. %The second argument is a handler macro for converting the original value %into a byte sequence, which needs to have the form \cs{cs} %\marg{original}\marg{tl}, where \meta{original} is the %original value and \meta{tl} is a token list in which to %store the byte sequence. % \begin{macrocode} \seq_new:N \l__datatool_wordlist_seq \tl_new:N \l__datatool_word_tl \NewDocumentCommand \DTLsortwordlist { m m } { \datatool_sortwordlist:NNN #1 #2 \__datatool_append_sorteditem:w } % \end{macrocode} %\end{macro} %Syntax: \meta{clist-var} \meta{handler-cs} \meta{append-cs} % %The \meta{append-cs} function is used after sorting to append % the sort markup to a token list register with each item in the form %\cs{\_\_datatool\_sorted:nnn} \marg{actual} \marg{sort value} %\marg{letter group}. The \meta{append-cs} function should have the % syntax \meta{sort value}\cs{q\_mark}\meta{actual}\cs{q\_stop} \meta{tl var} % \begin{macrocode} \cs_new:Nn \datatool_sortwordlist:NNN { \__datatool_sortword_list:NN #1 #2 \__datatool_finish_sortword_list:NN #1 #3 } % \end{macrocode} %Syntax: \meta{clist-var} \meta{handler-cs} % \begin{macrocode} \cs_new:Nn \__datatool_sortword_list:NN { % \end{macrocode} %Scope to localise the effect of the hook. % \begin{macrocode} \group_begin: \dtl@SortWordCommands@hook \seq_clear:N \l__datatool_wordlist_seq % \end{macrocode} %Convert clist to temporary sequence variable, according to the %current settings. % \begin{macrocode} \exp_args:No \__datatool_create_tmp_list:n { #1 } \__datatool_sortword_seq:NN \l__datatool_tmp_seq #2 \exp_args:No \__datatool_start_sortword_list:n { \l__datatool_wordlist_seq } } % \end{macrocode} % %Alternative function if list is already in a sequence. %Syntax: \meta{seq-var} \meta{handler-cs} % \begin{macrocode} \cs_new:Nn \datatool_sortwordseq:NN { \datatool_sortwordseq:NNN #1 #2 \__datatool_append_sorted_seq_item:w } % \end{macrocode} %Syntax: \meta{seq-var} \meta{handler-cs} \meta{append-cs} % \begin{macrocode} \cs_new:Nn \datatool_sortwordseq:NNN { \__datatool_sortword_seqlist:NN #1 #2 \__datatool_finish_sortword_seq:NN #1 #3 } % \end{macrocode} %Syntax: \meta{seq-var} \meta{handler-cs} % \begin{macrocode} \cs_new:Nn \__datatool_sortword_seqlist:NN { % \end{macrocode} %Scope to localise the effect of the hook. % \begin{macrocode} \group_begin: \dtl@SortWordCommands@hook \seq_clear:N \l__datatool_wordlist_seq \__datatool_sortword_seq:NN #1 #2 \exp_args:No \__datatool_start_sortword_list:n { \l__datatool_wordlist_seq } } % \end{macrocode} % %Syntax: \meta{seq-var} \meta{handler-cs} %Inner workings. % \begin{macrocode} \cs_new:Nn \__datatool_sortword_seq:NN { \seq_map_inline:Nn #1 { \bool_if:NTF \l__datatool_sort_datum_bool { \DTLparse \l__datatool_tmpa_tl { ##1 } \tl_set:No \l__datatool_word_tl { ##1 } #2 { ##1 } { \l__datatool_word_tl } \exp_args:Noo \__datatool_sortword_append:nn { \l__datatool_word_tl } { \l__datatool_tmpa_tl } } { \tl_set:Nn \l__datatool_word_tl { ##1 } #2 { ##1 } { \l__datatool_word_tl } \exp_args:No \__datatool_sortword_append:nn { \l__datatool_word_tl } { ##1 } } } } % \end{macrocode} %End group to cancel effect of sort hook. This allows the original %definitions to be used by the fallback function. % \begin{macrocode} \cs_new:Nn \__datatool_start_sortword_list:n { \group_end: \tl_set:Nn \l__datatool_wordlist_seq { #1 } \seq_sort:Nn \l__datatool_wordlist_seq { \__datatool_compare_sortitem:w ##1 ##2 } } % \end{macrocode} %Add sort element to sequence. % \begin{macrocode} \cs_new:Nn \__datatool_sortword_append:nn { \seq_put_right:Nn \l__datatool_wordlist_seq { #1 \q_mark #2 \q_stop } } % \end{macrocode} %Comparator used by \cs{DTLsortwordlist} %\begin{definition} %\cs{\_\_datatool\_compare\_sortitem:w} %\meta{sort1}\cs{q\_mark} \meta{actual1}\cs{q\_stop} %\meta{sort2}\cs{q\_mark} \meta{actual2}\cs{q\_stop} %\end{definition} % \begin{macrocode} \cs_new:Npn \__datatool_compare_sortitem:w #1\q_mark#2\q_stop#3\q_mark#4\q_stop { \__datatool_compare_sortitem:nnnn { #1 } { #2 } { #3 } { #4 } \int_compare:nNnTF { \dtl@sortresult } < { \c_zero_int } { \sort_return_same: } { \int_compare:nNnTF { \dtl@sortresult } > { \c_zero_int } { \sort_return_swapped: } { \bool_if:NTF \l__datatool_sort_reverse_bool { \__datatool_fallback_action:nnnn { #4 } { #2 } { \sort_return_swapped: } { \sort_return_same: } } { \__datatool_fallback_action:nnnn { #2 } { #4 } { \sort_return_swapped: } { \sort_return_same: } } } } } % \end{macrocode} %Syntax: \marg{sort A}\marg{actual A}\marg{sort B}\marg{actual B} %If the actual values are in the datum format, a numeric comparison %can be used if the datum format indicates the original value was %identified as numeric. % \begin{macrocode} \cs_new:Nn \__datatool_compare_sortitem:nnnn { \tl_clear:N \l__datatool_tmpc_tl \tl_clear:N \l__datatool_tmpd_tl \int_set_eq:NN \@dtl@datatype \c_datatool_string_int \tl_if_head_eq_meaning:nNTF { #2 } \__datatool_datum:nnnn { \__datatool_get_datum:w #2 \q_nil \q_stop \int_set_eq:NN \l__datatool_tmp_datatype_int \@dtl@datatype \tl_set:No \l__datatool_tmpc_tl { \datatool_datum_currency:Nnnnn #2 } } { \datatool_if_null:nT { #2 } { \int_set_eq:NN \l__datatool_tmp_datatype_int \c_datatool_unknown_int } } \tl_if_head_eq_meaning:nNTF { #4 } \__datatool_datum:nnnn { \__datatool_get_datum:w #4 \q_nil \q_stop \tl_set:No \l__datatool_tmpd_tl { \datatool_datum_currency:Nnnnn #4 } } { \datatool_if_null:nT { #4 } { \int_set_eq:NN \@dtl@datatype \c_datatool_unknown_int } } % \end{macrocode} %If they have different currency symbols, treat them as strings. % \begin{macrocode} \tl_if_eq:NNF \l__datatool_tmpc_tl \l__datatool_tmpd_tl { \int_set_eq:NN \l__datatool_tmp_datatype_int \c_datatool_string_int \int_set_eq:NN \@dtl@datatype \c_datatool_string_int } \int_compare:nNnTF { \l__datatool_tmp_datatype_int } = { \c_datatool_unknown_int } { % \end{macrocode} %The first value has an unknown type. % \begin{macrocode} \int_compare:nNnTF { \@dtl@datatype } > { \c_datatool_string_int } { % \end{macrocode} %The first value has an unknown type. The second is numeric, %so treat the first as zero. % \begin{macrocode} \__datatool_numcmp:Nnn \dtl@sortresult { 0 } { \datatool_datum_value:Nnnnn #4 } } { % \end{macrocode} %The first value has an unknown type. If the second is not numeric %so use a string comparison. % \begin{macrocode} \__datatool_strcmp:Nnn \dtl@sortresult { #1 } { #3 } } } { % \end{macrocode} %The first value has a known type. % \begin{macrocode} \int_case:nnF { \@dtl@datatype } { { \c_datatool_unknown_int } { % \end{macrocode} %The first value is known. The second is unknown. % \begin{macrocode} \int_compare:nNnTF { \l__datatool_tmp_datatype_int } > { \c_datatool_string_int } { % \end{macrocode} %The first value is numeric. The second is unknown so treat as 0. % \begin{macrocode} \__datatool_numcmp:Nnn \dtl@sortresult { \datatool_datum_value:Nnnnn #2 } { 0 } } { % \end{macrocode} %The first value is not numeric. The second is unknown so use a %string comparison. % \begin{macrocode} \__datatool_strcmp:Nnn \dtl@sortresult { #1 } { #3 } } } { \c_datatool_string_int } { % \end{macrocode} %The first value is known. The second is a string. Use a string %comparison % \begin{macrocode} \__datatool_strcmp:Nnn \dtl@sortresult { #1 } { #3 } } } { % \end{macrocode} %The first value is known. The second is numeric. % \begin{macrocode} \int_compare:nNnTF { \l__datatool_tmp_datatype_int } > { \c_datatool_string_int } { % \end{macrocode} %Both values are numeric. % \begin{macrocode} \__datatool_numcmp:Nnn \dtl@sortresult { \datatool_datum_value:Nnnnn #2 } { \datatool_datum_value:Nnnnn #4 } } { % \end{macrocode} %The first value is a string. The second is numeric. Use a string %comparison. % \begin{macrocode} \__datatool_strcmp:Nnn \dtl@sortresult { #1 } { #3 } } } } \bool_if:NT \l__datatool_sort_reverse_bool { \int_compare:nNnTF { \dtl@sortresult } < { \c_zero_int } { \int_set:Nn \dtl@sortresult { \c_one_int } } { \int_set:Nn \dtl@sortresult { - \dtl@sortresult } } } } % \end{macrocode} % %Finish off after sorting. %The first argument is the clist in which to store the sorted %values. The second argument is the function that extracts the %required information and appends it to the clist. % \begin{macrocode} \cs_new:Nn \__datatool_finish_sortword_list:NN { \clist_clear:N #1 \seq_map_inline:Nn \l__datatool_wordlist_seq { % \end{macrocode} %The argument \verb|##1| should be in the form %\meta{sort}\cs{q\_mark} \meta{actual}\cs{q\_stop}\marg{tl} % \begin{macrocode} #2 ##1 #1 } } % \end{macrocode} % %Similarly, but for a sequence variable. % \begin{macrocode} \cs_new:Nn \__datatool_finish_sortword_seq:NN { \seq_clear:N #1 \seq_map_inline:Nn \l__datatool_wordlist_seq { % \end{macrocode} %The argument \verb|##1| should be in the form %\meta{sort}\cs{q\_mark} \meta{actual}\cs{q\_stop}\marg{tl} % \begin{macrocode} #2 ##1 #1 } } % \end{macrocode} % %This is similar to the datum marker but allows the sort value and %initial letter (which may be required for letter groups) to be %available. The three arguments are: original item, sort string, %letter group. % \begin{macrocode} \cs_new:Nn \__datatool_sorted:nnn { \exp_not:n { #1 }} % \end{macrocode} %\begin{definition} % %Unlike the datum markers, these are more likely to be passed %directly (for example, in the argument of \cs{do}) so the \meta{sort %marker} is expected to be in the form %\cs{\_\_datatool\_sorted:nnn}\marg{actual}\marg{sort}\marg{letter}. %If using \cs{@for} to iterate over the list then the loop control %sequence will need to be expanded first. %\begin{macro}{\DTLsortedactual} %\begin{definition} %\cs{DTLsortedactual}\marg{sort marker} %\end{definition} %The original item. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLsortedactual} [1] { \use_ii:nnnn #1 } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLsortedvalue} %\begin{definition} %\cs{DTLsortedvalue}\marg{sort marker} %\end{definition} %The sort value. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLsortedvalue} [1] { \use_iii:nnnn #1 } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLsortedletter} %\begin{definition} %\cs{DTLsortedletter}\marg{sort marker} %\end{definition} %The letter group. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLsortedletter} [1] { \use_iv:nnnn #1 } % \end{macrocode} %\end{macro} % %\cs{\_\_datatool\_append\_sorteditem:w} %\meta{sort}\cs{q\_mark} \meta{actual}\cs{q\_stop} \meta{clist\_var} %\end{definition} % \begin{macrocode} \cs_new:Npn \__datatool_append_sorteditem:w #1 \q_mark #2 \q_stop #3 { \tl_clear:N \l__datatool_tmpa_tl \DTLassignlettergroup { #2 } { #1 } { \l__datatool_tmpa_tl } \clist_put_right:Nx #3 { \exp_not:N \__datatool_sorted:nnn { \exp_not:n { #2 } } { \exp_not:n { #1 } } { \exp_not:o { \l__datatool_tmpa_tl } } } } % \end{macrocode} % % \begin{macrocode} \cs_new:Npn \__datatool_append_sorted_seq_item:w #1 \q_mark #2 \q_stop #3 { \tl_clear:N \l__datatool_tmpa_tl \DTLassignlettergroup { #2 } { #1 } { \l__datatool_tmpa_tl } \seq_put_right:Nx #3 { \exp_not:N \__datatool_sorted:nnn { \exp_not:n { #2 } } { \exp_not:n { #1 } } { \exp_not:o { \l__datatool_tmpa_tl } } } } % \end{macrocode} % %\begin{macro}{\DTLassignlettergroup} %\begin{definition} %\cs{DTLassignlettergroup}\marg{actual}\marg{sort value}\marg{tl} %\end{definition} %Obtains the letter group from the sort value. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLassignlettergroup} [3] { \@dtl@datatype = \c_datatool_string_int \tl_if_head_eq_meaning:nNT { #1 } \__datatool_datum:nnnn { \__datatool_get_datum:w #1 \q_nil \q_stop } \int_case:nn { \@dtl@datatype } { { \c_datatool_string_int } { \DTLCurrentLocaleGetGroupString { #1 } { #2 } #3 \exp_args:NV \datatool_get_first_grapheme:nN #3 #3 \exp_args:NV \datatool_if_letter:nTF #3 { \exp_args:NV \DTLCurrentLocaleGetInitialLetter #3 { #3 } \DTLPreProcessLetterGroup { #3 } \tl_set:Nx #3 { \exp_not:N \dtllettergroup { #3 } } } { \DTLPreProcessNonLetterGroup { #3 } \tl_set:Nx #3 { \exp_not:N \dtlnonlettergroup { #3 } } } } { \c_datatool_integer_int } { \DTLPreProcessIntegerGroup { \l__datatool_datum_value_tl } { #1 } \tl_set:Nx #3 { \exp_not:N \dtlnumbergroup { \l__datatool_datum_value_tl } } } { \c_datatool_decimal_int } { \DTLPreProcessDecimalGroup { \l__datatool_datum_value_tl } { #1 } \tl_set:Nx #3 { \exp_not:N \dtlnumbergroup { \l__datatool_datum_value_tl } } } { \c_datatool_currency_int } { \DTLPreProcessCurrencyGroup { \l__datatool_datum_currency_tl } { \l__datatool_datum_value_tl } { #1 } \tl_set:Nx #3 { \exp_not:N \dtlcurrencygroup { \exp_not:o { \l__datatool_datum_currency_tl } } { \l__datatool_datum_value_tl } } } { \c_datatool_datetime_int } { \DTLPreProcessDateTimeGroup { \l__datatool_datum_value_tl } { #1 } \tl_set:Nx #3 { \exp_not:N \dtldatetimegroup { \l__datatool_datum_value_tl } } } { \c_datatool_date_int } { \DTLPreProcessDateGroup { \l__datatool_datum_value_tl } { #1 } \tl_set:Nx #3 { \exp_not:N \dtldategroup { \l__datatool_datum_value_tl } } } { \c_datatool_time_int } { \DTLPreProcessTimeGroup { \l__datatool_datum_value_tl } { #1 } \tl_set:Nx #3 { \exp_not:N \dtltimegroup { \l__datatool_datum_value_tl } } } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLCurrentLocaleGetGroupString} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{DTLCurrentLocaleGetGroupString}\marg{actual}\marg{sort value}\marg{tl} %\end{definition} %May consist of more than one character. %This determines whether to use the sort or actual value to obtain %the letter group. The returned value may be truncated as only the %initial part is of interest. % \begin{macrocode} \newcommand{\DTLCurrentLocaleGetGroupString}[3] { \tl_set:Nn #3 { #1 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLPreProcessLetterGroup} %\changes{3.0}{2025-03-03}{new} % Hook to pre-process letter group. The argument is a token list % variable containing the current value. % \begin{macrocode} \newcommand{\DTLPreProcessLetterGroup}[1]{} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLPreProcessNonLetterGroup} %\changes{3.0}{2025-03-03}{new} % Hook to pre-process non-letter group. The argument is a token list % variable containing the current value. % \begin{macrocode} \newcommand{\DTLPreProcessNonLetterGroup}[1]{} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLPreProcessIntegerGroup} %\changes{3.0}{2025-03-03}{new} % Hook to pre-process integer number group. The first argument is a token list % variable containing the current value. The second argument is the % actual value. The hook may adjust the token list variable. % \begin{macrocode} \newcommand{\DTLPreProcessIntegerGroup}[2]{} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLPreProcessDecimalGroup} %\changes{3.0}{2025-03-03}{new} % Hook to pre-process decimal number group. The first argument is a token list % variable containing the current value. The second argument is the % actual value. The hook may adjust the token list variable. % \begin{macrocode} \newcommand{\DTLPreProcessDecimalGroup}[2]{} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLPreProcessCurrencyGroup} %\begin{definition} %\cs{DTLPreProcessCurrencyGroup}\marg{sym tl var}\marg{num tl var}\marg{actual} %\end{definition} %\changes{3.0}{2025-03-03}{new} % Hook to pre-process currency number group. The \meta{num tl var} is a token list % variable containing the current numeric value. The \meta{sym tl var} is a token list % variable containing the currency symbol. % The \meta{actual} argument is the % actual value. The hook may adjust the token list variable. % \begin{macrocode} \newcommand{\DTLPreProcessCurrencyGroup}[3]{} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLPreProcessDateTimeGroup} %\changes{3.0}{2025-03-03}{new} % Hook to pre-process datetime group. The first argument is a token list % variable containing the current value. The second argument is the % actual value. The hook may adjust the token list variable. % \begin{macrocode} \newcommand{\DTLPreProcessDateTimeGroup}[2]{} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLPreProcessDateGroup} %\changes{3.0}{2025-03-03}{new} % Hook to pre-process date group. The first argument is a token list % variable containing the current value. The second argument is the % actual value. The hook may adjust the token list variable. % \begin{macrocode} \newcommand{\DTLPreProcessDateGroup}[2]{} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLPreProcessTimeGroup} %\changes{3.0}{2025-03-03}{new} % Hook to pre-process time group. The first argument is a token list % variable containing the current value. The second argument is the % actual value. The hook may adjust the token list variable. % \begin{macrocode} \newcommand{\DTLPreProcessTimeGroup}[2]{} % \end{macrocode} %\end{macro} % % %\begin{macro}{\dtllettergroup} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtllettergroup}[1]{ \text_titlecase_first:n { #1 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlnonlettergroup} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtlnonlettergroup}[1]{\detokenize{#1}} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlnumbergroup} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtlnumbergroup}[1]{#1} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlcurrencygroup} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtlcurrencygroup}[2]{#1} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtldatetimegroup} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtldatetimegroup}[1]{#1} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtldategroup} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtldategroup}[1]{#1} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtltimegroup} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtltimegroup}[1]{#1} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlfallbackaction} %\begin{definition} %\cs{dtlfallbackaction}\marg{val1}\marg{val2}\marg{swap code}\marg{no swap code} %\end{definition} %\changes{3.0}{2025-03-03}{new} %Used when two sort values are identical. This uses the original %values for comparison. % \begin{macrocode} \newcommand{\dtlfallbackaction}[4]{ \DTLifstringgt { #1 } { #2 } { #3 } { #4 } } \cs_new:Nn \__datatool_fallback_action:nnnn { \dtlfallbackaction { #1 } { #2 } { #3 } { #4 } } % \end{macrocode} %\end{macro} % %Word sort doesn't discard spaces or punctuation. So the first word %of a compound word or phrase will come before a longer word that %happens to have the other word at the start. This means that, %for example, ``sea lion'' will come before ``seal'' because the %space character comes before the character ``l''. This isn't %exactly the same as a character sort as the localisation handler %may convert the token list to series of bytes that alters the %ordering. % %Letter sort is only really concerned with alphanumerics (letters %and digits). Spaces and punctuation aren't considered relevant to %order. Commands will be expanded first, then stripped after the %locale handler has been applied. This will ensure that any accent %commands, such as \verb|\'e|, will be converted to UTF-8 before %the locale handler is applied. % % \begin{macrocode} \cs_new:Nn \datatool_sort_handler_preprocess:Nn { \protected@edef #1 { #2 } } % \end{macrocode} % %Handlers should set this to true if the sort value is converted to %lowercase and false otherwise. % \begin{macrocode} \bool_new:N \l_datatool_sort_to_lowercase_bool \bool_set_true:N \l_datatool_sort_to_lowercase_bool % \end{macrocode} % %\begin{macro}{\DTLsortwordhandler} %\begin{definition} %\cs{DTLsortwordhandler}\marg{original}\marg{cs} %\end{definition} %\changes{3.0}{2025-03-03}{new} %\changes{3.1}{2025-03-03}{changed \cs{tl\_set:Nx} to \cs{tl\_set:Ne}} %\changes{3.1}{2025-03-03}{moved case-change} % \begin{macrocode} \newcommand{\DTLsortwordhandler}[2]{ \bool_set_true:N \l_datatool_sort_to_lowercase_bool \datatool_sort_handler_preprocess:Nn #2 { #1 } \DTLDefaultLocaleWordHandler { #2 } % \end{macrocode} %v3.1: Case change has been moved from pre-process to here: % \begin{macrocode} \tl_set:Ne #2 { \text_lowercase:n { \text_purify:n { #2 } } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLsortwordcasehandler} %\begin{definition} %\cs{DTLsortwordcasehandler}\marg{original}\marg{cs} %\end{definition} %Case-sensitive handler. %\changes{3.0}{2025-03-03}{new} %\changes{3.1}{2025-03-03}{changed \cs{tl\_set:Nx} to \cs{tl\_set:Ne}} % \begin{macrocode} \newcommand{\DTLsortwordcasehandler}[2]{ \bool_set_false:N \l_datatool_sort_to_lowercase_bool \datatool_sort_handler_preprocess:Nn #2 { #1 } \DTLDefaultLocaleWordHandler { #2 } \tl_set:Ne #2 { \text_purify:n { #2 } } } % \end{macrocode} %\end{macro} % %Regular expression for content that should be stripped for letter %sort. % \begin{macrocode} \regex_new:N \l_datatool_letter_regex \regex_set:Nn \l_datatool_letter_regex { [\-[:space:]] } % \end{macrocode} % %\begin{macro}{\DTLsortletterhandler} %\begin{definition} %\cs{DTLsortletterhandler}\marg{original}\marg{cs} %\end{definition} %\changes{3.0}{2025-03-03}{new} %\changes{3.1}{2025-03-03}{changed \cs{tl\_set:Nx} to \cs{tl\_set:Ne}} %\changes{3.1}{2025-03-03}{moved case-change} %Hyphens and spaces are discarded from the sort value. % \begin{macrocode} \newcommand{\DTLsortletterhandler}[2]{ \bool_set_true:N \l_datatool_sort_to_lowercase_bool \datatool_sort_handler_preprocess:Nn #2 { #1 } \regex_replace_all:NnN \l_datatool_letter_regex {} #2 \DTLDefaultLocaleWordHandler { #2 } % \end{macrocode} %v3.1: Case change has been moved from pre-process to here: % \begin{macrocode} \tl_set:Ne #2 { \text_lowercase:n { \text_purify:n { #2 } } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLsortlettercasehandler} %\begin{definition} %\cs{DTLsortlettercasehandler}\marg{original}\marg{cs} %\end{definition} %\changes{3.0}{2025-03-03}{new} %\changes{3.1}{2025-03-03}{changed \cs{tl\_set:Nx} to \cs{tl\_set:Ne}} %Case-sensitive handler. % \begin{macrocode} \newcommand{\DTLsortlettercasehandler}[2]{ \bool_set_false:N \l_datatool_sort_to_lowercase_bool \datatool_sort_handler_preprocess:Nn #2 { #1 } \regex_replace_all:NnN \l_datatool_letter_regex {} #2 \DTLDefaultLocaleWordHandler { #2 } \tl_set:Ne #2 { \text_purify:n { #2 } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsortlist} %\changes{2.27}{2016-07-28}{new} %\begin{definition} % \cs{dtlsortlist}\marg{list-cs}\marg{criteria cmd} %\end{definition} % Sorts the given comma-separated list according to the % \meta{criteria} command, which must take % three arguments. % \begin{macrocode} \newcommand{\dtlsortlist}[2]{% \exp_args:No \__datatool_create_tmp_list:n { #1 } \seq_set_eq:NN \l__datatool_wordlist_seq \l__datatool_tmp_seq \seq_sort:Nn \l__datatool_wordlist_seq { \bool_if:NTF \l__datatool_sort_reverse_bool { #2 { \dtl@sortresult } { ##2 } { ##1 } } { #2 { \dtl@sortresult } { ##1 } { ##2 } } \ifnum\dtl@sortresult > 0\relax \sort_return_swapped: \else \sort_return_same: \fi } \tl_clear:N #1 \seq_map_inline:Nn \l__datatool_wordlist_seq { \tl_if_empty:NF #1 { \tl_put_right:Nn #1 { , } } \tl_if_in:nnTF { ##1 } { , } { \tl_put_right:Nn #1 { { ##1 } } } { \tl_put_right:Nn #1 { ##1 } } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlinsertinto} %\changes{2.27}{2016-07-28}{new} %\changes{3.0}{2025-03-03}{made robust} %\begin{definition} % \cs{dtlinsertinto}\marg{element}\marg{sorted-list}\marg{criteria cmd} %\end{definition} % Globally inserts \meta{element} into the sorted list \meta{sorted-list} % according to the criteria given by \meta{criteria cmd}, which % should be a command that takes three arguments % \marg{reg}\marg{A}\marg{B}, where \meta{reg} is a count register % in which to store the result, \meta{A} is the first element and % \meta{B} is the second element to compare. % \begin{macrocode} \newrobustcmd{\dtlinsertinto}[3]{% \exp_args:No \__datatool_create_tmp_list:n { #2 } \seq_set_eq:NN \l__datatool_wordlist_seq \l__datatool_tmp_seq \tl_gclear:N #2 \@dtl@insertdonefalse \seq_map_inline:Nn \l__datatool_wordlist_seq { \tl_if_empty:NF #2 { \tl_gput_right:Nn #2 { , } } \if@dtl@insertdone \else #3 { \dtl@sortresult } { ##1 } { #1 } \ifnum\dtl@sortresult > 0\relax \@dtl@insertdonetrue \tl_if_in:nnTF { #1 } { , } { \tl_gput_right:Nn #2 { { #1 } , } } { \tl_gput_right:Nn #2 { #1 , } } \fi \fi \tl_if_in:nnTF { ##1 } { , } { \tl_gput_right:Nn #2 { { ##1 } } } { \tl_gput_right:Nn #2 { ##1 } } } \if@dtl@insertdone \else \tl_if_empty:NF #2 { \tl_gput_right:Nn #2 { , } } \tl_if_in:nnTF { #1 } { , } { \tl_gput_right:Nn #2 { { #1 } } } { \tl_gput_right:Nn #2 { #1 } } \fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\edtlinsertinto} %\changes{2.27}{2016-07-28}{new} %\begin{definition} % \cs{edtlinsertinto}\marg{element}\marg{sorted-list}\marg{criteria cmd} %\end{definition} %First expands \meta{element} before inserting into the list. % \begin{macrocode} \newcommand*{\edtlinsertinto}[3]{% \exp_args:Nx \dtlinsertinto {#1} {#2} {#3}% } \ExplSyntaxOff % \end{macrocode} %\end{macro} % %\begin{macro}{\if@dtl@insertdone} % Define conditional to indicate whether the new entry has % been inserted into the sorted list. % \begin{macrocode} \newif\if@dtl@insertdone % \end{macrocode} %\end{macro} %\begin{macro}{\dtl@sortresult} % Define \cs{dtl@sortresult} to be set by comparison macro. % TODO: replace with L3 int var? (NB used by glossaries.sty) % \begin{macrocode} \newcount\dtl@sortresult % \end{macrocode} %\end{macro} % % \begin{macrocode} \ExplSyntaxOn % \end{macrocode} %\begin{macro}{\DTLshufflelist} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{DTLshufflelist}{\meta{clist-var}} %\end{definition} %Shuffle a comma-separated list. This just converts the list to %a sequence and shuffles that using \verb|seq_shuffle:N| and then %converts that back to a comma-separated list. % \begin{macrocode} \NewDocumentCommand \DTLshufflelist { m } { % \end{macrocode} %Convert clist to temporary sequence variable, according to the %current settings. % \begin{macrocode} \group_begin: \exp_args:No \__datatool_create_tmp_list:n { #1 } \seq_shuffle:N \l__datatool_tmp_seq \clist_clear:N \l__datatool_tmp_clist \seq_map_inline:Nn \l__datatool_tmp_seq { \clist_put_right:Nn \l__datatool_tmp_clist { ##1 } } \exp_args:NNNV \group_end: \tl_set:Nn #1 \l__datatool_tmp_clist } % \end{macrocode} %\end{macro} % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} % %This next command is based on the list iteration exercise %given at %\url{http://www.dickimaw-books.com/latex/admin/html/foreachtips.shtml#oxfordcomma} %It's designed to format a comma-separated list using %\cs{DTLlistformatsep} %between each item except for the last. The separator for the %last pair is \cs{DTLlistformatlastsep} if the list only contains two %items or \cs{DTLlistformatoxford}\cs{DTLlistformatlastsep} if the list contains three or %more items. Each item is formatted according to %\cs{DTLlistformatitem}. %\begin{macro}{\DTLlistformatsep} %\changes{2.28}{2017-11-10}{new} % \begin{macrocode} \newcommand*{\DTLlistformatsep}{, } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLlistformatoxford} %\changes{2.28}{2017-11-10}{new} % \begin{macrocode} \newcommand*{\DTLlistformatoxford}{} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLandname} %\changes{2.28}{2017-11-10}{new} % \begin{macrocode} \ifdef\andname {\newcommand*{\DTLandname}{\andname}} {\newcommand*{\DTLandname}{\&}} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLlistformatlastsep} %\changes{2.28}{2017-11-10}{new} % \begin{macrocode} \newcommand*{\DTLlistformatlastsep}{ \DTLlistand\space} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLlistformatitem} %\changes{2.28}{2017-11-10}{new} % \begin{macrocode} \newcommand*{\DTLlistformatitem}[1]{#1} % \end{macrocode} %\end{macro} % %\begin{macro}{\@dtl@formatlist@handler} %\changes{2.28}{2017-11-10}{new} % \begin{macrocode} \newcommand*{\@dtl@formatlist@handler}[1]{% \@dtl@formatlist@itemsep \@dtl@formatlist@lastitem \renewcommand{\@dtl@formatlist@lastitem}{% \renewcommand{\@dtl@formatlist@itemsep}{% \DTLlistformatsep \renewcommand*{\@dtl@formatlist@prelastitemsep}{% \DTLlistformatoxford}}% \renewcommand{\@dtl@formatlist@prelastitem}{% \@dtl@formatlist@prelastitemsep \DTLlistformatlastsep}% \DTLlistformatitem{#1}% }% }% % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLformatlist} %\changes{2.28}{2017-11-10}{new} %Formats the comma-separated list supplied in its argument. %The unstarred version adds grouping. % \begin{macrocode} \newrobustcmd*{\DTLformatlist}{% \@ifstar{\s@dtlformatlist}{\@dtlformatlist}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\s@dtlformatlist} %\changes{2.28}{2017-11-10}{new} %\changes{3.0}{2025-03-03}{rewritten to (partially) use \LaTeX3} %Starred version of \cs{DTLformatlist} doesn't add grouping. % \begin{macrocode} \ExplSyntaxOn \newcommand*{\s@dtlformatlist}[1]{% \def\@dtl@formatlist@itemsep{}% \def\@dtl@formatlist@lastitem{}% \def\@dtl@formatlist@prelastitem{}% \def\@dtl@formatlist@prelastitemsep{}% \@dtl@assigntmpseq{#1}% \seq_map_function:NN \l__datatool_tmp_seq \@dtl@formatlist@handler \@dtl@formatlist@prelastitem\@dtl@formatlist@lastitem } \ExplSyntaxOff % \end{macrocode} %\end{macro} % %\begin{macro}{\@dtlformatlist} %\changes{2.28}{2017-11-10}{new} %Unstarred version of \cs{DTLformatlist} adds grouping. % \begin{macrocode} \newcommand*{\@dtlformatlist}[1]{{\s@dtlformatlist{#1}}} % \end{macrocode} %\end{macro} % % \subsubsection{General Token Utilities} %These commands are for altering the content of token registers. %They're mostly used by \sty{datatool} for the internal database %structure. %\changes{2.28}{2017-11-10}{renamed \cs{toks@g...} to %\cs{dtl@toks@g...}} % % \begin{macrocode} \ExplSyntaxOn % \end{macrocode} % %\begin{macro}{\@dtl@toks@gput@right@cx} %\begin{definition} %\cs{dtl@toks@gput@right@cx}\marg{toks name}\marg{stuff} %\end{definition} % Globally appends stuff to token register \cs{}\meta{toks name} %Deprecated. %TODO remove % \begin{macrocode} \newcommand{\@dtl@toks@gput@right@cx}[2]{% \__datatool_token_register_gput_right:cx #1 { #2 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\@dtl@toks@gconcat@middle@cx} %\begin{definition} %\cs{dtl@toks@gconcat@middle@cx}\marg{toks name}\marg{before toks}\marg{stuff}\marg{after toks} %\end{definition} % Globally sets token register \cs{}\meta{toks name} to % the contents of \meta{before toks} concatenated with % \meta{stuff} (expanded) and the contents of \meta{after toks} %Deprecated. %TODO remove % \begin{macrocode} \newcommand{\@dtl@toks@gconcat@middle@cx}[4]{% \__datatool_token_register_gset:cx { #1 } { \the #2 #3 \the #4 } } % \end{macrocode} %\end{macro} % % Use a token register. % \begin{macrocode} \cs_new:Nn \__datatool_token_register_use:N { \the #1 } \cs_generate_variant:Nn \__datatool_token_register_use:N { c } % \end{macrocode} % Set a token register equal to another. % \begin{macrocode} \cs_new:Nn \__datatool_token_register_set_eq:NN { #1 = #2 } \cs_generate_variant:Nn \__datatool_token_register_set_eq:NN { cN, Nc, cc } % \end{macrocode} % Globally set a token register equal to another. % \begin{macrocode} \cs_new:Nn \__datatool_token_register_gset_eq:NN { \global #1 = #2 } \cs_generate_variant:Nn \__datatool_token_register_gset_eq:NN { cN, Nc, cc } % \end{macrocode} % Set the contents of a token register. % \begin{macrocode} \cs_new:Nn \__datatool_token_register_set:Nn { #1 = { #2 } } \cs_generate_variant:Nn \__datatool_token_register_set:Nn { cn, cx, co, cV, No, Nx, NV } % \end{macrocode} % Globally set the contents of a token register. % \begin{macrocode} \cs_new:Nn \__datatool_token_register_gset:Nn { \global #1 = { #2 } } \cs_generate_variant:Nn \__datatool_token_register_gset:Nn { cn, cx, co, cV, No, Nx } % \end{macrocode} % Append content to a token register. % \begin{macrocode} \cs_new:Nn \__datatool_token_register_put_right:Nn { \__datatool_token_register_set:No #1 { \the #1 #2 } } \cs_generate_variant:Nn \__datatool_token_register_put_right:Nn { cn, cx, co, cV, No, Nx } % \end{macrocode} % Globally append content to a token register. % \begin{macrocode} \cs_new:Nn \__datatool_token_register_gput_right:Nn { \__datatool_token_register_gset:No #1 { \the #1 #2 } } \cs_generate_variant:Nn \__datatool_token_register_gput_right:Nn { cn, cx, co, cV, No, Nx } % \end{macrocode} % % Concatenate the content of two registers with extra stuff in % between % \begin{macrocode} \cs_new:Nn \__datatool_token_register_concat_middle:NNnN { \__datatool_token_register_set:Nx #1 { \the #2 \exp_not:n { #3 } \the #4 } } \cs_generate_variant:Nn \__datatool_token_register_concat_middle:NNnN { cNnN, cNxN, NNxN } % \end{macrocode} % Globally concatenate. % \begin{macrocode} \cs_new:Nn \__datatool_token_register_gconcat_middle:NNnN { \__datatool_token_register_gset:Nx #1 { \the #2 \exp_not:n { #3 } \the #4 } } \cs_generate_variant:Nn \__datatool_token_register_gconcat_middle:NNnN { cNnN, cNxN, NNxN } % \end{macrocode} % %\subsection{Encodings} %\label{sec:code:encodings} % % Load file dealing with non-ASCII characters, but first initialise % ASCII defaults. Each character is represented by both a string % variable (for use by the sort comparator) and a token list % variable (for typesetting, if required). The difference in % expansion is more noticeable with \sty{input} than with native % Unicode engines. %\changes{3.0}{2025-03-03}{added encoding files} % %Define new variables with default ASCII value. % %Regular expression to match apostrophe. % \begin{macrocode} \regex_new:N \l_datatool_apos_regex \regex_set:Nn \l_datatool_apos_regex { \' } % \end{macrocode} % %Most symbols are currency signs, but mid-point is also added as %it's sometimes used as a number separator. % \begin{macrocode} \cs_new:Nn \__datatool_new_symbol:nn { \__datatool_new_symbol:nnn { #1 } { #2 } { #2 } } \cs_new:Nn \__datatool_new_symbol:nnn { \tl_new:c { l_datatool_ #1 _tl } \tl_set:cn { l_datatool_ #1 _tl } { #2 } \str_new:c { l_datatool_ #1 _str } \str_set:cn { l_datatool_ #1 _str } { #3 } } \cs_generate_variant:Nn \__datatool_new_symbol:nnn { nnV } % \end{macrocode} %Regular expression to search for these symbols, which may be used %in localisation files. Only those characters supported by the %encoding will be added. % \begin{macrocode} \regex_new:N \l_datatool_currencysigns_regex \regex_set:Nn \l_datatool_currencysigns_regex { \$ } % \end{macrocode} %Allow encoding file to change the values. % \begin{macrocode} \cs_new:Nn \datatool_set_symbol:nn { \str_set:cn { l_datatool_ #1 _str } { #2 } \tl_set:cn { l_datatool_ #1 _tl } { #2 } } \cs_generate_variant:Nn \datatool_set_symbol:nn { ne } % \end{macrocode} % Designed for \sty{inputenc} to set by character code: % \begin{macrocode} \cs_new:Nn \datatool_set_symbol_from_charcode:nn { \str_set:ce { l_datatool_ #1 _str } { \char_generate:nn { #2 } { 12 } } \exp_args:Nno \tl_set:co { l_datatool_ #1 _tl } { \char_generate:nn { #2 } { 13 } } } % \end{macrocode} %Just for currency signs so that they can be added to the regular %expression and list of known currencies: % \begin{macrocode} \cs_new:Nn \datatool_set_currencysign:nn { \datatool_set_symbol:nn { #1 } { #2 } \regex_set:Nn \l_datatool_currencysigns_regex { \ur { l_datatool_currencysigns_regex } | \u { l_datatool_ #1 _str } } \DTLnewcurrencysymbol { #2 } } \cs_generate_variant:Nn \datatool_set_currencysign:nn { ne } % \end{macrocode} % % \begin{macrocode} \cs_new:Nn \datatool_set_currencysign_from_charcode:nn { \datatool_set_symbol_from_charcode:nn { #1 } { #2 } \regex_set:Nn \l_datatool_currencysigns_regex { \ur { l_datatool_currencysigns_regex } | \u { l_datatool_ #1 _str } } } % \end{macrocode} %These may be redefined by an encoding file, if available. %See section~\ref{sec:code:encodings}. % \begin{macrocode} \__datatool_new_symbol:nn { cent } { c } \__datatool_new_symbol:nn { pound } { L } \__datatool_new_symbol:nnV { currency } { \# } \c_hash_str \__datatool_new_symbol:nn { yen } { Y } \__datatool_new_symbol:nn { middot } { . } \__datatool_new_symbol:nn { florin } { f } \__datatool_new_symbol:nn { baht } { B } \__datatool_new_symbol:nn { ecu } { CE } \__datatool_new_symbol:nn { colonsign } { C } \__datatool_new_symbol:nn { cruzerio } { Cr } \__datatool_new_symbol:nn { frenchfranc } { F } \__datatool_new_symbol:nn { lira } { L } \__datatool_new_symbol:nn { mill } { m } \__datatool_new_symbol:nn { naira } { N } \__datatool_new_symbol:nn { peseta } { Pts } \__datatool_new_symbol:nn { rupee } { Rs } \__datatool_new_symbol:nn { won } { W } \__datatool_new_symbol:nn { shekel } { S } \__datatool_new_symbol:nn { dong } { d } \__datatool_new_symbol:nn { euro } { E } \__datatool_new_symbol:nn { kip } { K } \__datatool_new_symbol:nn { tugrik } { T } \__datatool_new_symbol:nn { drachma } { Dr } \__datatool_new_symbol:nn { germanpenny } { p } \__datatool_new_symbol:nn { peso } { P } \__datatool_new_symbol:nn { guarani } { G. } \__datatool_new_symbol:nn { austral } { A } \__datatool_new_symbol:nn { hryvnia } { S } \__datatool_new_symbol:nn { cedi } { C } \__datatool_new_symbol:nn { livretournois } { lt } \__datatool_new_symbol:nn { spesmilo } { Sm } \__datatool_new_symbol:nn { tenge } { T } \__datatool_new_symbol:nn { indianrupee } { R } \__datatool_new_symbol:nn { turkishlira } { L } \__datatool_new_symbol:nn { nordicmark } { M } \__datatool_new_symbol:nn { manat } { M } \__datatool_new_symbol:nn { ruble } { R } \__datatool_new_symbol:nn { lari } { L } \__datatool_new_symbol:nn { bitcoin } { B } \__datatool_new_symbol:nn { som } { c } % \end{macrocode} % % An internal list that stores all known currencies. % This sequence variable and \cs{DTLnewcurrencysymbol} need to be % defined before the ldf encoding file is input. % \begin{macrocode} \seq_new:N \l__datatool_known_currencies_seq % \end{macrocode} % A list of all defined currency labels: % \begin{macrocode} \seq_new:N \l_datatool_currencies_seq % \end{macrocode} % A list of all defined currency labels registered by regions: % \begin{macrocode} \seq_new:N \l_datatool_regional_currencies_seq % \end{macrocode} % Map region to currency code: % \begin{macrocode} \prop_new:N \l_datatool_regional_currencies_prop % \end{macrocode} % %\begin{macro}{\DTLnewcurrencysymbol} %\changes{3.0}{2025-03-03}{changed to document command} %\begin{definition} % \cs{DTLaddcurrency}\marg{symbol} %\end{definition} % Adds \meta{symbol} to the list of known currencies % \begin{macrocode} \NewDocumentCommand \DTLnewcurrencysymbol { m } { \seq_if_in:NnF \l__datatool_known_currencies_seq { #1 } { \seq_put_right:Nn \l__datatool_known_currencies_seq { #1 } } } % \end{macrocode} %\end{macro} % % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} % % \begin{macrocode} \InputIfFileExists {datatool-\TrackLangEncodingName .ldf} {} { \PackageInfo{datatool-base} {Missing file `datatool-\TrackLangEncodingName .ldf'. Falling back on US-ASCII for \string\datatool\string_...\string_str commands} } % \end{macrocode} % % %\subsection{Locale Dependent Information} % % \begin{macrocode} \ExplSyntaxOn % \end{macrocode} % %\begin{macro}{\@dtl@decimal} %\changes{3.0}{2025-03-03}{replaced} % The current decimal character was stored in \cs{@dtl@decimal} % prior to version 3.0. As from v3.0, there are separate variables % for formatting and parsing. % \begin{macrocode} \tl_new:N \__datatool_decimal_tl % \end{macrocode} %Parsing may either be a token list: % \begin{macrocode} \tl_new:N \__datatool_decimal_parse_tl % \end{macrocode} %or a regular expression: % \begin{macrocode} \regex_new:N \__datatool_decimal_parse_regex % \end{macrocode} %\end{macro} %\begin{macro}{\@dtl@numbergroupchar} %\changes{3.0}{2025-03-03}{replaced} % The current number group character was stored in % \cs{@dtl@numbergroupchar} % prior to version 3.0. As from v3.0, there are separate variables % for formatting and parsing. % \begin{macrocode} \tl_new:N \__datatool_numbergroup_tl % \end{macrocode} %Parsing may either be a token list: % \begin{macrocode} \tl_new:N \__datatool_numbergroup_parse_tl % \end{macrocode} %or a regular expression: % \begin{macrocode} \regex_new:N \__datatool_numbergroup_parse_regex % \end{macrocode} %\end{macro} %Regular expressions for parsing numeric values. % For parsing locale numbers: % \begin{macrocode} \regex_new:N \l_datatool_locale_numeric_regex \regex_new:N \l_datatool_locale_fractional_regex \regex_new:N \l_datatool_locale_fractional_nozero_regex % \end{macrocode} % TeX's maximum integer limit is 0x7FFFFFFF so provide a regular % expression to capture large integers that would otherwise trigger % an error. (For example, a numeric ID or a bare telephone number % without parentheses or hyphens could well exceed this value.) % \begin{macrocode} \regex_new:N \l_datatool_locale_bigint_regex % \end{macrocode} % For splitting standard decimals into number groups % (dot for decimal separator and no number groups): % \begin{macrocode} \regex_const:Nn \c_datatool_decimal_grps_regex { \A ([+\-])? 0* (\d{1,3}) \. (\d+) \Z } \regex_const:Nn \c_datatool_decimal_grps_i_regex { \A ([+\-])? 0* (\d{1,3}) (\d{3}) \. (\d+) \Z } \regex_const:Nn \c_datatool_decimal_grps_ii_regex { \A ([+\-])? 0* (\d{1,3}) (\d{3}) (\d{3}) \. (\d+) \Z } \regex_const:Nn \c_datatool_decimal_grps_iii_regex { \A ([+\-])? 0* (\d+) (\d{3}) (\d{3}) (\d{3}) \. (\d+) \Z } % \end{macrocode} % Similarly for integers: % \begin{macrocode} \regex_const:Nn \c_datatool_integer_grps_i_regex { \A ([+\-])? 0* (\d{1,3}) (\d{3}) \Z } \regex_const:Nn \c_datatool_integer_grps_ii_regex { \A ([+\-])? 0* (\d{1,3}) (\d{3}) (\d{3}) \Z } \regex_const:Nn \c_datatool_integer_grps_iii_regex { \A ([+\-])? 0* (\d+) (\d{3}) (\d{3}) (\d{3}) \Z } \regex_const:Nn \c_datatool_integer_no_grps_regex { \A ([+\-])? (\d{1,3}) \Z } % \end{macrocode} %Trimming: % \begin{macrocode} \regex_const:Nn \c_datatool_numeric_leading_zeros_regex { \A ([+\-])? 0* (\d+ (?: (\.\d+) )? ) \Z } \regex_const:Nn \c_datatool_integer_leading_zeros_regex { \A ([+\-])? 0* (\d+ ) \Z } \regex_const:Nn \c_datatool_decimal_redundant_zeros_regex { \A ([+\-])? 0* (\d+ \. \d+ ) 0* \Z } \regex_const:Nn \c_datatool_decimal_implicit_zero_regex { \A ([+\-])? ( \. \d+ ) 0* \Z } % \end{macrocode} %Any unformatted number: % \begin{macrocode} \regex_const:Nn \c_datatool_any_numeric_regex { \A ([+\-])? \d* \. ? \d+ ( [eE] [+\-]? \d+ ) ? \Z } % \end{macrocode} %Get fractional part: % \begin{macrocode} \cs_new:Nn \datatool_decimal_frac:n { \__datatool_decimal_frac:w #1 . 0 . 0 \q_stop } \cs_generate_variant:Nn \datatool_decimal_frac:n { e , o } % \end{macrocode} % Internal command: % \begin{macrocode} \cs_new:Npn \__datatool_decimal_frac:w #1.#2.#3 \q_stop { \tl_if_head_eq_meaning:nNT { #1 } - { - } 0 . #2 } % \end{macrocode} %Right zero pad or truncate decimals. % \begin{macrocode} \cs_new:Npn \__datatool_decimal_places_i:w #1.#2#3\q_stop { #1.#2 } \cs_new:Npn \__datatool_decimal_places_ii:w #1.#2#3#4\q_stop { #1.#2#3 } \cs_new:Npn \__datatool_decimal_places_iii:w #1.#2#3#4#5\q_stop { #1.#2#3#4 } \cs_new:Npn \__datatool_decimal_places_iv:w #1.#2#3#4#5#6\q_stop { #1.#2#3#4#5 } \cs_new:Npn \__datatool_decimal_places_v:w #1.#2#3#4#5#6#7\q_stop { #1.#2#3#4#5#6 } \cs_new:Npn \__datatool_decimal_places_vi:w #1.#2#3#4#5#6#7#8\q_stop { #1.#2#3#4#5#6#7 } \cs_new:Npn \__datatool_decimal_places_vii:w #1.#2#3#4#5#6#7#8#9\q_stop { #1.#2#3#4#5#6#7#8 } % \end{macrocode} %NB First check that the number is a decimal. %Expand to padded value: % \begin{macrocode} \cs_new:Nn \__datatool_decimal_trunc_pad_zeros:nn { \int_case:nnF { #2 } { { 1 } { \__datatool_decimal_places_i:w #1 0 \q_stop } { 2 } { \__datatool_decimal_places_ii:w #1 00 \q_stop } { 3 } { \__datatool_decimal_places_iii:w #1 000 \q_stop } { 4 } { \__datatool_decimal_places_iv:w #1 0000 \q_stop } { 5 } { \__datatool_decimal_places_v:w #1 00000 \q_stop } { 6 } { \__datatool_decimal_places_vi:w #1 000000 \q_stop } } { \__datatool_decimal_places_vii:w #1 0000000 \q_stop } } % \end{macrocode} % %\begin{macro}{\DTLsetnumberchars} %\begin{definition} %\cs{DTLsetnumberchars}\marg{number group char}\marg{decimal char} %\end{definition} % This sets the decimal character and number group % characters. % \begin{macrocode} \NewDocumentCommand \DTLsetnumberchars { m m } { \datatool_set_numberchars:nn { #1 } { #2 } } % \end{macrocode} %\end{macro} % \begin{macrocode} \cs_new:Nn \datatool_set_numberchars:nn { \datatool_set_numberchars:nnnn { #1 } { #2 } { #1 } { #2 } } \cs_generate_variant:Nn \datatool_set_numberchars:nn { nV , Vn , VV } % \end{macrocode} %Syntax: \marg{format number group char} \marg{format decimal char} % \marg{parse number group char} \marg{parse decimal char} % \begin{macrocode} \cs_new:Nn \datatool_set_numberchars:nnnn { \tl_set:Nn \__datatool_numbergroup_tl { #1 } \tl_set:Nn \__datatool_numbergroup_parse_tl { #3 } \tl_set:Nn \__datatool_decimal_tl { #2 } \tl_set:Nn \__datatool_decimal_parse_tl { #4 } % \end{macrocode} %Regular expressions used for parsing. % \begin{macrocode} \regex_set:Nn \l_datatool_locale_bigint_regex { \A [+\-]?\d* [2-9] \u{__datatool_numbergroup_parse_tl}? \d{3} \u{__datatool_numbergroup_parse_tl}? \d{3} \u{__datatool_numbergroup_parse_tl}? \d{3} \Z } \regex_set:Nn \l_datatool_locale_numeric_regex { \A ([+\-]?\d+) (?:\u{__datatool_numbergroup_parse_tl}(\d{3}))? (?:\u{__datatool_numbergroup_parse_tl}(\d{3}))? (?:\u{__datatool_numbergroup_parse_tl}(\d{3}))? (?:\u{__datatool_decimal_parse_tl}(\d+))? \Z } \regex_set:Nn \l_datatool_locale_fractional_regex { \A ([+\-]?\d+) \u{__datatool_decimal_parse_tl} (\d+) \Z } \regex_set:Nn \l_datatool_locale_fractional_nozero_regex { \A ([+\-]?) \u{__datatool_decimal_parse_tl} (\d+) \Z } } \cs_generate_variant:Nn \datatool_set_numberchars:nnnn { VVVV , eeee , VnVn , nnVn } % \end{macrocode} %Set the default. % \begin{macrocode} \DTLsetnumberchars{,}{.} % \end{macrocode} % %As above but where a regular expression is needed for parsing. % \begin{macrocode} \cs_new:Nn \datatool_set_numberchars_regex:nnnn { \tl_set:Nn \__datatool_numbergroup_tl { #1 } \tl_clear:N \__datatool_numbergroup_parse_tl \regex_set:Nn \__datatool_numbergroup_parse_regex { #3 } \tl_set:Nn \__datatool_decimal_tl { #2 } \tl_clear:N \__datatool_decimal_parse_tl \regex_set:Nn \__datatool_decimal_parse_regex { #4 } % \end{macrocode} %Regular expressions used for parsing. % \begin{macrocode} \regex_set:Nn \l_datatool_locale_bigint_regex { \A [+\-]?\d* [2-9] \ur{__datatool_numbergroup_parse_regex}? \d{3} \ur{__datatool_numbergroup_parse_regex}? \d{3} \ur{__datatool_numbergroup_parse_regex}? \d{3} \Z } \regex_set:Nn \l_datatool_locale_numeric_regex { \A ([+\-]?\d+) (?:\ur{__datatool_numbergroup_parse_regex}(\d{3}))? (?:\ur{__datatool_numbergroup_parse_regex}(\d{3}))? (?:\ur{__datatool_numbergroup_parse_regex}(\d{3}))? (?:\ur{__datatool_decimal_parse_regex}(\d+))? \Z } \regex_set:Nn \l_datatool_locale_fractional_regex { \A ([+\-]?\d+) \ur{__datatool_decimal_parse_regex} (\d+) \Z } \regex_set:Nn \l_datatool_locale_fractional_nozero_regex { \A ([+\-]?) \ur{__datatool_decimal_parse_regex} (\d+) \Z } } \cs_generate_variant:Nn \datatool_set_numberchars_regex:nnnn { VVnn , Vnnn , nVnn } % \end{macrocode} % %As above but regex is needed for number group and token %list for decimal group when parsing. % \begin{macrocode} \cs_new:Nn \datatool_set_numberchars_regex_tl:nnnn { \tl_set:Nn \__datatool_numbergroup_tl { #1 } \tl_clear:N \__datatool_numbergroup_parse_tl \regex_set:Nn \__datatool_numbergroup_parse_regex { #3 } \tl_set:Nn \__datatool_decimal_tl { #2 } \tl_set:Nn \__datatool_decimal_parse_tl { #4 } % \end{macrocode} %Regular expressions used for parsing. % \begin{macrocode} \regex_set:Nn \l_datatool_locale_bigint_regex { \A [+\-]?\d* [2-9] \ur{__datatool_numbergroup_parse_regex}? \d{3} \ur{__datatool_numbergroup_parse_regex}? \d{3} \ur{__datatool_numbergroup_parse_regex}? \d{3} \Z } \regex_set:Nn \l_datatool_locale_numeric_regex { \A ([+\-]?\d+) (?:\ur{__datatool_numbergroup_parse_regex}(\d{3}))? (?:\ur{__datatool_numbergroup_parse_regex}(\d{3}))? (?:\ur{__datatool_numbergroup_parse_regex}(\d{3}))? (?:\u{__datatool_decimal_parse_tl}(\d+))? \Z } \regex_set:Nn \l_datatool_locale_fractional_regex { \A ([+\-]?\d+) \u{__datatool_decimal_parse_tl} (\d+) \Z } \regex_set:Nn \l_datatool_locale_fractional_nozero_regex { \A ([+\-]?) \u{__datatool_decimal_parse_tl} (\d+) \Z } } \cs_generate_variant:Nn \datatool_set_numberchars_regex_tl:nnnn { VVnn , Vnnn , nVnn , nVnV , nnnV } % \end{macrocode} % %As above but token list is needed for number group and regex for %decimal group when parsing. % \begin{macrocode} \cs_new:Nn \datatool_set_numberchars_tl_regex:nnnn { \tl_set:Nn \__datatool_numbergroup_tl { #1 } \tl_set:Nn \__datatool_numbergroup_parse_tl { #3 } \tl_set:Nn \__datatool_decimal_tl { #2 } \tl_clear:N \__datatool_decimal_parse_tl \regex_set:Nn \__datatool_decimal_parse_regex { #4 } % \end{macrocode} %Regular expressions used for parsing. % \begin{macrocode} \regex_set:Nn \l_datatool_locale_bigint_regex { \A [+\-]?\d* [2-9] \u{__datatool_numbergroup_parse_tl}? \d{3} \u{__datatool_numbergroup_parse_tl}? \d{3} \u{__datatool_numbergroup_parse_tl}? \d{3} \Z } \regex_set:Nn \l_datatool_locale_numeric_regex { \A ([+\-]?\d+) (?:\u{__datatool_numbergroup_parse_tl}(\d{3}))? (?:\u{__datatool_numbergroup_parse_tl}(\d{3}))? (?:\u{__datatool_numbergroup_parse_tl}(\d{3}))? (?:\ur{__datatool_decimal_parse_regex}(\d+))? \Z } \regex_set:Nn \l_datatool_locale_fractional_regex { \A ([+\-]?\d+) \ur{__datatool_decimal_parse_regex} (\d+) \Z } \regex_set:Nn \l_datatool_locale_fractional_nozero_regex { \A ([+\-]?) \ur{__datatool_decimal_parse_regex} (\d+) \Z } } \cs_generate_variant:Nn \datatool_set_numberchars_tl_regex:nnnn { VVnn , Vnnn , nVnn , VnVn , nnVn } % \end{macrocode} % %\changes{3.0}{2025-03-03}{added thinspace, apostrophe, and underscore %number group commands} %Convenient shortcut to set thin-space number group: % \begin{macrocode} \datatool_if_unicode_engine:TF { \cs_new:Nn \datatool_set_thinspace_group_decimal_char:n { \datatool_set_numberchars_regex_tl:nnnn { \, } { #1 } { \c{,} | \s | \x{2009} } { #1 } } } { \cs_new:Nn \datatool_set_thinspace_group_decimal_char:n { \datatool_set_numberchars_regex_tl:nnnn { \, } { #1 } { \c{,} | \s | \x{E2} \x{80} \x{89} } { #1 } } } \cs_generate_variant:Nn \datatool_set_thinspace_group_decimal_char:n { V } % \end{macrocode} % %Similarly for apostrophe: % \begin{macrocode} \datatool_if_unicode_engine:TF { \regex_const:Nn \c_datatool_apostrophe_regex { \x{27} | \x{2019} } } { \regex_const:Nn \c_datatool_apostrophe_regex { \x{27} | \x{E2} \x{80} \x{99} } } \cs_new:Nn \datatool_set_apos_group_decimal_char:n { \datatool_set_numberchars_regex_tl:nnnn { ' } { #1 } { \ur{c_datatool_apostrophe_regex} } { #1 } } \cs_generate_variant:Nn \datatool_set_apos_group_decimal_char:n { V } % \end{macrocode} % % %Similarly for underscore number group character. % \begin{macrocode} \cs_new:Nn \datatool_set_underscore_group_decimal_char:n { \datatool_set_numberchars_regex_tl:nnnn { \_ } { #1 } { \c{_} | \x{5F} } { #1 } } \cs_generate_variant:Nn \datatool_set_underscore_group_decimal_char:n { V } % \end{macrocode} % % \subsubsection{Determining Data Types} % The control sequence \cs{\_\_datatool\_datum:nnnn} parses its % argument to determine the data type of its argument. Each data % type is represented by one of the following values: 0 (string), % 1 (integer), 2 (decimal), 3 (currency), 4 (datetime), 5 (date), % or 6 (time). The unknown data type -1 % is used when the data is empty. Numeric values are expected to use % the number group and decimal characters currently in effect. % % The temporal data types (datetime, date and time) are new to v3.0 % and have a numeric value. % This is mainly provided for the benefit of datatooltk % as imported data typically treats date/time data as numeric % and it may be useful to retain the numeric value and ISO format. % The Julian day number (an integer) is used to represent a date, % the Julian time (a decimal between $-0.5$ an $0.5$, where 0 is % midday) is used to represent a time, and the Julian date % (the sum of the Julian day number and the Julian time) is used to % represent a timestamp (datetime). % %\begin{macro}{\@dtl@datatype} %Scratch variable used to represent the data type identifier. %This may be replaced with an integer variable at some point. % \begin{macrocode} \newcount\@dtl@datatype % \end{macrocode} %\end{macro} %Scratch variables. Integers: % \begin{macrocode} \int_new:N \l__datatool_tmpa_int \int_new:N \l__datatool_tmpb_int \int_new:N \l__datatool_tmpc_int \int_new:N \l__datatool_tmpd_int % \end{macrocode} %Used to count or index items: % \begin{macrocode} \int_new:N \l__datatool_count_int % \end{macrocode} %Temporary storage of data type identifier: % \begin{macrocode} \int_new:N \l__datatool_tmp_datatype_int % \end{macrocode} %Token lists (the first one may have already been %defined if \sty{datatool} has been loaded): % \begin{macrocode} \tl_clear_new:N \l__datatool_tmpa_tl \tl_new:N \l__datatool_tmpb_tl \tl_new:N \l__datatool_tmpc_tl \tl_new:N \l__datatool_tmpd_tl \tl_new:N \l__datatool_tmp_currency_tl \tl_new:N \l__datatool_tmp_initial_tl \tl_new:N \l__datatool_result_tl \tl_new:N \l__datatool_resulta_tl \tl_new:N \l__datatool_resultb_tl \tl_new:N \l__datatool_dialect_tl % \end{macrocode} %Floating point: % \begin{macrocode} \fp_new:N \l__datatool_tmpa_fp \fp_new:N \l__datatool_tmpb_fp \fp_new:N \l__datatool_tmpc_fp \fp_new:N \l__datatool_tmpd_fp \fp_new:N \l__datatool_mean_fp \fp_new:N \l__datatool_total_fp \fp_new:N \l__datatool_datum_value_fp % \end{macrocode} %Dimensions: % \begin{macrocode} \dim_new:N \l__datatool_tmpa_dim \dim_new:N \l__datatool_tmpb_dim % \end{macrocode} %Strings: % \begin{macrocode} \str_new:N \l__datatool_tmpa_str \str_new:N \l__datatool_tmpb_str \str_new:N \l__datatool_tmpc_str % \end{macrocode} %Clist. % \begin{macrocode} \clist_new:N \l__datatool_tmp_clist % \end{macrocode} %Sequences. % \begin{macrocode} \seq_new:N \l__datatool_tmp_seq \seq_new:N \l__datatool_tmpa_seq \seq_new:N \l__datatool_tmpb_seq % \end{macrocode} %The following may also be used by the localisation files, so not a private %variable, but still just a scratch variable. % \begin{macrocode} \seq_new:N \l_datatool_regex_match_seq \seq_new:N \l_datatool_timestamp_match_seq % \end{macrocode} %Regular expressions % \begin{macrocode} \regex_new:N \l__datatool_tmpa_regex \regex_new:N \l__datatool_tmpb_regex % \end{macrocode} %Data type identifiers. % \begin{macrocode} \int_const:Nn \c_datatool_unknown_int {-1} \int_const:Nn \c_datatool_string_int {0} \int_const:Nn \c_datatool_integer_int {1} \int_const:Nn \c_datatool_decimal_int {2} \int_const:Nn \c_datatool_currency_int {3} \int_const:Nn \c_datatool_datetime_int {4} \int_const:Nn \c_datatool_date_int {5} \int_const:Nn \c_datatool_time_int {6} % \end{macrocode} %Maximum known value. This will be updated if any new types are %added in future. % \begin{macrocode} \cs_new:Nn \datatool_max_known_type: { \c_datatool_time_int } % \end{macrocode} % %\begin{macro}{\DTLgetDataTypeName} %\changes{3.0}{2025-03-03}{new} %Get textual name for data type (expandable): % \begin{macrocode} \newcommand*{\DTLgetDataTypeName}[1] { \int_case:nnF { #1 } { { \c_datatool_unknown_int } { \DTLdatatypeunsetname } { \c_datatool_string_int } { \DTLdatatypestringname } { \c_datatool_integer_int } { \DTLdatatypeintegername } { \c_datatool_decimal_int } { \DTLdatatypedecimalname } { \c_datatool_currency_int } { \DTLdatatypecurrencyname } { \c_datatool_datetime_int } { \DTLdatatypedatetimename } { \c_datatool_date_int } { \DTLdatatypedatename } { \c_datatool_time_int } { \DTLdatatypetimename } } { \DTLdatatypeinvalidname } } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLdatatypeunsetname} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand \DTLdatatypeunsetname { unset } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLdatatypestringname} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand \DTLdatatypestringname { string } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLdatatypeintegername} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand \DTLdatatypeintegername { integer } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLdatatypedecimalname} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand \DTLdatatypedecimalname { decimal } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLdatatypecurrencyname} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand \DTLdatatypecurrencyname { currency } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLdatatypedatetimename} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand \DTLdatatypedatetimename { date-time } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLdatatypedatename} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand \DTLdatatypedatename { date } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLdatatypetimename} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand \DTLdatatypetimename { time } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLdatatypeinvalidname} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand \DTLdatatypeinvalidname { invalid } % \end{macrocode} %\end{macro} % %Test for for datum types. The argument should be a number. %Test if the number is a recognised type (including \qt{unknown}): % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_valid_datum_type:n #1 { p, T, F, TF } { \bool_lazy_or:nnTF { \int_compare_p:nNn { #1 } < { \c_datatool_unknown_int } } { \int_compare_p:nNn { #1 } > { \datatool_max_known_type: } } { \prg_return_false: } { \prg_return_true: } } % \end{macrocode} %Test if the argument is a numeric datum type ID (currency, dates and times are %considered numeric, in addition to integers and decimals): % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_numeric_datum_type:n #1 { p, T, F, TF } { \bool_lazy_or:nnTF { \int_compare_p:nNn { #1 } < { \c_datatool_integer_int } } { \int_compare_p:nNn { #1 } > { \c_datatool_time_int } } { \prg_return_false: } { \prg_return_true: } } % \end{macrocode} %Test if the argument is a temporal datum type ID (timestamp, date %or time): % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_temporal_datum_type:n #1 { p, T, F, TF } { \bool_lazy_or:nnTF { \int_compare_p:nNn { #1 } < { \c_datatool_datetime_int } } { \int_compare_p:nNn { #1 } > { \c_datatool_time_int } } { \prg_return_false: } { \prg_return_true: } } % \end{macrocode} %Test if the argument is an integer or decimal datum type ID (not %currency or temporal): % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_number_only_datum_type:n #1 { p, T, F, TF } { \bool_lazy_or:nnTF { \int_compare_p:nNn { #1 } = { \c_datatool_integer_int } } { \int_compare_p:nNn { #1 } = { \c_datatool_decimal_int } } { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} %Test if the argument is any datum type that has an integer %value (integer or date): % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_any_int_datum_type:n #1 { p, T, F, TF } { \bool_lazy_or:nnTF { \int_compare_p:nNn { #1 } = { \c_datatool_integer_int } } { \int_compare_p:nNn { #1 } = { \c_datatool_date_int } } { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % %\changes{3.0}{2025-03-03}{moved lower-level null commands from \sty{datatool} to \sty{datatool-base}} %The null commands were originally in \sty{datatool} but as from %v3.0, the lower-level commands are now in %\sty{datatool-base} since commands like \cs{DTLdatumvalue} need to check for %null. The \sty{person} package, which may be used with just %\sty{datatool-base} and not \sty{datatool}, performs a null test. %\begin{macro}{\@dtlnovalue} %\changes{3.0}{2025-03-03}{replaced with \cs{c\_nullvalue\_tl}} %The old internal command \cs{@dtlnovalue} has been replaced with %the constant \verb|\c_datatool_nullvalue_tl|. %This is similar in concept to \verb|c_novalue_tl| but is intended %to represent missing values in \sty{datatool} return values, %particularly when querying database values. This starts with a %space in the event that a null value is expanded in a context that tests for %an initial letter without checking the category code. It ends with a space %that has a letter category code. This mixture of category codes is designed %to make it harder to accidentally identify the %string \qt{ Undefined Value } as null. % %Note that neither the old internal command nor this new constant %are intended to be included in a list of data or in a %database. DatatoolTk does still use the old command name %as a marker for its own internal use, although it shouldn't make its %way into an output file. % \begin{macrocode} \tl_const:Nx \c_datatool_nullvalue_tl { \c_catcode_other_space_tl \tl_to_str:n { Undefined } \c_catcode_other_space_tl \tl_to_str:n { Value } \char_generate:nn { 32 } { 11 } } % \end{macrocode} %\end{macro} %\begin{macro}{\dtlnovalue} %\changes{3.0}{2025-03-03}{moved from \sty{datatool} to \sty{datatool-base}} % \begin{macrocode} \newcommand* \dtlnovalue { \c_datatool_nullvalue_tl } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLstringnull} %\changes{3.0}{2025-03-03}{moved from \sty{datatool} to \sty{datatool-base}} % String null value: % \begin{macrocode} \newcommand* \DTLstringnull { \@dtlstringnull } % \end{macrocode} %\end{macro} %\begin{macro}{\@dtlstringnull} %\changes{2.13}{2013-01-15}{new} %\changes{3.0}{2025-03-03}{moved from \sty{datatool} to \sty{datatool-base}} % String null value: % \begin{macrocode} \newcommand* \@dtlstringnull {NULL} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLnumbernull} %\changes{3.0}{2025-03-03}{moved from \sty{datatool} to \sty{datatool-base}} % Number null value: % \begin{macrocode} \newcommand* \DTLnumbernull { \@dtlnumbernull } % \end{macrocode} %\end{macro} %\begin{macro}{\@dtlnumbernull} %\changes{2.13}{2013-01-15}{new} %\changes{3.0}{2025-03-03}{moved from \sty{datatool} to \sty{datatool-base}} % Number null value: % \begin{macrocode} \newcommand*{\@dtlnumbernull}{0} % \end{macrocode} %\end{macro} % %Constant representing an empty datum value with unknown type. % \begin{macrocode} \tl_const:Nn \c_datatool_empty_datum_tl { \__datatool_datum:nnnn { } { } { } { \c_datatool_unknown_int } } % \end{macrocode} % %\cs{DTLifnull} is still defined in \sty{datatool}. Provide low-level %commands. Test token for null. NB this doesn't test against the expansion %text of the string and number null values as they could easily be %valid non-null values, but it will test against the actual null %constant value. % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_null:N #1 { p, T, F, TF } { \bool_lazy_any:nTF { { \tl_if_eq_p:NN #1 \dtlnovalue } { \tl_if_eq_p:NN #1 \DTLstringnull } { \tl_if_eq_p:NN #1 \DTLnumbernull } { \tl_if_eq_p:NN #1 \c_datatool_nullvalue_tl } } { \prg_return_true: } { \prg_return_false: } } \cs_generate_variant:Nn \datatool_if_null:NTF { cTF } % \end{macrocode} %Test for null where the argument may be a single token %or may be a token list. If the argument is a single token, then %the test is the same as the above otherwise the result will be %false. % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_null:n #1 { T, F, TF } { \tl_if_single_token:nTF { #1 } { \bool_lazy_any:nTF { { \tl_if_eq_p:NN #1 \dtlnovalue } { \tl_if_eq_p:NN #1 \DTLstringnull } { \tl_if_eq_p:NN #1 \DTLnumbernull } { \tl_if_eq_p:NN #1 \c_datatool_nullvalue_tl } } { \prg_return_true: } { \tl_if_eq:NnTF \dtlnovalue { #1 } { \prg_return_true: } { \tl_if_eq:NnTF \DTLstringnull { #1 } { \prg_return_true: } { \tl_if_eq:NnTF \DTLnumbernull { #1 } { \prg_return_true: } { \prg_return_false: } } } } } { \tl_if_eq:NnTF \c_datatool_nullvalue_tl { #1 } { \prg_return_true: } { \prg_return_false: } } } % \end{macrocode} %Test for null or empty. This includes testing for the empty datum. % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_null_or_empty:N #1 { p, T, F, TF } { \bool_lazy_any:nTF { { \tl_if_empty_p:N #1 } { \tl_if_eq_p:NN #1 \dtlnovalue } { \tl_if_eq_p:NN #1 \DTLstringnull } { \tl_if_eq_p:NN #1 \DTLnumbernull } { \tl_if_eq_p:NN #1 \c_datatool_nullvalue_tl } { \tl_if_eq_p:NN #1 \c_datatool_empty_datum_tl } } { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} %Test for null or empty where the argument may be a single token %or may be a token list. If the argument is a single token, then %the test is the same as the above. If the argument isn't a %single token the result will be false unless the argument is empty. % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_null_or_empty:n #1 { T, F, TF } { \tl_if_single_token:nTF { #1 } { \bool_lazy_any:nTF { { \tl_if_empty_p:N #1 } { \tl_if_eq_p:NN #1 \dtlnovalue } { \tl_if_eq_p:NN #1 \DTLstringnull } { \tl_if_eq_p:NN #1 \DTLnumbernull } { \tl_if_eq_p:NN #1 \c_datatool_nullvalue_tl } { \tl_if_eq_p:NN #1 \c_datatool_empty_datum_tl } } { \prg_return_true: } { \tl_if_eq:NnTF \dtlnovalue { #1 } { \prg_return_true: } { \tl_if_eq:NnTF \DTLstringnull { #1 } { \prg_return_true: } { \tl_if_eq:NnTF \DTLnumbernull { #1 } { \prg_return_true: } { \prg_return_false: } } } } } { \tl_if_empty:nTF { #1 } { \prg_return_true: } { \tl_if_eq:NnTF \c_datatool_nullvalue_tl { #1 } { \prg_return_true: } { \tl_if_eq:NnTF \c_datatool_empty_datum_tl { #1 } { \prg_return_true: } { \prg_return_false: } } } } } % \end{macrocode} % %Datum marker. The first argument is the original content. The %second should expand to a (non-locale) numeric representation or empty for %non-numeric data. (The second argument may contain additional %content that will ordinarily be discarded on expansion, but may be %retrieved if required.) %The third argument is the currency symbol or %empty if not currency. The final argument is the integer data type identifier. % \begin{macrocode} \cs_new:Nn \__datatool_datum:nnnn { \exp_not:n { #1 } } % \end{macrocode} % This means that the datum control sequence content consists of five % things: the datum marker and its four arguments. % %The weird datum is designed to allow easy lookup in databases. %It uses a similar trick to \sty{etoolbox} that uses the bar \verb"|" %character with category code~3 as a marker. % \begin{macrocode} \group_begin: % \end{macrocode} %Locally change category code of \verb"|" % \begin{macrocode} \char_set_catcode_math_toggle:N \| % \end{macrocode} %Create a constant token list that expands to this character with %this category code. % \begin{macrocode} \tl_const:Nn \c__datatool_datum_weird_marker_tl { | } % \end{macrocode} %Define the weird function to expand to the regular datum function. %This is designed for expandable contexts to perform a quick conversion % without expanding the arguments. % \begin{macrocode} \cs_new:Npn \__datatool_datum:w | #1 | #2 | #3 | #4 | { \exp_not:N \__datatool_datum:nnnn { \exp_not:n { #1 } } { \exp_not:n { #2 } } { \exp_not:n { #3 } } { \exp_not:n { #4 } } } % \end{macrocode} %Allow marker to expand. % \begin{macrocode} \cs_new:Npn \__datatool_datum_use:w | #1 | #2 | #3 | #4 | { \__datatool_datum:nnnn { #1 } { #2 } { #3 } { #4 } } % \end{macrocode} %Only the first argument (but don't expand that): % \begin{macrocode} \cs_new:Npn \__datatool_datum_use_i:w | #1 | #2 | #3 | #4 | { \exp_not:n { #1 } } % \end{macrocode} %Define the reverse. Four arguments provided (datum marker not %included). This only prevents the string and currency arguments %from expanding. % \begin{macrocode} \cs_new:Nn \__datatool_weird_datum:nnnn { \exp_not:N \__datatool_datum:w | \exp_not:n { #1 } | #2 | \exp_not:n { #3 } | #4 | } % \end{macrocode} %Five arguments provided, starting with the %datum marker that needs to be discarded. % \begin{macrocode} \cs_new:Nn \__datatool_weird_datum:Nnnnn { \exp_not:N \__datatool_datum:w | \exp_not:n { #2 } | \exp_not:n { #3 } | \exp_not:n { #4 } | \exp_not:n { #5 } | } % \end{macrocode} %Parse content that starts with \cs{\_\_datatool\_weird\_datum:w}. % \begin{macrocode} \cs_new:Npn \__datatool_get_weird_datum:w \__datatool_datum:w | #1 | #2 | #3 | #4 | #5 \q_stop { \quark_if_nil:nTF { #5 } { \@dtl@datatype = #4 \tl_set:Nn \l__datatool_datum_original_value_tl { #1 } \tl_set:Nn \l__datatool_datum_value_tl { #2 } \tl_set:Nn \l__datatool_datum_currency_tl { #3 } } { \@dtl@datatype = \c_datatool_string_int } } % \end{macrocode} %Restore category code. % \begin{macrocode} \group_end: % \end{macrocode} % %Test for equality where one or other value might be in either datum %format. % %Syntax: \meta{tl var1} \meta{tl var2} \marg{true}\marg{false} % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_value_eq:NN #1 #2 { T, F, TF } { \exp_args:NV \tl_if_head_eq_meaning:nNTF #1 \__datatool_datum:w { \exp_args:NV \tl_if_head_eq_meaning:nNTF #2 \__datatool_datum:w { \datatool_if_value_eq:eeTF { #1 } { #2 } { \prg_return_true: } { \prg_return_false: } } { \datatool_if_value_eq:eVTF { #1 } #2 { \prg_return_true: } { \prg_return_false: } } } { \exp_args:NV \tl_if_head_eq_meaning:nNTF #2 \__datatool_datum:w { \datatool_if_value_eq:VeTF #1 { #2 } { \prg_return_true: } { \prg_return_false: } } { \datatool_if_value_eq:VVTF #1 #2 { \prg_return_true: } { \prg_return_false: } } } } % \end{macrocode} %Syntax: \meta{tl var} \marg{tl}\marg{true}\marg{false} % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_value_eq:Nn #1 #2 { T, F, TF } { \exp_args:NV \tl_if_head_eq_meaning:nNTF #1 \__datatool_datum:w { \datatool_if_value_eq:enTF { #1 } { #2 } { \prg_return_true: } { \prg_return_false: } } { \datatool_if_value_eq:VnTF #1 { #2 } { \prg_return_true: } { \prg_return_false: } } } % \end{macrocode} %Syntax: \marg{tl} \meta{tl var} \marg{true}\marg{false} % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_value_eq:nN #1 #2 { T, F, TF } { \exp_args:NV \tl_if_head_eq_meaning:nNTF #2 \__datatool_datum:w { \datatool_if_value_eq:neTF { #1 } { #2 } { \prg_return_true: } { \prg_return_false: } } { \datatool_if_value_eq:nVTF { #1 } #2 { \prg_return_true: } { \prg_return_false: } } } % \end{macrocode} %Syntax: \marg{tl1}\marg{tl2}\marg{true}\marg{false} % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_value_eq:nn #1 #2 { T, F, TF } { \tl_if_eq:nnTF { #1 } { #2 } { \prg_return_true: } { \tl_if_head_eq_meaning:nNTF { #1 } \__datatool_datum:nnnn { \tl_if_head_eq_meaning:nNTF { #2 } \__datatool_datum:nnnn { % \end{macrocode} %Both in datum format. If both numeric use numeric comparison, %otherwise use string comparison. % \begin{macrocode} \bool_lazy_and:nnTF { \int_compare_p:nNn { \__datatool_datum_type:n { #1 } } > { \c_datatool_string_int } } { \int_compare_p:nNn { \__datatool_datum_type:n { #2 } } > { \c_datatool_string_int } } { % \end{macrocode} %Both numeric. Test both the value and currency symbol. % \begin{macrocode} \tl_if_eq:eeTF { \__datatool_datum_currency:n { #1 } } { \__datatool_datum_currency:n { #2 } } { \fp_compare:nNnTF { \__datatool_datum_value:n { #1 } } = { \__datatool_datum_value:n { #2 } } { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } { % \end{macrocode} %One or other isn't numeric. Compare string values only. % \begin{macrocode} \tl_if_eq:eeTF { \__datatool_datum_string:n { #1 } } { \__datatool_datum_string:n { #2 } } { \prg_return_true: } { \prg_return_false: } } } { % \end{macrocode} %First in datum format, second isn't. Compare string values only % \begin{macrocode} \tl_if_eq:enTF { #1 } { #2 } { \prg_return_true: } { \prg_return_false: } } } { % \end{macrocode} %First isn't in datum format. Compare string values only % \begin{macrocode} \tl_if_eq:enTF { #1 } { #2 } { \prg_return_true: } { \prg_return_false: } } } } \cs_generate_variant:Nn \datatool_if_value_eq:nnTF { VVTF , VnTF , nVTF, eeTF, neTF, enTF, eVTF, VeTF } % \end{macrocode} %Function to convert a token list variable so that it contains a %weird datum function if the original contained a datum marker but %don't parse and convert otherwise. % \begin{macrocode} \cs_new:Nn \__datatool_to_weird_datum_no_parse:N { \exp_args:NV \__datatool_to_weird_datum_no_parse:nN #1 #1 } \cs_new:Nn \__datatool_to_weird_datum_no_parse:nN { \tl_if_head_eq_meaning:nNTF { #1 } \__datatool_datum:nnnn { \tl_set:Nx #2 { \__datatool_weird_datum:Nnnnn #1 } } { \tl_set:Nn #2 { #1 } } } % \end{macrocode} %Function to convert a token list variable so that it contains a %weird datum function. % \begin{macrocode} \cs_new:Nn \__datatool_to_weird_datum:N { \exp_args:NV \__datatool_to_weird_datum:nN #1 #1 } \cs_new:Nn \__datatool_to_weird_datum:nN { \tl_if_head_eq_meaning:nNTF { #1 } \__datatool_datum:w { % \end{macrocode} %Already in weird format. % \begin{macrocode} \tl_set:Nn #2 { #1 } } { % \end{macrocode} %Convert to datum. % \begin{macrocode} \__datatool_to_datum:nN { #1 } #2 % \end{macrocode} %Leave null unprocessed. % \begin{macrocode} \datatool_if_null:NF #2 { % \end{macrocode} %At this point the token list variable should start with the datum %marker but check to make sure. % \begin{macrocode} \tl_if_head_eq_meaning:nNT { #1 } \__datatool_datum:nnnn { \tl_put_left:Nn #2 { \__datatool_weird_datum:Nnnnn } \tl_set:Nx #2 { #2 } } } } } % \end{macrocode} %Function to convert a token list variable so that it contains a %normal datum function. % \begin{macrocode} \cs_new:Nn \__datatool_to_datum:N { \exp_args:NV \__datatool_to_datum:nN #1 #1 } % \end{macrocode} %Function to convert a token list so that it contains a %normal datum function and save it in the token list variable. % \begin{macrocode} \cs_new:Nn \__datatool_to_datum:nN { \datatool_if_null:nTF { #1 } { % \end{macrocode} %Set variable to null. % \begin{macrocode} \tl_set_eq:NN #2 \dtlnovalue } { \tl_if_head_eq_meaning:nNTF { #1 } \__datatool_datum:w { % \end{macrocode} %Convert from weird format: % \begin{macrocode} \tl_set:Nx #2 { #1 } } { \tl_if_head_eq_meaning:nNTF { #1 } \__datatool_datum:nnnn { % \end{macrocode} %Already in the correct format: % \begin{macrocode} \tl_set:Nn #2 { #1 } } { % \end{macrocode} %Needs parsing. % \begin{macrocode} \__datatool_parse_datum:n { #1 } \tl_if_empty:NTF \l__datatool_datum_update_value_tl { \int_compare:nNnTF { \@dtl@datatype } = { \c_datatool_unknown_int } { % \end{macrocode} % NB this may be blank (only a space) not empty, but it's still classed as % unknown. % \begin{macrocode} \tl_set:Nn #2 { \__datatool_datum:nnnn { #1 } { } { } { \c_datatool_unknown_int } } } { \tl_set_eq:NN #2 \l__datatool_datum_original_value_tl } } { \tl_set_eq:NN #2 \l__datatool_datum_update_value_tl } } } } } % \end{macrocode} %Similar but don't parse. Only convert if it starts with weird form. % \begin{macrocode} \cs_new:Nn \__datatool_rm_weird_datum:N { \exp_args:NV \__datatool_rm_weird_datum:nN #1 #1 } \cs_new:Nn \__datatool_rm_weird_datum:nN { \tl_if_head_eq_meaning:nNTF { #1 } \__datatool_datum:w { % \end{macrocode} %Convert from weird format: % \begin{macrocode} \tl_set:Nx #2 { #1 } } { \tl_set:Nn #2 { #1 } } } % \end{macrocode} %Remove either datum. % \begin{macrocode} \cs_new:Nn \__datatool_rm_datum:N { \exp_args:NV \__datatool_rm_datum:nN #1 #1 } \cs_new:Nn \__datatool_rm_datum:nN { \__datatool_rm_weird_datum:nN { #1 } #2 \exp_args:NV \tl_if_head_eq_meaning:nNT #2 \__datatool_datum:nnnn { % \end{macrocode} %Convert from datum format: % \begin{macrocode} \tl_set:Nx #2 { #2 } } } % \end{macrocode} % %Used to encapsulate floating point datum value. %The first argument is the (non-locale) original floating point value. The second %is the \sty{l3fp} content to allow for reconstruction. The third is %the decimal value for use where scientific notation can't be %parsed. For example, with \sty{fp} functions. % \begin{macrocode} \tl_if_eq:NnTF \@dtl@mathprocessor { fp } { \cs_new:Nn \datatool_datum_fp:nnn { #3 } } { \cs_new:Nn \datatool_datum_fp:nnn { #1 } } % \end{macrocode} %Pick out the \sty{l3fp} content: % \begin{macrocode} \cs_new:Nn \datatool_datum_fp:Nnnn { \exp_not:n { #3 } } % \end{macrocode} % % \begin{macrocode} \cs_new:Nn \datatool_set_fp:Nn { \tl_if_single_token:nTF { #2 } { \exp_args:No \tl_if_head_eq_meaning:nNTF { #2 } \__datatool_datum:nnnn { \exp_last_unbraced:NV \__datatool_get_datum:w #2 \q_nil \q_stop } { \exp_args:No \tl_if_head_eq_meaning:nNTF { #2 } \__datatool_datum:w { \exp_last_unbraced:NV \__datatool_get_weird_datum:w #2 \q_nil \q_stop } { \exp_args:No \__datatool_parse_datum:n { #2 } } } } { \tl_if_head_eq_meaning:nNTF { #2 } \__datatool_datum:nnnn { \__datatool_get_datum:w #2 \q_nil \q_stop } { \tl_if_head_eq_meaning:nNTF { #2 } \__datatool_datum:w { \__datatool_get_weird_datum:w #2 \q_nil \q_stop } { \__datatool_parse_datum:n { #2 } } } } \datatool_if_numeric_datum_type:nF { \@dtl@datatype } { \exp_args:Nx \__datatool_parse_datum:n { \tl_trim_spaces:e { \text_expand:n { #2 } } } } \tl_if_empty:NTF \l__datatool_datum_value_tl { \fp_zero:N #1 } { \exp_args:NV \tl_if_head_eq_meaning:nNTF \l__datatool_datum_value_tl { \datatool_datum_fp:nnn } { \tl_set:Nx #1 { \exp_after:wN \datatool_datum_fp:Nnnn \l__datatool_datum_value_tl } } { \fp_set:Nn #1 { \l__datatool_datum_value_tl } } } } \cs_generate_variant:Nn \datatool_set_fp:Nn { NV, No } % \end{macrocode} % %\begin{macro}{\DTLusedatum} %\begin{definition} %\cs{DTLusedatum}\marg{tl} %\end{definition} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \cs_new:Nn \__datatool_datum_string:n { \datatool_datum_string:Nnnnn #1 } \newcommand{\DTLusedatum} [1] { \tl_if_eq:NNTF #1 \dtlnovalue { \dtlnovalue } { \exp_args:NV \__datatool_datum_string:n #1 } } % \end{macrocode} %\end{macro} %At the time of writing there were no \cs{use...} commands for five %arguments. There now are, but these are provided anyway in case of %a slightly older kernel and they are better semantically. % \begin{macrocode} \cs_new:Nn \datatool_datum_string:Nnnnn { #2 } % \end{macrocode} % %\begin{macro}{\DTLdatumvalue} %\begin{definition} %\cs{DTLdatumvalue}\marg{tl} %\end{definition} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \cs_new:Nn \__datatool_datum_value:n { \datatool_datum_value:Nnnnn #1 } \newcommand{\DTLdatumvalue} [1] { \tl_if_eq:NNTF #1 \dtlnovalue { \DTLnumbernull } { \exp_args:NV \__datatool_datum_value:n #1 } } % \end{macrocode} %\end{macro} % \begin{macrocode} \cs_new:Nn \datatool_datum_value:Nnnnn { #3 } % \end{macrocode} % %\begin{macro}{\DTLdatumcurrency} %\begin{definition} %\cs{DTLdatumcurrency}\marg{tl} %\end{definition} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \cs_new:Nn \__datatool_datum_currency:n { \datatool_datum_currency:Nnnnn #1 } \newcommand{\DTLdatumcurrency} [1] { \tl_if_eq:NNTF #1 \dtlnovalue { \dtlnovalue } { \exp_args:NV \__datatool_datum_currency:n #1 } } % \end{macrocode} %\end{macro} % \begin{macrocode} \cs_new:Nn \datatool_datum_currency:Nnnnn { \exp_not:n { #4 } } % \end{macrocode} % %\begin{macro}{\DTLdatumtype} %\begin{definition} %\cs{DTLdatumtype}\marg{tl} %\end{definition} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \cs_new:Nn \__datatool_datum_type:n { \datatool_datum_type:Nnnnn #1 } \newcommand{\DTLdatumtype} [1] { \tl_if_eq:NNTF #1 \dtlnovalue { \int_use:N \c_datatool_unknown_int } { \int_eval:n { \exp_args:NV \__datatool_datum_type:n #1 } } } % \end{macrocode} %\end{macro} % \begin{macrocode} \cs_new:Nn \datatool_datum_type:Nnnnn { #5 } % \end{macrocode} % %Debugging: % \begin{macrocode} \cs_new:Nn \datatool_datum_show:N { \int_compare:nNnTF { \tl_count:N #1 } = { 5 } { \exp_after:wN \__datatool_datum_show:NNnnnn \exp_after:wN #1 #1 } { \datatool_if_null:NTF #1 { \msg_show:nnnV { datatool-base } { show-datum-var-null } { #1 } #1 } { \tl_if_empty:NTF #1 { \msg_show:nnn { datatool-base } { show-datum-var-empty } { #1 } } { \PackageError { datatool-base } { \tl_to_str:N #1 \c_space_tl is ~ not ~ a ~ datum ~ variable } { } } } } } % \end{macrocode} % % \begin{macrocode} \cs_new:Nn \__datatool_datum_show:NNnnnn { \tl_if_eq:NNTF #2 \__datatool_datum:nnnn { \int_case:nnF { #6 } { { \c_datatool_unknown_int } { \msg_show:nnnn { datatool-base } { show-datum-var-unset } { #1 } { #3 } } { \c_datatool_string_int } { \msg_show:nnnn { datatool-base } { show-datum-var-string } { #1 } { #3 } } { \c_datatool_integer_int } { \msg_show:nnnnn { datatool-base } { show-datum-var-integer } { #1 } { #3 } { #4 } } { \c_datatool_decimal_int } { \msg_show:nnnnn { datatool-base } { show-datum-var-decimal } { #1 } { #3 } { #4 } } { \c_datatool_currency_int } { \msg_show:nnnnnn { datatool-base } { show-datum-var-currency } { #1 } { #3 } { #4 } { #5 } } { \c_datatool_datetime_int } { \msg_show:nnnnn { datatool-base } { show-datum-var-datetime } { #1 } { #3 } { #4 } } { \c_datatool_date_int } { \msg_show:nnnnn { datatool-base } { show-datum-var-date } { #1 } { #3 } { #4 } } { \c_datatool_time_int } { \msg_show:nnnnn { datatool-base } { show-datum-var-time } { #1 } { #3 } { #4 } } } { \PackageError { datatool-base } { datum ~ variable ~ \tl_to_str:N #1 \c_space_tl has ~ invalid ~ data ~ type: ~ \int_eval:n { #6 } } { } } } { \tl_if_eq:NNTF #2 \__datatool_datum:w { \int_case:nnF { #6 } { { \c_datatool_unknown_int } { \msg_show:nnnn { datatool-base } { show-weird-datum-var-unset } { #1 } { #3 } } { \c_datatool_string_int } { \msg_show:nnnn { datatool-base } { show-weird-datum-var-string } { #1 } { #3 } } { \c_datatool_integer_int } { \msg_show:nnnnn { datatool-base } { show-weird-datum-var-integer } { #1 } { #3 } { #4 } } { \c_datatool_decimal_int } { \msg_show:nnnnn { datatool-base } { show-weird-datum-var-decimal } { #1 } { #3 } { #4 } } { \c_datatool_currency_int } { \msg_show:nnnnnn { datatool-base } { show-weird-datum-var-currency } { #1 } { #3 } { #4 } { #5 } } { \c_datatool_datetime_int } { \msg_show:nnnnn { datatool-base } { show-weird-datum-var-datetime } { #1 } { #3 } { #4 } } { \c_datatool_date_int } { \msg_show:nnnnn { datatool-base } { show-weird-datum-var-date } { #1 } { #3 } { #4 } } { \c_datatool_time_int } { \msg_show:nnnnn { datatool-base } { show-weird-datum-var-time } { #1 } { #3 } { #4 } } } { \PackageError { datatool-base } { weird ~ datum ~ variable ~ \tl_to_str:N #1 \c_space_tl has ~ invalid ~ data ~ type: ~ \int_eval:n { #6 } } { } } } { \PackageError { datatool-base } { Can't ~ show ~ \tl_to_str:N #1 : ~ \tl_to_str:N #2 \c_space_tl is ~ not ~ a ~ datum ~ marker } { } } } } % \end{macrocode} % Messages: % \begin{macrocode} \msg_new:nnn { datatool-base } { show-datum-var-null } { variable ~ \tl_to_str:N #1 \c_space_tl is ~ null \\ > ~ #2 } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-datum-var-empty } { variable ~ \tl_to_str:N #1 \c_space_tl is ~ empty } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-datum-var-unset } { Showing ~ unset ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ ` #2 ' } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-weird-datum-var-unset } { Showing ~ unset ~ weird ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ ` #2 ' } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-datum-var-string } { Showing ~ string ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ #2 } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-weird-datum-var-string } { Showing ~ string ~ weird ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ #2 } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-datum-var-integer } { Showing ~ integer ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ #2 \\ > ~ numeric ~ value ~ #3 } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-weird-datum-var-integer } { Showing ~ integer ~ weird ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ #2 \\ > ~ numeric ~ value ~ #3 } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-datum-var-decimal } { Showing ~ decimal ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ #2 \\ > ~ numeric ~ value ~ #3 } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-weird-datum-var-decimal } { Showing ~ decimal ~ weird ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ #2 \\ > ~ numeric ~ value ~ #3 } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-datum-var-currency } { Showing ~ currency ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ #2 \\ > ~ numeric ~ value ~ #3 \\ > ~ currency ~ symbol ~ #4 } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-weird-datum-var-currency } { Showing ~ currency ~ weird ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ #2 \\ > ~ numeric ~ value ~ #3 \\ > ~ currency ~ symbol ~ #4 } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-datum-var-datetime } { Showing ~ datetime ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ #2 \\ > ~ numeric ~ value ~ #3 } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-weird-datum-var-datetime } { Showing ~ datetime ~ weird ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ #2 \\ > ~ numeric ~ value ~ #3 } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-datum-var-date } { Showing ~ date ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ #2 \\ > ~ numeric ~ value ~ #3 } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-weird-datum-var-date } { Showing ~ date ~ weird ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ #2 \\ > ~ numeric ~ value ~ #3 } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-datum-var-time } { Showing ~ time ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ #2 \\ > ~ numeric ~ value ~ #3 } % \end{macrocode} % % \begin{macrocode} \msg_new:nnn { datatool-base } { show-weird-datum-var-time } { Showing ~ time ~ weird ~ datum ~ variable ~ \tl_to_str:N #1 \\ > ~ string ~ value ~ #2 \\ > ~ numeric ~ value ~ #3 } % \end{macrocode} % %The above document commands can be used on \meta{cs} obtained from parsing %data. Document commands: %\begin{macro}{\DTLparse} %\begin{definition} %\cs{DTLparse}\marg{cs}\marg{arg} %\end{definition} %\changes{3.0}{2025-03-03}{new} %Note that any dates or times will be treated as strings. % \begin{macrocode} \NewDocumentCommand \DTLparse { m m } { \__datatool_parse:Nn #1 { #2 } } % \end{macrocode} %\end{macro} %Internal function easier to remember the argument order: % \begin{macrocode} \cs_new:Nn \__datatool_parse:Nn { \tl_clear:N \l__datatool_datum_value_tl \tl_clear:N \l__datatool_datum_currency_tl \tl_clear:N \l__datatool_datum_update_value_tl \datatool_if_null:nTF { #2 } { \tl_set_eq:NN #1 \dtlnovalue \tl_set:Nn \l__datatool_datum_original_value_tl { #2 } \int_set_eq:NN \@dtl@datatype \c_datatool_unknown_int } { \tl_if_head_eq_meaning:nNTF { #2 } \__datatool_datum:w { % \end{macrocode} %Convert from weird format: % \begin{macrocode} \tl_set:Nx #1 { #2 } } { \tl_set:Nn #1 { #2 } } \exp_args:NV \tl_if_head_eq_meaning:nNTF #1 \__datatool_datum:nnnn { \int_set:Nn \@dtl@datatype { \exp_args:NV \__datatool_datum_type:n #1 } \tl_set:Nx \l__datatool_datum_original_value_tl { #1 } \tl_set:Nx \l__datatool_datum_currency_tl { \exp_args:NV \__datatool_datum_currency:n #1 } \tl_set:Nx \l__datatool_datum_value_tl { \exp_args:NV \__datatool_datum_value:n #1 } } { \exp_args:NV \__datatool_parse_datum:n #1 \tl_if_empty:NTF \l__datatool_datum_update_value_tl { \int_compare:nNnTF { \@dtl@datatype } = { \c_datatool_unknown_int } { % \end{macrocode} % NB this may be blank (only a space) not empty, but it's still classed as % unknown. % \begin{macrocode} \tl_set:Nn #1 { \__datatool_datum:nnnn { #2 } { } { } { \c_datatool_unknown_int } } } { \tl_set_eq:NN #1 \l__datatool_datum_original_value_tl } } { \tl_set_eq:NN #1 \l__datatool_datum_update_value_tl } } } } \cs_generate_variant:Nn \__datatool_parse:Nn { NV } % \end{macrocode} % \begin{macrocode} \cs_new:Nn \__datatool_parse:N { \exp_args:NNV \__datatool_parse:Nn #1 #1 } % \end{macrocode} %\begin{macro}{\DTLxparse} %\begin{definition} %\cs{DTLxparse}\marg{cs}\marg{arg} %\end{definition} %\changes{3.0}{2025-03-03}{new} %As above but expands second argument. % \begin{macrocode} \NewDocumentCommand \DTLxparse { m m } { \exp_args:NNx \__datatool_parse:Nn #1 { #2 } } % \end{macrocode} %\end{macro} % %Low-level set datum parts. %\begin{definition} %\cs{datatool\_set\_datum:Nnnnn} \meta{datum-var} \marg{string} %\marg{value} \marg{currency symbol} \marg{type} %\end{definition} %The type may be an integer expression % \begin{macrocode} \cs_new:Nn \datatool_set_datum:Nnnnn { \int_case:nnF { #5 } { { \c_datatool_unknown_int } { \tl_set:Nn #1 { \__datatool_datum:nnnn { #2 } { } { } { \c_datatool_unknown_int } } } { \c_datatool_string_int } { \tl_set:Nn #1 { \__datatool_datum:nnnn { #2 } { } { } { \c_datatool_string_int } } } { \c_datatool_integer_int } { \tl_set:Nn #1 { \__datatool_datum:nnnn { #2 } { #3 } { } { \c_datatool_integer_int } } } { \c_datatool_decimal_int } { \tl_set:Nn #1 { \__datatool_datum:nnnn { #2 } { #3 } { } { \c_datatool_decimal_int } } } { \c_datatool_currency_int } { \tl_set:Nn #1 { \__datatool_datum:nnnn { #2 } { #3 } { #4 } { \c_datatool_currency_int } } } { \c_datatool_datetime_int } { \tl_set:Nn #1 { \__datatool_datum:nnnn { #2 } { #3 } { } { \c_datatool_datetime_int } } } { \c_datatool_date_int } { \tl_set:Nn #1 { \__datatool_datum:nnnn { #2 } { #3 } { } { \c_datatool_date_int } } } { \c_datatool_time_int } { \tl_set:Nn #1 { \__datatool_datum:nnnn { #2 } { #3 } { } { \c_datatool_time_int } } } } { \PackageError { datatool-base } { Unsupported ~ datum ~ type ~ \int_eval:n { #5 } } { } } } \cs_generate_variant:Nn \datatool_set_datum:Nnnnn { Neeen , Nnenn , NnVVn , NnVnn } % \end{macrocode} % No sanity check: % \begin{macrocode} \cs_new:Nn \__datatool_set_datum:Nnnnn { \tl_set:Nn #1 { \__datatool_datum:nnnn { #2 } { #3 } { #4 } { #5 } } } \cs_generate_variant:Nn \__datatool_set_datum:Nnnnn { Neeen , NnVnn , NeVnn } % \end{macrocode} % %Document-level commands: %\begin{macro}{\DTLsetintegerdatum} %\begin{definition} %\cs{DTLsetintegerdatum}\marg{cs}\marg{formatted value}\marg{value} %\end{definition} %\changes{3.0}{2025-03-03}{new} %Sets \meta{cs} to an integer datum. % \begin{macrocode} \NewDocumentCommand \DTLsetintegerdatum { m m m } { \tl_set:Nn #1 { \__datatool_datum:nnnn { #2 } { #3 } { } { \c_datatool_integer_int } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLxsetintegerdatum} %\begin{definition} %\cs{DTLxsetintegerdatum}\marg{cs}\marg{formatted value}\marg{value} %\end{definition} %\changes{3.0}{2025-03-03}{new} %As above but expands \meta{formatted value} and \meta{value}. % \begin{macrocode} \NewDocumentCommand \DTLxsetintegerdatum { m m m } { \exp_args:Neee \DTLsetintegerdatum { \exp_not:N #1 } { #2 } { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLsetfpdatum} %\begin{definition} %\cs{DTLsetdecimaldatum}\marg{cs}\marg{formatted value}\marg{value} %\end{definition} %\changes{3.0}{2025-03-03}{new} %Sets \meta{cs} to a floating point datum. % \begin{macrocode} \NewDocumentCommand \DTLsetfpdatum { m m m } { \fp_set:Nn \l__datatool_datum_value_fp { #3 } \tl_set:Nx #1 { \exp_not:N \__datatool_datum:nnnn { \exp_not:n { #2 } } { \exp_not:N \datatool_datum_fp:nnn { #3 } { \exp_not:V \l__datatool_datum_value_fp } { \fp_to_decimal:N \l__datatool_datum_value_fp } } { } { \c_datatool_decimal_int } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLsetdecimaldatum} %\begin{definition} %\cs{DTLsetdecimaldatum}\marg{cs}\marg{formatted value}\marg{value} %\end{definition} %\changes{3.0}{2025-03-03}{new} %Sets \meta{cs} to a decimal datum. % \begin{macrocode} \NewDocumentCommand \DTLsetdecimaldatum { m m m } { \tl_set:Nn #1 { \__datatool_datum:nnnn { #2 } { #3 } { } { \c_datatool_decimal_int } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLxsetdecimaldatum} %\begin{definition} %\cs{DTLsxetdecimaldatum}\marg{cs}\marg{formatted value}\marg{value} %\end{definition} %\changes{3.0}{2025-03-03}{new} %As above but expands \meta{formatted value} and \meta{value}. % \begin{macrocode} \NewDocumentCommand \DTLxsetdecimaldatum { m m m } { \exp_args:Neee \DTLsetdecimaldatum { \exp_not:N #1 } { #2 } { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLsetcurrencydatum} %\begin{definition} %\cs{DTLsetcurrencydatum}\marg{cs}\marg{formatted %value}\marg{value}\marg{currency symbol} %\end{definition} %\changes{3.0}{2025-03-03}{new} %Sets \meta{cs} to a currency datum. % \begin{macrocode} \NewDocumentCommand \DTLsetcurrencydatum { m m m m } { \tl_set:Nn #1 { \__datatool_datum:nnnn { #2 } { #3 } { #4 } { \c_datatool_currency_int } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLxsetcurrencydatum} %\begin{definition} %\cs{DTLxsetcurrencydatum}\marg{cs}\marg{formatted %value}\marg{value}\marg{currency symbol} %\end{definition} %\changes{3.0}{2025-03-03}{new} %As above but expands \meta{formatted value}, \meta{value} and %\meta{currency symbol}. % \begin{macrocode} \NewDocumentCommand \DTLxsetcurrencydatum { m m m m } { \exp_args:Neee \DTLsetcurrencydatum { \exp_not:N #1 } { #2 } { #3 } { #4 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLsettemporaldatum} %\begin{definition} %\cs{DTLsettemporaldatum}\marg{cs}\marg{formatted value}\marg{ISO} %\end{definition} %\changes{3.0}{2025-03-03}{new} %Sets \meta{cs} to a date/time datum. The actual type is determined %by parsing \meta{ISO}. If the formatted value should be %automatically calculated, use %\cs{DTLsetup}\{datetime=reformat\}\cs{DTLparse}\marg{cs}\marg{ISO} %and adjust the formatting commands as applicable. % \begin{macrocode} \NewDocumentCommand \DTLsettemporaldatum { m m m } { \__datatool_parse_datetime:nTF { #3 } { \__datatool_set_datetime_value:Nn #1 { #2 } } { \PackageError { datatool-base } { Invalid ~ date/time format ~ ` \tl_to_str:n { #3 } '} { The ~ date/time ~ value ~ needs ~ to ~ be ~ a ~ date ~ in ~ the ~ form ~ YYYY-MM-DD ~ or ~ a ~ time ~ in ~ the ~ form ~ hh:mm ~ or ~ hh:mm:ss ~ or ~ a ~ timestamp ~ in ~ the ~ form ~ YYYY-MM-DD ~ hh:mm:ssTZ ~ where ~ the ~ timezone ~ TZ ~ is ~ optional ~ and ~ may ~ be ~ the ~ letter ~ `Z' ~ or ~ in ~ the ~ form ~ [+-]hh:mm ~ (if ~ omitted ~ `Z' ~ is ~ assumed). ~ The ~ separator ~ may ~ either ~ be ~ a ~ space ~ or ~ the ~ letter ~ `T' } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLxsettemporaldatum} %\begin{definition} %\cs{DTLxsetdatetimedatum}\marg{cs}\marg{formatted value}\marg{ISO} %\end{definition} %\changes{3.0}{2025-03-03}{new} %As above but expands all but the first argument. % \begin{macrocode} \NewDocumentCommand \DTLxsettemporaldatum { m m m } { \exp_args:Neee \DTLsettemporaldatum { \exp_not:N #1 } { #2 } { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLsetstringdatum} %\begin{definition} %\cs{DTLsetstringdatum}\marg{cs}\marg{string} %\end{definition} %\changes{3.0}{2025-03-03}{new} %Sets \meta{cs} to a string datum. % \begin{macrocode} \NewDocumentCommand \DTLsetstringdatum { m m } { \tl_set:Nn #1 { \__datatool_datum:nnnn { #2 } { } { } { \c_datatool_string_int } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLxsetstringdatum} %\begin{definition} %\cs{DTLxsetstringdatum}\marg{cs}\marg{string} %\end{definition} %\changes{3.0}{2025-03-03}{new} %As above but expands \meta{string}. % \begin{macrocode} \NewDocumentCommand \DTLxsetstringdatum { m m } { \exp_args:NNe \DTLxsetstringdatum #1 { #2 } } % \end{macrocode} %\end{macro} % %Temporary storage used when extracting data. %The original value: % \begin{macrocode} \tl_new:N \l__datatool_datum_original_value_tl % \end{macrocode} %The numeric value: % \begin{macrocode} \tl_new:N \l__datatool_datum_value_tl % \end{macrocode} %The currency symbol: % \begin{macrocode} \tl_new:N \l__datatool_datum_currency_tl % \end{macrocode} %The currency code: % \begin{macrocode} \tl_new:N \l__datatool_datum_currency_code_tl % \end{macrocode} %If the data hasn't been parsed yet, the following is set to the %tagged information: % \begin{macrocode} \tl_new:N \l__datatool_datum_update_value_tl % \end{macrocode} %Regular expression for scientific notation. % \begin{macrocode} \regex_const:Nn \c_datatool_sci_regex { [+\-]? [0-9]* \.? [0-9]+ \s* [eE] [+\-]? [0-9]+ } % \end{macrocode} %Determine data type. For temporal data types, the argument must %match the relevant format. See %\verb|\__datatool_parse_datetime_if_enabled:n| and %\verb|\__datatool_parse_datetime:n| conditionals. % \begin{macrocode} \cs_new:Nn \__datatool_parse_datum:n { % \end{macrocode} %Initialise to unknown. % \begin{macrocode} \@dtl@datatype = \c_datatool_unknown_int \tl_set:Nn \l__datatool_datum_original_value_tl { #1 } \tl_clear:N \l__datatool_datum_value_tl \tl_clear:N \l__datatool_datum_currency_tl \tl_clear:N \l__datatool_datum_currency_code_tl \tl_clear:N \l__datatool_datum_update_value_tl \tl_if_blank:nF { #1 } { % \end{macrocode} %Has the argument already had its data type set? % \begin{macrocode} \tl_if_head_eq_meaning:nNTF { #1 } \__datatool_datum:nnnn { \__datatool_get_datum:w #1 \q_nil \q_stop } { \tl_if_head_eq_meaning:nNTF { #1 } \__datatool_datum:w { \__datatool_get_weird_datum:w #1 \q_nil \q_stop } { % \end{macrocode} %Doesn't start with the special marker, so it needs parsing. First %check for scientific notation. % \begin{macrocode} \regex_match:NnTF \c_datatool_sci_regex { #1 } { \fp_set:Nn \l__datatool_datum_value_fp { #1 } \@dtl@datatype = \c_datatool_decimal_int \tl_set:Nx \l__datatool_datum_value_tl { \exp_not:N \datatool_datum_fp:nnn { #1 } { \exp_not:V \l__datatool_datum_value_fp } { \fp_to_decimal:N \l__datatool_datum_value_fp } } \__datatool_if_auto_reformat_on:nTF { si } { % \end{macrocode} %Auto-reformat on. % \begin{macrocode} \tl_set:Nx \l__datatool_datum_update_value_tl { \exp_not:N \__datatool_datum:nnnn { \exp_not:N \DTLscinum { \exp_not:n { #1 } } } { \exp_not:V \l__datatool_datum_value_tl } { } { \exp_not:N \c_datatool_decimal_int } } } { \tl_set:Nx \l__datatool_datum_update_value_tl { \exp_not:N \__datatool_datum:nnnn { \exp_not:n { #1 } } { \exp_not:V \l__datatool_datum_value_tl } { } { \exp_not:N \c_datatool_decimal_int } } } } { % \end{macrocode} %Next for date/time if applicable. % \begin{macrocode} \__datatool_parse_datetime_if_enabled:nTF { #1 } { \__datatool_if_auto_reformat_on:TF { \__datatool_set_datetime_value: } { \__datatool_set_datetime_value:n { #1 } } } { % \end{macrocode} %Not date/time. %Next check if it starts with a + or - to allow for a sign before the %currency symbol. % \begin{macrocode} \bool_lazy_or:nnTF { \tl_if_head_eq_meaning_p:nN { #1 } + } { \tl_if_head_eq_meaning_p:nN { #1 } - } { \exp_args:Nxx \__datatool_parse_item:nn { \tl_tail:n { #1 } } { \tl_head:n { #1 } } } { \__datatool_parse_item:nn { #1 } { } } } } } } } } % \end{macrocode} % Parse remaining after prefix check: % \begin{macrocode} \cs_new:Nn \__datatool_parse_item:nn { \tl_set:Nn \l__datatool_datum_original_value_tl { #1 } % \end{macrocode} %Check if it starts with a known currency marker. % \begin{macrocode} \tl_if_head_eq_meaning:nNTF { #1 } { \DTLcurrency } { \__datatool_get_currency_datum:w #1 \q_nil \q_stop } { \tl_if_head_eq_meaning:nNTF { #1 } { \DTLfmtcurrency } { \__datatool_get_fmtcurrency_datum:w #1 \q_nil \q_stop } { \tl_if_head_eq_meaning:nNTF { #1 } { \DTLfmtcurr } { \__datatool_get_fmtcurr_datum:w #1 \q_nil \q_stop } { % \end{macrocode} %Doesn't start with \cs{DTLcurrency} or \cs{DTLfmtcurrency} or %\cs{DTLfmtcurr}. %Does it start with a known currency symbol? % \begin{macrocode} \__datatool_check_known_currencies: \tl_if_empty:NTF \l__datatool_datum_currency_tl { % \end{macrocode} %Not currency. Is it numeric? % \begin{macrocode} \__datatool_parse_numeric:n { #1 } \int_compare:nNnT { \@dtl@datatype } = { \c_datatool_unknown_int } { \int_set_eq:NN \@dtl@datatype \c_datatool_string_int } } { % \end{macrocode} %Starts (or ends) with a currency symbol. Is it followed by a numeric value? % \begin{macrocode} \tl_trim_spaces:N \l__datatool_suffix_tl \__datatool_parse_numeric:N \l__datatool_suffix_tl \int_compare:nNnTF { \@dtl@datatype } = { \c_datatool_string_int } { \tl_clear:N \l__datatool_datum_currency_tl } { \int_set_eq:NN \@dtl@datatype \c_datatool_currency_int } } } } } % \end{macrocode} %Check for a double sign which will be treated as a string. % \begin{macrocode} \tl_if_empty:nF { #2 } { \int_compare:nNnT { \@dtl@datatype } > { \c_datatool_string_int } { \bool_lazy_or:nnTF { \tl_if_head_eq_meaning_p:nN { #1 } + } { \tl_if_head_eq_meaning_p:nN { #1 } - } { \int_set_eq:NN \@dtl@datatype \c_datatool_string_int } { \tl_if_eq:nnT { #2 } { - } { \tl_set:Nx \l__datatool_datum_value_tl { \fp_to_tl:n { - ( \l__datatool_datum_value_tl ) } } } } } } \int_case:nn { \@dtl@datatype } { { \c_datatool_string_int } { % \end{macrocode} %Non-numeric. % \begin{macrocode} \tl_set:Nn \l__datatool_datum_update_value_tl { \__datatool_datum:nnnn { #2 #1 } { } { } { \c_datatool_string_int } } } { \c_datatool_integer_int } { % \end{macrocode} %Integer. % \begin{macrocode} \__datatool_if_auto_reformat_on:nTF { integer } { % \end{macrocode} %Auto-reformat on. % \begin{macrocode} \tl_set:Nx \l__datatool_datum_update_value_tl { \int_eval:n { \l__datatool_datum_value_tl } } \regex_replace_case_once:nN { \c_datatool_integer_grps_i_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 } \c_datatool_integer_grps_ii_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_numbergroup_tl} \4 } \c_datatool_integer_grps_iii_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_numbergroup_tl} \4 \u{__datatool_numbergroup_tl} \5 } } \l__datatool_datum_update_value_tl \tl_set:Ne \l__datatool_datum_update_value_tl { \exp_not:N \__datatool_datum:nnnn { \exp_not:V \l__datatool_datum_update_value_tl } { \int_eval:n { \l__datatool_datum_value_tl } } { } { \c_datatool_integer_int } } } { \tl_set:Nx \l__datatool_datum_update_value_tl { \exp_not:N \__datatool_datum:nnnn { \exp_not:n { #2 #1 } } { \int_eval:n { \l__datatool_datum_value_tl } } { } { \c_datatool_integer_int } } } } { \c_datatool_decimal_int } { % \end{macrocode} %Decimal. % \begin{macrocode} \tl_set_eq:NN \l__datatool_datum_update_value_tl \l__datatool_datum_value_tl \fp_set:Nn \l__datatool_datum_value_fp { \l__datatool_datum_value_tl } \tl_set:Nx \l__datatool_datum_value_tl { \exp_not:N \datatool_datum_fp:nnn { \exp_not:V \l__datatool_datum_value_tl } { \exp_not:V \l__datatool_datum_value_fp } { \fp_to_decimal:N \l__datatool_datum_value_fp } } \__datatool_if_auto_reformat_on:nTF { decimal } { % \end{macrocode} %Auto-reformat on. % \begin{macrocode} \regex_replace_case_once:nN { \c_datatool_decimal_grps_regex { \1 \2 \u{__datatool_decimal_tl} \3 } \c_datatool_decimal_grps_i_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_decimal_tl} \4 } \c_datatool_decimal_grps_ii_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_numbergroup_tl} \4 \u{__datatool_decimal_tl} \5 } \c_datatool_decimal_grps_iii_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_numbergroup_tl} \4 \u{__datatool_numbergroup_tl} \5 \u{__datatool_decimal_tl} \6 } } \l__datatool_datum_update_value_tl \tl_set:Ne \l__datatool_datum_update_value_tl { \exp_not:N \__datatool_datum:nnnn { \exp_not:V \l__datatool_datum_update_value_tl } { \exp_not:V \l__datatool_datum_value_tl } { } { \c_datatool_decimal_int } } } { % \end{macrocode} %Auto-reformat off. % \begin{macrocode} \tl_set:Nx \l__datatool_datum_update_value_tl { \exp_not:N \__datatool_datum:nnnn { \exp_not:n { #2 #1 } } { \exp_not:V \l__datatool_datum_value_tl } { } { \c_datatool_decimal_int } } } } { \c_datatool_currency_int } { % \end{macrocode} %Currency. % \begin{macrocode} \__datatool_if_auto_reformat_on:nTF { currency } { % \end{macrocode} %Auto-reformat on. % \begin{macrocode} \__datatool_decimal_to_currency:VVNV \l__datatool_datum_currency_tl \l__datatool_datum_value_tl \l__datatool_datum_update_value_tl \l__datatool_datum_currency_code_tl } { % \end{macrocode} %Auto-reformat off. % \begin{macrocode} \tl_set:Nx \l__datatool_datum_update_value_tl { \exp_not:N \__datatool_datum:nnnn { \exp_not:n { #2 #1 } } { \l__datatool_datum_value_tl } { \exp_not:V \l__datatool_datum_currency_tl } { \c_datatool_currency_int } } } } } \tl_set:Nn \l__datatool_datum_original_value_tl { #2 #1 } } % \end{macrocode} %Parse for a numeric value, using the current group and decimal %characters. % \begin{macrocode} \tl_new:N \l__datatool_parser_num_tl \cs_new:Nn \__datatool_parse_numeric:N { \exp_args:No \__datatool_parse_numeric:n { #1 } } \cs_new:Nn \__datatool_parse_numeric:n { \tl_set:Nn \l__datatool_parser_num_tl { #1 } \tl_if_empty:nF { #1 } { \regex_match:NnTF \l_datatool_locale_bigint_regex { #1 } { \@dtl@datatype = \c_datatool_string_int } { \regex_replace_case_once:nNTF { \l_datatool_locale_numeric_regex { \c{q_mark} \1 \2 \3 \4 . \5 \c{q_stop}} \l_datatool_locale_fractional_regex { \c{q_mark} \1 . \2 \c{q_stop}} \l_datatool_locale_fractional_nozero_regex { \c{q_mark} \1 0 . \2 \c{q_stop}} } \l__datatool_parser_num_tl { \exp_after:wN \__datatool_parse_number:w \l__datatool_parser_num_tl } { \@dtl@datatype = \c_datatool_string_int } } } } \cs_new:Npn \__datatool_parse_number:w \q_mark #1.#2\q_stop { \tl_if_empty:nTF { #2 } { \@dtl@datatype = \c_datatool_integer_int \tl_set:Nn \l__datatool_datum_value_tl { #1 } } { \@dtl@datatype = \c_datatool_decimal_int \tl_if_empty:nTF { #1 } { \tl_set:Nn \l__datatool_datum_value_tl { 0.#2 } } { \tl_set:Nn \l__datatool_datum_value_tl { #1.#2 } } } } % \end{macrocode} %Check for known currencies. % \begin{macrocode} \cs_new:Nn \__datatool_check_known_currencies: { % \end{macrocode} % Check regions first (this will obtain the % currency code to save searching for it again). % \begin{macrocode} \tl_clear:N \l__datatool_datum_currency_code_tl \prop_map_function:NN \l_datatool_regional_currencies_prop \__datatool_prop_parse_currency_do:nn \tl_if_empty:NT \l__datatool_datum_currency_tl { \seq_map_function:NN \l__datatool_known_currencies_seq \__datatool_seq_parse_currency_do:n } % \end{macrocode} %If suffix is blank then it may just be the symbol. % \begin{macrocode} \exp_args:NV \tl_if_blank:nT \l__datatool_suffix_tl { \tl_clear:N \l__datatool_datum_currency_tl } } % \end{macrocode} %Handler macro for currency iterator for property map. % \begin{macrocode} \cs_new:Nn \__datatool_prop_parse_currency_do:nn { \__datatool_if_starts_or_ends_with:VvTF \l__datatool_datum_original_value_tl { dtl@curr@ #2 @tl } { \tl_set_eq:NN \l__datatool_datum_currency_tl \l__datatool_prefix_tl \tl_set:Nn \l__datatool_datum_currency_code_tl { #2 } } { \__datatool_if_starts_or_ends_with:VvTF \l__datatool_datum_original_value_tl { dtl@curr@ #2 @sym } { \tl_set_eq:NN \l__datatool_datum_currency_tl \l__datatool_prefix_tl \tl_set:Nn \l__datatool_datum_currency_code_tl { #2 } } { \cs_if_exist:cT { datatool #1 symbolprefix } { \__datatool_if_starts_or_ends_with:VeTF \l__datatool_datum_original_value_tl { #1 \exp_not:v { dtl@curr@ #2 @tl } } { \tl_set_eq:NN \l__datatool_datum_currency_tl \l__datatool_prefix_tl \tl_set:Nn \l__datatool_datum_currency_code_tl { #2 } } { \__datatool_if_starts_or_ends_with:VeTF \l__datatool_datum_original_value_tl { #1 \exp_not:v { dtl@curr@ #2 @sym } } { \tl_set_eq:NN \l__datatool_datum_currency_tl \l__datatool_prefix_tl \tl_set:Nn \l__datatool_datum_currency_code_tl { #2 } } } } } } \tl_if_empty:NF \l__datatool_datum_currency_tl { \prop_map_break: } } % \end{macrocode} %Handler macro for currency iterator for sequence map. % \begin{macrocode} \cs_new:Nn \__datatool_seq_parse_currency_do:n { \__datatool_if_starts_or_ends_with:VnTF \l__datatool_datum_original_value_tl { #1 } { \tl_set_eq:NN \l__datatool_datum_currency_tl \l__datatool_prefix_tl \seq_map_break: } } % \end{macrocode} %Parse content that starts with \cs{\_\_datatool\_datum:nnnn}. % \begin{macrocode} \cs_new:Npn \__datatool_get_datum:w \__datatool_datum:nnnn #1 #2 #3 #4 #5 \q_stop { \quark_if_nil:nTF { #5 } { \@dtl@datatype = #4 \tl_set:Nn \l__datatool_datum_original_value_tl { #1 } \tl_set:Nn \l__datatool_datum_value_tl { #2 } \tl_set:Nn \l__datatool_datum_currency_tl { #3 } } { \@dtl@datatype = \c_datatool_string_int } } % \end{macrocode} %Parse content that starts with \cs{DTLcurrency}. % \begin{macrocode} \cs_new:Npn \__datatool_get_currency_datum:w \DTLcurrency #1 #2 \q_stop { \quark_if_nil:nTF { #2 } { \__datatool_parse_numeric:n { #1 } \ifnum\@dtl@datatype = \c_datatool_string_int \else \tl_set:Nn \l__datatool_datum_currency_tl { \@dtl@currency } \@dtl@datatype = \c_datatool_currency_int \fi } { \@dtl@datatype = \c_datatool_string_int } } % \end{macrocode} %Parse content that starts with \cs{DTLfmtcurrency}. % \begin{macrocode} \cs_new:Npn \__datatool_get_fmtcurrency_datum:w \DTLfmtcurrency #1 #2 #3 \q_stop { \quark_if_nil:nTF { #3 } { \__datatool_parse_numeric:n { #2 } \ifnum\@dtl@datatype = \c_datatool_string_int \else \@dtl@datatype = \c_datatool_currency_int \tl_set:Nn \l__datatool_datum_currency_tl { #1 } \fi } { \@dtl@datatype = \c_datatool_string_int } } % \end{macrocode} %Parse content that starts with \cs{DTLfmtcurr}. % \begin{macrocode} \cs_new:Npn \__datatool_get_fmtcurr_datum:w \DTLfmtcurr #1 #2 #3 \q_stop { \quark_if_nil:nTF { #3 } { \__datatool_parse_numeric:n { #2 } \ifnum\@dtl@datatype = \c_datatool_string_int \else \tl_set:Nn \l__datatool_datum_currency_code_tl { #1 } \@dtl@datatype = \c_datatool_currency_int \tl_set:Nn \l__datatool_datum_currency_tl { \DTLcurr { #1 } } \fi } { \@dtl@datatype = \c_datatool_string_int } } % \end{macrocode} % % Only parse for dates and times if enabled. Note that the boolean setting % is just for general parsing commands. Temporal values can be % explicitly parsed. % \begin{macrocode} \prg_new_conditional:Npnn \__datatool_parse_datetime_if_enabled:n #1 { T, F, TF } { \bool_if:NTF \l__datatool_parse_datetime_bool { \__datatool_parse_datetime:nTF { #1 } { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } % \end{macrocode} % %Test if the given argument is a token list containing datum. % \begin{macrocode} \cs_new:Nn \__datatool_if_datum_tl:nTF { \tl_if_single_token:nTF { #1 } { \exp_args:No \tl_if_head_eq_meaning:nNTF { #1 } \__datatool_datum:nnnn { #2 } { #3 } } { #3 } } % \end{macrocode} % %Added v3.1: %Not a number warning: % \begin{macrocode} \cs_new:Nn \datatool_warn_not_number:n { \PackageWarning { datatool-base } { #1 } } % \end{macrocode} % %\begin{macro}{\DTLconverttodecimal} %\begin{definition} % \cs{DTLconverttodecimal}\marg{num}\marg{cmd} %\end{definition} % \cs{DTLconverttodecimal} will convert locale dependent \meta{num} % a decimal number in a standard form that can be used in % arithmetical calculations. The resulting % number is stored in \meta{cmd}. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLconverttodecimal { m m } { \tl_if_single_token:nTF { #1 } { \exp_args:No \__datatool_parse_datum:n { #1 } } { \__datatool_parse_datum:n { #1 } } \datatool_if_numeric_datum_type:nF { \@dtl@datatype } { \datatool_warn_not_number:n { Can't ~ convert ~ non-numerical ~ ` #1 ' ~ to ~ decimal } } \tl_if_empty:NTF \l__datatool_datum_value_tl { \tl_set:Nn #2 { 0 } } { \tl_set_eq:NN #2 \l__datatool_datum_value_tl } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLdecimaltolocale} %\begin{definition} % \cs{DTLdecimaltolocale}\marg{number}\marg{cmd} %\end{definition} % Define command to convert a decimal number into the locale % dependent format. Stores result in \meta{cmd} which must be % a control sequence. % \begin{macrocode} \newrobustcmd*{\DTLdecimaltolocale}[2]{% \tl_set:Nn #2 { #1 } \regex_replace_case_once:nN { \c_datatool_numeric_leading_zeros_regex { \1 \2 } \c_datatool_decimal_redundant_zeros_regex { \1 \2 } \c_datatool_decimal_implicit_zero_regex { \1 0 \2 } } #2 \regex_replace_case_once:nNTF { \c_datatool_decimal_grps_regex { \1 \2 \u{__datatool_decimal_tl} \3 } \c_datatool_decimal_grps_i_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_decimal_tl} \4 } \c_datatool_decimal_grps_ii_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_numbergroup_tl} \4 \u{__datatool_decimal_tl} \5 } \c_datatool_decimal_grps_iii_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_numbergroup_tl} \4 \u{__datatool_numbergroup_tl} \5 \u{__datatool_decimal_tl} \6 } } #2 { \tl_set:Ne #2 { \exp_not:N \__datatool_datum:nnnn { \exp_not:V #2 } { #1 } { } { \exp_not:N \c_datatool_decimal_int } } } { \regex_replace_case_once:nNTF { \c_datatool_integer_grps_i_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 } \c_datatool_integer_grps_ii_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_numbergroup_tl} \4 } \c_datatool_integer_grps_iii_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_numbergroup_tl} \4 \u{__datatool_numbergroup_tl} \5 } % \end{macrocode} %This last one ensures that the true branch is followed: % \begin{macrocode} \c_datatool_integer_no_grps_regex { \1 \2 } } #2 { \tl_set:Ne #2 { \exp_not:N \__datatool_datum:nnnn { \exp_not:V #2 } { #1 } { } { \exp_not:N \c_datatool_integer_int } } } { % \end{macrocode} %This is only a warning rather than an error to allow for databases %that contain markup (e.g. \cs{textemdash} or \qt{N/A}) to indicate a missing value. %The numeric functions may either interpret strings as zero or skip them. % \begin{macrocode} \datatool_warn_not_number:n { Can't ~ convert ~ `#1' ~ to ~ decimal: ~ not ~ a ~ number } \tl_set:Nn #2 { \__datatool_datum:nnnn { #1 } { } { } { \c_datatool_string_int } } } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLdecimaltocurrency} %\begin{definition} % \cs{DTLdecimaltocurrency}\oarg{currency symbol}\marg{number}\marg{cmd} %\end{definition} % This converts a decimal number into the locale % dependent currency format. Stores result in \meta{cmd} which must be % a control sequence. An empty value will be treated as zero. %\changes{3.0}{2025-03-03}{added optional argument} % \begin{macrocode} \NewDocumentCommand{\DTLdecimaltocurrency} { O{} m m } { \__datatool_decimal_to_currency:nnN { #1 } { #2 } { #3 } } % \end{macrocode} %\end{macro} %Round currency to this many places (set to empty for no rounding): % \begin{macrocode} \newcommand\DTLCurrentLocaleCurrencyDP{2} % \end{macrocode} % % \begin{macrocode} \cs_new:Nn \__datatool_decimal_to_currency:nnN { \__datatool_decimal_to_currency:nnNn { #1 } { #2 } #3 { } } \cs_generate_variant:Nn \__datatool_decimal_to_currency:nnN { VVN , nVN } % \end{macrocode} % % \begin{macrocode} \cs_new:Nn \__datatool_decimal_to_currency:nnNn { \regex_match:NnTF \c_datatool_any_numeric_regex { #2 } { \tl_if_empty:NTF \DTLCurrentLocaleCurrencyDP { \tl_set:Nn #3 { #2 } \tl_if_empty:NTF #3 { \tl_set:Nn #3 { 0 } } { \regex_replace_once:NnN \c_datatool_integer_leading_zeros_regex { \1 \2 } #3 } } { \tl_if_empty:nTF { #2 } { \dtlround { #3 } { 0 } { \DTLCurrentLocaleCurrencyDP } } { \dtlround { #3 } { #2 } { \DTLCurrentLocaleCurrencyDP } } \tl_set:Ne #3 { \exp_args:Ne \__datatool_decimal_trunc_pad_zeros:nn { #3 } { \DTLCurrentLocaleCurrencyDP } } } \regex_replace_case_once:nN { \c_datatool_decimal_grps_regex { \1 \2 \u{__datatool_decimal_tl} \3 } \c_datatool_decimal_grps_i_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_decimal_tl} \4 } \c_datatool_decimal_grps_ii_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_numbergroup_tl} \4 \u{__datatool_decimal_tl} \5 } \c_datatool_decimal_grps_iii_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_numbergroup_tl} \4 \u{__datatool_numbergroup_tl} \5 \u{__datatool_decimal_tl} \6 } \c_datatool_integer_grps_i_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 } \c_datatool_integer_grps_ii_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_numbergroup_tl} \4 } \c_datatool_integer_grps_iii_regex { \1 \2 \u{__datatool_numbergroup_tl} \3 \u{__datatool_numbergroup_tl} \4 \u{__datatool_numbergroup_tl} \5 } } #3 \tl_set:Ne \l__datatool_datum_currency_code_tl { #4 } \tl_if_blank:nTF { #1 } { \tl_if_empty:NTF \l__datatool_datum_currency_code_tl { \seq_if_in:NVTF \l_datatool_regional_currencies_seq \DTLCurrencyCode { \tl_set:Ne #3 { \exp_not:N \__datatool_datum:nnnn { \exp_not:N \DTLfmtcurr { \DTLCurrencyCode } { \exp_not:V #3 } } { #2 } { \exp_not:V \@dtl@currency } { \exp_not:N \c_datatool_currency_int } } } { \tl_set:Ne #3 { \exp_not:N \__datatool_datum:nnnn { \exp_not:N \DTLfmtcurrency { \exp_not:V \@dtl@currency } { \exp_not:V #3 } } { #2 } { \exp_not:V \@dtl@currency } { \exp_not:N \c_datatool_currency_int } } } } { \tl_set:Ne #3 { \exp_not:N \__datatool_datum:nnnn { \exp_not:N \DTLfmtcurr { \l__datatool_datum_currency_code_tl } { \exp_not:V #3 } } { #2 } { \exp_not:N \DTLcurr { \l__datatool_datum_currency_code_tl } } { \exp_not:N \c_datatool_currency_int } } } } { \tl_if_empty:NT \l__datatool_datum_currency_code_tl { \datatool_get_currency_code:NV \l__datatool_datum_currency_code_tl \l__datatool_datum_currency_tl \tl_if_eq:NnT \l__datatool_datum_currency_code_tl { XXX } { \tl_clear:N \l__datatool_datum_currency_code_tl } } \tl_if_empty:NTF \l__datatool_datum_currency_code_tl { \tl_set:Ne #3 { \exp_not:N \__datatool_datum:nnnn { \exp_not:N \DTLfmtcurrency { \exp_not:n { #1 } } { \exp_not:V #3 } } { #2 } { \exp_not:n { #1 } } { \exp_not:N \c_datatool_currency_int } } } { \tl_set:Ne #3 { \exp_not:N \__datatool_datum:nnnn { \exp_not:N \DTLfmtcurr { \l__datatool_datum_currency_code_tl } { \exp_not:V #3 } } { #2 } { \exp_not:n { #1 } } { \exp_not:N \c_datatool_currency_int } } } } } { % \end{macrocode} %This is only a warning rather than an error to allow for databases %that contain markup (e.g. \cs{textemdash} or \qt{N/A}) to indicate a missing value. %The numeric functions may either interpret strings as zero or skip them. % \begin{macrocode} \datatool_warn_not_number:n { Can't ~ convert ~ `#2' ~ to ~ currency: ~ not ~ a ~ number } \tl_set:Nn #3 { \__datatool_datum:nnnn { #2 } { } { } { \c_datatool_string_int } } } } \cs_generate_variant:Nn \__datatool_decimal_to_currency:nnNn { VVNV , nVNV } % \end{macrocode} % %\begin{macro}{\DTLdecimaltodatetime} %\begin{definition} % \cs{DTLdecimaltodatetime}\marg{number}\marg{cmd} %\end{definition} % Define command to convert a decimal number into a datetime datum. % Stores result in \meta{cmd} which must be % a control sequence. % \begin{macrocode} \NewDocumentCommand \DTLdecimaltodatetime { m m } { \datatool_decimal_to_temporal:Nnn #2 { \c_datatool_datetime_int } { #1 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLdecimaltodate} %\begin{definition} % \cs{DTLdecimaltodate}\marg{JDN}\marg{cmd} %\end{definition} % Define command to convert a number (a Julian Day Number) into a date datum. % Stores result in \meta{cmd} which must be % a control sequence. % \begin{macrocode} \NewDocumentCommand \DTLdecimaltodate { m m } { \datatool_decimal_to_temporal:Nnn #2 { \c_datatool_date_int } { #1 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLdecimaltotime} %\begin{definition} % \cs{DTLdecimaltotime}\marg{JT}\marg{cmd} %\end{definition} % Define command to convert a decimal number (Julian time of day fraction) into a time datum. % Stores result in \meta{cmd} which must be % a control sequence. % \begin{macrocode} \NewDocumentCommand \DTLdecimaltotime { m m } { \datatool_decimal_to_temporal:Nnn #2 { \c_datatool_time_int } { #1 } } % \end{macrocode} %\end{macro} % %\subsubsection{Dates and Times} %New to version 3.0. No localisation is performed. These functions %just deal with parsing ISO dates, times and timestamps. There are in %total eight numeric elements of a timestamp: year, month number %(1--12), day of month number (1--31), hour of day number (0--23), %minute of hour number (0--59), seconds of minute number (0--59), %time zone hour offset ($-12$--$+12$), and time zone minute offset %(0--59). This can lead to commands with a lot of arguments, so a %specially formatted sequence with 8 items is sometimes used instead. %Each item is formatted when it is set in the sequence to make it %easier to reconstruct a timestamp. % %Dates, times and timestamps can be represented numerically. The %Julian Day Number (JDN) is the integer number of whole solar days since noon %Universal Time. A value of zero represents Monday 1st January 4713~BC, %according to the proleptic Julian Calendar. So, noon 1st January %2000 has the Julian Day Number 2451545. %The time of day is represented as a fractional number from noon. %A negative value is a time before noon. A positive number is a time %after noon. Noon has a value of $0.0$. Twelve hours before noon, that %is, the previous midnight has a value of $-0.5$. Twelve hours after %noon, that is, the following midnight has a value of $0.5$. % %The Julian Date is a decimal that is simply the sum of the Julian %Day Number and the time fraction. For example, %$2451545 + 0.5 = 2451545.5$ is midnight after noon 2000-01-01 %and $2451545 - 0.5 = 2451544.5$ is midnight before %noon 2000-01-01. Note that while the term \qt{Julian Date} is sometimes %used to refer to the ordinal day of year in other works, from \sty{datatool}'s %point of view, \qt{Julian Date} is the decimal sum of the JDN and %the time of day fraction. % %Since JDN~0 is a Monday, the day of the week (starting with %Monday=0) can be obtained from the remainder of the %integer division of the Julian Day Number and 7 (that is, %\meta{JDN} modulo 7). % %A temporal datum command has the data type set to one of the %temporal identifiers (datetime, date or time). The string part may %be the original string parsed or maybe a formatted version of the %temporal value. % %\begin{macro}{\DTLtemporalvalue} %\changes{3.0}{2025-03-03}{new} %It's useful to retain the original ISO specification. Since the %string part of the datum variable may be used to provide a %localised format, the original ISO format is hidden in the value %part, which is set to: %\begin{definition} %\cs{DTLtemporalvalue}\marg{number}\marg{ISO} %\end{definition} %This allows both the original ISO representation to be extracted as %well as the corresponding numerical value. %The default definition allows the ISO part to be discarded %in numerical contexts. This means that using \cs{DTLdatumvalue} %will still expand to a numeric value, but it requires more %expansion than the other numeric data types. % %This command isn't an internal command as it %may occur in a dbtex-3 file, which expects the normal document %category codes. The number depends on the datum type: a decimal %Julian date for timestamps, an integer Julian day for date only %or a decimal (fraction of a day) for time only. % \begin{macrocode} \newcommand \DTLtemporalvalue [2] { #1 } % \end{macrocode} %\end{macro} % % %\begin{definition} %\cs{datatool\_decimal\_to\_temporal:Nnn} %\meta{datum-cs}\marg{data-type}\marg{number} %\end{definition} %Converts a number to a datum-cs. % \begin{macrocode} \cs_new_nopar:Nn \datatool_decimal_to_temporal:Nnn { \int_case:nnF { #2 } { { \c_datatool_datetime_int } { \datatool_from_julian_date:nN { #3 } \l__datatool_datetime_seq \datatool_timestamp_to_datetime_datum:NeN \l__datatool_datetime_seq { #3 } #1 } { \c_datatool_date_int } { \int_set:Nn \l__datatool_julian_int { \fp_to_int:n { #3 } } \datatool_from_julian_day:VNNN \l__datatool_julian_int \l__datatool_year_int \l__datatool_month_int \l__datatool_day_int \tl_set:Ne #1 { \exp_not:N \__datatool_datum:nnnn {% formatted string \exp_not:N \DataToolDateFmt { \int_use:N \l__datatool_year_int } { \int_use:N \l__datatool_month_int } { \int_use:N \l__datatool_day_int } { \datatool_julian_day_to_dow:n { \l__datatool_julian_int } } } {% value \exp_not:N \DTLtemporalvalue { \int_use:N \l__datatool_julian_int } { \int_use:N \l__datatool_year_int - \datatool_two_digits:N \l__datatool_month_int - \datatool_two_digits:N \l__datatool_day_int } } { }% no currency symbol { \exp_not:N \c_datatool_date_int } } } { \c_datatool_time_int } { \datatool_from_julian_time:eNNN { #3 } \l__datatool_hour_int \l__datatool_minute_int \l__datatool_second_int \tl_set:Ne #1 { \exp_not:N \__datatool_datum:nnnn {% formatted string \exp_not:N \DataToolTimeFmt { \int_use:N \l__datatool_hour_int } { \int_use:N \l__datatool_minute_int } { \int_use:N \l__datatool_second_int } } {% value \exp_not:N \DTLtemporalvalue { #3 } { \datatool_two_digits:n { \l__datatool_hour_int } \c_colon_str \datatool_two_digits:N \l__datatool_minute_int \c_colon_str \datatool_two_digits:N \l__datatool_second_int } } { }% no currency symbol { \exp_not:N \c_datatool_time_int } } } } { \PackageError { datatool-base } { Invalid ~ data ~ type ~ \int_eval:n { #2 }: ~ not ~ a ~ temporal ~ type } { The ~ second ~ argument ~ of \token_to_str:N \datatool_decimal_to_temporal:Nnn ~ \c_space_tl ~ should ~ be ~ one ~ of: ~ \exp_not:N \c_datatool_datetime_int , ~ \exp_not:N \c_datatool_date_int , ~ or ~ \exp_not:N \c_datatool_time_int } } } \cs_generate_variant:Nn \datatool_decimal_to_temporal:Nnn { NnV } % \end{macrocode} % %\begin{definition} %\cs{datatool\_extract\_timestamp:NN}\marg{datum-cs}\marg{result-tl} %\end{definition} %Extracts the date/time data stored in the given datum command and %defines the supplied token list command to that value. %If there's no date/time data in the datum command, the result token %list variable will be set to empty. This may mean that the datum variable %isn't a temporal variable, but may also mean that the value has %lost its special markup due to expansion. % \begin{macrocode} \cs_new:Nn \datatool_extract_timestamp:NN { \tl_clear:N #2 \group_begin: \cs_set_eq:NN \DTLtemporalvalue \use_ii:nn \tl_set:Ne \l__datatool_tmpa_tl { \DTLdatumvalue { #1 } } \regex_match:NVTF \c_datatool_temporal_regex \l__datatool_tmpa_tl { \tl_set:Ne \l__datatool_tmpa_tl { \exp_not:N \tl_set:Nn \exp_not:N #2 { \l__datatool_tmpa_tl } } } { \tl_clear:N \l__datatool_tmpa_tl } \exp_after:wN \group_end: \l__datatool_tmpa_tl } % \end{macrocode} % %\begin{macro}{\DTLdatumtoDTM} %\begin{definition} %\cs{DTLdatumtoDTM}\marg{datum-cs}\marg{DTM-name} %\end{definition} %Converts the time stamp stored in the given datum command to a %\sty{datetime2} date saved with \cs{DTMsavetimestamp}, %\cs{DTMsavedate}, or \cs{DTLsavetime}. If the time stamp is %missing, this will attempt to recalculate the relevant information %based on the numeric value and data type. % \begin{macrocode} \NewDocumentCommand \DTLdatumtoDTM { m m } { \cs_if_exist:NTF \DTMsavetimestamp { \group_begin: \cs_set_eq:NN \DTLtemporalvalue \use_ii:nn % \end{macrocode} %If the value contains \cs{DTLtemporalvalue} the temporary token %list variable will be set to the ISO format in the second argument. % \begin{macrocode} \tl_set:Ne \l__datatool_tmpa_tl { \DTLdatumvalue { #1 } } \regex_match:NVTF \c_datatool_timestamp_regex \l__datatool_tmpa_tl { % \end{macrocode} %Result matches a timestamp, so use \cs{DTMsavetimestamp}. % \begin{macrocode} \tl_set:Ne \l__datatool_tmpa_tl { \exp_not:N \DTMsavetimestamp { #2 } { \l__datatool_tmpa_tl } } } { \regex_match:NVTF \c_datatool_date_regex \l__datatool_tmpa_tl { % \end{macrocode} %Result only matches a date, so use \cs{DTMsavedate}. % \begin{macrocode} \tl_set:Ne \l__datatool_tmpa_tl { \exp_not:N \DTMsavedate { #2 } { \l__datatool_tmpa_tl } } } { \regex_extract_once:NVNTF \c_datatool_time_regex \l__datatool_tmpa_tl \l_datatool_timestamp_match_seq { % \end{macrocode} %Result only matches a time, so use \cs{DTMsavetime} but the seconds %may be missing, which are required. % \begin{macrocode} \tl_if_empty:eTF { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { \tl_set:Ne \l__datatool_tmpa_tl { \exp_not:N \DTMsavetime { #2 } { \l__datatool_tmpa_tl : 00 } } } { \tl_set:Ne \l__datatool_tmpa_tl { \exp_not:N \DTMsavetime { #2 } { \l__datatool_tmpa_tl } } } } { \tl_if_empty:NTF \l__datatool_tmpa_tl { \PackageError { datatool-base } { Can't ~ obtain ~ timestamp. ~ No ~ value ~ associated with ` \token_to_str:N #1 ' } { The ~ provided ~ value ~ doesn't ~ seem ~ to ~ be ~ numeric. ~ If ~ you ~ were ~ expecting ~ it ~ to ~ be ~ parsed ~ as ~ a ~ date/time ~ then ~ \bool_if:NTF \l__datatool_parse_datetime_bool { use ~ \token_to_str:N \DTLsetup { datetime=parse-only } ~ or ~ \token_to_str:N \DTLsetup { datetime=reformat } } { check ~ the ~ original ~ source ~ matched ~ the ~ correct ~ format } } } { \int_set:Nn \@dtl@datatype { \DTLdatumtype { #1 } } \int_case:nn { \@dtl@datatype } { { \c_datatool_integer_int } { \int_set_eq:NN \@dtl@datatype \c_datatool_date_int } { \c_datatool_decimal_int } { \fp_compare:nNnTF { \fp_abs:n { \l__datatool_tmpa_tl } } < { \c_one_fp } { \int_set_eq:NN \@dtl@datatype \c_datatool_time_int } { \int_set_eq:NN \@dtl@datatype \c_datatool_datetime_int } } } \int_case:nnF { \@dtl@datatype } { { \c_datatool_datetime_int } { \PackageWarning { datatool-base } { No ~ timestamp ~ could ~ be ~ found ~ in ~ the ~ provided ~ datum ~ command. ~ Value ~ found: ~ \l__datatool_tmpa_tl . ~ I'm ~ going ~ to ~ assume ~ this ~ is ~ a ~ Julian ~ Date } \datatool_from_julian_date:VN \l__datatool_tmpa_tl \l__datatool_datetime_seq \tl_set:Ne \l__datatool_tmpa_tl { \exp_not:N \DTMsavetimestamp { #2 } { \datatool_timestamp_to_iso:N \l__datatool_datetime_seq } } } { \c_datatool_date_int } { \PackageWarning { datatool-base } { No ~ timestamp ~ could ~ be ~ found ~ in ~ the ~ provided ~ datum ~ command. ~ Value ~ found: ~ \l__datatool_tmpa_tl . ~ I'm ~ going ~ to ~ assume ~ this ~ is ~ a ~ Julian ~ Day ~ Number } \int_set:Nn \l__datatool_julian_int { \l__datatool_tmpa_tl } \datatool_from_julian_day:VNNN \l__datatool_julian_int \l__datatool_year_int \l__datatool_month_int \l__datatool_day_int \tl_set:Ne \l__datatool_tmpa_tl { \exp_not:N \DTMsavenoparsedate { #2 } { \int_use:N \l__datatool_year_int } { \int_use:N \l__datatool_month_int } { \int_use:N \l__datatool_day_int } { \datatool_julian_day_to_dow:n { \l__datatool_julian_int } } } } { \c_datatool_time_int } { \PackageWarning { datatool-base } { No ~ timestamp ~ could ~ be ~ found ~ in ~ the ~ provided ~ datum ~ command. ~ Value ~ found: ~ \l__datatool_tmpa_tl . ~ I'm ~ going ~ to ~ assume ~ this ~ is ~ a ~ Julian ~ time ~ of ~ day ~ fraction } \fp_set:Nn \l__datatool_julian_fp { \l__datatool_tmpa_tl } \datatool_from_julian_time:nNNN { \l__datatool_julian_fp } \l__datatool_hour_int \l__datatool_minute_int \l__datatool_second_int \tl_set:Ne \l__datatool_tmpa_tl { \exp_not:N \DTMsavetime { #2 } { \int_use:N \l__datatool_year_int \c_colon_str \int_use:N \l__datatool_month_int \c_colon_str \int_use:N \l__datatool_day_int } } } } { \PackageError { datatool-base } { No ~ timestamp ~ could ~ be ~ found ~ in ~ the ~ provided ~ datum ~ command. ~ Value ~ found: ~ \l__datatool_tmpa_tl } { The ~ datum ~ value ~ supplied ~ can't ~ be ~ interpreted ~ as ~ a ~ timestamp, ~ date ~ or ~ time. } } } } } } \exp_after:wN \group_end: \l__datatool_tmpa_tl } { \PackageError { datatool-base } { \token_to_str:N \DTLdatumtoDTM ~ \c_space_tl ~ can't ~ save ~ time ~ stamp. ~ Command ~ \token_to_str:N \DTMsavetimestamp ~ \c_space_tl ~ undefined ~ (datetime2 ~ required) } { You ~ need ~ to ~ load ~ datetime2.sty } } } % \end{macrocode} %\end{macro} % %\paragraph{Formatting} % %Syntax: %\meta{datum-cs}\meta{value-cs}\marg{string value} % %Used to set the value part of a temporal datum %and the update token list variable. %If the \marg{string value} argument is empty, %the string part is obtained by formatting the temporal value. % \begin{macrocode} \cs_new:Nn \__datatool_set_datetime_value:NNn { \int_case:nn { \@dtl@datatype } { { \c_datatool_datetime_int } { \tl_set:Ne #2 { \exp_not:N \DTLtemporalvalue { \fp_use:N \l__datatool_julian_fp } { \__datatool_tm_iso: } } \tl_if_empty:nTF { #3 } { \__datatool_set_datum:NeVnn #1 { \exp_not:N \DataToolDateTimeFmt { { \__datatool_tm_yr: } { \__datatool_tm_mn: } { \__datatool_tm_dy: } { \__datatool_tm_dow: } } { { \__datatool_tm_hr: } { \__datatool_tm_mi: } { \__datatool_tm_se: } } { { \__datatool_tm_tzh: } { \__datatool_tm_tzm: } } } #2 { } { \c_datatool_datetime_int } } { \__datatool_set_datum:NnVnn #1 { #3 } #2 { } { \c_datatool_datetime_int } } } { \c_datatool_date_int } { \tl_set:Ne #2 { \exp_not:N \DTLtemporalvalue { \int_use:N \l__datatool_julian_int } { \__datatool_tm_iso_date: } } \tl_if_empty:nTF { #3 } { \__datatool_set_datum:NeVnn #1 { \exp_not:N \DataToolDateFmt { \__datatool_tm_yr: } { \__datatool_tm_mn: } { \__datatool_tm_dy: } { \__datatool_tm_dow: } } #2 { } { \c_datatool_date_int } } { \__datatool_set_datum:NnVnn #1 { #3 } #2 { } { \c_datatool_date_int } } } { \c_datatool_time_int } { \tl_set:Ne #2 { \exp_not:N \DTLtemporalvalue { \fp_use:N \l__datatool_time_fp } { \__datatool_tm_iso_time: } } \tl_if_empty:nTF { #3 } { \__datatool_set_datum:NeVnn #1 { \exp_not:N \DataToolTimeFmt { \__datatool_tm_hr: } { \__datatool_tm_mi: } { \__datatool_tm_se: } } #2 { } { \c_datatool_time_int } } { \__datatool_set_datum:NnVnn #1 { #3 } #2 { } { \c_datatool_time_int } } } } } % \end{macrocode} % %\paragraph{Constants and Scratch Variables} % %Scratch sequence for timestamps: % \begin{macrocode} \seq_new:N \l__datatool_datetime_seq % \end{macrocode} %Individual integer elements of a timestamp. % \begin{macrocode} \int_new:N \l__datatool_year_int \int_new:N \l__datatool_month_int \int_new:N \l__datatool_day_int \int_new:N \l__datatool_hour_int \int_new:N \l__datatool_minute_int \int_new:N \l__datatool_second_int \int_new:N \l__datatool_tzhour_int \int_new:N \l__datatool_tzminute_int % \end{macrocode} %Integer Julian Day Number: % \begin{macrocode} \int_new:N \l__datatool_julian_int \int_new:N \l__datatool_local_julian_int % \end{macrocode} %Floating point values for the Julian date and time of day fraction: % \begin{macrocode} \fp_new:N \l__datatool_julian_fp \fp_new:N \l__datatool_time_fp % \end{macrocode} %Re-initialise date/time individual scratch variables. %That is, set them all back to zero. % \begin{macrocode} \cs_new:Nn \__datatool_tm_var_init: { \int_zero:N \l__datatool_year_int \int_zero:N \l__datatool_month_int \int_zero:N \l__datatool_day_int \int_zero:N \l__datatool_hour_int \int_zero:N \l__datatool_minute_int \int_zero:N \l__datatool_second_int \int_zero:N \l__datatool_tzhour_int \int_zero:N \l__datatool_tzminute_int \int_zero:N \l__datatool_julian_int \fp_zero:N \l__datatool_julian_fp \fp_zero:N \l__datatool_time_fp } % \end{macrocode} % % Date time stamp regular expression. Time zone may be omitted. % Separator between date and time may be either space or \qt{T}. % \begin{macrocode} \regex_const:Nn \c_datatool_timestamp_regex { \A \s* ( \d+ ) \x { 2D } ( \d{2} ) \x { 2D } ( \d{2} ) (?: \x { 54 } | \s+ ) ( \d{2} ) \x { 3A } ( \d{2} ) \x { 3A } ( \d{2} ) ( \x { 5A } | [\+\-]? \d{2} \x { 3A } ? \d{2} ) ? \s* \Z } % \end{macrocode} %Date only. % \begin{macrocode} \regex_const:Nn \c_datatool_date_regex { \A \s* ( \d+ ) \x { 2D } ( \d{2} )\x { 2D } ( \d{2} ) \s* \Z } % \end{macrocode} %Time only. Seconds are optional. % \begin{macrocode} \regex_const:Nn \c_datatool_time_regex { \A \s* ( [\+\-]? \d{2} ) \x { 3A } ( \d{2} ) (?: \x { 3A } ( \d{2} ) )? \s* \Z } % \end{macrocode} %Maybe timestamp or just date or just time: % \begin{macrocode} \regex_const:Nn \c_datatool_temporal_regex { (?: (?: \d+ ) \x { 2D } (?: \d{2} ) \x { 2D } (?: \d{2} ) (?: (?: \x { 54 } | \s+ ) (?: \d{2} ) \x { 3A } (?: \d{2} ) \x { 3A } (?: \d{2} ) (?: \x { 5A } | [\+\-]? \d{2} \x { 3A } ? \d{2} ) ? ) ? ) | (?: (?: \d{2} ) \x { 3A } (?: \d{2} ) (?: \x { 3A } (?: \d{2} ) )? ) } % \end{macrocode} % %\paragraph{Timestamp Sequences} % %Time stamp sequences are just a nine-item sequence, where each %item is a numeric value. All but the year (item 1) and day of week %(item 9) should be correctly zero-padded. The year should include the century. %For example, if the year is given as 24 then that means 0024 not %2024. The day of week item may be negative to indicate a missing %value. % %The following commands are simply wrappers that use normal sequence commands. % % \begin{macrocode} \cs_new:Nn \datatool_timestamp_new:N { \seq_new:N #1 \datatool_timestamp_zero:N #1 } % \end{macrocode} % %Constant representing 0000-00-00T00:00:00+00:00 %(used for initialisation). % \begin{macrocode} \seq_const_from_clist:Nn \c_datatool_timestamp_zero_seq { 0000 , 00 , 00 , 00 , 00 , 00 , +00 , 00, -1 } % \end{macrocode} % %Resets all elements of a timestamp sequence. % \begin{macrocode} \cs_new:Nn \datatool_timestamp_zero:N { \seq_set_eq:NN #1 \c_datatool_timestamp_zero_seq } % \end{macrocode} % %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_seq_init: { \datatool_timestamp_zero:N \l__datatool_datetime_seq } % \end{macrocode} % %The following commands assume the provided sequence has been %correctly defined with nine numeric items, as described above. %Check for empty argument as regex match can have optional parts. % %Extract year from a timestamp sequence. (First item.) % \begin{macrocode} \cs_new:Nn \datatool_timestamp_get_year:N { \seq_item:Nn #1 { 1 } } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_yr: { \datatool_timestamp_get_year:N \l__datatool_datetime_seq } % \end{macrocode} %Set year (item 1). If empty, assume current year. % \begin{macrocode} \cs_new:Nn \datatool_timestamp_set_year:Nn { \tl_if_empty:nTF { #2 } { \exp_args:NNne \seq_set_item:Nnn #1 { 1 } { \int_use:N \c_sys_year_int } } { \seq_set_item:Nnn #1 { 1 } { #2 } } } \cs_generate_variant:Nn \datatool_timestamp_set_year:Nn { Ne, NV } % \end{macrocode} %As above but add century if between 0 and 99. % \begin{macrocode} \cs_new:Nn \datatool_timestamp_set_year_add_century:Nn { \tl_if_empty:nTF { #2 } { \exp_args:NNne \seq_set_item:Nnn #1 { 1 } { \int_use:N \c_sys_year_int } } { \bool_lazy_or:nnTF { \int_compare_p:nNn { #2 } < { \c_zero_int } } { \int_compare_p:nNn { #2 } > { 99 } } { \seq_set_item:Nnn #1 { 1 } { #2 } } { \datatool_timestamp_set_year:Ne #1 { \int_eval:n { ( \int_div_truncate:nn { \c_sys_year_int } { 100 } ) * 100 + #2 } } } } } \cs_generate_variant:Nn \datatool_timestamp_set_year_add_century:Nn { Ne, NV } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_set_yr:n { \datatool_timestamp_set_year:Nn \l__datatool_datetime_seq { #1 } } \cs_generate_variant:Nn \__datatool_tm_set_yr:n { e } % \end{macrocode} % %Extract month from sequence. (Second item.) % \begin{macrocode} \cs_new:Nn \datatool_timestamp_get_month:N { \seq_item:Nn #1 { 2 } } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_mn: { \datatool_timestamp_get_month:N \l__datatool_datetime_seq } % \end{macrocode} %Set month (item 2): % \begin{macrocode} \cs_new:Nn \datatool_timestamp_set_month:Nn { \tl_if_empty:nTF { #2 } { \seq_set_item:Nnn #1 { 2 } { 00 } } { \exp_args:NNne \seq_set_item:Nnn #1 { 2 } { \datatool_two_digits:n { #2 } } } } \cs_generate_variant:Nn \datatool_timestamp_set_month:Nn { Ne, NV } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_set_mn:n { \datatool_timestamp_set_month:Nn \l__datatool_datetime_seq { #1 } } \cs_generate_variant:Nn \__datatool_tm_set_mn:n { e } % \end{macrocode} % %Extract day from sequence. (Third item.) % \begin{macrocode} \cs_new:Nn \datatool_timestamp_get_day:N { \seq_item:Nn #1 { 3 } } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_dy: { \datatool_timestamp_get_day:N \l__datatool_datetime_seq } % \end{macrocode} %Set day (item 3): % \begin{macrocode} \cs_new:Nn \datatool_timestamp_set_day:Nn { \tl_if_empty:nTF { #2 } { \seq_set_item:Nnn #1 { 3 } { 00 } } { \exp_args:NNne \seq_set_item:Nnn #1 { 3 } { \datatool_two_digits:n { #2 } } } } \cs_generate_variant:Nn \datatool_timestamp_set_day:Nn { Ne, NV } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_set_dy:n { \datatool_timestamp_set_day:Nn \l__datatool_datetime_seq { #1 } } \cs_generate_variant:Nn \__datatool_tm_set_dy:n { e } % \end{macrocode} % %Extract hour from sequence. (Fourth item.) % \begin{macrocode} \cs_new:Nn \datatool_timestamp_get_hour:N { \seq_item:Nn #1 { 4 } } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_hr: { \datatool_timestamp_get_hour:N \l__datatool_datetime_seq } % \end{macrocode} %Set hour (item 4): % \begin{macrocode} \cs_new:Nn \datatool_timestamp_set_hour:Nn { \tl_if_empty:nTF { #2 } { \seq_set_item:Nnn #1 { 4 } { 00 } } { \exp_args:NNne \seq_set_item:Nnn #1 { 4 } { \datatool_two_digits:n { #2 } } } } \cs_generate_variant:Nn \datatool_timestamp_set_hour:Nn { Ne, NV } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_set_hr:n { \datatool_timestamp_set_hour:Nn \l__datatool_datetime_seq { #1 } } \cs_generate_variant:Nn \__datatool_tm_set_hr:n { e } % \end{macrocode} % %Extract minute from sequence. (Fifth item.) % \begin{macrocode} \cs_new:Nn \datatool_timestamp_get_minute:N { \seq_item:Nn #1 { 5 } } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_mi: { \datatool_timestamp_get_minute:N \l__datatool_datetime_seq } % \end{macrocode} %Set minute (item 5): % \begin{macrocode} \cs_new:Nn \datatool_timestamp_set_minute:Nn { \tl_if_empty:nTF { #2 } { \seq_set_item:Nnn #1 { 5 } { 00 } } { \exp_args:NNne \seq_set_item:Nnn #1 { 5 } { \datatool_two_digits:n { #2 } } } } \cs_generate_variant:Nn \datatool_timestamp_set_minute:Nn { Ne, NV } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_set_mi:n { \datatool_timestamp_set_minute:Nn \l__datatool_datetime_seq { #1 } } \cs_generate_variant:Nn \__datatool_tm_set_mi:n { e } % \end{macrocode} % %Extract second from sequence. (Sixth item.) % \begin{macrocode} \cs_new:Nn \datatool_timestamp_get_second:N { \seq_item:Nn #1 { 6 } } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_se: { \datatool_timestamp_get_second:N \l__datatool_datetime_seq } % \end{macrocode} %Set second (item 6): % \begin{macrocode} \cs_new:Nn \datatool_timestamp_set_second:Nn { \tl_if_empty:nTF { #2 } { \seq_set_item:Nnn #1 { 6 } { 00 } } { \exp_args:NNne \seq_set_item:Nnn #1 { 6 } { \datatool_two_digits:n { #2 } } } } \cs_generate_variant:Nn \datatool_timestamp_set_second:Nn { Ne, NV } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_set_se:n { \datatool_timestamp_set_second:Nn \l__datatool_datetime_seq { #1 } } \cs_generate_variant:Nn \__datatool_tm_set_se:n { e } % \end{macrocode} % %Extract time zone hour from sequence. (Seventh item.) % \begin{macrocode} \cs_new:Nn \datatool_timestamp_get_tzhour:N { \seq_item:Nn #1 { 7 } } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_tzh: { \datatool_timestamp_get_tzhour:N \l__datatool_datetime_seq } % \end{macrocode} %Set time zone hour (item 7): % \begin{macrocode} \cs_new:Nn \datatool_timestamp_set_tzhour:Nn { \tl_if_empty:nTF { #2 } { \seq_set_item:Nnn #1 { 7 } { +00 } } { \exp_args:NNne \seq_set_item:Nnn #1 { 7 } { \datatool_signed_two_digits:n { #2 } } } } \cs_generate_variant:Nn \datatool_timestamp_set_tzhour:Nn { Ne, NV } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_set_tzh:n { \datatool_timestamp_set_tzhour:Nn \l__datatool_datetime_seq { #1 } } \cs_generate_variant:Nn \__datatool_tm_set_tzh:n { e } % \end{macrocode} % %Extract time zone minute from sequence. (Eighth item.) % \begin{macrocode} \cs_new:Nn \datatool_timestamp_get_tzminute:N { \seq_item:Nn #1 { 8 } } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_tzm: { \datatool_timestamp_get_tzminute:N \l__datatool_datetime_seq } % \end{macrocode} %Set time zone minute (item 8): % \begin{macrocode} \cs_new:Nn \datatool_timestamp_set_tzminute:Nn { \tl_if_empty:nTF { #2 } { \seq_set_item:Nnn #1 { 8 } { 00 } } { \exp_args:NNne \seq_set_item:Nnn #1 { 8 } { \datatool_two_digits:n { #2 } } } } \cs_generate_variant:Nn \datatool_timestamp_set_tzminute:Nn { Ne, NV } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_set_tzm:n { \datatool_timestamp_set_tzminute:Nn \l__datatool_datetime_seq { #1 } } \cs_generate_variant:Nn \__datatool_tm_set_tzm:n { e } % \end{macrocode} % %Extract day of week from sequence. (Ninth item.) % \begin{macrocode} \cs_new:Nn \datatool_timestamp_get_dow:N { \seq_item:Nn #1 { 9 } } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_dow: { \datatool_timestamp_get_dow:N \l__datatool_datetime_seq } % \end{macrocode} %Set day of week (item 9): % \begin{macrocode} \cs_new:Nn \datatool_timestamp_set_dow:Nn { \tl_if_empty:nTF { #2 } { \seq_set_item:Nnn #1 { 9 } { -1 } } { \exp_args:NNne \seq_set_item:Nnn #1 { 9 } { #2 } } } \cs_generate_variant:Nn \datatool_timestamp_set_dow:Nn { Ne, NV } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_set_dow:n { \datatool_timestamp_set_dow:Nn \l__datatool_datetime_seq { #1 } } \cs_generate_variant:Nn \__datatool_tm_set_dow:n { e, V } % \end{macrocode} % % %Convert timestamp sequence to ISO string. Note that the supplied %argument must be a timestamp sequence. That is, a sequence with 8 %numeric items that have already been correctly zero-padded. % \begin{macrocode} \cs_new:Nn \datatool_timestamp_to_iso:N { \datatool_timestamp_get_year:N #1 - \datatool_timestamp_get_month:N #1 - \datatool_timestamp_get_day:N #1 T \datatool_timestamp_get_hour:N #1 \c_colon_str \datatool_timestamp_get_minute:N #1 \c_colon_str \datatool_timestamp_get_second:N #1 \datatool_timestamp_get_tzhour:N #1 \c_colon_str \datatool_timestamp_get_tzminute:N #1 } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_iso: { \datatool_timestamp_to_iso:N \l__datatool_datetime_seq } % \end{macrocode} % %Just the year, month and date: % \begin{macrocode} \cs_new:Nn \datatool_timestamp_to_iso_date:N { \datatool_timestamp_get_year:N #1 - \datatool_timestamp_get_month:N #1 - \datatool_timestamp_get_day:N #1 } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_iso_date: { \datatool_timestamp_to_iso_date:N \l__datatool_datetime_seq } % \end{macrocode} % %Just the time (hours, minutes and seconds): % \begin{macrocode} \cs_new:Nn \datatool_timestamp_to_iso_time:N { \datatool_timestamp_get_hour:N #1 \c_colon_str \datatool_timestamp_get_minute:N #1 \c_colon_str \datatool_timestamp_get_second:N #1 } % \end{macrocode} %Shortcut for internal scratch variable. % \begin{macrocode} \cs_new:Nn \__datatool_tm_iso_time: { \datatool_timestamp_to_iso_time:N \l__datatool_datetime_seq } % \end{macrocode} % %\begin{definition} %\cs{datatool\_timestamp\_to\_datetime\_datum:NnN} %\meta{timestamp seq-var} %\marg{Julian Date} %\meta{datum cs} %\end{definition} %Convert a timestamp sequence variable to a datetime datum variable. % \begin{macrocode} \cs_new:Nn \datatool_timestamp_to_datetime_datum:NnN { \tl_set:Ne #3 { \exp_not:N \__datatool_datum:nnnn {% formatted string \exp_not:N \DataToolDateTimeFmt { { \datatool_timestamp_get_year:N #1 } { \datatool_timestamp_get_month:N #1 } { \datatool_timestamp_get_day:N #1 } { \datatool_timestamp_get_dow:N #1 } } { { \datatool_timestamp_get_hour:N #1 } { \datatool_timestamp_get_minute:N #1 } { \datatool_timestamp_get_second:N #1 } } { { \datatool_timestamp_get_tzhour:N #1 } { \datatool_timestamp_get_tzminute:N #1 } } } {% value \exp_not:N \DTLtemporalvalue { #2 } { \datatool_timestamp_to_iso:N #1 } } { }% no currency symbol { \exp_not:N \c_datatool_datetime_int } } } \cs_generate_variant:Nn \datatool_timestamp_to_datetime_datum:NnN { NeN, NVN } % \end{macrocode} % %\begin{definition} %\cs{datatool\_timestamp\_to\_date\_datum:NnN} %\meta{timestamp seq-var} %\marg{Julian Day} %\meta{datum cs} %\end{definition} %Convert a timestamp sequence variable to a date datum variable. % \begin{macrocode} \cs_new:Nn \datatool_timestamp_to_date_datum:NnN { \tl_set:Ne #3 { \exp_not:N \__datatool_datum:nnnn {% formatted string \exp_not:N \DataToolDateFmt { \datatool_timestamp_get_year:N #1 } { \datatool_timestamp_get_month:N #1 } { \datatool_timestamp_get_day:N #1 } { \datatool_julian_day_to_dow:n { #2 } } } {% value \exp_not:N \DTLtemporalvalue { #2 } { \datatool_timestamp_to_iso_date:N #1 } } { }% no currency symbol { \exp_not:N \c_datatool_date_int } } } \cs_generate_variant:Nn \datatool_timestamp_to_date_datum:NnN { NeN, NVN } % \end{macrocode} % %\begin{definition} %\cs{datatool\_timestamp\_to\_time\_datum:NnN} %\meta{timestamp seq-var} %\marg{Julian Time} %\meta{datum cs} %\end{definition} %Convert a timestamp sequence variable to a time datum variable. % \begin{macrocode} \cs_new:Nn \datatool_timestamp_to_time_datum:NnN { \tl_set:Ne #3 { \exp_not:N \__datatool_datum:nnnn {% formatted string \exp_not:N \DataToolTimeFmt { \datatool_timestamp_get_hour:N #1 } { \datatool_timestamp_get_minute:N #1 } { \datatool_timestamp_get_second:N #1 } } {% value \exp_not:N \DTLtemporalvalue { #2 } { \datatool_timestamp_to_iso_time:N #1 } } { }% no currency symbol { \exp_not:N \c_datatool_time_int } } } \cs_generate_variant:Nn \datatool_timestamp_to_time_datum:NnN { NeN, NVN } % \end{macrocode} % %\paragraph{Calculations} % %Convert Unix time to Julian date. % \begin{macrocode} \cs_new:Nn \datatool_unix_to_julian:n { \fp_eval:n { #1 / 86400 + 2440587.5 } } % \end{macrocode} %Convert a Julian date to Unix time. Note that the Unix time may be %too large to be a TeX integer. The result will be a decimal. % \begin{macrocode} \cs_new:Nn \datatool_julian_to_unix:n { \fp_eval:n { (#1 - 2440587.5 ) * 86400 } } % \end{macrocode} % %\begin{definition} %\cs{datatool\_julian\_day\_to\_dow:n} %\marg{JDN} %\end{definition} %Obtains the day of week from the integer Julian Day Number. % \begin{macrocode} \cs_new:Nn \datatool_julian_day_to_dow:n { \int_mod:nn { #1 } { 7 } } % \end{macrocode} % %\begin{definition} %\cs{datatool\_calc\_julian\_date:NN} %\meta{result fp-var} %\meta{timestamp seq-var} %\end{definition} %Calculate the Julian Date for the timestamp data contained in %the timestamp sequence variable \meta{timestamp seq-var} and %store the resulting value in the floating-point variable %\meta{result fp-var}. % \begin{macrocode} \cs_new:Nn \datatool_calc_julian_date:NN { \int_compare:nNnTF { \seq_count:N #2 } = { 8 } { \seq_set_eq:NN \l__datatool_datetime_seq #2 \_datatool_calc_julian_date: \fp_set_eq:NN #1 \l__datatool_julian_fp } { \PackageError { datatool-base } { Not ~ a ~ timestamp ~ sequence: ~ \token_to_str:N #2 } { The ~ second ~ argument ~ of ~ \token_to_str:N \datatool_calc_julian_date:NN ~ \c_string_tl ~ must ~ be ~ a ~ sequence ~ containing ~ 8 ~ integer ~ elements } } } % \end{macrocode} % %\begin{definition} %\cs{datatool\_calc\_julian\_day:Nnnn} \meta{result fp-var} %\marg{year} \marg{month} \marg{day} %\end{definition} % Calculate the Julian Day value from the given year, month and day. % NB very sensitive to rounding! Don't rearrange. % \begin{macrocode} \cs_new:Nn \datatool_calc_julian_day:Nnnn { \int_set:Nn #1 { #4 - 32075 + \int_div_truncate:nn { 1461 * ( #2 + 4800 + \int_div_truncate:nn { #3 - 14 } { 12 } ) } { 4 } + \int_div_truncate:nn { 367 * ( #3 - 2 - \int_div_truncate:nn { #3 - 14 } { 12 } * 12 ) } { 12 } - \int_div_truncate:nn { 3 * ( \int_div_truncate:nn { #2 + 4900 + \int_div_truncate:nn { #3 - 14 } { 12 } } { 100 } ) } { 4 } } } \cs_generate_variant:Nn \datatool_calc_julian_day:Nnnn { NVVV , Neee } % \end{macrocode} % %\begin{definition} %\cs{datatool\_from\_julian\_time:nNNN} %\marg{JT} \meta{hour int var} \meta{minute int var} \meta{second int var} %\end{definition} %Calculate the time of day from the Julian time decimal fraction. % \begin{macrocode} \cs_new:Nn \datatool_from_julian_time:nNNN { \int_set:Nn #4 { 43200 + \fp_to_int:n { #1 * 86400 } } \int_set:Nn #3 { \int_mod:nn { \int_div_truncate:nn { #4 } { 60 } } { 60 } } \int_set:Nn #2 { \int_div_truncate:nn { #4 } { 3600 } } \int_set:Nn #4 { \int_mod:nn { #4 } { 60 } } } \cs_generate_variant:Nn \datatool_from_julian_time:nNNN { VNNN, eNNN } % \end{macrocode} % %\begin{definition} %\cs{datatool\_from\_julian\_day:nNNN} %\marg{JDN} \meta{year int var} \meta{month int var} \meta{day int var} %\end{definition} %Calculate the year, month and day from the Julian Day Number. % \begin{macrocode} \cs_new:Nn \datatool_from_julian_day:nNNN { \int_set:Nn \l__datatool_tmpa_int { #1 + 68569 } \int_set:Nn \l__datatool_tmpb_int { \int_div_truncate:nn { 4 * \l__datatool_tmpa_int } { 146097 } } \int_set:Nn \l__datatool_tmpa_int { \l__datatool_tmpa_int - \int_div_truncate:nn { 146097 * \l__datatool_tmpb_int + 3 } { 4 } } \int_set:Nn \l__datatool_tmpc_int { \int_div_truncate:nn { 4000 * \l__datatool_tmpa_int + 1 } { 1461001 } } \int_set:Nn \l__datatool_tmpa_int { \l__datatool_tmpa_int - \int_div_truncate:nn { 1461 * \l__datatool_tmpc_int } { 4 } + 31 } \int_set:Nn \l__datatool_tmpd_int { \int_div_truncate:nn { 80 * \l__datatool_tmpa_int } { 2447 } } \int_set:Nn #4 { \l__datatool_tmpa_int - \int_div_truncate:nn { 2447 * \l__datatool_tmpd_int } { 80 } } \int_set:Nn \l__datatool_tmpa_int { \int_div_truncate:nn { \l__datatool_tmpd_int } { 11 } } \int_set:Nn \l__datatool_tmpd_int { \l__datatool_tmpd_int + 2 - 12 * \l__datatool_tmpa_int } \int_set:Nn \l__datatool_tmpc_int { 100 * ( \l__datatool_tmpb_int - 49 ) + \l__datatool_tmpc_int + \l__datatool_tmpa_int } \int_set_eq:NN #2 \l__datatool_tmpc_int \int_set_eq:NN #3 \l__datatool_tmpd_int } \cs_generate_variant:Nn \datatool_from_julian_day:nNNN { VNNN , eNNN } % \end{macrocode} % %\begin{definition} %\marg{JD} \meta{year int var} \meta{month int var} \meta{day int var} %\meta{hour int var} \meta{minute int var} \meta{second int var} %\end{definition} %Calculate the timestamp (UTC+0) from the Julian Date. % \begin{macrocode} \cs_new:Nn \datatool_from_julian_date:nNNNNNN { \int_set:Nn \l__datatool_julian_int { \fp_to_int:n { round ( #1 ) } } \datatool_from_julian_day:nNNN { \l__datatool_julian_int } #2 #3 #4 \exp_args:Ne \datatool_from_julian_time:nNNN { \fp_eval:n { #1 - \l__datatool_julian_int } } #5 #6 #7 } \cs_generate_variant:Nn \datatool_from_julian_date:nNNNNNN { VNNNNNN, eNNNNNN } % \end{macrocode} %\begin{definition} %\cs{datatool\_from\_julian\_date:nN} \marg{JD} \meta{timestamp seq-var} %\end{definition} %As above but the result should be stored in a timestamp sequence %variable. % \begin{macrocode} \cs_new:Nn \datatool_from_julian_date:nN { \datatool_from_julian_date:nNNNNNN { #1 } \l__datatool_year_int \l__datatool_month_int \l__datatool_day_int \l__datatool_hour_int \l__datatool_minute_int \l__datatool_second_int \datatool_timestamp_zero:N #2 \datatool_timestamp_set_year:NV #2 \l__datatool_year_int \datatool_timestamp_set_month:NV #2 \l__datatool_month_int \datatool_timestamp_set_day:NV #2 \l__datatool_day_int \datatool_timestamp_set_hour:NV #2 \l__datatool_hour_int \datatool_timestamp_set_minute:NV #2 \l__datatool_minute_int \datatool_timestamp_set_second:NV #2 \l__datatool_second_int \datatool_timestamp_set_dow:Ne #2 { \datatool_julian_day_to_dow:n { \fp_eval:n { round ( #1 ) } } } } \cs_generate_variant:Nn \datatool_from_julian_date:nN { VN , eN } % \end{macrocode} % %\paragraph{Internal Calculation Functions} % %Calculate the Julian day from values provided by the scratch %date and time variables (which needs to be set first). %Time zone adjustment needed: % \begin{macrocode} \cs_new:Nn \__datatool_calc_julian_day_tmz: { \__datatool_calc_julian_day: % \end{macrocode} %Keep a record of the unadjusted Julian day: % \begin{macrocode} \int_set_eq:NN \l__datatool_local_julian_int \l__datatool_julian_int % \end{macrocode} %Implement time zone shift: % \begin{macrocode} \int_if_zero:nF { \l__datatool_tzminute_int } { \int_sub:Nn \l__datatool_minute_int { \l__datatool_tzminute_int } \int_compare:nNnTF { \l__datatool_minute_int } < { \c_zero_int } { \int_add:Nn \l__datatool_minute_int { 60 } \int_decr:N \l__datatool_hour_int } { \int_compare:nNnT { \l__datatool_minute_int } > { 59 } { \int_sub:Nn \l__datatool_minute_int { 60 } \int_incr:N \l__datatool_hour_int } } } \int_if_zero:nF { \l__datatool_tzhour_int } { \int_sub:Nn \l__datatool_hour_int { \l__datatool_tzhour_int } } \int_compare:nNnTF { \l__datatool_hour_int } < { \c_zero_int } { \int_add:Nn \l__datatool_hour_int { 23 } \int_decr:N \l__datatool_julian_int } { \int_compare:nNnT { \l__datatool_hour_int } > { 23 } { \int_decr:Nn \l__datatool_hour_int { 24 } \int_incr:N \l__datatool_julian_int } } } % \end{macrocode} %Calculate the Julian day number from just the year, month and day %scratch variables (noon UTC+0). % \begin{macrocode} \cs_new:Nn \__datatool_calc_julian_day: { \datatool_calc_julian_day:Nnnn \l__datatool_julian_int { \l__datatool_year_int } { \l__datatool_month_int } { \l__datatool_day_int } } % \end{macrocode} %Calculate the Julian time, which is a fraction of the day starting %from 12noon. (Note that this isn't the same as TeX's \cs{time} %value, which is an integer number of minutes since midnight.) %For example, 6am is $(6-12)/24 + 0 / 1440 + 0 / 86400 = -0.25$ %and 6pm is $(18-12)/24 + 0/1440 + 0/86400 = +0.25$. % \begin{macrocode} \cs_new:Nn \__datatool_calc_julian_time: { \fp_set:Nn \l__datatool_time_fp { ( \l__datatool_hour_int - 12) / 24 + \l__datatool_minute_int / 1440 + \l__datatool_second_int / 86400 } } % \end{macrocode} %The Julian date can then be obtained by adding the Julian day and %Julian time. % %Perform the reverse: % \begin{macrocode} \cs_new:Nn \__datatool_from_julian_time: { \__datatool_from_julian_time:n { \l__datatool_time_fp } } \cs_new:Nn \__datatool_from_julian_time:n { \datatool_from_julian_time:nNNN { #1 } \l__datatool_hour_int \l__datatool_minute_int \l__datatool_second_int } % \end{macrocode} % %Convert Julian Day number (integer) to Gregorian year, month and %day. % \begin{macrocode} \cs_new:Nn \__datatool_from_julian_day: { \__datatool_from_julian_day:n { \l__datatool_julian_int } } \cs_new:Nn \__datatool_from_julian_day:n { \datatool_from_julian_day:nNNN { #1 } \l__datatool_year_int \l__datatool_month_int \l__datatool_day_int } % \end{macrocode} % %Convert from decimal Julian date to UTC+0. % \begin{macrocode} \cs_new:Nn \__datatool_from_julian_date: { \__datatool_from_julian_date:n { \l__datatool_julian_int } } \cs_new:Nn \__datatool_from_julian_date:n { \datatool_from_julian_date:nNNNNNN { #1 } \l__datatool_year_int \l__datatool_month_int \l__datatool_day_int \l__datatool_hour_int \l__datatool_minute_int \l__datatool_second_int } % \end{macrocode} % %\paragraph{Parsing} %Timestamps, dates and times must be in ISO format in order to be %parsed correctly unless support is provided by localisation files. % %\begin{definition} %\cs{datatool\_parse\_timestamp:NnTF} %\meta{timestamp seq var} %\marg{ISO} %\marg{true} \marg{false} %\end{definition} %Parse \meta{ISO} timestamp and save the numeric values in the %provided timestamp sequence variable. Does \meta{true} if \meta{ISO} %successfully parsed. Otherwise does \meta{false}. %The timestamp format should be in the form %\meta{YYYY}-\meta{MM}-\meta{DD}T\meta{hh}:\meta{mm}:\meta{ss}\meta{TZhr}:\meta{TZmin} %The time zone parts may be omitted or may simply be the letter \qt{Z}, %in which case the time zone is UTC+0. %The \qt{T} separator may be a space instead. Leading and trailing %space is ignored. % \begin{macrocode} \prg_new_conditional:Npnn \datatool_parse_timestamp:Nn #1 #2 { T, F, TF } { \__datatool_parse_timestamp:nTF { #2 } { \seq_set_eq:NN #1 \l__datatool_datetime_seq \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % %\begin{definition} %\cs{datatool\_parse\_date:NnTF} %\meta{timestamp seq var} %\marg{date} %\marg{true} \marg{false} %\end{definition} %Parses the \meta{date} argument, which must be in the form %\meta{YYYY}-\meta{MM}-\meta{DD} and saves the year, month and date %in the provided timestamp sequence variable. Note that the other %elements aren't changed unless the sequence was originally empty. %Does \meta{true} if \meta{date} successfully parsed. Otherwise does \meta{false}. % \begin{macrocode} \prg_new_conditional:Npnn \datatool_parse_date:Nn #1 #2 { T, F, TF } { \__datatool_parse_date:nTF { #2 } { \seq_if_empty:NTF #1 { \seq_set_eq:NN #1 \l__datatool_datetime_seq } { \datatool_timestamp_set_year:Ne #1 { \__datatool_tm_yr: } \datatool_timestamp_set_month:Ne #1 { \__datatool_tm_mn: } \datatool_timestamp_set_day:Ne #1 { \__datatool_tm_day: } } \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % %\begin{definition} %\cs{datatool\_parse\_time:NnTF} %\meta{timestamp seq var} %\marg{time} %\marg{true} \marg{false} %\end{definition} %Parses the \meta{time} argument, which must be in the form %\meta{hh}:\meta{mm}:\meta{ss} or \meta{hh}:\meta{mm} %and saves the hour, minute and second (0, if omitted) %in the provided timestamp sequence variable. Note that the other %elements aren't changed unless the sequence was originally empty. %Does \meta{true} if \meta{time} successfully parsed. Otherwise does \meta{false}. % \begin{macrocode} \prg_new_conditional:Npnn \datatool_parse_time:Nn #1 #2 { T, F, TF } { \__datatool_parse_time:nTF { #2 } { \seq_if_empty:NTF #1 { \seq_set_eq:NN #1 \l__datatool_datetime_seq } { \datatool_timestamp_set_hour:Ne #1 { \__datatool_tm_hr: } \datatool_timestamp_set_minute:Ne #1 { \__datatool_tm_mi: } \datatool_timestamp_set_second:Ne #1 { \__datatool_tm_se: } } \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % % %Parse given timestamp and save values in the datetime scratch %sequence variable. % \begin{macrocode} \prg_new_conditional:Npnn \__datatool_parse_timestamp:n #1 { T, F, TF } { \bool_if:NTF \l__datatool_parse_datetime_iso_bool { \regex_extract_once:NnNTF \c_datatool_timestamp_regex { #1 } \l_datatool_timestamp_match_seq { % \end{macrocode} %Initialise all elements to zero. % \begin{macrocode} \__datatool_tm_seq_init: % \end{macrocode} %Year: % \begin{macrocode} \__datatool_tm_set_yr:e { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} %Month: % \begin{macrocode} \__datatool_tm_set_mn:e { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} %Day: % \begin{macrocode} \__datatool_tm_set_dy:e { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} %Hour: % \begin{macrocode} \__datatool_tm_set_hr:e { \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } % \end{macrocode} %Minute: % \begin{macrocode} \__datatool_tm_set_mi:e { \seq_item:Nn \l_datatool_timestamp_match_seq { 6 } } % \end{macrocode} %Second: % \begin{macrocode} \__datatool_tm_set_se:e { \seq_item:Nn \l_datatool_timestamp_match_seq { 7 } } % \end{macrocode} %Check time zone present. % \begin{macrocode} \tl_if_empty:eF { \seq_item:Nn \l_datatool_timestamp_match_seq { 8 } } { \exp_args:NNe \regex_extract_once:NnNT \c_datatool_time_regex { \seq_item:Nn \l_datatool_timestamp_match_seq { 8 } } \l_datatool_timestamp_match_seq { \__datatool_tm_set_tzh:e { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } \__datatool_tm_set_tzm:e { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } } } \prg_return_true: } { \bool_if:NTF \l__datatool_parse_datetime_regional_bool { \DTLCurrentLocaleParseTimeStamp \l__datatool_datetime_seq { #1 } { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } } { \bool_if:NTF \l__datatool_parse_datetime_regional_bool { \DTLCurrentLocaleParseTimeStamp \l__datatool_datetime_seq { #1 } { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } } % \end{macrocode} % %\begin{macro}{\DTLCurrentLocaleParseTimeStamp} %\begin{definition} %\cs{DTLCurrentLocaleParseTimeStamp}\marg{seq}\marg{value}\marg{true}\marg{false} %\end{definition} %\changes{3.0}{2025-03-03}{new} %Allow for regional support. %If implemented, this should set the given timestamp sequence variable % and do \meta{true} otherwise it should do \meta{false}. % \begin{macrocode} \newcommand{\DTLCurrentLocaleParseTimeStamp}[4]{#4} % \end{macrocode} %\end{macro} %Parse a date (no time): % \begin{macrocode} \prg_new_conditional:Npnn \__datatool_parse_date:n #1 { T, F, TF } { \bool_if:NTF \l__datatool_parse_datetime_iso_bool { \regex_extract_once:NnNTF \c_datatool_date_regex { #1 } \l_datatool_timestamp_match_seq { % \end{macrocode} %Initialise all elements to zero: % \begin{macrocode} \__datatool_tm_seq_init: % \end{macrocode} %Year: % \begin{macrocode} \__datatool_tm_set_yr:e { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} %Month: % \begin{macrocode} \__datatool_tm_set_mn:e { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} %Day: % \begin{macrocode} \__datatool_tm_set_dy:e { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } \prg_return_true: } { \bool_if:NTF \l__datatool_parse_datetime_regional_bool { \DTLCurrentLocaleParseDate \l__datatool_datetime_seq { #1 } { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } } { \bool_if:NTF \l__datatool_parse_datetime_regional_bool { \DTLCurrentLocaleParseDate \l__datatool_datetime_seq { #1 } { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } } % \end{macrocode} % %\begin{macro}{\DTLCurrentLocaleParseDate} %\begin{definition} %\cs{DTLCurrentLocaleParseDate}\marg{seq}\marg{value}\marg{true}\marg{false} %\end{definition} %\changes{3.0}{2025-03-03}{new} %Allow for regional support. %If implemented, this should set the given date sequence variable % and do \meta{true} otherwise it should do \meta{false}. % \begin{macrocode} \newcommand{\DTLCurrentLocaleParseDate}[4]{#4} % \end{macrocode} %\end{macro} %Parse a time (no date): % \begin{macrocode} \prg_new_conditional:Npnn \__datatool_parse_time:n #1 { T, F, TF } { \bool_if:NTF \l__datatool_parse_datetime_iso_bool { \regex_extract_once:NnNTF \c_datatool_time_regex { #1 } \l_datatool_timestamp_match_seq { \__datatool_tm_seq_init: % \end{macrocode} %Hour: % \begin{macrocode} \__datatool_tm_set_hr:e { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} %Minute: % \begin{macrocode} \__datatool_tm_set_mi:e { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} %Second: % \begin{macrocode} \__datatool_tm_set_se:e { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } \prg_return_true: } { \bool_if:NTF \l__datatool_parse_datetime_regional_bool { \DTLCurrentLocaleParseTime \l__datatool_datetime_seq { #1 } { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } } { \bool_if:NTF \l__datatool_parse_datetime_regional_bool { \DTLCurrentLocaleParseTime \l__datatool_datetime_seq { #1 } { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } } % \end{macrocode} % %\begin{macro}{\DTLCurrentLocaleParseTime} %\begin{definition} %\cs{DTLCurrentLocaleParseTime}\marg{seq}\marg{value}\marg{true}\marg{false} %\end{definition} %\changes{3.0}{2025-03-03}{new} %Allow for regional support. %If implemented, this should set the given date sequence variable % and do \meta{true} otherwise it should do \meta{false}. % \begin{macrocode} \newcommand{\DTLCurrentLocaleParseTime}[4]{#4} % \end{macrocode} %\end{macro} % %Parse argument to determine if it's a timestamp, date only, time %only or none of those. In the last case, false part is done, %otherwise \cs{@dtl@datatype} will be set to the applicable data %type, and the Julian Day number, Julian Date decimal and time %fraction will be calculated, and then true part is done. % \begin{macrocode} \prg_new_conditional:Npnn \__datatool_parse_datetime:n #1 { T, F, TF } { \seq_clear:N \l__datatool_datetime_seq \fp_zero:N \l__datatool_julian_fp \fp_zero:N \l__datatool_time_fp \int_zero:N \l__datatool_local_julian_int \int_zero:N \l__datatool_julian_int \__datatool_parse_timestamp:nTF { #1 } { \@dtl@datatype = \c_datatool_datetime_int } { \__datatool_parse_date:nTF { #1 } { \@dtl@datatype = \c_datatool_date_int } { \__datatool_parse_time:nT { #1 } { \@dtl@datatype = \c_datatool_time_int } } } \seq_if_empty:NTF \l__datatool_datetime_seq { \prg_return_false: } { \_datatool_calc_julian_date: \prg_return_true: } } % \end{macrocode} %Calculate the Julian Date from the scratch timestamp sequence. % \begin{macrocode} \cs_new:Nn \_datatool_calc_julian_date: { \int_set:Nn \l__datatool_year_int { \__datatool_tm_yr: } \int_set:Nn \l__datatool_month_int { \__datatool_tm_mn: } \int_set:Nn \l__datatool_day_int { \__datatool_tm_dy: } \int_set:Nn \l__datatool_hour_int { \__datatool_tm_hr: } \int_set:Nn \l__datatool_minute_int { \__datatool_tm_mi: } \int_set:Nn \l__datatool_second_int { \__datatool_tm_se: } \int_set:Nn \l__datatool_tzhour_int { \__datatool_tm_tzh: } \int_set:Nn \l__datatool_tzminute_int { \__datatool_tm_tzm: } % \end{macrocode} %Calculate the Julian day with time zone adjustment. % \begin{macrocode} \__datatool_calc_julian_day_tmz: % \end{macrocode} %Update the day of week item in the timestamp sequence. % \begin{macrocode} \__datatool_tm_set_dow:e { \datatool_julian_day_to_dow:n { \l__datatool_local_julian_int } } % \end{macrocode} %Calculate the Julian date. First the time part: % \begin{macrocode} \__datatool_calc_julian_time: % \end{macrocode} %Then add the day and time values: % \begin{macrocode} \fp_set:Nn \l__datatool_julian_fp { \int_use:N \l__datatool_julian_int + \l__datatool_time_fp } } % \end{macrocode} %Set the update token variable for a temporal datum % \begin{macrocode} \cs_new:Nn \__datatool_set_datetime_value: { \__datatool_set_datetime_value:n { } } \cs_new:Nn \__datatool_set_datetime_value:n { \__datatool_set_datetime_value:NNn \l__datatool_datum_update_value_tl \l__datatool_datum_value_tl { #1 } } \cs_new:Nn \__datatool_set_datetime_value:Nn { \__datatool_set_datetime_value:NNn #1 \l__datatool_datum_value_tl { #2 } } % \end{macrocode} % %Some common formats for use with regions. %First a property variable to store timezone mappings. % \begin{macrocode} \prop_new:N \l_datatool_timezone_map_prop \tl_new:N \l_datatool_timezone_map_value_tl % \end{macrocode} %The regions can store applicable time zones in the format %\meta{region-tag} \verb|/| \meta{zone-id} %(for example, \texttt{GB / GMT} and \texttt{GB / BST}). %The value should be in the form \meta{sign}\meta{hh}:\meta{mm}. % % \begin{macrocode} \cs_new:Nn \datatool_region_set_timezone_map:nn { \prop_put:Nnn \l_datatool_timezone_map_prop { #1 } { #2 } } % \end{macrocode} %Get the value and store in the scratch token list. % \begin{macrocode} \cs_new:Nn \datatool_region_get_timezone_map:n { \prop_get:NnN \l_datatool_timezone_map_prop { #1 } \l_datatool_timezone_map_value_tl } % \end{macrocode} %Add regionless identifiers UTC and Z: % \begin{macrocode} \datatool_region_set_timezone_map:nn { UTC } { +00:00 } \datatool_region_set_timezone_map:nn { Z } { +00:00 } % \end{macrocode} % % \begin{macrocode} \newcommand{\DTLCurrentLocaleGetTimeZoneMap}[1]{ \tl_set_eq:NN \l_datatool_timezone_map_value_tl \q_no_value } % \end{macrocode} % Similarly for month name to number mappings: % \begin{macrocode} \prop_new:N \l_datatool_monthname_map_prop \tl_new:N \l_datatool_monthname_map_value_tl % \end{macrocode} %Set month name mapping: % \begin{macrocode} \cs_new:Nn \datatool_region_set_monthname_map:nn { \prop_put:Nnn \l_datatool_monthname_map_prop { #1 } { #2 } } % \end{macrocode} %Get the value and store in the scratch token list. % \begin{macrocode} \cs_new:Nn \datatool_region_get_monthname_map:n { \prop_get:NnN \l_datatool_monthname_map_prop { #1 } \l_datatool_monthname_map_value_tl } % \end{macrocode} % % \begin{macrocode} \newcommand{\DTLCurrentLocaleGetMonthNameMap}[1]{ \tl_set_eq:NN \l_datatool_monthname_map_value_tl \q_no_value } % \end{macrocode} % % Day of month regular expression (1-31): % \begin{macrocode} \regex_const:Nn \c_datatool_day_of_month_regex { ( 0?[1-9] | [12][0-9] | 30 | 31 ) } % \end{macrocode} %One or two digit month number (1-12): % \begin{macrocode} \regex_const:Nn \c_datatool_month_number_regex { ( 0?[1-9] | 10 | 11 | 12 ) } % \end{macrocode} %One or two digit hour (0-24): % \begin{macrocode} \regex_const:Nn \c_datatool_hour_regex { ( 0?[0-9] | 1[0-9] | 2[0-4] ) } % \end{macrocode} %Two digit minute or second (0-59): % \begin{macrocode} \regex_const:Nn \c_datatool_minsec_regex { [0-5][0-9] } % \end{macrocode} %Time zone offset, optionally prefixed with GMT or UTC or UT: % \begin{macrocode} \regex_const:Nn \c_datatool_timezone_regex { (?:GMT|UTC?)? ( [\+\-]? \ur{c_datatool_hour_regex} ) (?: \: ? ( \ur{c_datatool_minsec_regex} ) ) ? } % \end{macrocode} % %Time zone (either uppercase identifier, which will require a %mapping, or offset): % \begin{macrocode} \regex_const:Nn \c_datatool_timezone_id_regex { [A-Z]+ | \ur{c_datatool_timezone_regex} } % \end{macrocode} %\begin{macro}{\DTLCurrentLocaleIfpmTF} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{DTLCurrentLocaleIfpmTF}\marg{id}\marg{true}\marg{false} %\end{definition} %If the identifier is recognised as indicating the afternoon, do %true, otherwise do false. Localisation support should redefine this %as appropriate. % \begin{macrocode} \newcommand \DTLCurrentLocaleIfpmTF [ 3 ] { \tl_if_eq:nnTF { #1 } { pm } { #2 } { #3 } } % \end{macrocode} %\end{macro} % %The following regular expression constants are for common formats. %Localisation support may use the applicable expression or provide %custom patterns. Note that it's important that the captured groups match the %applicable parsing function. % %Non-anchored expressions allow them to be used as a sub-expression %for timestamp parsing. % % Common dd mm yyyy formats. % \begin{macrocode} \regex_const:Nn \c_datatool_slash_ddmmyyyy_date_regex { ( \ur{c_datatool_day_of_month_regex} ) \/ ( \ur{c_datatool_month_number_regex} ) \/ ( \d+ ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_slash_anchored_ddmmyyyy_date_regex { \A \s* ( \ur{c_datatool_day_of_month_regex} ) \/ ( \ur{c_datatool_month_number_regex} ) \/ ( \d+ ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_slash_ddmmyy_date_regex { ( \ur{c_datatool_day_of_month_regex} ) \/ ( \ur{c_datatool_month_number_regex} ) \/ ( \d{2} ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_slash_anchored_ddmmyy_date_regex { \A \s* ( \ur{c_datatool_day_of_month_regex} ) \/ ( \ur{c_datatool_month_number_regex} ) \/ ( \d{2} ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_hyphen_ddmmyyyy_date_regex { ( \ur{c_datatool_day_of_month_regex} ) \- ( \ur{c_datatool_month_number_regex} ) \- ( \d+ ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_hyphen_anchored_ddmmyyyy_date_regex { \A \s* ( \ur{c_datatool_day_of_month_regex} ) \- ( \ur{c_datatool_month_number_regex} ) \- ( \d+ ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_hyphen_ddmmyy_date_regex { ( \ur{c_datatool_day_of_month_regex} ) \- ( \ur{c_datatool_month_number_regex} ) \- ( \d{2} ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_hyphen_anchored_ddmmyy_date_regex { \A \s* ( \ur{c_datatool_day_of_month_regex} ) \- ( \ur{c_datatool_month_number_regex} ) \- ( \d{2} ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_dot_ddmmyyyy_date_regex { ( \ur{c_datatool_day_of_month_regex} ) \. ( \ur{c_datatool_month_number_regex} ) \. ( \d+ ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_dot_anchored_ddmmyyyy_date_regex { \A \s* ( \ur{c_datatool_day_of_month_regex} ) \. ( \ur{c_datatool_month_number_regex} ) \. ( \d+ ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_dot_ddmmyy_date_regex { ( \ur{c_datatool_day_of_month_regex} ) \. ( \ur{c_datatool_month_number_regex} ) \. ( \d{2} ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_dot_anchored_ddmmyy_date_regex { \A \s* ( \ur{c_datatool_day_of_month_regex} ) \. ( \ur{c_datatool_month_number_regex} ) \. ( \d{2} ) \s* \Z } % \end{macrocode} % % Common mm dd yyyy formats. % % \begin{macrocode} \regex_const:Nn \c_datatool_slash_mmddyyyy_date_regex { ( \ur{c_datatool_month_number_regex} ) \/ ( \ur{c_datatool_day_of_month_regex} ) \/ ( \d+ ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_slash_anchored_mmddyyyy_date_regex { \A \s* ( \ur{c_datatool_month_number_regex} ) \/ ( \ur{c_datatool_day_of_month_regex} ) \/ ( \d+ ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_slash_mmddyy_date_regex { ( \ur{c_datatool_month_number_regex} ) \/ ( \ur{c_datatool_day_of_month_regex} ) \/ ( \d{2} ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_slash_anchored_mmddyy_date_regex { \A \s* ( \ur{c_datatool_month_number_regex} ) \/ ( \ur{c_datatool_day_of_month_regex} ) \/ ( \d{2} ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_hyphen_mmddyyyy_date_regex { ( \ur{c_datatool_month_number_regex} ) \- ( \ur{c_datatool_day_of_month_regex} ) \- ( \d+ ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_hyphen_anchored_mmddyyyy_date_regex { \A \s* ( \ur{c_datatool_month_number_regex} ) \- ( \ur{c_datatool_day_of_month_regex} ) \- ( \d+ ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_hyphen_mmddyy_date_regex { ( \ur{c_datatool_month_number_regex} ) \- ( \ur{c_datatool_day_of_month_regex} ) \- ( \d{2} ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_hyphen_anchored_mmddyy_date_regex { \A \s* ( \ur{c_datatool_month_number_regex} ) \- ( \ur{c_datatool_day_of_month_regex} ) \- ( \d{2} ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_dot_mmddyyyy_date_regex { ( \ur{c_datatool_month_number_regex} ) \. ( \ur{c_datatool_day_of_month_regex} ) \. ( \d+ ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_dot_anchored_mmddyyyy_date_regex { \A \s* ( \ur{c_datatool_month_number_regex} ) \. ( \ur{c_datatool_day_of_month_regex} ) \. ( \d+ ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_dot_mmddyy_date_regex { ( \ur{c_datatool_month_number_regex} ) \. ( \ur{c_datatool_day_of_month_regex} ) \. ( \d{2} ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_dot_anchored_mmddyy_date_regex { \A \s* ( \ur{c_datatool_month_number_regex} ) \. ( \ur{c_datatool_day_of_month_regex} ) \. ( \d{2} ) \s* \Z } % \end{macrocode} % % Common yyyy mm dd formats. % % \begin{macrocode} \regex_const:Nn \c_datatool_slash_yyyymmdd_date_regex { ( \d+ ) \/ ( \ur{c_datatool_month_number_regex} ) \/ ( \ur{c_datatool_day_of_month_regex} ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_slash_anchored_yyyymmdd_date_regex { \A \s* ( \d+ ) \/ ( \ur{c_datatool_month_number_regex} ) \/ ( \ur{c_datatool_day_of_month_regex} ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_slash_yymmdd_date_regex { ( \d{2} ) \/ ( \ur{c_datatool_month_number_regex} ) \/ ( \ur{c_datatool_day_of_month_regex} ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_slash_anchored_yymmdd_date_regex { \A \s* ( \d{2} ) \/ ( \ur{c_datatool_month_number_regex} ) \/ ( \ur{c_datatool_day_of_month_regex} ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_hyphen_yyyymmdd_date_regex { ( \d+ ) \- ( \ur{c_datatool_month_number_regex} ) \- ( \ur{c_datatool_day_of_month_regex} ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_hyphen_anchored_yyyymmdd_date_regex { \A \s* ( \d+ ) \- ( \ur{c_datatool_month_number_regex} ) \- ( \ur{c_datatool_day_of_month_regex} ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_hyphen_yymmdd_date_regex { ( \d{2} ) \- ( \ur{c_datatool_month_number_regex} ) \- ( \ur{c_datatool_day_of_month_regex} ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_hyphen_anchored_yymmdd_date_regex { \A \s* ( \d{2} ) \- ( \ur{c_datatool_month_number_regex} ) \- ( \ur{c_datatool_day_of_month_regex} ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_dot_yyyymmdd_date_regex { ( \d+ ) \. ( \ur{c_datatool_month_number_regex} ) \. ( \ur{c_datatool_day_of_month_regex} ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_dot_anchored_yyyymmdd_date_regex { \A \s* ( \d+ ) \. ( \ur{c_datatool_month_number_regex} ) \. ( \ur{c_datatool_day_of_month_regex} ) \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_dot_yymmdd_date_regex { ( \d{2} ) \. ( \ur{c_datatool_month_number_regex} ) \. ( \ur{c_datatool_day_of_month_regex} ) } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_dot_anchored_yymmdd_date_regex { \A \s* ( \d{2} ) \. ( \ur{c_datatool_month_number_regex} ) \. ( \ur{c_datatool_day_of_month_regex} ) \s* \Z } % \end{macrocode} % %Time formats. If a regional format needs to support other am/pm %identifiers, they'll need to provide their own regular expressions. % \begin{macrocode} \regex_const:Nn \c_datatool_colon_hhmmss_time_regex { ( \ur{c_datatool_hour_regex} ) \: ( \ur{c_datatool_minsec_regex} ) (?: \: ( \ur{c_datatool_minsec_regex} ) ) ? (?: \s* ( am | pm ) ) ? } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_colon_anchored_hhmmss_time_regex { \A \s* ( \ur{c_datatool_hour_regex} ) \: ( \ur{c_datatool_minsec_regex} ) (?: \: ( \ur{c_datatool_minsec_regex} ) ) ? (?: \s* ( am | pm ) ) ? \s* \Z } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_dot_hhmmss_time_regex { ( \ur{c_datatool_hour_regex} ) \. ( \ur{c_datatool_minsec_regex} ) (?: \. ( \ur{c_datatool_minsec_regex} ) ) ? (?: \s* ( am | pm ) ) ? } % \end{macrocode} % % \begin{macrocode} \regex_const:Nn \c_datatool_dot_anchored_hhmmss_time_regex { \A \s* ( \ur{c_datatool_hour_regex} ) \. ( \ur{c_datatool_minsec_regex} ) (?: \. ( \ur{c_datatool_minsec_regex} ) ) ? (?: \s* ( am | pm ) ) ? \s* \Z } % \end{macrocode} % %The timestamp parsing functions may either have a single regular %expression that captures both the date and time, or may have %two separate regular expressions for the date %and time. In either case, the time zone part is optional and %will be parsed with \cs{datatool\_parse\_regional\_timezone:NnTF}. % %Once the date, time and timestamp have been extracted, they can %then be matched separately. This would be easier if named captured %groups were supported, but they aren't at the time of %writing, so multiple functions are needed to reference the groups by index. % %Syntax: \meta{date-regex} \meta{time-regex} \meta{timestamp-var} %\marg{date} \marg{time} \marg{timezone} %\marg{return true}\marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_ddmmyyyy_hhmmss_tz_parse_datetime:NNNnnnTF { % \end{macrocode} %First extract the date: % \begin{macrocode} \regex_extract_once:NnNTF #1 { #4 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} %Now extract the time. % \begin{macrocode} \regex_extract_once:NnNTF #2 { #5 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Set the hour: % \begin{macrocode} \exp_args:Ne \DTLCurrentLocaleIfpmTF { \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } { \datatool_timestamp_set_hour:Ne #3 { \int_eval:n { 12 + \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } } } { \datatool_timestamp_set_hour:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } } % \end{macrocode} % Set the minute: % \begin{macrocode} \datatool_timestamp_set_minute:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} % Set the second: % \begin{macrocode} \datatool_timestamp_set_second:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } } { #8 } % \end{macrocode} % Parse timezone if present: % \begin{macrocode} \datatool_parse_regional_timezone:NnTF #3 { #6 } { #7 } { #8 } } { % \end{macrocode} % No match on month: % \begin{macrocode} #8 } } { #8 } } \cs_generate_variant:Nn \datatool_ddmmyyyy_hhmmss_tz_parse_datetime:NNNnnnTF { NNNeeeTF } % \end{macrocode} % %As above but century needs to be added. % \begin{macrocode} \cs_new:Nn \datatool_ddmmyy_hhmmss_tz_parse_datetime:NNNnnnTF { % \end{macrocode} %First extract the date: % \begin{macrocode} \regex_extract_once:NnNTF #1 { #4 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year_add_century:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} %Now extract the time. % \begin{macrocode} \regex_extract_once:NnNTF #2 { #5 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Set the hour: % \begin{macrocode} \exp_args:Ne \DTLCurrentLocaleIfpmTF { \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } { \datatool_timestamp_set_hour:Ne #3 { \int_eval:n { 12 + \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } } } { \datatool_timestamp_set_hour:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } } % \end{macrocode} % Set the minute: % \begin{macrocode} \datatool_timestamp_set_minute:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} % Set the second: % \begin{macrocode} \datatool_timestamp_set_second:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } } { #8 } % \end{macrocode} % Parse timezone if present: % \begin{macrocode} \datatool_parse_regional_timezone:NnTF #3 { #6 } { #7 } { #8 } } { % \end{macrocode} % No match on month: % \begin{macrocode} #8 } } { #8 } } \cs_generate_variant:Nn \datatool_ddmmyy_hhmmss_tz_parse_datetime:NNNnnnTF { NNNeeeTF } % \end{macrocode} % %Same again but the month is in the first captured group and the day %is in the second. % %Syntax: \meta{date-regex} \meta{time-regex} \meta{timestamp-var} %\marg{date} \marg{time} \marg{timezone} %\marg{return true}\marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_mmddyyyy_hhmmss_tz_parse_datetime:NNNnnnTF { % \end{macrocode} %First extract the date: % \begin{macrocode} \regex_extract_once:NnNTF #1 { #4 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} %Now extract the time. % \begin{macrocode} \regex_extract_once:NnNTF #2 { #5 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Set the hour: % \begin{macrocode} \exp_args:Ne \DTLCurrentLocaleIfpmTF { \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } { \datatool_timestamp_set_hour:Ne #3 { \int_eval:n { 12 + \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } } } { \datatool_timestamp_set_hour:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } } % \end{macrocode} % Set the minute: % \begin{macrocode} \datatool_timestamp_set_minute:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} % Set the second: % \begin{macrocode} \datatool_timestamp_set_second:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } } { #8 } % \end{macrocode} % Parse timezone if present: % \begin{macrocode} \datatool_parse_regional_timezone:NnTF #3 { #6 } { #7 } { #8 } } { % \end{macrocode} % No match on month: % \begin{macrocode} #8 } } { #8 } } \cs_generate_variant:Nn \datatool_mmddyyyy_hhmmss_tz_parse_datetime:NNNnnnTF { NNNeeeTF } % \end{macrocode} % %As above but century needs to be added: % \begin{macrocode} \cs_new:Nn \datatool_mmddyy_hhmmss_tz_parse_datetime:NNNnnnTF { % \end{macrocode} %First extract the date: % \begin{macrocode} \regex_extract_once:NnNTF #1 { #4 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year_add_century:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} %Now extract the time. % \begin{macrocode} \regex_extract_once:NnNTF #2 { #5 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Set the hour: % \begin{macrocode} \exp_args:Ne \DTLCurrentLocaleIfpmTF { \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } { \datatool_timestamp_set_hour:Ne #3 { \int_eval:n { 12 + \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } } } { \datatool_timestamp_set_hour:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } } % \end{macrocode} % Set the minute: % \begin{macrocode} \datatool_timestamp_set_minute:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} % Set the second: % \begin{macrocode} \datatool_timestamp_set_second:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } } { #8 } % \end{macrocode} % Parse timezone if present: % \begin{macrocode} \datatool_parse_regional_timezone:NnTF #3 { #6 } { #7 } { #8 } } { % \end{macrocode} % No match on month: % \begin{macrocode} #8 } } { #8 } } \cs_generate_variant:Nn \datatool_mmddyy_hhmmss_tz_parse_datetime:NNNnnnTF { NNNeeeTF } % \end{macrocode} % %Same again but the year is in the first captured group, % the month is in the second captured group and the day %is in the third. % %Syntax: \meta{date-regex} \meta{time-regex} \meta{timestamp-var} %\marg{date} \marg{time} \marg{timezone} %\marg{return true}\marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_yyyymmdd_hhmmss_tz_parse_datetime:NNNnnnTF { % \end{macrocode} %First extract the date: % \begin{macrocode} \regex_extract_once:NnNTF #1 { #4 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} %Now extract the time. % \begin{macrocode} \regex_extract_once:NnNTF #2 { #5 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Set the hour: % \begin{macrocode} \exp_args:Ne \DTLCurrentLocaleIfpmTF { \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } { \datatool_timestamp_set_hour:Ne #3 { \int_eval:n { 12 + \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } } } { \datatool_timestamp_set_hour:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } } % \end{macrocode} % Set the minute: % \begin{macrocode} \datatool_timestamp_set_minute:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} % Set the second: % \begin{macrocode} \datatool_timestamp_set_second:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } } { #8 } % \end{macrocode} % Parse timezone if present: % \begin{macrocode} \datatool_parse_regional_timezone:NnTF #3 { #6 } { #7 } { #8 } } { % \end{macrocode} % No match on month: % \begin{macrocode} #8 } } { #8 } } \cs_generate_variant:Nn \datatool_yyyymmdd_hhmmss_tz_parse_datetime:NNNnnnTF { NNNeeeTF } % \end{macrocode} % %As above, but the century needs to be added. (Unlikely with year %first format, but provided for completeness.) % \begin{macrocode} \cs_new:Nn \datatool_yymmdd_hhmmss_tz_parse_datetime:NNNnnnTF { % \end{macrocode} %First extract the date: % \begin{macrocode} \regex_extract_once:NnNTF #1 { #4 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year_add_century:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} %Now extract the time. % \begin{macrocode} \regex_extract_once:NnNTF #2 { #5 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Set the hour: % \begin{macrocode} \exp_args:Ne \DTLCurrentLocaleIfpmTF { \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } { \datatool_timestamp_set_hour:Ne #3 { \int_eval:n { 12 + \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } } } { \datatool_timestamp_set_hour:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } } % \end{macrocode} % Set the minute: % \begin{macrocode} \datatool_timestamp_set_minute:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} % Set the second: % \begin{macrocode} \datatool_timestamp_set_second:Ne #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } } { #8 } % \end{macrocode} % Parse timezone if present: % \begin{macrocode} \datatool_parse_regional_timezone:NnTF #3 { #6 } { #7 } { #8 } } { % \end{macrocode} % No match on month: % \begin{macrocode} #8 } } { #8 } } \cs_generate_variant:Nn \datatool_yymmdd_hhmmss_tz_parse_datetime:NNNnnnTF { NNNeeeTF } % \end{macrocode} % % %The timestamp separator used when date and time patterns provided %separately: % \begin{macrocode} \regex_new:N \l_datatool_regional_timestamp_sep_regex \regex_set:Nn \l_datatool_regional_timestamp_sep_regex { \,? \s+ } % \end{macrocode} % %Syntax: \marg{date-regex-varname} \marg{time-regex-varname} %\meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % %The date regular expression should have the following groups (in order): %day, month, year. %The time regular expression should have the following groups: %hour, minute, second (may be empty), %am/pm (may be empty). %The am/pm part can be anything that \cs{DTLCurrentLocaleIfpmTF} %can detect as afternoon (that is, add 12 to the hour). %This assumes that the date comes first and is separated from the %time by %\verb|\l_datatool_regional_timestamp_sep_regex|. % \begin{macrocode} \cs_new:Nn \datatool_ddmmyyyy_hhmmss_tz_parse_timestamp:NNNnTF { \tl_set_eq:NN \l__datatool_tmpa_regex #1 \tl_set_eq:NN \l__datatool_tmpb_regex #2 \regex_extract_once:nnNTF { \A \s* ( \ur{l__datatool_tmpa_regex} ) \ur{l_datatool_regional_timestamp_sep_regex} ( \ur{l__datatool_tmpb_regex} ) \s* ( \ur{c_datatool_timezone_id_regex} ) ? \s* \Z } { #4 } \l_datatool_timestamp_match_seq { \datatool_timestamp_zero:N #3 \datatool_ddmmyyyy_hhmmss_tz_parse_datetime:NNNeeeTF #1 #2 #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } { #5 } { #6 } } { #6 } } \cs_generate_variant:Nn \datatool_ddmmyyyy_hhmmss_tz_parse_timestamp:NNNnTF { ccNnTF } % \end{macrocode} %As above, but century needs to be added. % \begin{macrocode} \cs_new:Nn \datatool_ddmmyy_hhmmss_tz_parse_timestamp:NNNnTF { \tl_set_eq:NN \l__datatool_tmpa_regex #1 \tl_set_eq:NN \l__datatool_tmpb_regex #2 \regex_extract_once:nnNTF { \A \s* ( \ur{l__datatool_tmpa_regex} ) \ur{l_datatool_regional_timestamp_sep_regex} ( \ur{l__datatool_tmpb_regex} ) \s* ( \ur{c_datatool_timezone_id_regex} ) ? \s* \Z } { #4 } \l_datatool_timestamp_match_seq { \datatool_timestamp_zero:N #3 \datatool_ddmmyy_hhmmss_tz_parse_datetime:NNNeeeTF #1 #2 #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } { #5 } { #6 } } { #6 } } \cs_generate_variant:Nn \datatool_ddmmyy_hhmmss_tz_parse_timestamp:NNNnTF { ccNnTF } % \end{macrocode} % %Same again, but month in first captured group. % \begin{macrocode} \cs_new:Nn \datatool_mmddyyyy_hhmmss_tz_parse_timestamp:NNNnTF { \tl_set_eq:NN \l__datatool_tmpa_regex #1 \tl_set_eq:NN \l__datatool_tmpb_regex #2 \regex_extract_once:nnNTF { \A \s* ( \ur{l__datatool_tmpa_regex} ) \ur{l_datatool_regional_timestamp_sep_regex} ( \ur{l__datatool_tmpb_regex} ) \s* ( \ur{c_datatool_timezone_id_regex} ) ? \s* \Z } { #4 } \l_datatool_timestamp_match_seq { \datatool_timestamp_zero:N #3 \datatool_mmddyyyy_hhmmss_tz_parse_datetime:NNNeeeTF #1 #2 #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } { #5 } { #6 } } { #6 } } \cs_generate_variant:Nn \datatool_mmddyyyy_hhmmss_tz_parse_timestamp:NNNnTF { ccNnTF } % \end{macrocode} %As above, but century needs to be added. % \begin{macrocode} \cs_new:Nn \datatool_mmddyy_hhmmss_tz_parse_timestamp:NNNnTF { \tl_set_eq:NN \l__datatool_tmpa_regex #1 \tl_set_eq:NN \l__datatool_tmpb_regex #2 \regex_extract_once:nnNTF { \A \s* ( \ur{l__datatool_tmpa_regex} ) \ur{l_datatool_regional_timestamp_sep_regex} ( \ur{l__datatool_tmpb_regex} ) \s* ( \ur{c_datatool_timezone_id_regex} ) ? \s* \Z } { #4 } \l_datatool_timestamp_match_seq { \datatool_timestamp_zero:N #3 \datatool_mmddyy_hhmmss_tz_parse_datetime:NNNeeeTF #1 #2 #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } { #5 } { #6 } } { #6 } } \cs_generate_variant:Nn \datatool_mmddyy_hhmmss_tz_parse_timestamp:NNNnTF { ccNnTF } % \end{macrocode} % %Same again, but year in first captured group. % \begin{macrocode} \cs_new:Nn \datatool_yyyymmdd_hhmmss_tz_parse_timestamp:NNNnTF { \tl_set_eq:NN \l__datatool_tmpa_regex #1 \tl_set_eq:NN \l__datatool_tmpb_regex #2 \regex_extract_once:nnNTF { \A \s* ( \ur{l__datatool_tmpa_regex} ) \ur{l_datatool_regional_timestamp_sep_regex} ( \ur{l__datatool_tmpb_regex} ) \s* ( \ur{c_datatool_timezone_id_regex} ) ? \s* \Z } { #4 } \l_datatool_timestamp_match_seq { \datatool_timestamp_zero:N #3 \datatool_yyyymmdd_hhmmss_tz_parse_datetime:NNNeeeTF #1 #2 #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } { #5 } { #6 } } { #6 } } \cs_generate_variant:Nn \datatool_yyyymmdd_hhmmss_tz_parse_timestamp:NNNnTF { ccNnTF } % \end{macrocode} %As above, but century needs to be added. % \begin{macrocode} \cs_new:Nn \datatool_yymmdd_hhmmss_tz_parse_timestamp:NNNnTF { \tl_set_eq:NN \l__datatool_tmpa_regex #1 \tl_set_eq:NN \l__datatool_tmpb_regex #2 \regex_extract_once:nnNTF { \A \s* ( \ur{l__datatool_tmpa_regex} ) \ur{l_datatool_regional_timestamp_sep_regex} ( \ur{l__datatool_tmpb_regex} ) \s* ( \ur{c_datatool_timezone_id_regex} ) ? \s* \Z } { #4 } \l_datatool_timestamp_match_seq { \datatool_timestamp_zero:N #3 \datatool_yymmdd_hhmmss_tz_parse_datetime:NNNeeeTF #1 #2 #3 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } { #5 } { #6 } } { #6 } } \cs_generate_variant:Nn \datatool_yymmdd_hhmmss_tz_parse_timestamp:NNNnTF { ccNnTF } % \end{macrocode} % %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % %The regular expression should have the following groups (in order): %day, month, year, hour, minute, second (may be empty), %am/pm (may be empty), time zone (may be empty). %The am/pm part can be anything that \cs{DTLCurrentLocaleIfpmTF} %can detect as afternoon (that is, add 12 to the hour). %The constants provided with this base package only match %\qt{am} and \qt{pm}. %Localisation files may provide their own regular expressions with %appropriate patterns. % \begin{macrocode} \cs_new:Nn \datatool_ddmmyyyy_hhmmss_tz_parse_timestamp:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Set the hour: % \begin{macrocode} \exp_args:Ne \DTLCurrentLocaleIfpmTF { \seq_item:Nn \l_datatool_timestamp_match_seq { 8 } } { \datatool_timestamp_set_hour:Ne #2 { \int_eval:n { 12 + \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } } } { \datatool_timestamp_set_hour:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } } % \end{macrocode} % Set the minute: % \begin{macrocode} \datatool_timestamp_set_minute:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 6 } } % \end{macrocode} % Set the second: % \begin{macrocode} \datatool_timestamp_set_second:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 7 } } % \end{macrocode} % Parse timezone if present: % \begin{macrocode} \datatool_parse_regional_timezone:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 9 } } { #4 } { #5 } } { % \end{macrocode} % No match on month: % \begin{macrocode} #5 } } { #5 } % No match } % \end{macrocode} % %As above but century missing. %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_ddmmyy_hhmmss_tz_parse_timestamp:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year_add_century:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Set the hour: % \begin{macrocode} \exp_args:Ne \DTLCurrentLocaleIfpmTF { \seq_item:Nn \l_datatool_timestamp_match_seq { 8 } } { \datatool_timestamp_set_hour:Ne #2 { \int_eval:n { 12 + \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } } } { \datatool_timestamp_set_hour:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } } % \end{macrocode} % Set the minute: % \begin{macrocode} \datatool_timestamp_set_minute:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 6 } } % \end{macrocode} % Set the second: % \begin{macrocode} \datatool_timestamp_set_second:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 7 } } % \end{macrocode} % Parse timezone if present: % \begin{macrocode} \datatool_parse_regional_timezone:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 9 } } { #4 } { #5 } } { % \end{macrocode} % No match on month: % \begin{macrocode} #5 } } { #5 } % No match } % \end{macrocode} % %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % %The regular expression should have the following groups (in order): %month, day, year, hour, minute, second (may be empty), %am/pm (may be empty), time zone (may be empty). % \begin{macrocode} \cs_new:Nn \datatool_mmddyyyy_hhmmss_tz_parse_timestamp:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Set the hour: % \begin{macrocode} \exp_args:Ne \DTLCurrentLocaleIfpmTF { \seq_item:Nn \l_datatool_timestamp_match_seq { 8 } } { \datatool_timestamp_set_hour:Ne #2 { \int_eval:n { 12 + \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } } } { \datatool_timestamp_set_hour:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } } % \end{macrocode} % Set the minute: % \begin{macrocode} \datatool_timestamp_set_minute:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 6 } } % \end{macrocode} % Set the second: % \begin{macrocode} \datatool_timestamp_set_second:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 7 } } % \end{macrocode} % Parse timezone if present: % \begin{macrocode} \datatool_parse_regional_timezone:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 9 } } { #4 } { #5 } } { % \end{macrocode} % No match on month: % \begin{macrocode} #5 } } { #5 } % No match } % \end{macrocode} % %As above but missing century. %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_mmddyy_hhmmss_tz_parse_timestamp:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year_add_century:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Set the hour: % \begin{macrocode} \exp_args:Ne \DTLCurrentLocaleIfpmTF { \seq_item:Nn \l_datatool_timestamp_match_seq { 8 } } { \datatool_timestamp_set_hour:Ne #2 { \int_eval:n { 12 + \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } } } { \datatool_timestamp_set_hour:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } } % \end{macrocode} % Set the minute: % \begin{macrocode} \datatool_timestamp_set_minute:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 6 } } % \end{macrocode} % Set the second: % \begin{macrocode} \datatool_timestamp_set_second:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 7 } } % \end{macrocode} % Parse timezone if present: % \begin{macrocode} \datatool_parse_regional_timezone:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 9 } } { #4 } { #5 } } { % \end{macrocode} % No match on month: % \begin{macrocode} #5 } } { #5 } % No match } % \end{macrocode} % %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % %The regular expression should have the following groups (in order): %year, month, day, hour, minute, second (may be empty), %am/pm (may be empty), time zone (may be empty). % \begin{macrocode} \cs_new:Nn \datatool_yyyymmdd_hhmmss_tz_parse_timestamp:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { % \end{macrocode} % Successfully set month. % Set the year: % \begin{macrocode} \datatool_timestamp_set_year:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Set the hour: % \begin{macrocode} \exp_args:Ne \DTLCurrentLocaleIfpmTF { \seq_item:Nn \l_datatool_timestamp_match_seq { 8 } } { \datatool_timestamp_set_hour:Ne #2 { \int_eval:n { 12 + \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } } } { \datatool_timestamp_set_hour:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } } % \end{macrocode} % Set the minute: % \begin{macrocode} \datatool_timestamp_set_minute:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 6 } } % \end{macrocode} % Set the second: % \begin{macrocode} \datatool_timestamp_set_second:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 7 } } % \end{macrocode} % Parse timezone if present: % \begin{macrocode} \datatool_parse_regional_timezone:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 9 } } { #4 } { #5 } } { % \end{macrocode} % No match on month: % \begin{macrocode} #5 } } { #5 } % No match } % \end{macrocode} % % As above but missing century. %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_yymmdd_hhmmss_tz_parse_timestamp:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { % \end{macrocode} % Successfully set month. % Set the year: % \begin{macrocode} \datatool_timestamp_set_year_add_century:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Set the hour: % \begin{macrocode} \exp_args:Ne \DTLCurrentLocaleIfpmTF { \seq_item:Nn \l_datatool_timestamp_match_seq { 8 } } { \datatool_timestamp_set_hour:Ne #2 { \int_eval:n { 12 + \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } } } { \datatool_timestamp_set_hour:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } } % \end{macrocode} % Set the minute: % \begin{macrocode} \datatool_timestamp_set_minute:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 6 } } % \end{macrocode} % Set the second: % \begin{macrocode} \datatool_timestamp_set_second:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 7 } } % \end{macrocode} % Parse timezone if present: % \begin{macrocode} \datatool_parse_regional_timezone:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 9 } } { #4 } { #5 } } { % \end{macrocode} % No match on month: % \begin{macrocode} #5 } } { #5 } % No match } % \end{macrocode} % %Syntax: \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_parse_regional_timezone:NnTF { \tl_if_empty:nTF { #2 } { % \end{macrocode} % Missing time zone valid so return true. % \begin{macrocode} #3 } { % \end{macrocode} % Check for known timezone identifier. First try regionless mapping. % \begin{macrocode} \prop_get:NnNF \l_datatool_timezone_map_prop { #2 } \l_datatool_timezone_map_value_tl { % \end{macrocode} % No regionless mapping so now try locale mapping. % \begin{macrocode} \DTLCurrentLocaleGetTimeZoneMap { #2 } \quark_if_no_value:NT \l_datatool_timezone_map_value_tl { % \end{macrocode} % No mapping so may be numeric. % \begin{macrocode} \tl_set:Nn \l_datatool_timezone_map_value_tl { #2 } } } \exp_args:NNV \regex_extract_once:NnNTF \c_datatool_timezone_regex \l_datatool_timezone_map_value_tl \l_datatool_regex_match_seq { \datatool_timestamp_set_tzhour:Ne #1 { \seq_item:Nn \l_datatool_regex_match_seq { 2 } } \tl_if_empty:eF { \seq_item:Nn \l_datatool_regex_match_seq { 3 } } { \datatool_timestamp_set_tzminute:Ne #1 { \seq_item:Nn \l_datatool_regex_match_seq { 3 } } } #3 } { #4 } } } \cs_generate_variant:Nn \datatool_parse_regional_timezone:NnTF { NeTF } % \end{macrocode} % %Syntax: \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_parse_regional_month:NnTF { % \end{macrocode} % Check for month name mapping. % \begin{macrocode} \DTLCurrentLocaleGetMonthNameMap { #2 } \quark_if_no_value:NT \l_datatool_monthname_map_value_tl { % \end{macrocode} % No mapping so check if numeric. % \begin{macrocode} \tl_set:Nn \l_datatool_monthname_map_value_tl { #2 } } \exp_args:NNV \regex_extract_once:NnNTF \c_datatool_month_number_regex \l_datatool_monthname_map_value_tl \l_datatool_regex_match_seq { \datatool_timestamp_set_month:Ne #1 { \seq_item:Nn \l_datatool_regex_match_seq { 2 } } #3 } { #4 } } \cs_generate_variant:Nn \datatool_parse_regional_month:NnTF { NeTF } % \end{macrocode} % %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % %The regular expression should have the following groups (in order): %day, month, year. % \begin{macrocode} \cs_new:Nn \datatool_ddmmyyyy_parse_date:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Success % \begin{macrocode} #4 } { % \end{macrocode} % No match on month: % \begin{macrocode} #5 } } { #5 } % No match } % \end{macrocode} % %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % %The regular expression should have the following groups (in order): %month, day, year. % \begin{macrocode} \cs_new:Nn \datatool_mmddyyyy_parse_date:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Success % \begin{macrocode} #4 } { % \end{macrocode} % No match on month: % \begin{macrocode} #5 } } { #5 } % No match } % \end{macrocode} % %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % %The regular expression should have the following groups (in order): %year, month, day. % \begin{macrocode} \cs_new:Nn \datatool_yyyymmdd_parse_date:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { % \end{macrocode} % Successfully set month. % Set the year: % \begin{macrocode} \datatool_timestamp_set_year:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Success % \begin{macrocode} #4 } { % \end{macrocode} % No match on month: % \begin{macrocode} #5 } } { #5 } % No match } % \end{macrocode} % % As above but century missing: %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_ddmmyy_parse_date:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year_add_century:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Success % \begin{macrocode} #4 } { % \end{macrocode} % No match on month: % \begin{macrocode} #5 } } { #5 } % No match } % \end{macrocode} % %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_mmddyy_parse_date:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year_add_century:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Success % \begin{macrocode} #4 } { % \end{macrocode} % No match on month: % \begin{macrocode} #5 } } { #5 } % No match } % \end{macrocode} % %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_yymmdd_parse_date:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { % \end{macrocode} % Successfully set month. % Set the year: % \begin{macrocode} \datatool_timestamp_set_year_add_century:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Success % \begin{macrocode} #4 } { % \end{macrocode} % No match on month: % \begin{macrocode} #5 } } { #5 } % No match } % \end{macrocode} % % %As above, but year missing. Assume current year: %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_ddmm_parse_date:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year:Nn #2 { } % \end{macrocode} % Success % \begin{macrocode} #4 } { % \end{macrocode} % No match on month: % \begin{macrocode} #5 } } { #5 } % No match } % \end{macrocode} % %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_mmdd_parse_date:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the month first, to skip rest on failure: % \begin{macrocode} \datatool_parse_regional_month:NeTF #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } { % \end{macrocode} % Successfully set month. % Set the day of month: % \begin{macrocode} \datatool_timestamp_set_day:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} % Set the year: % \begin{macrocode} \datatool_timestamp_set_year:Nn #2 { } % \end{macrocode} % Success % \begin{macrocode} #4 } { % \end{macrocode} % No match on month: % \begin{macrocode} #5 } } { #5 } % No match } % \end{macrocode} % %Syntax: \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_hhmmss_parse_time:NnTF { \datatool_hhmmss_parse_time:NNnTF \c_datatool_colon_anchored_hhmmss_time_regex #1 { #2 } { #3 } { #4 } } % \end{macrocode} %Syntax: \meta{regex-var} \meta{timestamp seq-var} \marg{text} % \marg{return true} \marg{return false} % \begin{macrocode} \cs_new:Nn \datatool_hhmmss_parse_time:NNnTF { \regex_extract_once:NnNTF #1 { #3 } \l_datatool_timestamp_match_seq { % \end{macrocode} % Initialise: % \begin{macrocode} \datatool_timestamp_zero:N #2 % \end{macrocode} % Set the hour: % \begin{macrocode} \exp_args:Ne \DTLCurrentLocaleIfpmTF { \seq_item:Nn \l_datatool_timestamp_match_seq { 5 } } { \datatool_timestamp_set_hour:Ne #2 { \int_eval:n { 12 + \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } } } { \datatool_timestamp_set_hour:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 2 } } } % \end{macrocode} % Set the minute: % \begin{macrocode} \datatool_timestamp_set_minute:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 3 } } % \end{macrocode} % Set the second: % \begin{macrocode} \datatool_timestamp_set_second:Ne #2 { \seq_item:Nn \l_datatool_timestamp_match_seq { 4 } } % \end{macrocode} % Success % \begin{macrocode} #4 } { #5 } % No match } \cs_generate_variant:Nn \datatool_hhmmss_parse_time:NNnTF { cNnTF } % \end{macrocode} % %\subsubsection{Currencies} %\begin{macro}{\@dtl@currencies} %\changes{3.0}{2025-03-03}{removed} %Renamed \cs{@dtl@currencies} since the internal structure %is changed. This will trigger an unknown control sequence error if %it's being used by anything. %The new name is \verb|\l__datatool_known_currencies_seq| %but it needs to be defined before \verb|\datatool_set_currencysign:nn| %is used. %\end{macro} % An internal list that stores all known currencies. % \begin{macrocode} \seq_put_right:Nn \l__datatool_known_currencies_seq { \$ } \seq_put_right:Nn \l__datatool_known_currencies_seq {\pounds } \seq_put_right:Nn \l__datatool_known_currencies_seq {\textdollar } \seq_put_right:Nn \l__datatool_known_currencies_seq {\textstirling } \seq_put_right:Nn \l__datatool_known_currencies_seq {\texteuro } \seq_put_right:Nn \l__datatool_known_currencies_seq {\textyen } \seq_put_right:Nn \l__datatool_known_currencies_seq {\textwon } \seq_put_right:Nn \l__datatool_known_currencies_seq {\textcurrency } % \end{macrocode} % %\begin{macro}{\@dtl@currency} %\cs{@dtl@currency} is set by \cs{DTLconverttodecimal} and %\cs{@dtl@checknumerical}. It is used by \cs{DTLcurrency}. % Set to "\$" by default. % \begin{macrocode} \newcommand*{\@dtl@currency}{\$} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLCurrencySymbol} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand*{\DTLCurrencySymbol}{\@dtl@currency} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLCurrencyCode} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand*{\DTLCurrencyCode}{XXX} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLsetdefaultcurrency} %\changes{3.0}{2025-03-03}{changed to document command} %\cs{DTLsetdefaultcurrency}\marg{symbol} sets the default currency. % \begin{macrocode} \NewDocumentCommand \DTLsetdefaultcurrency { m } { % \end{macrocode} % Detokenize in test in case argument isn't a currency code to allow % for backward-compatibility. % \begin{macrocode} \tl_if_exist:cTF { dtl@curr@ \tl_to_str:n { #1 } @sym } { % \end{macrocode} % Defined currency code. % \begin{macrocode} \tl_set:Ne \@dtl@currency { \exp_not:c { DTLcurr #1 } } \tl_set:Ne \DTLCurrencyCode { #1 } \tl_set:Ne \DTLfmtcurrency { \exp_not:c { dtl@curr@ #1 @fmt } } } { % \end{macrocode} % Unknown currency. % \begin{macrocode} \tl_set:Nn \DTLCurrencyCode { XXX } \tl_set:Nn \@dtl@currency { #1 } } } % \end{macrocode} %\end{macro} % %For use with the region files. % \begin{macrocode} \cs_new:Nn \datatool_register_regional_currency_code:nn { \seq_put_right:Nn \l_datatool_regional_currencies_seq { #2 } \prop_put:Nnn \l_datatool_regional_currencies_prop { #1 } { #2 } } % \end{macrocode} % % \begin{macrocode} \cs_new:Nn \datatool_currency_symbol_region_prefix:n { \DTLcurrCodeOrSymOrChar { } { \datatoolcurrencysymbolprefixfmt { #1 } } { \datatoolcurrencysymbolprefixfmt { #1 } } } % \end{macrocode} % %\begin{macro}{\DTLcurrSym} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLcurrSym}[1]{\csuse{dtl@curr@ #1 @sym}} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLcurrChar} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLcurrChar}[1]{\csuse{dtl@curr@ #1 @tl}} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLcurrStr} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLcurrStr}[1]{\csuse{dtl@curr@ #1 @str}} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLdefcurrency} %Define currency. %\begin{definition} %\cs{DTLdefcurrency}\oarg{fmt}\marg{iso-code}\marg{symbol}\marg{string} %\end{definition} %\changes{3.0}{2025-03-03}{new} %NB \cs{DTLdefcurrency} doesn't pick up the fourth \meta{string} %argument. Category code of \$ needs to be changed first. % \begin{macrocode} \NewDocumentCommand{\DTLdefcurrency} { O{\dtlcurrdefaultfmt} m m } { \group_begin: \char_set_catcode_other:N \$ \__datatool_def_currency:nnnn { #1 } { #2 } { #3 } } \cs_new:Nn \__datatool_def_currency:nnnn { \group_end: \datatool_def_currency:nnne { #1 } { #2 } { #3 } { #4 } } % \end{macrocode} %\end{macro} %Lower-level command. Three arguments, use default format: % \begin{macrocode} \cs_new:Nn \datatool_def_currency:nnn { \datatool_def_currency:nnnn { \dtlcurrdefaultfmt } { #1 } { #2 } { #3 } } \cs_generate_variant:Nn \datatool_def_currency:nnn { nnV , nne , nVV } % \end{macrocode} % Four arguments, supply formatting command in first argument: % \begin{macrocode} \cs_new:Nn \datatool_def_currency:nnnn { \seq_put_right:Nn \l_datatool_currencies_seq { #2 } \str_set:cn { dtl@curr@ #2 @str } { #4 } \tl_set:cn { dtl@curr@ #2 @tl } { #4 } \tl_set:cn { dtl@curr@ #2 @sym } { #3 } \csedef { DTLcurr #2 } { \exp_not:N \dtltexorsort { \exp_not:N \DTLcurrCodeOrSymOrChar { #2 } { \exp_not:N \DTLcurrSym { #2 } } { \exp_not:N \DTLcurrChar { #2 } } } { \exp_not:N \DTLcurrStr { #2 } } } \DTLnewcurrencysymbol { #3 } \DTLnewcurrencysymbol { #4 } \exp_args:Nc \DTLnewcurrencysymbol { DTLcurr #2 } \csdef { dtl@curr@ #2 @fmt } { #1 } } \cs_generate_variant:Nn \datatool_def_currency:nnnn { nnnV , nnne , nnVV } % \end{macrocode} % % Find first declared regional currency code that matches the given % symbol. Returns empty if no match. First tests if a % match on current currency. % \begin{macrocode} \cs_new:Nn \datatool_get_currency_code:Nn { \__datatool_if_current_currency:nTF { #2 } { \tl_set_eq:NN #1 \DTLCurrencyCode } { \tl_clear:N #1 \seq_map_inline:Nn \l_datatool_regional_currencies_seq { \tl_if_eq:nnTF { ##1 } { #2 } { \tl_set:Nn #1 { ##1 } \seq_map_break: } { \tl_if_eq:nnTF { \DTLcurr { ##1 } } { #2 } { \tl_set:Nn #1 { ##1 } \seq_map_break: } { \exp_args:Ne \tl_if_eq:nnTF { \exp_not:c { DTLcurr ##1 } } { #2 } { \tl_set:Nn #1 { ##1 } \seq_map_break: } { \tl_if_eq:cnTF { dtl@curr@ ##1 @sym } { #2 } { \tl_set:Nn #1 { ##1 } \seq_map_break: } { \str_if_eq:vnT { dtl@curr@ ##1 @str } { #2 } { \tl_set:Nn #1 { ##1 } \seq_map_break: } } } } } } } } \cs_generate_variant:Nn \datatool_get_currency_code:Nn { NV } % \end{macrocode} % %Test if the supplied token list matches the current symbol or %string. % \begin{macrocode} \prg_new_conditional:Npnn \__datatool_if_current_currency:n #1 { T, F, TF } { \tl_if_eq:NnTF \@dtl@currency { #1 } { \prg_return_true: } { \tl_if_exist:cTF { DTLcurr \DTLCurrencyCode } { \tl_if_eq:cnTF { DTLcurr \DTLCurrencyCode } { #1 } { \prg_return_true: } { \exp_args:Nv \tl_if_eq:nnTF { dtl@curr@ \DTLCurrencyCode @sym } { #1 } { \prg_return_true: } { \str_if_eq:vnTF { dtl@curr@ \DTLCurrencyCode @str } { #1 } { \prg_return_true: } { \exp_args:Ne \tl_if_eq:nnTF { \exp_not:N \DTLcurr { \DTLCurrencyCode } } { #1 } { \prg_return_true: } { \prg_return_false: } } } } } { \prg_return_false: } } } % \end{macrocode} % %Allow the command used for the currency to be changed. %Note that this will add the new symbol to the list of known %currency symbols but won't remove the old one. % \begin{macrocode} \cs_new:Nn \datatool_set_currency_symbol:nn { \tl_if_exist:cTF { dtl@curr@ #1 @sym } { \csdef { dtl@curr@ #1 @sym } { #2 } \DTLnewcurrencysymbol { #2 } } { \PackageError { datatool-base } { Can't ~ set ~ currency ~ symbol ~ to ~ \tl_to_str:n { #2 } ~ : ~ Currency ~ ` #1 ' ~ not ~ defined } { Check ~ that ~ you ~ have ~ spelt ~ the ~ currency ~ code ~ correctly } } } \cs_generate_variant:Nn \datatool_set_currency_symbol:nn { nV , ne } % \end{macrocode} % %\begin{macro}{\DTLcurrency} %\changes{3.0}{2025-03-03}{new} %Format currency using current symbol. % \begin{macrocode} \newcommand{\DTLcurrency}[1]{\DTLfmtcurrency{\@dtl@currency}{#1}} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLfmtcurr} %\changes{3.0}{2025-03-03}{new} %Format currency according to the given currency code or use the %default format if not defined. May fully expand (unless currency %symbol is fragile, which is less likely with newer \LaTeX\ kernel). % \begin{macrocode} \newcommand{\DTLfmtcurr}[2]{% \cs_if_exist:cTF { dtl@curr@ #1 @fmt } { \use:c { dtl@curr@ #1 @fmt } { \DTLcurr { #1 } } { #2 } } { \DTLcurrency { #2 } } } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLfmtcurrency} %\changes{3.0}{2025-03-03}{new} %Format currency using given symbol. % \begin{macrocode} \newcommand{\DTLfmtcurrency}{\dtlcurrdefaultfmt} % \end{macrocode} %\end{macro} %\begin{macro}{\dtlcurrdefaultfmt} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtlcurrdefaultfmt}{\dtlcurrprefixfmt} % \end{macrocode} %\end{macro} %\begin{macro}{\dtlcurrprefixfmt} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtlcurrprefixfmt}[2]{ \datatool_prefix_adjust_sign:nnn { #1 } { \dtlcurrfmtsep } { #2 } } % \end{macrocode} %\end{macro} % \begin{macrocode} \cs_new:Nn \datatool_prefix_adjust_sign:nnn { \bool_lazy_or:nnTF { \tl_if_head_eq_charcode_p:nN { #3 } + } { \tl_if_head_eq_charcode_p:nN { #3 } - } { \exp_args:Ne \datatool_adjust_sign_fmt:n { \tl_head:n { #3 } } #1 #2 \tl_tail:n { #3 } } { #1 #2 #3 } } % \end{macrocode} %Allow the sign to be formatted if outside of math mode. % \begin{macrocode} \cs_new:Nn \datatool_adjust_sign_fmt:n { \ifmmode #1 \else \tl_if_head_eq_charcode:nNTF { #1 } - { \textminus } { #1 } \fi } % \end{macrocode} %\begin{macro}{\dtlcurrsuffixfmt} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtlcurrsuffixfmt}[2]{ \datatool_suffix_adjust_sign:nnn { #1 } { \dtlcurrfmtsep } { #2 } } % \end{macrocode} %\end{macro} % \begin{macrocode} \cs_new:Nn \datatool_suffix_adjust_sign:nnn { \bool_lazy_or:nnTF { \tl_if_head_eq_charcode_p:nN { #3 } + } { \tl_if_head_eq_charcode_p:nN { #3 } - } { \exp_args:Ne \datatool_adjust_sign_fmt:n { \tl_head:n { #3 } } \tl_tail:n { #3 } #2 #1 } { #3 #2 #1 } } % \end{macrocode} %\begin{macro}{\dtlcurrfmtsymsep} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtlcurrfmtsymsep}{} % \end{macrocode} %\end{macro} % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} %\begin{macro}{\dtlcurrfmtsep} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\dtlcurrfmtsep}{\DTLcurrCodeOrSymOrChar{~}{\dtlcurrfmtsymsep}{\dtlcurrfmtsymsep}} % \end{macrocode} %\end{macro} % \begin{macrocode} \ExplSyntaxOn % \end{macrocode} %\begin{macro}{\DTLcurr} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLcurr}[1]{\ifcsdef{DTLcurr#1}{\csuse{DTLcurr#1}}{#1}} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLdefaultEURcurrencyfmt} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLdefaultEURcurrencyfmt}{\dtlcurrdefaultfmt} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLcurrXXX} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \datatool_def_currency:nnV { XXX } { \textcurrency } \l_datatool_currency_str % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLcurrXBT} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \cs_if_exist:NT \faBtc { \datatool_def_currency:nnV { XBT } { \faBtc } \l_datatool_bitcoin_str } % \end{macrocode} %\end{macro} % % Try to guess the command used for the Euro currency symbol. % Mainly provided to retain backward compatibility with pre v3.0. % If incorrect, this may be changed with % \verb|\datatool_set_currency_symbol:nn| % Other currencies can be defined in the applicable region file. % \begin{macrocode} \tl_new:N \l__datatool_eurocs_tl % \end{macrocode} % % \begin{macrocode} \cs_if_exist:NTF \euro { \tl_set:Nn \l__datatool_eurocs_tl { \euro } } { \cs_if_exist:NTF \Euro { \tl_set:Nn \l__datatool_eurocs_tl { \Euro } } { \cs_if_exist:NTF \EUR { \tl_set:Nn \l__datatool_eurocs_tl { \EUR } } { \cs_if_exist:NTF \faEur { \tl_set:Nn \l__datatool_eurocs_tl { \faEur } } { \cs_if_exist:NTF \wasyeuro { \tl_set:Nn \l__datatool_eurocs_tl { \wasyeuro } } { \cs_if_exist:NTF \texteuro { \tl_set:Nn \l__datatool_eurocs_tl { \texteuro } } { \tl_set:Nn \l__datatool_eurocs_tl { \l_datatool_euro_str } } } } } } } % \end{macrocode} %\begin{macro}{\DTLcurrEUR} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \datatool_def_currency:nnVV { \DTLdefaultEURcurrencyfmt } { EUR } \l__datatool_eurocs_tl \l_datatool_euro_str % \end{macrocode} %\end{macro} % %\begin{macro}{\datatoolSetCurrencySort} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand \datatoolSetCurrencySort { \let \textdollar \c_dollar_str \let \textdollaroldstyle \c_dollar_str \let \textcentoldstyle \l_datatool_cent_str \let \textcent \l_datatool_cent_str \let \textsterling \l_datatool_pound_str \let \pounds \l_datatool_pound_str \let \textcurrency \l_datatool_currency_str \let \textyen \l_datatool_yen_str \let \textflorin \l_datatool_florin_str \let \texteuro \l_datatool_euro_str \let \textcolonmonetary \l_datatool_colonsign_str \let \textwon \l_datatool_won_str \let \textnaira \l_datatool_naira_str \let \textguarani \l_datatool_guarani_str \let \textpeso \l_datatool_peso_str \let \textlira \l_datatool_lira_str \let \textdong \l_datatool_dong_str \let \textbaht \l_datatool_baht_str } % \end{macrocode} %\end{macro} % % \subsection{Floating Point Arithmetic} % % The commands defined in this section are designed for localised % numeric values. They all have to first convert the formatted value % to a numeric value acceptable to the underlying arithmetic function and then % convert back again. If the original supplied values had different % data types, the data type of the result depends on which type % is dominant. % %First provide some common functions to update the datum structure after operating on %two values with potentially different types. % %Determine dominant data type: decimal overrides integer, currency %overrides integer and decimal, temporal values override other %values, but time should be converted to datetime if %added to a date or datetime or other numeric value, and date should %be converted to datetime if added to anything other than an integer. % %NB if two currencies are added, their symbols are assumed to %represent the same currency unit. No exchange rate information is %available. % \begin{macrocode} \cs_new:Nn \__datatool_update_datatype: { \datatool_update_datatype:NNNN \@dtl@datatype \l__datatool_tmp_datatype_int \l__datatool_datum_currency_tl \l__datatool_tmp_currency_tl } % \end{macrocode} %Syntax: \meta{type1 int-var} \meta{type2 int-var} %\meta{curr-sym1 tl-var} \meta{curr-sym2 tl-var} %This will update \meta{type1 int-var} to the dominant type %and (if the dominant type is currency) %\meta{curr-sym1 tl-var} to the currency symbol. % \begin{macrocode} \cs_new:Nn \datatool_update_datatype:NNNN { \bool_lazy_or:nnF { \int_compare_p:nNn { #1 } = { #2 } } { \int_compare_p:nNn { #2 } = { \c_datatool_unknown_int } } { \int_compare:nNnTF { #1 } = { \c_datatool_unknown_int } { \int_set_eq:NN #1 #2 \tl_set_eq:NN #3 #4 } { \bool_lazy_or:nnTF { \datatool_if_temporal_datum_type_p:n { #1 } } { \datatool_if_temporal_datum_type_p:n { #2 } } { % \end{macrocode} %If either is a temporal value (and already checked they are not the %same type), the result will be a timestamp unless an integer has %been added to a date. % \begin{macrocode} \bool_lazy_or:nnTF { \bool_lazy_and_p:nn { \int_compare_p:nNn { #1 } = { \c_datatool_date_int } } { \int_compare_p:nNn { #2 } = { \c_datatool_integer_int } } } { \bool_lazy_and_p:nn { \int_compare_p:nNn { #2 } = { \c_datatool_date_int } } { \int_compare_p:nNn { #1 } = { \c_datatool_integer_int } } } { \int_set_eq:NN #1 \c_datatool_date_int } { \int_set_eq:NN #1 \c_datatool_datetime_int } % \end{macrocode} %Doesn't make sense to add a currency to a date/time value. % \begin{macrocode} \tl_clear:N #3 } { % \end{macrocode} %Neither is a temporal type, so order of precedence can simply be %determined by the type's numeric ID. % \begin{macrocode} \int_compare:nNnT { #1 } < { #2 } { \int_set_eq:NN #1 #2 \tl_set_eq:NN #3 #4 } } } } } % \end{macrocode} %Convert the numeric value to localised format. %Note that the \verb|\l__datatool_result_tl| should be correctly %expanded before this function. % \begin{macrocode} \cs_new:Nn \__datatool_assign_result:N { \datatool_assign_result:NNNN #1 \@dtl@datatype \l__datatool_result_tl \l__datatool_datum_currency_tl } % \end{macrocode} %Syntax: \meta{tl-var} %\meta{type int-var} %\meta{value tl-var} %\meta{curr-sym tl-var} % \begin{macrocode} \cs_new:Nn \datatool_assign_result:NNNN { \datatool_if_temporal_datum_type:nTF { #2 } { \datatool_decimal_to_temporal:Nnn #1 { #2 } { #3 } } { \tl_if_empty:NTF #4 { \exp_args:NV \DTLdecimaltolocale #3 #1 } { \exp_args:NVV \__datatool_decimal_to_currency:nnN #4 #3 #1 } } } % \end{macrocode} % %\begin{definition} %\cs{datatool\_numeric\_fn:NnnNN} %\meta{result tl-var} \marg{num1} \marg{num2} % \meta{decimal-operator-cs} \meta{int-operator-cs} %\end{definition} %Perform a numerical operation on two values that need parsing or %that are already in datum format. If they are both integers, %use the \meta{int-operator-cs} function, which should take two %arguments and expand to the result, otherwise use the %\cs{decimal-operator-cs} function, which should take three arguments %where the first is the result token list variable and the others %are the numeric values. % \begin{macrocode} \cs_new:Nn \datatool_numeric_fn:NnnNN { \DTLconverttodecimal { #2 } \l__datatool_resulta_tl \int_set_eq:NN \l__datatool_tmp_datatype_int \@dtl@datatype \tl_set_eq:NN \l__datatool_tmp_currency_tl \l__datatool_datum_currency_tl \DTLconverttodecimal { #3 } \l__datatool_resultb_tl % \end{macrocode} %If the data types are different, need to determine the dominant %one. % \begin{macrocode} \__datatool_update_datatype: % \end{macrocode} %Determine which function should be used. % \begin{macrocode} \datatool_if_any_int_datum_type:nTF { \@dtl@datatype } { % \end{macrocode} %Version 3.1: changed \cs{tl\_set:Nx} to \cs{tl\_set:Ne} %(not sure if this makes difference). % \begin{macrocode} \tl_set:Ne \l__datatool_result_tl { #5 { \l__datatool_resulta_tl } { \l__datatool_resultb_tl } } } { #4 { \l__datatool_result_tl } { \l__datatool_resulta_tl } { \l__datatool_resultb_tl } } \__datatool_assign_result:N #1 } % \end{macrocode} % %\begin{definition} %\cs{datatool\_numeric\_fn:NnNN} %\meta{result tl-var} \marg{num} % \meta{decimal-operator-cs} \meta{int-operator-cs} %\end{definition} %Perform a numerical operation on one value that needs parsing or %that is already in datum format. If the value is an integer, %use the \meta{int-operator-cs} function, which should take one %argument and expand to the result, otherwise use the %\cs{decimal-operator-cs} function, which should take two arguments %where the first is the result token list variable and the second is %the numeric value. % \begin{macrocode} \cs_new:Nn \datatool_numeric_fn:NnNN { \DTLconverttodecimal { #2 } \l__datatool_resulta_tl % \end{macrocode} %Determine which function should be used. % \begin{macrocode} \datatool_if_any_int_datum_type:nTF { \@dtl@datatype } { % \end{macrocode} %Version 3.1: changed \cs{tl\_set:Nx} to \cs{tl\_set:Ne} %(not sure if this makes difference). % \begin{macrocode} \tl_set:Ne \l__datatool_result_tl { #4 { \l__datatool_resulta_tl } } } { #3 { \l__datatool_result_tl } { \l__datatool_resulta_tl } } \__datatool_assign_result:N #1 } % \end{macrocode} % %\begin{definition} %\cs{datatool\_numeric\_fn:NnN} %\meta{result tl-var} \marg{num} % \meta{decimal-operator-cs} %\end{definition} %Perform a numerical operation on one value that needs parsing or %that is already in datum format. The calculation is performed by %\cs{decimal-operator-cs} function, which should take two arguments %where the first is the result token list variable and the second is %the numeric value. The result is not expected to be an integer. % \begin{macrocode} \cs_new:Nn \datatool_numeric_fn:NnN { \DTLconverttodecimal { #2 } \l__datatool_resulta_tl #3 { \l__datatool_result_tl } { \l__datatool_resulta_tl } \datatool_if_any_int_datum_type:nTF { \@dtl@datatype } { \int_set_eq:NN \@dtl@datatype \c_datatool_decimal_int } \__datatool_assign_result:N #1 } % \end{macrocode} % %\begin{definition} %\cs{datatool\_numeric\_list\_fn:NnNN} %\meta{result tl-var} \marg{num list} % \meta{decimal-operator-cs} \meta{int-operator-cs} %\end{definition} %Perform a numerical operation on a list of values that need parsing or %that are already in datum format. As \cs{datatool\_numeric\_fn:NnnNN} %but iterates through the list performing the applicable function %sequentially. The result will be set to \cs{DTLnumbernull} if the %list is empty (and a warning will occur). % \begin{macrocode} \cs_new:Nn \datatool_numeric_list_fn:NnNN { % \end{macrocode} %Convert the list to the scratch sequence: % \begin{macrocode} \@dtl@assigntmpseq { #2 } % \end{macrocode} %Keep a note of the number of items in the sequence if required. % \begin{macrocode} \int_set:Nn \l__datatool_count_int { \seq_count:N \l__datatool_tmp_seq } % \end{macrocode} %If sequence is empty, set the result to number null: % \begin{macrocode} \int_if_zero:nTF { \l__datatool_count_int } { \PackageWarning { datatool-base } { empty ~ list ~ ` #2 ' ~ found ~ in ~ aggregate ~ function } \tl_set_eq:NN #1 \DTLnumbernull } { % \end{macrocode} %Pop the first item from the sequence: % \begin{macrocode} \seq_pop_left:NN \l__datatool_tmp_seq \l__datatool_resulta_tl \exp_args:NV \DTLconverttodecimal \l__datatool_resulta_tl \l__datatool_result_tl \seq_map_inline:Nn \l__datatool_tmp_seq { % \end{macrocode} % Save previous: % \begin{macrocode} \int_set_eq:NN \l__datatool_tmp_datatype_int \@dtl@datatype \tl_set_eq:NN \l__datatool_tmp_currency_tl \l__datatool_datum_currency_tl \tl_set_eq:NN \l__datatool_resulta_tl \l__datatool_result_tl % \end{macrocode} %Parse this value. % \begin{macrocode} \DTLconverttodecimal { ##1 } \l__datatool_resultb_tl % \end{macrocode} %If the data types are different, need to determine the dominant %one. % \begin{macrocode} \__datatool_update_datatype: % \end{macrocode} %Determine which function should be used. % \begin{macrocode} \datatool_if_any_int_datum_type:nTF { \@dtl@datatype } { % \end{macrocode} %Version 3.1: changed \cs{tl\_set:Nx} to \cs{tl\_set:Ne} %(not sure if this makes difference). % \begin{macrocode} \tl_set:Ne \l__datatool_result_tl { #4 { \l__datatool_resulta_tl } { \l__datatool_resultb_tl } } } { #3 { \l__datatool_result_tl } { \l__datatool_resulta_tl } { \l__datatool_resultb_tl } } } \__datatool_assign_result:N #1 } } % \end{macrocode} % %\begin{definition} %\cs{datatool\_decimal\_list\_fn:NnN} %\meta{result fp-var} \marg{num list} % \meta{fp-operator-cs} %\end{definition} %Similar to the above but the result is expected to be a floating %point variable and the function should by a l2fp update function, %such as \cs{fp\_add:Nn}. % \begin{macrocode} \cs_new:Nn \datatool_decimal_list_fn:NnN { % \end{macrocode} %Convert the list to the scratch sequence: % \begin{macrocode} \@dtl@assigntmpseq { #2 } % \end{macrocode} %Keep a note of the number of items in the sequence if required. % \begin{macrocode} \int_set:Nn \l__datatool_count_int { \seq_count:N \l__datatool_tmp_seq } % \end{macrocode} %Trigger an error if the list is empty: % \begin{macrocode} \int_if_zero:nTF { \l__datatool_count_int } { \PackageError { datatool-base } { empty ~ list ~ ` #2 ' ~ found ~ in ~ aggregate ~ function } { one ~ or ~ more ~ numeric ~ items ~ are ~ expected } } { % \end{macrocode} %Pop the first item from the sequence: % \begin{macrocode} \seq_pop_left:NN \l__datatool_tmp_seq \l__datatool_resulta_tl \datatool_set_fp:NV #1 \l__datatool_resulta_tl \seq_map_inline:Nn \l__datatool_tmp_seq { % \end{macrocode} % Save previous datum information: % \begin{macrocode} \int_set_eq:NN \l__datatool_tmp_datatype_int \@dtl@datatype \tl_set_eq:NN \l__datatool_tmp_currency_tl \l__datatool_datum_currency_tl % \end{macrocode} %Get this value as fp variable. % \begin{macrocode} \datatool_set_fp:Nn \l__datatool_tmpa_fp { ##1 } % \end{macrocode} %If the data types are different, need to determine the dominant %one. % \begin{macrocode} \__datatool_update_datatype: % \end{macrocode} %Perform the update. % \begin{macrocode} #3 #1 { \l__datatool_tmpa_fp } } } } % \end{macrocode} % % %\begin{macro}{\DTLadd} %\begin{definition} % \cs{DTLadd}\marg{cmd}\marg{num1}\marg{num2} %\end{definition} % Sets \meta{cmd} = \meta{num1} + \meta{num2} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLadd { m m m } { \datatool_numeric_fn:NnnNN #1 { #2 } { #3 } \dtladd \datatool_int_add:nn } % \end{macrocode} %For use in the above and related commands: % \begin{macrocode} \cs_new:Nn \datatool_int_add:nn { \int_eval:n { #1 + #2 } } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgadd} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgadd { m m m } { \DTLadd { \l__datatool_resulta_tl } { #2 } { #3 } \tl_gset_eq:NN #1 \l__datatool_resulta_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLaddall} %\begin{definition} %\cs{DTLaddall}\marg{cmd}\marg{num list} %\end{definition} % Sums all the values in \meta{num list} and stores in % \meta{cmd} which must be a control sequence. %\changes{1.01}{2007 Aug 17}{removed extraneous space} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLaddall { m m } { \datatool_numeric_list_fn:NnNN #1 { #2 } \dtladd \datatool_int_add:nn } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgaddall} %\begin{definition} %\cs{DTLgaddall}\marg{cmd}\marg{num list} %\end{definition} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgaddall { m m } { \DTLaddall { \l__datatool_resultb_tl } { #2 } \tl_gset_eq:NN #1 \l__datatool_resultb_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLsub} %\begin{definition} % \cs{DTLsub}\marg{cmd}\marg{num1}\marg{num2} %\end{definition} % Sets \meta{cmd} = \meta{num1} - \meta{num2} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLsub { m m m } { \datatool_numeric_fn:NnnNN #1 { #2 } { #3 } \dtlsub \datatool_int_sub:nn } % \end{macrocode} %For use in the above: % \begin{macrocode} \cs_new:Nn \datatool_int_sub:nn { \int_eval:n { #1 - ( #2 ) } } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgsub} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgsub { m m m } { \DTLsub { \l__datatool_resulta_tl } {#2} {#3} \tl_gset_eq:NN #1 \l__datatool_resulta_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLmul} %\begin{definition} % \cs{DTLmul}\marg{cmd}\marg{num1}\marg{num2} %\end{definition} % Sets \meta{cmd} = \meta{num1} $\times$ \meta{num2} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLmul { m m m } { \datatool_numeric_fn:NnnNN #1 { #2 } { #3 } \dtlmul \datatool_int_mul:nn } % \end{macrocode} %For use in the above: % \begin{macrocode} \cs_new:Nn \datatool_int_mul:nn { \int_eval:n { ( #1 ) * ( #2 ) } } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgmul} % Global version % \begin{macrocode} \newcommand*{\DTLgmul}[3]{% \DTLmul{ \l__datatool_resulta_tl } {#2} {#3} \tl_gset_eq:NN #1 \l__datatool_resulta_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLdiv} %\begin{definition} % \cs{DTLdiv}\marg{cmd}\marg{num1}\marg{num2} %\end{definition} % Sets \meta{cmd} = \meta{num1} / \meta{num2} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLdiv { m m m } { \datatool_numeric_fn:NnnNN #1 { #2 } { #3 } \dtldiv \int_div_round:nn } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgdiv} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgdiv { m m m } { \DTLdiv{ \l__datatool_resulta_tl } {#2} {#3} \tl_gset_eq:NN #1 \l__datatool_resulta_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLabs} %\begin{definition} % \cs{DTLabs}\marg{cmd}\marg{num} %\end{definition} % Sets \meta{cmd} = abs(\meta{num}) %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLabs { m m } { \datatool_numeric_fn:NnNN #1 { #2 } \dtlabs \int_abs:n } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgabs} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgabs { m m } { \DTLabs{ \l__datatool_resulta_tl } {#2} \tl_gset_eq:NN #1 \l__datatool_resulta_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLneg} %\begin{definition} % \cs{DTLneg}\marg{cmd}\marg{num} %\end{definition} % Sets \meta{cmd} = -\meta{num} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLneg { m m } { \datatool_numeric_fn:NnNN #1 { #2 } \dtlneg \datatool_int_neg:n } % \end{macrocode} %Integer negation function for use in the above. % \begin{macrocode} \cs_new:Nn \datatool_int_neg:n { \int_eval:n { - ( #1 ) } } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgneg} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgneg { m m } { \DTLneg{ \l__datatool_resulta_tl } {#2} \tl_gset_eq:NN #1 \l__datatool_resulta_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLsqrt} %\begin{definition} % \cs{DTLsqrt}\marg{cmd}\marg{num} %\end{definition} % Sets \meta{cmd} = sqrt(\meta{num}) %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLsqrt { m m } { \datatool_numeric_fn:NnN #1 { #2 } \dtlroot } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgsqrt} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgsqrt { m m } { \DTLsqrt{ \l__datatool_resulta_tl } {#2} \tl_gset_eq:NN #1 \l__datatool_resulta_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLmin} %\begin{definition} % \cs{DTLmin}\marg{cmd}\marg{num1}\marg{num2} %\end{definition} % Sets \meta{cmd} = min(\meta{num1}, \meta{num2}) %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLmin { m m m } { \datatool_numeric_fn:NnnNN #1 { #2 } { #3 } \dtlmin \int_min:nn } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgmin} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgmin { m m m } { \DTLmin{ \l__datatool_resulta_tl } {#2} {#3} \tl_gset_eq:NN #1 \l__datatool_resulta_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLminall} %\begin{definition} %\cs{DTLminall}\marg{cmd}\marg{num list} %\end{definition} % Finds the minimum value in \meta{num list} and stores in % \meta{cmd} which must be a control sequence. %\changes{1.01}{2007 Aug 17}{removed extraneous space} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLminall { m m } { \datatool_numeric_list_fn:NnNN #1 { #2 } \dtlmin \int_min:nn } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgminall} %\begin{definition} %\cs{DTLgminall}\marg{cmd}\marg{num list} %\end{definition} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgminall { m m } { \DTLminall{ \l__datatool_resultb_tl } { #2 } \tl_gset_eq:NN #1 \l__datatool_resultb_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLmax} %\begin{definition} % \cs{DTLmax}\marg{cmd}\marg{num1}\marg{num2} %\end{definition} % Sets \meta{cmd} = max(\meta{num1}, \meta{num2}) %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLmax { m m m } { \datatool_numeric_fn:NnnNN #1 { #2 } { #3 } \dtlmax \int_max:nn } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgmax} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgmax { m m m } { \DTLmax { \l__datatool_resultb_tl } { #2 } { #3 } \tl_gset_eq:NN #1 \l__datatool_resultb_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLmaxall} %\begin{definition} %\cs{DTLmaxall}\marg{cmd}\marg{num list} %\end{definition} % Finds the maximum value in \meta{num list} and stores in % \meta{cmd} which must be a control sequence. %\changes{1.01}{2007 Aug 17}{removed extraneous space} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLmaxall { m m } { \datatool_numeric_list_fn:NnNN #1 { #2 } \dtlmax \int_max:nn } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgmaxall} %\begin{definition} %\cs{DTLgmaxall}\marg{cmd}\marg{num list} %\end{definition} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgmaxall { m m } { \DTLmaxall{ \l__datatool_resultb_tl } { #2 } \tl_gset_eq:NN #1 \l__datatool_resultb_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLmeanforall} %\begin{definition} %\cs{DTLmeanforall}\marg{cmd}\marg{num list} %\end{definition} % Computes the arithmetic mean of all the values in \meta{num list} % and stores in \meta{cmd} which must be a control sequence. %\changes{1.01}{2007 Aug 17}{removed extraneous space} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLmeanforall { m m } { \datatool_decimal_list_fn:NnN \l__datatool_total_fp { #2 } \fp_add:Nn \int_if_zero:nTF { \l__datatool_count_int } { \tl_set_eq:NN #1 \DTLnumbernull } { \fp_set:Nn \l__datatool_mean_fp { \l__datatool_total_fp / \l__datatool_count_int } % \end{macrocode} %\changes{3.1}{2025-03-10}{changed \cs{tl\_set:Nx} to \cs{tl\_set:Ne}} % \begin{macrocode} \tl_set:Ne \l__datatool_result_tl { \fp_use:N \l__datatool_mean_fp } \__datatool_assign_result:N #1 } } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgmeanforall} %\begin{definition} %\cs{DTLgmeanforall}\marg{cmd}\marg{num list} %\end{definition} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgmeanforall { m m } { \DTLmeanforall{ \l__datatool_resultb_tl } { #2 } \tl_gset_eq:NN #1 \l__datatool_resultb_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLvarianceforall} %\begin{definition} %\cs{DTLvarianceforall}\marg{cmd}\marg{num list} %\end{definition} % Computes the variance of all the values in \meta{num list} % and stores in \meta{cmd} which must be a control sequence. % This is more complicated than the previous aggregate functions. %\changes{1.01}{2007 Aug 17}{fixed bug} %\changes{1.01}{2007 Aug 17}{removed extraneous space} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLvarianceforall { m m } { \@dtl@assigntmpseq{#2} \int_zero:N \l__datatool_count_int \fp_zero:N \l__datatool_total_fp \int_set_eq:NN \@dtl@datatype \c_datatool_unknown_int \tl_clear:N \l__datatool_datum_currency_tl \seq_clear:N \l__datatool_tmpb_seq \seq_map_inline:Nn \l__datatool_tmp_seq { \int_incr:N \l__datatool_count_int % \end{macrocode} % Save previous datum information: % \begin{macrocode} \int_set_eq:NN \l__datatool_tmp_datatype_int \@dtl@datatype \tl_set_eq:NN \l__datatool_tmp_currency_tl \l__datatool_datum_currency_tl % \end{macrocode} %Convert: % \begin{macrocode} \DTLconverttodecimal { ##1 } \l__datatool_resulta_tl \__datatool_update_datatype: \seq_put_right:No \l__datatool_tmpb_seq { \l__datatool_resulta_tl } \fp_add:Nn \l__datatool_total_fp { \l__datatool_resulta_tl } } \int_if_zero:nTF { \l__datatool_count_int } { \PackageWarning { datatool-base } { empty ~ list ~ ` #2 ' ~ found ~ in ~ aggregate ~ function } \tl_set_eq:NN #1 \DTLnumbernull } { \fp_set:Nn \l__datatool_mean_fp { \l__datatool_total_fp / \l__datatool_count_int } \fp_zero:N \l__datatool_tmpa_fp \seq_map_inline:Nn \l__datatool_tmpb_seq { \fp_set:Nn \l__datatool_tmpb_fp { ##1 - \l__datatool_mean_fp } \fp_add:Nn \l__datatool_tmpa_fp { \l__datatool_tmpb_fp * \l__datatool_tmpb_fp } } \fp_set:Nn \l__datatool_tmpa_fp { \l__datatool_tmpa_fp / \l__datatool_count_int } % \end{macrocode} %\changes{3.1}{2025-03-10}{changed \cs{tl\_set:Nx} to \cs{tl\_set:Ne}} % \begin{macrocode} \tl_set:Ne \l__datatool_result_tl { \fp_use:N \l__datatool_tmpa_fp } \__datatool_assign_result:N #1 } } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgvarianceforall} %\begin{definition} %\cs{DTLgvarianceforall}\marg{cmd}\marg{num list} %\end{definition} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgvarianceforall { m m } { \DTLvarianceforall { \l__datatool_resultb_tl } { #2 } \tl_gset_eq:NN #1 \l__datatool_resultb_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLsdforall} %\begin{definition} %\cs{DTLsdforall}\marg{cmd}\marg{num list} %\end{definition} % Computes the standard deviation of all the values in \meta{num list} % and stores in \meta{cmd} which must be a control sequence. %\changes{1.01}{2007 Aug 17}{fixed bug} %\changes{1.01}{2007 Aug 17}{removed extraneous space} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLsdforall { m m } { \@dtl@assigntmpseq{#2} \int_zero:N \l__datatool_count_int \fp_zero:N \l__datatool_total_fp \int_set_eq:NN \@dtl@datatype \c_datatool_unknown_int \tl_clear:N \l__datatool_datum_currency_tl \seq_clear:N \l__datatool_tmpb_seq \seq_map_inline:Nn \l__datatool_tmp_seq { \int_incr:N \l__datatool_count_int % \end{macrocode} % Save previous datum information: % \begin{macrocode} \int_set_eq:NN \l__datatool_tmp_datatype_int \@dtl@datatype \tl_set_eq:NN \l__datatool_tmp_currency_tl \l__datatool_datum_currency_tl % \end{macrocode} %Convert: % \begin{macrocode} \DTLconverttodecimal { ##1 } \l__datatool_resulta_tl \__datatool_update_datatype: \seq_put_right:No \l__datatool_tmpb_seq { \l__datatool_resulta_tl } \fp_add:Nn \l__datatool_total_fp { \l__datatool_resulta_tl } } \int_if_zero:nTF { \l__datatool_count_int } { \PackageWarning { datatool-base } { empty ~ list ~ ` #2 ' ~ found ~ in ~ aggregate ~ function } \tl_set_eq:NN #1 \DTLnumbernull } { \fp_set:Nn \l__datatool_mean_fp { \l__datatool_total_fp / \l__datatool_count_int } \fp_zero:N \l__datatool_tmpa_fp \seq_map_inline:Nn \l__datatool_tmpb_seq { \fp_set:Nn \l__datatool_tmpb_fp { ##1 - \l__datatool_mean_fp } \fp_add:Nn \l__datatool_tmpa_fp { \l__datatool_tmpb_fp * \l__datatool_tmpb_fp } } \fp_set:Nn \l__datatool_tmpa_fp { sqrt ( \l__datatool_tmpa_fp / \l__datatool_count_int ) } % \end{macrocode} %\changes{3.1}{2025-03-10}{changed \cs{tl\_set:Nx} to \cs{tl\_set:Ne}} % \begin{macrocode} \tl_set:Ne \l__datatool_result_tl { \fp_use:N \l__datatool_tmpa_fp } \__datatool_assign_result:N #1 } } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgsdforall} %\begin{definition} %\cs{DTLgsdforall}\marg{cmd}\marg{num list} %\end{definition} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgsdforall { m m } { \DTLsdforall { \l__datatool_resultb_tl } { #2 } \tl_gset_eq:NN #1 \l__datatool_resultb_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLround} %\begin{definition} % \cs{DTLround}\marg{cmd}\marg{num}\marg{num digits} %\end{definition} % Sets \meta{cmd} to \meta{num} rounded to \meta{num digits} % digits after the decimal character. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLround { m m m } { \DTLconverttodecimal{#2} \l__datatool_result_tl \dtlround { \l__datatool_result_tl } { \l__datatool_result_tl } { #3 } \__datatool_assign_result:N #1 } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLground} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLground { m m m } { \DTLround { \l__datatool_resulta_tl } { #2 } { #3 } \tl_gset_eq:NN #1 \l__datatool_resulta_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLtrunc} %\begin{definition} % \cs{DTLtrunc}\marg{cmd}\marg{num}\marg{num digits} %\end{definition} % Sets \meta{cmd} to \meta{num} truncated to \meta{num digits} % digits after the decimal character. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLtrunc { m m m } { \DTLconverttodecimal { #2 } \l__datatool_result_tl \dtltrunc { \l__datatool_result_tl } { \l__datatool_result_tl } { #3 } \__datatool_assign_result:N #1 } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgtrunc} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgtrunc { m m m } { \DTLtrunc{ \l__datatool_resulta_tl } {#2} {#3} \tl_gset_eq:NN #1 \l__datatool_resulta_tl } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLclip} %\begin{definition} % \cs{DTLclip}\marg{cmd}\marg{num} %\end{definition} % Sets \meta{cmd} to \meta{num} with all unnecessary 0's removed. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLclip { m m } { \DTLconverttodecimal{#2} \l__datatool_result_tl \dtlclip { \l__datatool_result_tl } { \l__datatool_result_tl } \__datatool_assign_result:N #1 } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLgclip} % Global version %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLgclip { m m } { \DTLclip{ \l__datatool_resulta_tl } {#2} \tl_gset_eq:NN #1 \l__datatool_resulta_tl } % \end{macrocode} %\end{macro} % %\subsection{String Macros} % %\begin{macro}{\DTLCurrentLocaleGetInitialLetter} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLCurrentLocaleGetInitialLetter}[2]{% \datatool_get_first_letter:nN { #1 } #2 } % \end{macrocode} %\end{macro} % % \begin{macrocode} \regex_new:N \l__datatool_initial_cs_regex \regex_set:Nn \l__datatool_initial_cs_regex { \A \c{.+} \cB(.) } % \end{macrocode} % %\begin{macro}{\DTLGetInitialLetter} %\changes{3.0}{2025-03-03}{new} %Designed to obtain the first letter for initials or letter groups. %This can't simply grab the first token as it may be a multi-byte %character or may start with a command. % \begin{macrocode} \NewDocumentCommand{\DTLGetInitialLetter} { m m } { \bool_if:NTF \l__datatool_initial_purify_early_bool { \tl_if_head_is_group:nTF { #1 } { \tl_set:Nx #2 { \text_purify:n { \tl_head:n { #1 } } } } { \exp_args:Nx \__datatool_get_initial_letter:nN { \text_purify:n { #1 } } #2 } } { \__datatool_get_initial_letter:nN { #1 } #2 } } \cs_new:Nn \__datatool_get_initial_letter:nN { \tl_if_blank:nTF { #1 } { \tl_clear:N #2 } { \tl_if_head_is_group:nTF { #1 } { \tl_set:Nx #2 { \tl_head:n { #1 } } \tl_set:Nx #2 { \text_purify:n { #2 } } } { \regex_match:NnTF \l__datatool_initial_cs_regex { #1 } { \tl_set:Nx #2 { \tl_tail:n { #1 } } \exp_args:NNx \tl_set:Nx #2 { \exp_args:No \tl_head:n { #2 } } \exp_args:No \DTLCurrentLocaleGetInitialLetter { #2 } { #2 } \tl_set:Nx #2 { \tl_head:n { #1 } { \exp_not:o { #2 } } } } { \DTLCurrentLocaleGetInitialLetter { #1 } { #2 } } } } } % \end{macrocode} %\end{macro} %Get the first grapheme (which may be letter or punctuation): % \begin{macrocode} \cs_new:Nn \datatool_get_first_grapheme:nN { \tl_clear:N #2 \exp_args:Nx \text_map_inline:nn { \text_purify:n { #1 } } { \tl_set:Nn #2 { ##1 } \text_map_break: } } % \end{macrocode} % %Getting an initial letter but skipping leading punctuation is awkward %because \verb|[:alpha:]| only matches ASCII letters. Need to also %allow for the fact that UTF-8 characters are %actually an active character with one or more arguments with %\sty{inputenc} and it's also necessary to allow for %control characters that are used to adjust sorting which have been %given a letter character code. % %So the following first tests if the category code is a letter (which will be %the case for UTF-8 letters with \XeLaTeX\ and \LuaLaTeX\ but not %with \pdfLaTeX). If not a letter category code, then the next part %is a bit of a hack and is based on the assumption that all %letters have a different upper and lower representation. The first %argument should be a single grapheme (which may be a multi-byte %character). It won't work if the first argument consists of a %mixture of letters and non-letters. % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_letter:n #1 { T, F, TF } { \tl_if_head_eq_catcode:nNTF { #1 } \c_catcode_letter_token { \prg_return_true: } { \exp_args:Nee \tl_if_eq:nnTF { \text_uppercase:n { #1 } } { \text_lowercase:n { #1 } } { \prg_return_false: } { \prg_return_true: } } } % \end{macrocode} %Get the first letter (skipping any preceding non-letters). % \begin{macrocode} \cs_new:Nn \datatool_get_first_letter:nN { \tl_clear:N #2 \exp_args:Nx \text_map_inline:nn { \text_purify:n { #1 } } { \datatool_if_letter:nT { ##1 } { \tl_set:Nn #2 { ##1 } \text_map_break: } } } % \end{macrocode} % %Convert a string into a sequence of words. %NB \verb|[:alpha:]| only matches ASCII letters (see above). %Multi-byte characters match \verb|[:punct:]|. % \begin{macrocode} \regex_new:N \l_datatool_word_head_regex \regex_set:Nn \l_datatool_word_head_regex { ([[:punct:][:digit:]]*) ([^[:punct:][:digit:][:space:]\~]+?) ([[:punct:][:digit:]]*) ((?:[[:space:]\~]|\c{protect}?\c{(?:nobreak)?space\ ?})+) } \regex_set:Nn \l_datatool_word_hyphen_regex { ([[:punct:][:digit:]]*) ([^[:punct:][:digit:][:space:]\~]+?) ([[:punct:][:digit:]]*) -{1} } % \end{macrocode} % % \begin{macrocode} \regex_set:Nn \l_datatool_word_apos_regex { ([[:punct:][:digit:]]*) ([^[:punct:][:digit:][:space:]\~]+) \ur{l_datatool_apos_regex} ( [^[:punct:][:digit:][:space:]\~]+ (?:\ur{l_datatool_apos_regex}[^[:punct:][:digit:][:space:]\~]+)* ) ([[:punct:][:digit:]]*) ((?:[[:space:]\~]|\c{protect}?\c{(?:nobreak)?space\ ?})+) } \regex_set:Nn \l_datatool_word_apos_hyphen_regex { ([[:punct:][:digit:]]*) ([^[:punct:][:digit:][:space:]\~]+) \ur{l_datatool_apos_regex} ( [^[:punct:][:digit:][:space:]\~]+ (?:\ur{l_datatool_apos_regex}[^[:punct:][:digit:][:space:]\~]+)* ) ([[:punct:][:digit:]]*) -{1} } \regex_set:Nn \l_datatool_word_apos_tail_regex { ([[:punct:][:digit:]]*) ([^[:punct:][:digit:][:space:]\~]+) \ur{l_datatool_apos_regex} ( [^[:punct:][:digit:][:space:]\~]+ (?:\ur{l_datatool_apos_regex}[^[:punct:][:digit:][:space:]\~]+)* ) ([[:punct:][:digit:]]*) \Z } \regex_set:Nn \l_datatool_word_tail_regex { ([[:punct:][:digit:]]*) ([^[:punct:][:digit:][:space:]\~]+) ([[:punct:][:digit:]]*) \Z } \regex_set:Nn \l_datatool_symbols_regex { ((?:[!-@\[\]\^\_`\{\|\}]|\c{[[:alpha:][:punct:]]+})+?) ((?:[[:space:]\-\~]|\c{(?:nobreak)?space})+) } \regex_set:Nn \l_datatool_other_regex { (.+?) ((?:[[:space:]\-\~]|\c{(?:nobreak)?space})+) } \cs_new:Nn \__datatool_leading_punc:n { \exp_not:n { #1 } } \cs_new:Nn \__datatool_trailing_punc:n { \exp_not:n { #1 } } \cs_new:Nn \__datatool_word:n { \exp_not:n { #1 } } \cs_new:Nn \__datatool_word_hyphen:n { \exp_not:n { #1 } } \cs_new:Nn \__datatool_word_apos:nn { \exp_not:n { #1 } ' \exp_not:n { #2 } } \cs_new:Nn \__datatool_word_apos_hyphen:nn { \exp_not:n { #1 } ' \exp_not:n { #2 } } \cs_new:Nn \__datatool_last_word_apos:nn { \exp_not:n { #1 } ' \exp_not:n { #2 } } \cs_new:Nn \__datatool_last_word:n { \exp_not:n { #1 } } \cs_new:Nn \__datatool_word_sep:n { \exp_not:n { #1 } } \cs_new:Nn \__datatool_hyphen_sep: { - } \cs_new:Nn \__datatool_symbol:n { \exp_not:n { #1 } } \cs_new:Nn \datatool_parse_words:N { \exp_args:NNo \datatool_parse_words:Nn #1 { #1 } } \cs_new:Nn \datatool_parse_words:Nn { \tl_set:Nx #1 { \tl_trim_spaces:n { #2 } } \regex_replace_case_all:nN { \l_datatool_word_head_regex { \c{__datatool_leading_punc:n}{\1} \c{__datatool_word:n}{\2} \c{__datatool_trailing_punc:n}{\3} \c{__datatool_word_sep:n}{\4} } \l_datatool_word_apos_hyphen_regex { \c{__datatool_leading_punc:n}{\1} \c{__datatool_word_apos_hyphen:nn}{\2}{\3} \c{__datatool_trailing_punc:n}{\4} \c{__datatool_hyphen_sep:n} } \l_datatool_word_hyphen_regex { \c{__datatool_leading_punc:n}{\1} \c{__datatool_word_hyphen:n}{\2} \c{__datatool_trailing_punc:n}{\3} \c{__datatool_hyphen_sep:n} } \l_datatool_word_apos_regex { \c{__datatool_leading_punc:n}{\1} \c{__datatool_word_apos:nn}{\2}{\3} \c{__datatool_trailing_punc:n}{\4} \c{__datatool_word_sep:n}{\5} } \l_datatool_word_apos_tail_regex { \c{__datatool_leading_punc:n}{\1} \c{__datatool_last_word_apos:nn}{\2}{\3} \c{__datatool_trailing_punc:n}{\4} } \l_datatool_word_tail_regex { \c{__datatool_leading_punc:n}{\1} \c{__datatool_last_word:n}{\2} \c{__datatool_trailing_punc:n}{\3} } \l_datatool_symbols_regex { \c{__datatool_symbol:n}{\1} \c{__datatool_word_sep:n}{\4} } \l_datatool_other_regex { \c{__datatool_symbol:n}{\1} \c{__datatool_word_sep:n}{\4} } } #1 } % \end{macrocode} % %\begin{macro}{\DTLinitials} %\begin{definition} %\cs{DTLinitials}\marg{string} %\end{definition} % Convert a string into initials. % (Any "~" character found is first converted into a space.) % As from v3.0 this just uses \cs{DTLstoreinitials} with a % temporary command. The extra grouping probably isn't necessary. %\changes{1.01}{2007 Aug 17}{now works with unbreakable space symbol} %\changes{1.01}{2007 Aug 17}{now uses \cs{DTLinitialhyphen}} %\changes{3.0}{2025-03-03}{changed to new document command} % \begin{macrocode} \NewDocumentCommand \DTLinitials { m } { \group_begin: \DTLstoreinitials { #1 } { \l__datatool_tmpb_tl } \l__datatool_tmpb_tl \group_end: } % \end{macrocode} %\end{macro} % %\begin{macro}{\xDTLinitials} %\begin{definition} %\cs{xDTLinitials}\marg{cmd} %\end{definition} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand \xDTLinitials { m } { \exp_args:No \DTLinitials { #1 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLstoreinitials} %\begin{definition} %\cs{DTLstoreinitials}\marg{string}\marg{cmd} %\end{definition} % Convert a string into initials and store in \meta{cmd}. % (Any "~" character found is first converted into a space.) %\changes{1.01}{2007 Aug 17}{now works with unbreakable space symbol} %\changes{1.01}{2007 Aug 17}{now uses \cs{DTLinitialhyphen}} %\changes{3.0}{2025-03-03}{changed to new document command} % \begin{macrocode} \NewDocumentCommand \DTLstoreinitials { m m } { \tl_clear:N #2 \tl_if_empty:nF { #1 } { \datatool_parse_words:Nn \l__datatool_tmpa_tl { #1 } \tl_put_right:Nn \l__datatool_tmpa_tl { \q_recursion_tail } \__datatool_store_initials:N #2 \q_recursion_stop } } \cs_new:Nn \__datatool_store_initials:N { \tl_set:Nx \l__datatool_tmp_initial_tl { \tl_head:N \l__datatool_tmpa_tl } \tl_set:Nx \l__datatool_tmpa_tl { \tl_tail:N \l__datatool_tmpa_tl } \tl_if_eq:NnTF \l__datatool_tmp_initial_tl { \__datatool_word:n } { \tl_set:Nx \l__datatool_tmp_initial_tl { \tl_head:N \l__datatool_tmpa_tl } \tl_set:Nx \l__datatool_tmpa_tl { \tl_tail:N \l__datatool_tmpa_tl } \exp_args:No \DTLStoreInitialGetLetter { \l__datatool_tmp_initial_tl } \l__datatool_tmp_initial_tl \tl_if_empty:NF \l__datatool_tmp_initial_tl { \tl_put_right:Nn #1 { \DTLinitialpunc } \tl_put_right:Nx #1 { { \l__datatool_tmp_initial_tl } } \tl_put_right:Nn #1 { { \DTLbetweeninitials } } } } { \tl_if_eq:NnTF \l__datatool_tmp_initial_tl { \__datatool_last_word:n } { \tl_set:Nx \l__datatool_tmp_initial_tl { \tl_head:N \l__datatool_tmpa_tl } \tl_set:Nx \l__datatool_tmpa_tl { \tl_tail:N \l__datatool_tmpa_tl } \exp_args:No \DTLStoreInitialGetLetter { \l__datatool_tmp_initial_tl } \l__datatool_tmp_initial_tl \tl_if_empty:NF \l__datatool_tmp_initial_tl { \tl_put_right:Nn #1 { \DTLinitialpunc } \tl_put_right:Nx #1 { { \l__datatool_tmp_initial_tl } } \tl_put_right:Nn #1 { { \DTLafterinitials } } } } { \tl_if_eq:NnTF \l__datatool_tmp_initial_tl { \__datatool_word_hyphen:n } { \tl_set:Nx \l__datatool_tmp_initial_tl { \tl_head:N \l__datatool_tmpa_tl } \tl_set:Nx \l__datatool_tmpa_tl { \tl_tail:N \l__datatool_tmpa_tl } \exp_args:No \DTLStoreInitialGetLetter { \l__datatool_tmp_initial_tl } \l__datatool_tmp_initial_tl \tl_if_empty:NF \l__datatool_tmp_initial_tl { \tl_put_right:Nn #1 { \DTLinitialpunc } \tl_put_right:Nx #1 { { \l__datatool_tmp_initial_tl } } \tl_put_right:Nn #1 { { \DTLafterinitialbeforehyphen } } \tl_put_right:Nn #1 { \DTLinitialhyphen } } } { \tl_if_eq:NnTF \l__datatool_tmp_initial_tl { \__datatool_word_apos:nn } { \tl_set:Nx \l__datatool_tmp_initial_tl { \tl_head:N \l__datatool_tmpa_tl } \tl_set:Nx \l__datatool_tmpa_tl { \tl_tail:N \l__datatool_tmpa_tl } \exp_args:No \DTLStoreInitialGetLetter { \l__datatool_tmp_initial_tl } \l__datatool_tmp_initial_tl \tl_if_empty:NTF \l__datatool_tmp_initial_tl { \tl_set:Nx \l__datatool_tmp_initial_tl { \tl_head:N \l__datatool_tmpa_tl } \tl_set:Nx \l__datatool_tmpa_tl { \tl_tail:N \l__datatool_tmpa_tl } \exp_args:No \DTLStoreInitialGetLetter { \l__datatool_tmp_initial_tl } \l__datatool_tmp_initial_tl \tl_if_empty:NF \l__datatool_tmp_initial_tl { \tl_put_right:Nn #1 { \DTLinitialpunc } \tl_put_right:Nx #1 { { \l__datatool_tmp_initial_tl } } \tl_put_right:Nn #1 { { \DTLbetweeninitials } } } } { \tl_put_right:Nn #1 { \DTLaposinitialpunc } \tl_put_right:Nx #1 { { \l__datatool_tmp_initial_tl } } \tl_set:Nx \l__datatool_tmp_initial_tl { \tl_head:N \l__datatool_tmpa_tl } \tl_set:Nx \l__datatool_tmpa_tl { \tl_tail:N \l__datatool_tmpa_tl } \exp_args:No \DTLStoreInitialGetLetter { \l__datatool_tmp_initial_tl } \l__datatool_tmp_initial_tl \tl_put_right:Nx #1 { { \l__datatool_tmp_initial_tl } } \tl_put_right:Nn #1 { { \DTLbetweeninitials } } } } { \tl_if_eq:NnTF \l__datatool_tmp_initial_tl { \__datatool_word_apos_hyphen:nn } { \tl_set:Nx \l__datatool_tmp_initial_tl { \tl_head:N \l__datatool_tmpa_tl } \tl_set:Nx \l__datatool_tmpa_tl { \tl_tail:N \l__datatool_tmpa_tl } \exp_args:No \DTLStoreInitialGetLetter { \l__datatool_tmp_initial_tl } \l__datatool_tmp_initial_tl \tl_if_empty:NTF \l__datatool_tmp_initial_tl { \tl_set:Nx \l__datatool_tmp_initial_tl { \tl_head:N \l__datatool_tmpa_tl } \tl_set:Nx \l__datatool_tmpa_tl { \tl_tail:N \l__datatool_tmpa_tl } \exp_args:No \DTLStoreInitialGetLetter { \l__datatool_tmp_initial_tl } \l__datatool_tmp_initial_tl \tl_if_empty:NF \l__datatool_tmp_initial_tl { \tl_put_right:Nn #1 { \DTLinitialpunc } \tl_put_right:Nx #1 { { \l__datatool_tmp_initial_tl } } \tl_put_right:Nn #1 { { \DTLafterinitialbeforehyphen } } \tl_put_right:Nn #1 { \DTLinitialhyphen } } } { \tl_put_right:Nn #1 { \DTLaposinitialpunc } \tl_put_right:Nx #1 { { \l__datatool_tmp_initial_tl } } \tl_set:Nx \l__datatool_tmp_initial_tl { \tl_head:N \l__datatool_tmpa_tl } \tl_set:Nx \l__datatool_tmpa_tl { \tl_tail:N \l__datatool_tmpa_tl } \exp_args:No \DTLStoreInitialGetLetter { \l__datatool_tmp_initial_tl } \l__datatool_tmp_initial_tl \tl_put_right:Nx #1 { { \l__datatool_tmp_initial_tl } } \tl_put_right:Nn #1 { { \DTLafterinitialbeforehyphen } } \tl_put_right:Nn #1 { \DTLinitialhyphen } } } { \tl_if_eq:NnT \l__datatool_tmp_initial_tl { \__datatool_last_word_apos:nn } { \tl_set:Nx \l__datatool_tmp_initial_tl { \tl_head:N \l__datatool_tmpa_tl } \tl_set:Nx \l__datatool_tmpa_tl { \tl_tail:N \l__datatool_tmpa_tl } \exp_args:No \DTLStoreInitialGetLetter { \l__datatool_tmp_initial_tl } \l__datatool_tmp_initial_tl \tl_if_empty:NTF \l__datatool_tmp_initial_tl { \tl_set:Nx \l__datatool_tmp_initial_tl { \tl_head:N \l__datatool_tmpa_tl } \tl_set:Nx \l__datatool_tmpa_tl { \tl_tail:N \l__datatool_tmpa_tl } \exp_args:No \DTLStoreInitialGetLetter { \l__datatool_tmp_initial_tl } \l__datatool_tmp_initial_tl \tl_if_empty:NF \l__datatool_tmp_initial_tl { \tl_put_right:Nn #1 { \DTLinitialpunc } \tl_put_right:Nx #1 { { \l__datatool_tmp_initial_tl } } \tl_put_right:Nn #1 { { \DTLafterinitials } } } } { \tl_put_right:Nn #1 { \DTLaposinitialpunc } \tl_put_right:Nx #1 { { \l__datatool_tmp_initial_tl } } \tl_set:Nx \l__datatool_tmp_initial_tl { \tl_head:N \l__datatool_tmpa_tl } \tl_set:Nx \l__datatool_tmpa_tl { \tl_tail:N \l__datatool_tmpa_tl } \exp_args:No \DTLStoreInitialGetLetter { \l__datatool_tmp_initial_tl } \l__datatool_tmp_initial_tl \tl_put_right:Nx #1 { { \l__datatool_tmp_initial_tl } } \tl_put_right:Nn #1 { { \DTLafterinitials } } } } } } } } } \quark_if_recursion_tail_stop:N \l__datatool_tmpa_tl \__datatool_store_initials:N #1 } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLStoreInitialGetLetter} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLStoreInitialGetLetter}[2]{\DTLGetInitialLetter{#1}{#2}} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLafterinitials} % Defines what to do after the final initial. % \begin{macrocode} \newcommand*{\DTLafterinitials}{.} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLbetweeninitials} % Defines what to do between initials. % \begin{macrocode} \newcommand*{\DTLbetweeninitials}{.} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLafterinitialbeforehyphen} % Defines what to do before a hyphen. % \begin{macrocode} \newcommand*{\DTLafterinitialbeforehyphen}{.} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLinitialhyphen} %Defines what to do at the hyphen %\changes{1.01}{2007 Aug 17}{new} % \begin{macrocode} \newcommand*{\DTLinitialhyphen}{-} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLinitialpunc} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLinitialpunc}[2]{#1#2} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLaposinitialpunc} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLaposinitialpunc}[3]{#1#3} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifAllUpperCase} %\begin{definition} %\cs{DTLifAllUpperCase}\marg{string}\marg{true part}\marg{false part} %\end{definition} % If \meta{string} only contains uppercase characters do \meta{true %part}, otherwise do \meta{false part}. This needs to take UTF-8 %characters into account. NB regular expression "lower" and "upper" character %classes doesn't match UTF-8 characters (regardless of the engine). %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\DTLifAllUpperCase}[3]{% \tl_set:Nx \l__datatool_tmpa_tl { \text_purify:n { #1 } } \tl_set:Nx \l__datatool_tmpb_tl { \text_uppercase:n { \l__datatool_tmpa_tl } } \tl_if_eq:NNTF \l__datatool_tmpa_tl \l__datatool_tmpb_tl { #2 } { #3 } } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLifAllLowerCase} %\begin{definition} %\cs{DTLifAllLowerCase}\marg{string}\marg{true part}\marg{false part} %\end{definition} % If \meta{string} only contains lowercase characters do \meta{true %part}, otherwise do \meta{false part}. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\DTLifAllLowerCase}[3]{% \tl_set:Nx \l__datatool_tmpa_tl { \text_purify:n { #1 } } \tl_set:Nx \l__datatool_tmpb_tl { \text_lowercase:n { \l__datatool_tmpa_tl } } \tl_if_eq:NNTF \l__datatool_tmpa_tl \l__datatool_tmpb_tl { #2 } { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLsubstitute} %\begin{definition} %\cs{DTLsubstitute}\marg{cmd}\marg{original}\marg{replacement} %\end{definition} % Substitutes first occurrence of \meta{original} with % \marg{replacement} within the string given by \meta{cmd} %\changes{3.0}{2025-03-03}{rewritten in \LaTeX3} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd{\DTLsubstitute}[3]{% \tl_replace_once:Nnn #1 { #2 } { #3 } } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLsplitstring} %\begin{definition} %\cs{DTLsplitstring}\marg{string}\marg{split text}\marg{before %cmd}\marg{after cmd} %\end{definition} % Splits string at \meta{split text} stores the pre split text % in \meta{before cmd} and the post split text in \meta{after cmd}. %\changes{1.01}{2007 Aug 17}{new} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\DTLsplitstring}[4]{% \def\dtl@splitstr##1#2##2\@nil{% \def#3{##1}% \def#4{##2}% \ifdefempty{#4}% {% \let\@dtl@replaced=\@empty }% {% \def\@dtl@replaced{#2}% \dtl@split@str##2\@nil }% }% \def\dtl@split@str##1#2\@nil{\def#4{##1}}% \dtl@splitstr#1#2\@nil } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLxsplitstring} %\begin{definition} %\cs{DTLxsplitstring}\marg{string}\marg{split text}\marg{before %cmd}\marg{after cmd} %\end{definition} %As above but expands the first two arguments %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newrobustcmd*{\DTLxsplitstring}[2]{% \exp_args:Noo \DTLsplitstring { #1 } { #2 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLsubstituteall} %\begin{definition} %\cs{DTLsubstituteall}\marg{cmd}\marg{original}\marg{replacement} %\end{definition} % Substitutes all occurrences of \meta{original} with % \marg{replacement} within the string given by \meta{cmd} %\changes{1.01}{2007 Aug 17}{fixed bug caused when certain commands % occur in the string} %\changes{2.10}{2012-07-18}{added \cs{long}} %\changes{3.0}{2025-03-03}{rewritten in \LaTeX3} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd{\DTLsubstituteall}[3]{% \tl_replace_all:Nnn #1 { #2 } { #3 } } % \end{macrocode} %\end{macro} % %\subsection{Conditionals} % %\begin{macro}{\if@dtl@condition} % \begin{macrocode} \newif\if@dtl@condition % \end{macrocode} %\end{macro} % \subsubsection{Testing for a prefix} % \begin{macrocode} % \end{macrocode} %The following are used when splitting content. % \begin{macrocode} \tl_new:N \l__datatool_prefix_tl \tl_new:N \l__datatool_suffix_tl % \end{macrocode} %Use to store the length: % \begin{macrocode} \int_new:N \l__datatool_prefix_int \int_new:N \l__datatool_suffix_int % \end{macrocode} % %Used to test for currency symbol at either end, but %consider the symbol the `prefix' and the rest the `suffix' %regardless of which way round they are. %The first argument is the token list the second is the possible %prefix or suffix. % \begin{macrocode} \prg_new_conditional:Npnn \__datatool_if_starts_or_ends_with:nn #1 #2 { T, F, TF} { \tl_clear:N \l__datatool_prefix_tl \tl_clear:N \l__datatool_suffix_tl \tl_if_eq:nnTF { #1 } { #2 } { \tl_set:Nn \l__datatool_prefix_tl { #2 } \prg_return_true: } { \int_set:Nn \l__datatool_prefix_int { \tl_count:n { #2 } } \int_set:Nn \l__datatool_suffix_int { \tl_count:n { #1 } } \int_compare:nNnTF { \l__datatool_suffix_int } > { \l__datatool_prefix_int } { \tl_if_eq:enTF { \tl_range:nnn { #1 } { \c_one_int } { \l__datatool_prefix_int } } { #2 } { \tl_set:Nn \l__datatool_prefix_tl { #2 } \tl_set:Ne \l__datatool_suffix_tl { \tl_range:nnn { #1 } { \l__datatool_prefix_int + \c_one_int } { \l__datatool_suffix_int } } \prg_return_true: } { \int_sub:Nn \l__datatool_suffix_int { \l__datatool_prefix_int } \tl_if_eq:enTF { \tl_range:nnn { #1 } { \l__datatool_suffix_int + \c_one_int } { - \c_one_int } } { #2 } { \tl_set:Nn \l__datatool_prefix_tl { #2 } \tl_set:Ne \l__datatool_suffix_tl { \tl_range:nnn { #1 } { \c_one_int } { \l__datatool_suffix_int } } \prg_return_true: } { \prg_return_false: } } } { \prg_return_false: } } } \cs_generate_variant:Nn \__datatool_if_starts_or_ends_with:nnTF { VvTF , VeTF, VnTF } % \end{macrocode} % % The following tests if the token list in the first argument starts with the % tokens in the second argument (without taking the category code % into account). If true, the prefix token list will % contain the tokens in the second argument (the prefix) and the % suffix token list will contain the remaining tokens. If false the % prefix token list will be empty and the suffix will contain all % the tokens in the first argument. % \begin{macrocode} \cs_new:Nn \__datatool_if_starts_with:NnTF { \exp_args:NV \__datatool_if_starts_with:nnTF #1 { #2 } { #3 } { #4 } } \cs_new:Nn \__datatool_if_starts_with:nnTF { % \end{macrocode} %Initialise the prefix to an empty list and the suffix to the %token list under examination. % \begin{macrocode} \tl_clear:N \l__datatool_prefix_tl \tl_set:Nn \l__datatool_suffix_tl { #1 } \tl_set:Nn \l__datatool_tmpa_tl { #1 \q_recursion_tail } \tl_set:Nn \l__datatool_tmpb_tl { #2 \q_recursion_tail } \__datatool_if_starts_with: \q_recursion_stop \__datatool_result:nn { #3 } { #4 } } \cs_new:Nn \__datatool_if_starts_with: { \exp_args:No \tl_if_head_is_space:nTF { \l__datatool_tmpa_tl } { \tl_set:Nn \l__datatool_tmpc_tl { ~ } \tl_set:Nx \l__datatool_tmpa_tl { \tl_head:N \l__datatool_tmpa_tl \tl_tail:N \l__datatool_tmpa_tl } } { \tl_set:Nx \l__datatool_tmpc_tl { \tl_head:N \l__datatool_tmpa_tl } \tl_set:Nx \l__datatool_tmpa_tl { \tl_tail:N \l__datatool_tmpa_tl } } \exp_args:No \tl_if_head_is_space:nTF { \l__datatool_tmpb_tl } { \tl_set:Nn \l__datatool_tmpd_tl { ~ } \tl_set:Nx \l__datatool_tmpb_tl { \tl_head:N \l__datatool_tmpb_tl \tl_tail:N \l__datatool_tmpb_tl } } { \tl_set:Nx \l__datatool_tmpd_tl { \tl_head:N \l__datatool_tmpb_tl } \tl_set:Nx \l__datatool_tmpb_tl { \tl_tail:N \l__datatool_tmpb_tl } } \cs_set:Nn \__datatool_next: { } \if_meaning:w \q_recursion_tail \l__datatool_tmpd_tl \cs_set_eq:NN \__datatool_next: \__datatool_starts_with_true: \else \if_meaning:w \q_recursion_tail \l__datatool_tmpc_tl \cs_set_eq:NN \__datatool_next: \__datatool_starts_with_false: \else \str_if_eq:NNTF \l__datatool_tmpc_tl \l__datatool_tmpd_tl { \tl_put_right:No \l__datatool_prefix_tl { \l__datatool_tmpd_tl } } { \cs_set_eq:NN \__datatool_next: \__datatool_starts_with_false: } \fi \fi \__datatool_next: \__datatool_if_starts_with: } \cs_new:Nn \__datatool_starts_with_false: { \cs_set:Nn \__datatool_result:nn { ##2 } \tl_clear:N \l__datatool_prefix_tl \use_none_delimit_by_q_recursion_stop:w } \cs_new:Nn \__datatool_starts_with_true: { \cs_set:Nn \__datatool_result:nn { ##1 } \if_meaning:w \q_recursion_tail \l__datatool_tmpc_tl \tl_clear:N \l__datatool_suffix_tl \else \tl_set_eq:NN \l__datatool_suffix_tl \l__datatool_tmpc_tl \exp_after:wN \__datatool_suffix_tail:w \l__datatool_tmpa_tl \fi \use_none_delimit_by_q_recursion_stop:w } \cs_new:Npn \__datatool_suffix_tail:w #1\q_recursion_tail { \tl_put_right:Nn \l__datatool_suffix_tl { #1 } } \cs_new:Npn \__datatool_if_starts_with:NnT #1#2#3 { \__datatool_if_starts_with:NnTF #1 { #2 } { #3 } { } } % \end{macrocode} % %Retain original internal command for checking if an argument is %numerical but updated to use new parsing. %\begin{macro}{\@dtl@checknumerical} %\begin{definition} %\cs{@dtl@checknumerical}\marg{arg} %\end{definition} % Checks if \meta{arg} is numerical % (includes decimal numbers, but not scientific notation.) % Sets \cs{@dtl@datatype}. % \begin{macrocode} \newcommand{\@dtl@checknumerical}[1]{% \tl_if_single_token:nTF { #1 } { \exp_args:No \__datatool_parse_datum:n { #1 } } { \__datatool_parse_datum:n { #1 }} } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifnumerical} %\begin{definition} %\cs{DTLifnumerical}\marg{arg}\marg{true part}\marg{false part} %\end{definition} % Tests the first argument, if % it's numerical do second argument, otherwise do third argument. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd{\DTLifnumerical}[3]{% \@dtl@checknumerical { #1 } \datatool_if_numeric_datum_type:nTF { \@dtl@datatype } { #2 } { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLiftemporal} %\begin{definition} %\cs{DTLiftemporal}\marg{arg}\marg{true part}\marg{false part} %\end{definition} % Tests the first argument, if % it's temporal do second argument, otherwise do third argument. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand \DTLiftemporal { m m m } { \tl_if_single_token:nTF { #1 } { \exp_args:No \__datatool_parse_datum:n { #1 } } { \__datatool_parse_datum:n { #1 }} \datatool_if_temporal_datum_type:nTF { \@dtl@datatype } { #2 } { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testbothnumerical} %\begin{definition} %\cs{dtl@testbothnumerical}\marg{arg1}\marg{arg2} %\end{definition} % Tests if both arguments are numerical. This sets % the conditional \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testbothnumerical}[2]{% \DTLifnumerical { #1 } { \DTLifnumerical { #2 } { \@dtl@conditiontrue } { \@dtl@conditionfalse } } { \@dtl@conditionfalse } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifreal} %\begin{definition} %\cs{DTLifreal}\marg{arg}\marg{true part}\marg{false part} %\end{definition} % Tests the first argument, if % it's a real number (not an integer or currency) do second argument, % otherwise do third argument. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd{\DTLifreal}[3]{% \tl_if_single_token:nTF { #1 } { \exp_args:No \__datatool_parse_datum:n { #1 } } { \__datatool_parse_datum:n { #1 }} \int_compare:nNnTF { \@dtl@datatype } = { \c_datatool_decimal_int } { #2 } { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifint} %\begin{definition} %\cs{DTLifint}\marg{arg}\marg{true part}\marg{false part} %\end{definition} % Tests the first argument, if % it's an integer do second argument, % otherwise do third argument. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd{\DTLifint}[3]{% \tl_if_single_token:nTF { #1 } { \exp_args:No \__datatool_parse_datum:n { #1 } } { \__datatool_parse_datum:n { #1 }} \int_compare:nNnTF { \@dtl@datatype } = { \c_datatool_integer_int } { #2 } { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifdatetime} %\begin{definition} %\cs{DTLifdatetime}\marg{arg}\marg{true part}\marg{false part} %\end{definition} % Tests the first argument, if % it's a timestamp (date and time) do second argument, % otherwise do third argument. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand \DTLiftimestamp { m m m } { \tl_if_single_token:nTF { #1 } { \exp_args:No \__datatool_parse_datum:n { #1 } } { \__datatool_parse_datum:n { #1 }} \int_compare:nNnTF { \@dtl@datatype } = { \c_datatool_datetime_int } { #2 } { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifdate} %\begin{definition} %\cs{DTLifdate}\marg{arg}\marg{true part}\marg{false part} %\end{definition} % Tests the first argument, if % it's a date (no time) do second argument, % otherwise do third argument. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand \DTLifdate { m m m } { \tl_if_single_token:nTF { #1 } { \exp_args:No \__datatool_parse_datum:n { #1 } } { \__datatool_parse_datum:n { #1 }} \int_compare:nNnTF { \@dtl@datatype } = { \c_datatool_date_int } { #2 } { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLiftime} %\begin{definition} %\cs{DTLiftime}\marg{arg}\marg{true part}\marg{false part} %\end{definition} % Tests the first argument, if % it's a time (no date) do second argument, % otherwise do third argument. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand \DTLiftime { m m m } { \tl_if_single_token:nTF { #1 } { \exp_args:No \__datatool_parse_datum:n { #1 } } { \__datatool_parse_datum:n { #1 }} \int_compare:nNnTF { \@dtl@datatype } = { \c_datatool_time_int } { #2 } { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifstring} %\begin{definition} %\cs{DTLifstring}\marg{arg}\marg{true part}\marg{false part} %\end{definition} % Tests the first argument, if % it's a string do second argument, % otherwise do third argument. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd{\DTLifstring}[3]{% \tl_if_single_token:nTF { #1 } { \exp_args:No \__datatool_parse_datum:n { #1 } } { \__datatool_parse_datum:n { #1 }} \int_compare:nNnTF { \@dtl@datatype } = { \c_datatool_string_int } { #2 } { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifcurrency} %\begin{definition} %\cs{DTLifcurrency}\marg{arg}\marg{true part}\marg{false part} %\end{definition} % Tests the first argument, if it's currency do second argument, % otherwise do third argument. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd{\DTLifcurrency}[3]{% \tl_if_single_token:nTF { #1 } { \exp_args:No \__datatool_parse_datum:n { #1 } } { \__datatool_parse_datum:n { #1 }} \int_compare:nNnTF { \@dtl@datatype } = { \c_datatool_currency_int } { #2 } { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifcurrencyunit} %\begin{definition} %\cs{DTLifcurrencyunit}\marg{arg}\marg{symbol}\marg{true % part}\marg{false part} %\end{definition} % This tests if \meta{arg} is currency, and uses the currency unit % \meta{symbol}. If true do third argument, otherwise % do fourth argument. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\DTLifcurrencyunit}[4]{% \tl_if_single_token:nTF { #1 } { \exp_args:No \__datatool_parse_datum:n { #1 } } { \__datatool_parse_datum:n { #1 }} \ifnum\@dtl@datatype=\c_datatool_currency_int \tl_if_eq:NnTF \l__datatool_datum_currency_tl { #2 } { #3 } { \tl_if_eq:NnTF \l__datatool_datum_currency_tl { \@dtl@currency } { \tl_if_eq:NnTF \@dtl@currency { #2 } { #3 } { #4 } } { #4 } } \else #4 \fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifcasedatatype} %\begin{definition} %\cs{DTLifcasedatatype}\marg{arg}\marg{string case}\marg{int %case}\marg{real case}\marg{currency case} %\end{definition} % If \meta{arg} is a string, do \meta{string case}, if \meta{arg} % is an integer do \meta{int case}, if \meta{arg} is a real number, % do \meta{real case}, if \meta{arg} is currency, do \meta{currency %case}. Does nothing with other cases (unknown, or any %data types introduced in new versions, such as date/time). % %Deprecated as it doesn't allow for new types. %Note that it's more efficient to convert to a datum variable first %and use \cs{int\_case:nn} on the datum type (which can be obtained %with \cs{DTLdatumtype}). %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd{\DTLifcasedatatype}[5]{% \tl_if_single_token:nTF { #1 } { \exp_args:No \__datatool_parse_datum:n { #1 } } { \__datatool_parse_datum:n { #1 }} \ifcase\@dtl@datatype #2% string \or #3% integer \or #4% number \or #5% currency \fi } % \end{macrocode} %\end{macro} % %\subsubsection{Locale Numerical Comparisons} % % Parse two numerical values for comparison. Any non-numeric value % will be treated as zero. % \begin{macrocode} \cs_new:Nn \__datatool_parse_numbers_ii:nnNN { \tl_if_single_token:nTF { #1 } { \exp_args:No \__datatool_parse_datum:n { #1 } } { \__datatool_parse_datum:n { #1 }} \ifnum\@dtl@datatype > \c_datatool_string_int \tl_set_eq:NN #3 \l__datatool_datum_value_tl \else \tl_set:Nn #3 { 0 } \fi \int_set_eq:NN \l__datatool_tmp_datatype_int \@dtl@datatype \tl_if_single_token:nTF { #2 } { \exp_args:No \__datatool_parse_datum:n { #2 } } { \__datatool_parse_datum:n { #2 }} \ifnum\@dtl@datatype > \c_datatool_string_int \tl_set_eq:NN #4 \l__datatool_datum_value_tl \else \tl_set:Nn #4 { 0 } \fi % \end{macrocode} % Ensure that the dominant data type can be picked up afterwards. % \begin{macrocode} \int_compare:nNnT { \l__datatool_tmp_datatype_int } > { \@dtl@datatype } { \int_set_eq:NN \@dtl@datatype \l__datatool_tmp_datatype_int } } % \end{macrocode} % % Parse three numerical values. % \begin{macrocode} \cs_new:Nn \__datatool_parse_numbers_iii:nnnNNN { \__datatool_parse_numbers_ii:nnNN { #1 } { #2 } #4 #5 \tl_if_single_token:nTF { #3 } { \exp_args:No \__datatool_parse_datum:n { #3 } } { \__datatool_parse_datum:n { #3 }} \ifnum\@dtl@datatype > \c_datatool_string_int \tl_set_eq:NN #6 \l__datatool_datum_value_tl \else \tl_set:Nn #6 { 0 } \fi } % \end{macrocode} % %\begin{macro}{\DTLifnumlt} %\begin{definition} %\cs{DTLifnumlt}\marg{num1}\marg{num2}\marg{true part}\marg{false part} %\end{definition} % Determines if \marg{num1} $<$ \marg{num2}. The numeric values need % to be obtained to ensure that they work with \cs{dtlifnumlt}. %\changes{2.10}{2012-07-18}{changed \cs{FPiflt} to \cs{dtlifnumlt}} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\DTLifnumlt}[4]{% \__datatool_parse_numbers_ii:nnNN { #1 } { #2 } \@dtl@numi \@dtl@numii \dtlifnumlt{\@dtl@numi}{\@dtl@numii} { #3 } { #4 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifnumgt} %\begin{definition} %\cs{DTLifnumgt}\marg{num1}\marg{num2}\marg{true part}\marg{false part} %\end{definition} % Determines if \marg{num1} $>$ \marg{num2}. The numeric values need % to be obtained to ensure that they work with \cs{dtlifnumgt}. %\changes{2.10}{2012-07-18}{changed \cs{FPifgt} to \cs{dtlifnumgt}} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\DTLifnumgt}[4]{% \__datatool_parse_numbers_ii:nnNN { #1 } { #2 } \@dtl@numi \@dtl@numii \dtlifnumgt{\@dtl@numi}{\@dtl@numii} { #3 } { #4 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifnumeq} %\begin{definition} %\cs{DTLifnumeq}\marg{num1}\marg{num2}\marg{true part}\marg{false part} %\end{definition} % Determines if \marg{num1} = \marg{num2}. The numeric values need % to be obtained to ensure that they work with \cs{dtlifnumeq}. %\changes{2.10}{2012-07-18}{changed \cs{FPifeq} to \cs{dtlifnumeq}} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\DTLifnumeq}[4]{% \__datatool_parse_numbers_ii:nnNN { #1 } { #2 } \@dtl@numi \@dtl@numii \dtlifnumeq{\@dtl@numi}{\@dtl@numii} { #3 } { #4 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifnumclosedbetween} %\begin{definition} %\cs{DTLifnumclosedbetween}\marg{num}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % Determines if \meta{min} $\leq$ \meta{num} $\leq$ \meta{max}. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\DTLifnumclosedbetween}[5]{% \__datatool_parse_numbers_iii:nnnNNN { #1 } { #2 } { #3 } \@dtl@numi \@dtl@numii \@dtl@numiii \DTLifFPclosedbetween{\@dtl@numi}{\@dtl@numii}{\@dtl@numiii}{#4}{#5}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifnumopenbetween} %\begin{definition} %\cs{DTLifnumopenbetween}\marg{num}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % Determines if \meta{min} $<$ \meta{num} $<$ \meta{max}. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\DTLifnumopenbetween}[5]{% \__datatool_parse_numbers_iii:nnnNNN { #1 } { #2 } { #3 } \@dtl@numi \@dtl@numii \@dtl@numiii \DTLifFPopenbetween{\@dtl@numi}{\@dtl@numii}{\@dtl@numiii}{#4}{#5}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLnumcompare} %\begin{definition} %\cs{DTLnumcompare}\meta{int var}\marg{num1}\marg{num2} %\end{definition} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand \DTLnumcompare { m m m } { \__datatool_parse_numbers_ii:nnNN { #2 } { #3 } \@dtl@numi \@dtl@numii \int_compare:nNnTF { \@dtl@datatype } < { \c_datatool_decimal_int } { \int_compare:nNnTF { \@dtl@numi } < { \@dtl@numii } { \int_set:Nn #1 { -1 } } { \int_compare:nNnTF { \@dtl@numi } > { \@dtl@numii } { \int_set_eq:NN #1 \c_one_int } { \int_zero:N #1 } } } { \fp_compare:nNnTF { \@dtl@numi } < { \@dtl@numii } { \int_set:Nn #1 { -1 } } { \fp_compare:nNnTF { \@dtl@numi } > { \@dtl@numii } { \int_set_eq:NN #1 \c_one_int } { \int_zero:N #1 } } } } % \end{macrocode} %\end{macro} % %\subsubsection{String Comparisons} % %\label{src:dtlcompare} %\begin{macro}{\dtlcompare} %\begin{definition} %\cs{dtlcompare}\marg{count}\marg{string1}\marg{string2} %\end{definition} % Compares \meta{string1} and \meta{string2}, and stores the % result in the count register \meta{count}. The result may be % one of: %\par\vskip\baselineskip\noindent %\begin{tabular}{rl} %-1 & if \meta{string1} is considered to be less than %\meta{string2}\\ %0 & if \meta{string1} is considered to be the same as %\meta{string2}\\ %1 & if \meta{string1} is considered to be greater than %\meta{string2} %\end{tabular} %\par\vskip\baselineskip\noindent %Note that for the purposes of string comparisons, commands within %\meta{string1} and \meta{string2} are ignored, except for %\cs{space} and "~", which are both treated as a space (character %code 32.) The following %examples assume that the count register \cs{mycount} has been %defined as follows: %\begin{verbatim} %\newcount\mycount %\end{verbatim} %\newcount\mycount\par\noindent %\textbf{Examples:} %\begin{enumerate} %\item %\begin{verbatim} %\dtlcompare{\mycount}{Z\"oe}{Zoe}\number\mycount %\end{verbatim} %produces: %\dtlcompare{\mycount}{Z\"oe}{Zoe}\number\mycount, since the accent %command is ignored. % %\item %\begin{verbatim} %\dtlcompare{\mycount}{foo}{Foo}\number\mycount %\end{verbatim} %produces: %\dtlcompare{\mycount}{foo}{Foo}\number\mycount, since the comparison %is case sensitive, however, note the following example: %\item %\begin{verbatim} %\dtlcompare{\mycount}{foo}{\uppercase{f}oo}\number\mycount %\end{verbatim} %which produces: %\dtlcompare{\mycount}{foo}{\uppercase{f}oo}\number\mycount, since %the \cs{uppercase} command is ignored. % %\item A control sequence is treated as having the character code %value of 0. Pre version 2.32, \cs{dtlcompare} was advertised here %as skipping control sequences when actually it was treating a %control sequence as character 0. To avoid breaking %backward-compatibility where a control sequence is expected to have %this behaviour, we now have a switch to determine whether to treat %control sequences as 0 or to skip them. % %So you can now ``trick'' \cs{dtlcompare} using a command which doesn't %output any text if switch this conditional on. Suppose you have defined the following command: %\begin{verbatim} %\newcommand*{\noopsort}[1]{} %\end{verbatim} %\providecommand*{\noopsort}[1]{} %then "\noopsort{a}foo" produces the text: \noopsort{a}foo, however %the following %\begin{verbatim} %\dtlcompare{\mycount}{\noopsort{a}foo}{bar}\number\mycount %\end{verbatim} %produces: %\dtlcompare{\mycount}{\noopsort{a}foo}{bar}\number\mycount, %since the command \cs{noopsort} is disregarded when the comparison %is made, so \cs{dtlcompare} just compares "{a}foo" with "bar", and %since "a" is less than "b", the first string is considered to be less %than the second string. % %\item Note that this also means that: %\begin{verbatim} %\def\mystr{abc}% %\dtlcompare{\mycount}{\mystr}{abc}\number\mycount %\end{verbatim} %produces: %\def\mystr{abc}\relax %\dtlcompare{\mycount}{\mystr}{abc}\number\mycount, since the command %\cs{mystr} is disregarded, which means that \cs{dtlcompare} is %comparing an empty string with the string "abc". % %\item Spaces count in the comparison: %\begin{verbatim} %\dtlcompare{\mycount}{ab cd}{abcd}\number\mycount %\end{verbatim} %produces: %\dtlcompare{\mycount}{ab cd}{abcd}\number\mycount, %but sequential spaces are treated as a single space: %\begin{verbatim} %\dtlcompare{\mycount}{ab cd}{ab cd}\number\mycount %\end{verbatim} %produces: %\dtlcompare{\mycount}{ab cd}{ab cd}\number\mycount. % %\item As usual, spaces following command names are ignored, so %\begin{verbatim} %\dtlcompare{\mycount}{ab\relax cd}{ab cd}\number\mycount %\end{verbatim} %produces: %\dtlcompare{\mycount}{ab\relax cd}{ab cd}\number\mycount. % %\item "~" and \cs{space} are considered to be the same as a % space: %\begin{verbatim} %\dtlcompare{\mycount}{ab cd}{ab~cd}\number\mycount %\end{verbatim} %produces: %\dtlcompare{\mycount}{ab cd}{ab~cd}\number\mycount. %\end{enumerate} %\changes{1.01}{2007 Aug 17}{replaces \cs{compare} (no % longer using compare.tex)} % %To ensure backward-compatibility, this still needs to strip %commands so that the \cs{noop} example can continue to work, but %it's better to prepare the sort values first for sorting lists. % \begin{macrocode} \newcommand*{\dtlcompare}[3]{% \__datatool_get_compare_sort:Nn \l__datatool_tmpa_str { #2 } \__datatool_get_compare_sort:Nn \l__datatool_tmpb_str { #3 } \__datatool_strcmp:NNN #1 \l__datatool_tmpa_str \l__datatool_tmpb_str } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlicompare} %\begin{definition} %\cs{dtlicompare}\marg{count}\marg{string1}\marg{string2} %\end{definition} % As \cs{dtlcompare} but ignores case. %\changes{1.01}{2007 Aug 17}{new} % \begin{macrocode} \newcommand*{\dtlicompare}[3]{% \__datatool_get_icompare_sort:Nn \l__datatool_tmpa_str { #2 } \__datatool_get_icompare_sort:Nn \l__datatool_tmpb_str { #3 } \__datatool_strcmp:NNN #1 \l__datatool_tmpa_str \l__datatool_tmpb_str } % \end{macrocode} %\end{macro} % %Compare two strings provided as token lists and store the result in %the count register provided in the first argument. % \begin{macrocode} \cs_new:Nn \__datatool_strcmp:NNN { \exp_args:NNoo \__datatool_strcmp:Nnn #1 { #2 } { #3 } } % \end{macrocode} % %If \cs{pdfstrcmp} or \cs{strcmp} is defined, use that for string comparisons %otherwise use \LaTeX3 string comparison commands. % \begin{macrocode} \cs_if_exist:NTF \strcmp { \cs_new:Nn \__datatool_strcmp:Nnn { \int_set:Nn #1 { \strcmp { #2 } { #3 } } } \cs_new:Nn \datatool_strcmp:nn { \strcmp { #1 } { #2 } } } { \cs_if_exist:NTF \pdfstrcmp { \cs_new:Nn \__datatool_strcmp:Nnn { \int_set:Nn #1 { \pdfstrcmp{ #2 } { #3 } } } \cs_new:Nn \datatool_strcmp:nn { \pdfstrcmp { #1 } { #2 } } } { % \end{macrocode} %Most likely LuaTeX. % \begin{macrocode} \cs_new:Nn \__datatool_strcmp:Nnn { \str_if_eq:nnTF { #2 } { #3 } { \int_zero:N #1 } { \str_compare:nNnTF { #2 } < { #3 } { \int_set:Nn #1 { -1 } } { \int_set_eq:NN #1 \c_one_int } } } \cs_new:Nn \datatool_strcmp:nn { \str_if_eq:nnTF { #1 } { #2 } { \c_zero_int } { \str_compare:nNnTF { #1 } < { #2 } { -1 } { \c_one_int } } } } } % \end{macrocode} % %Compare two numeric values and store the result in %the count register provided in the first argument. % \begin{macrocode} \cs_new:Nn \__datatool_numcmp:Nnn { \dtlifnumlt { #2 } { #3 } { #1=-1\relax } { \dtlifnumgt { #2 } { #3} { #1=1\relax } { #1=0\relax } } } % \end{macrocode} % % \begin{macrocode} \tl_new:N \l__datatool_cmpa_tl \tl_new:N \l__datatool_cmpb_tl % \end{macrocode} % The token list mapping function skips spaces, so need to replace % spaces with a token that represents a space. % \begin{macrocode} \tl_const:Nn \c__datatool_space_tl { ~ } % \end{macrocode} %Case-sensitive: % \begin{macrocode} \cs_new:Nn \__datatool_get_compare_sort:Nn { \bool_if:NTF \l__datatool_compare_expand_cs_bool { \tl_set:Nx \l__datatool_cmpb_tl { \text_purify:n { #2 } } } { \tl_set:Nn \l__datatool_cmpb_tl { #2 } } \tl_replace_all:Nnn \l__datatool_cmpb_tl { ~ } { \c__datatool_space_tl } \tl_clear:N \l__datatool_cmpa_tl \exp_args:No \tl_map_function:nN { \l__datatool_cmpb_tl } \__datatool_get_compare_sort_fn:n \bool_if:NTF \l__datatool_compare_expand_cs_bool { \str_set:Nx #1 { \l__datatool_cmpa_tl } } { \str_set:Nx #1 { \text_purify:n { \l__datatool_cmpa_tl } } } } % \end{macrocode} %Case-insensitive: % \begin{macrocode} \cs_new:Nn \__datatool_get_icompare_sort:Nn { \bool_if:NTF \l__datatool_compare_expand_cs_bool { \tl_set:Nx \l__datatool_cmpb_tl { \text_purify:n { #2 } } } { \tl_set:Nn \l__datatool_cmpb_tl { #2 } } \tl_replace_all:Nnn \l__datatool_cmpb_tl { ~ } { \c__datatool_space_tl } \tl_clear:N \l__datatool_cmpa_tl \exp_args:No \tl_map_function:nN { \l__datatool_cmpb_tl } \__datatool_get_compare_sort_fn:n \bool_if:NTF \l__datatool_compare_expand_cs_bool { \str_set:Nx #1 { \text_lowercase:n { \l__datatool_cmpa_tl } } } { \str_set:Nx #1 { \text_purify:n { \text_lowercase:n { \l__datatool_cmpa_tl } } } } } \cs_new:Nn \__datatool_get_compare_sort_fn:n { \tl_if_head_eq_catcode:nNTF { #1 } \relax { \tl_if_eq:nnTF { \c__datatool_space_tl } { #1 } { \tl_put_right:Nn \l__datatool_cmpa_tl { ~ } } { \ifdtlcompareskipcs \else \tl_put_right:Nn \l__datatool_cmpa_tl { ^^J } \fi } } { \tl_put_right:Nn \l__datatool_cmpa_tl { #1 } } } % \end{macrocode} % %\begin{macro}{\dtlwordindexcompare} % Word breaks come before all other letters of the alphabet % (since the space character comes before all visible characters). % Note that, as from v3.0, \cs{@dtl@wordbreak} is no longer used, % except to provide a non-empty fourth argument for % \cs{@dtldictcompare}. % \begin{macrocode} \newcommand*{\dtlwordindexcompare}[3]{% \@dtldictcompare{#1}{#2}{#3}{\@dtl@wordbreak}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlletterindexcompare} % Word breaks are ignored. % \begin{macrocode} \newcommand*{\dtlletterindexcompare}[3]{% \@dtldictcompare{#1}{#2}{#3}{}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\@dtldictcompare} % Word or letter compare. Fourth argument should be % empty for letter compare. % \begin{macrocode} \newcommand*{\@dtldictcompare}[4]{% \group_begin: \dtl@SortWordCommands@hook \exp_args:Nxx \__datatool_set_tmp_dictcomp:nn { #2 } { #3 } \tl_if_empty:nT { #4 } { \tl_replace_all:Nnn \l__datatool_dictcompa_tl { ~ } {} \tl_replace_all:Nnn \l__datatool_dictcompb_tl { ~ } {} } \str_set:NV \l__datatool_tmpa_str \l__datatool_dictcompa_tl \str_set:NV \l__datatool_tmpb_str \l__datatool_dictcompb_tl \__datatool_strcmp:NNN #1 \l__datatool_tmpa_str \l__datatool_tmpb_str } \tl_new:N \l__datatool_dictcompa_tl \tl_new:N \l__datatool_dictcompb_tl \cs_new:Nn \__datatool_set_tmp_dictcomp:nn { \group_end: \DTLsortwordhandler { #1 } { \l__datatool_dictcompa_tl } \DTLsortwordhandler { #2 } { \l__datatool_dictcompb_tl } } % \end{macrocode} %\end{macro} % % Need to indicate type of inversion. %\begin{macro}{\datatoolpersoncomma} % \begin{macrocode} \newcommand*{\datatoolpersoncomma}{,\space} % \end{macrocode} %\end{macro} % %\begin{macro}{\datatoolplacecomma} % \begin{macrocode} \newcommand*{\datatoolplacecomma}{,\space} % \end{macrocode} %\end{macro} % %\begin{macro}{\datatoolsubjectcomma} % \begin{macrocode} \newcommand*{\datatoolsubjectcomma}{,\space} % \end{macrocode} %\end{macro} % %\begin{macro}{\datatoolparenstart} % \begin{macrocode} \newcommand*{\datatoolparenstart}{\space} % \end{macrocode} %\end{macro} % %\begin{macro}{\datatoolparen} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand*{\datatoolparen}[1]{\space (#1)} % \end{macrocode} %\end{macro} % % %The following commands \cs{dtlicomparewords} and %\cs{dtlcomparewords} were never documented and don't seem to do %anything different from \cs{dtlicompare} and \cs{dtlcompare}. %They have existed since at least v2.25. %\begin{macro}{\dtlicomparewords} %\begin{definition} %\cs{dtlicomparewords}\marg{count}\marg{word A}\marg{word B} %\end{definition} %This does a case insensitive comparison. % \begin{macrocode} \newcommand{\dtlicomparewords}[3]{% \dtlicompare{#1}{#2}{#3}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlcomparewords} %\begin{definition} %\cs{dtlcomparewords}\marg{count}\marg{word A}\marg{word B} %\end{definition} %This does a case sensitive comparison. % \begin{macrocode} \newcommand{\dtlcomparewords}[3]{% \dtlcompare{#1}{#2}{#3}% } % \end{macrocode} %\end{macro} % %\begin{macro}{macro} %\begin{definition} %\cs{dtlifcasechargroup}\marg{char}\marg{case letter}\marg{case %digit}\marg{case symbol} %\end{definition} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\dtlifcasechargroup}[4]{% \regex_match:nnTF { \A \d \Z} { #1 } { #3 } { \regex_match:nnTF { \A [[:alpha:]] \Z} { #1 } { #2 } { #3 } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlparsewords} %\changes{2.13}{2013-01-15}{new} %\begin{definition} %\cs{dtlparsewords}\marg{phrase}\marg{handler cs} %\end{definition} % Iterates through the given phrase. Hyphens are considered word % boundaries. Punctuation and digits before and after words are discarded. % \begin{macrocode} \newcommand*{\dtlparsewords}[2]{% \group_begin: \tl_set:Nx \l__datatool_tmpa_tl { \tl_trim_spaces:n { #1 } } \regex_replace_case_all:nN { \l_datatool_word_head_regex { \2 \cM\| } \l_datatool_word_hyphen_regex { \2 \cM\| } \l_datatool_word_tail_regex { \2 } \l_datatool_symbols_regex { } } \l__datatool_tmpa_tl \forlistloop { #2 } { \l__datatool_tmpa_tl } \group_end: } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifstringlt} %\begin{definition} %\cs{DTLifstringlt}\marg{string1}\marg{string2}\marg{true part}\marg{false part} %\end{definition} % String comparison (Starred version ignores case) %\changes{1.01}{2007 Aug 17}{added starred version} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLifstringlt { s m m m m } { \IfBooleanTF { #1 } { \@sDTLifstringlt { #2 } { #3 } { #4 } { #5 } } { \@DTLifstringlt { #2 } { #3 } { #4 } { #5 } } } % \end{macrocode} %\end{macro} %The internals are used by \cs{DTLiflt} as well. %\begin{macro}{\@DTLifstringlt} % Unstarred version (case-sensitive). % \begin{macrocode} \newcommand*{\@DTLifstringlt}[4]{ \exp_args:NNo \__datatool_get_compare_sort:Nn \l__datatool_tmpa_str { #1 } \exp_args:NNo \__datatool_get_compare_sort:Nn \l__datatool_tmpb_str { #2 } \str_compare:eNeTF { \l__datatool_tmpa_str } < { \l__datatool_tmpb_str } { #3 } { #4 } } % \end{macrocode} %\end{macro} %\begin{macro}{\s@DTLifstringlt} % Starred version (case-sensitive). % \begin{macrocode} \newcommand*{\@sDTLifstringlt}[4]{ \exp_args:NNo \__datatool_get_icompare_sort:Nn \l__datatool_tmpa_str { #1 } \exp_args:NNo \__datatool_get_icompare_sort:Nn \l__datatool_tmpb_str { #2 } \str_compare:eNeTF { \l__datatool_tmpa_str } < { \l__datatool_tmpb_str } { #3 } { #4 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLiflt} %\begin{definition} %\cs{DTLiflt}\marg{arg1}\marg{arg2}\marg{true part}\marg{false part} %\end{definition} % Does \cs{DTLifnumlt} if both \meta{arg1} and \meta{arg2} are % numerical, otherwise do \cs{DTLifstringlt} (unstarred version) % or \cs{DTLifstringlt*} (starred version). %\changes{1.01}{2007 Aug 17}{added starred version} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLiflt { s m m m m } { \IfBooleanTF { #1 } { \@sDTLiflt { #2 } { #3 } { #4 } { #5 } } { \@DTLiflt { #2 } { #3 } { #4 } { #5 } } } % \end{macrocode} %\end{macro} %\begin{macro}{\@DTLiflt} % Unstarred version (also used in \cs{DTLislt}). % \begin{macrocode} \newcommand*{\@DTLiflt}[4]{ \dtl@testbothnumerical {#1} {#2} \if@dtl@condition \DTLifnumlt {#1} {#2} {#3} {#4} \else \@DTLifstringlt {#1} {#2} {#3} {#4} \fi } % \end{macrocode} %\end{macro} %\begin{macro}{\@sDTLiflt} % Starred version (also used in \cs{DTLisilt}). % \begin{macrocode} \newcommand*{\@sDTLiflt}[4]{ \dtl@testbothnumerical {#1} {#2} \if@dtl@condition \DTLifnumlt {#1} {#2} {#3} {#4} \else \@sDTLifstringlt {#1} {#2} {#3} {#4} \fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifstringgt} %\begin{definition} %\cs{DTLifstringgt}\marg{string1}\marg{string2}\marg{true part}\marg{false part} %\end{definition} % String comparison (starred version ignores case) %\changes{1.01}{2007 Aug 17}{added starred version} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLifstringgt { s m m m m } { \IfBooleanTF { #1 } { \@sDTLifstringgt { #2 } { #3 } { #4 } { #5 } } { \@DTLifstringgt { #2 } { #3 } { #4 } { #5 } } } % \end{macrocode} %\end{macro} %The internals are used by \cs{DTLifgt} as well. %\begin{macro}{\@DTLifstringgt} % Unstarred version (case-sensitive). % \begin{macrocode} \newcommand*{\@DTLifstringgt}[4]{ \exp_args:NNo \__datatool_get_compare_sort:Nn \l__datatool_tmpa_str { #1 } \exp_args:NNo \__datatool_get_compare_sort:Nn \l__datatool_tmpb_str { #2 } \str_compare:eNeTF { \l__datatool_tmpa_str } > { \l__datatool_tmpb_str } { #3 } { #4 } } % \end{macrocode} %\end{macro} %\begin{macro}{\@sDTLifstringgt} % Starred version (case-insensitive). % \begin{macrocode} \newcommand*{\@sDTLifstringgt}[4]{ \exp_args:NNo \__datatool_get_icompare_sort:Nn \l__datatool_tmpa_str { #1 } \exp_args:NNo \__datatool_get_icompare_sort:Nn \l__datatool_tmpb_str { #2 } \str_compare:eNeTF { \l__datatool_tmpa_str } > { \l__datatool_tmpb_str } { #3 } { #4 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifgt} %\begin{definition} %\cs{DTLifgt}\marg{arg1}\marg{arg2}\marg{true part}\marg{false part} %\end{definition} % Does \cs{DTLifnumgt} if both \meta{arg1} and \meta{arg2} are % numerical, otherwise do \cs{DTLifstringgt} or \cs{DTLifstringgt*}. %\changes{1.01}{2007 Aug 17}{added starred version} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLifgt { s m m m m } { \IfBooleanTF { #1 } { \@sDTLifgt { #2 } { #3 } { #4 } { #5 } } { \@DTLifgt { #2 } { #3 } { #4 } { #5 } } } % \end{macrocode} %\end{macro} %\begin{macro}{\@DTLifgt} % Unstarred version (also used in \cs{DTLislt}). % \begin{macrocode} \newcommand*{\@DTLifgt}[4]{ \dtl@testbothnumerical {#1} {#2} \if@dtl@condition \DTLifnumgt {#1} {#2} {#3} {#4} \else \@DTLifstringgt {#1} {#2} {#3} {#4} \fi } % \end{macrocode} %\end{macro} %\begin{macro}{\@sDTLifgt} % Starred version (also used in \cs{DTLisigt}). % \begin{macrocode} \newcommand*{\@sDTLifgt}[4]{ \dtl@testbothnumerical {#1} {#2} \if@dtl@condition \DTLifnumgt {#1} {#2} {#3} {#4} \else \@sDTLifstringgt {#1} {#2} {#3} {#4} \fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifstringeq} %\begin{definition} %\cs{DTLifstringeq}\marg{string1}\marg{string2}\marg{true part}\marg{false part} %\end{definition} % String comparison (starred version ignores case) %\changes{1.01}{2007 Aug 17}{added starred version} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLifstringeq { s m m m m } { \IfBooleanTF { #1 } { \@sDTLifstringeq { #2 } { #3 } { #4 } { #5 } } { \@DTLifstringeq { #2 } { #3 } { #4 } { #5 } } } % \end{macrocode} %\end{macro} %The internals are used by \cs{DTLifeq} as well. %\begin{macro}{\@DTLifstringeq} % Unstarred version (case-sensitive). % \begin{macrocode} \newcommand*{\@DTLifstringeq}[4]{ \exp_args:NNo \__datatool_get_compare_sort:Nn \l__datatool_tmpa_str { #1 } \exp_args:NNo \__datatool_get_compare_sort:Nn \l__datatool_tmpb_str { #2 } \str_compare:eNeTF { \l__datatool_tmpa_str } = { \l__datatool_tmpb_str } { #3 } { #4 } } % \end{macrocode} %\end{macro} %\begin{macro}{\@sDTLifstringeq} % Starred version (case-insensitive). % \begin{macrocode} \newcommand*{\@sDTLifstringeq}[4]{ \exp_args:NNo \__datatool_get_icompare_sort:Nn \l__datatool_tmpa_str { #1 } \exp_args:NNo \__datatool_get_icompare_sort:Nn \l__datatool_tmpb_str { #2 } \str_compare:eNeTF { \l__datatool_tmpa_str } = { \l__datatool_tmpb_str } { #3 } { #4 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifeq} %\begin{definition} %\cs{DTLifeq}\marg{arg1}\marg{arg2}\marg{true part}\marg{false part} %\end{definition} % Does \cs{DTLifnumeq} if both \meta{arg1} and \meta{arg2} are % numerical, otherwise do \cs{DTLifstringeq} or \cs{DTLifstringeq*}. %\changes{1.01}{2007 Aug 17}{added starred version} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLifeq { s m m m m } { \IfBooleanTF { #1 } { \@sDTLifeq { #2 } { #3 } { #4 } { #5 } } { \@DTLifeq { #2 } { #3 } { #4 } { #5 } } } % \end{macrocode} %\end{macro} %\begin{macro}{\@DTLifeq} % Unstarred version (also used in \cs{DTLiseq}). % \begin{macrocode} \newcommand*{\@DTLifeq}[4]{ \dtl@testbothnumerical {#1} {#2} \if@dtl@condition \DTLifnumeq {#1} {#2} {#3} {#4} \else \@DTLifstringeq {#1} {#2} {#3} {#4} \fi } % \end{macrocode} %\end{macro} %\begin{macro}{\@sDTLifeq} % Starred version (also used in \cs{DTLisieq}). % \begin{macrocode} \newcommand*{\@sDTLifeq}[4]{ \dtl@testbothnumerical {#1} {#2} \if@dtl@condition \DTLifnumeq {#1} {#2} {#3} {#4} \else \@sDTLifstringeq {#1} {#2} {#3} {#4} \fi } % \end{macrocode} %\end{macro} % % \begin{macrocode} \regex_new:N \l_datatool_space_regex \regex_set:Nn \l_datatool_space_regex { ((?:[[:space:]\~])|(?:\c{protect}?\c{(?:nobreak)?space\ ?})) } % \end{macrocode} %\begin{macro}{\DTLifSubString} %\begin{definition} %\cs{DTLifSubString}\marg{string}\marg{sub string}\marg{true %part}\marg{false part} %\end{definition} % If \meta{sub string} is contained in \meta{string} does %\meta{true part}, otherwise does \meta{false part}. %\changes{1.01}{2007 Aug 17}{new} %\changes{3.0}{2025-03-03}{made robust} %\changes{3.0}{2025-03-03}{added starred version} %Spaces are replaced with \verb|~| to prevent leading/trailing %spaces from being trimmed. % \begin{macrocode} \newrobustcmd*{\DTLifSubString}{\@ifstar\@sDTLifSubString\@DTLifSubString} % \end{macrocode} %\end{macro} %\begin{macro}{\@DTLifSubString} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand*{\@DTLifSubString}[4]{% \tl_set:Nx \l__datatool_tmpa_tl { \text_purify:n { #1 } } \tl_set:Nx \l__datatool_tmpb_tl { \text_purify:n { #2 } } \regex_replace_all:NnN \l_datatool_space_regex { \~ } \l__datatool_tmpa_tl \regex_replace_all:NnN \l_datatool_space_regex { \~ } \l__datatool_tmpb_tl \exp_args:NVV \str_if_in:nnTF \l__datatool_tmpa_tl \l__datatool_tmpb_tl { #3 } { #4 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\@sDTLifSubString} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand*{\@sDTLifSubString}[2]{% \@DTLifSubString{ \text_lowercase:n { #1 } }{ \text_lowercase:n { #2 } }% } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifStartsWith} %\begin{definition} %\cs{DTLifStartsWith}\marg{string}\marg{substring}\marg{true %part}\marg{false part} %\end{definition} %If \meta{string} starts with \meta{substring}, this does %\meta{true part}, otherwise it does \meta{false part}. %\changes{1.01}{2007 Aug 17}{new} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\DTLifStartsWith}{\@ifstar\@sDTLifStartsWith\@DTLifStartsWith} % \end{macrocode} %\end{macro} %\begin{macro}{\@DTLifStartsWith} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand*{\@DTLifStartsWith}[4]{% \tl_set:Nx \l__datatool_tmpa_tl { \text_purify:n { #1 } } \tl_set:Nx \l__datatool_tmpb_tl { \text_purify:n { #2 } } \regex_replace_all:NnN \l_datatool_space_regex { \~ } \l__datatool_tmpa_tl \regex_replace_all:NnN \l_datatool_space_regex { \~ } \l__datatool_tmpb_tl \exp_args:NVV \__datatool_if_starts_with:nnTF \l__datatool_tmpa_tl \l__datatool_tmpb_tl { #3 } { #4 } } % \end{macrocode} %\end{macro} %\begin{macro}{\@sDTLifStartsWith} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand*{\@sDTLifStartsWith}[2]{% \@DTLifStartsWith{ \text_lowercase:n { #1 } }{ \text_lowercase:n { #2 } }% } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifEndsWith} %\begin{definition} %\cs{DTLifEndsWith}\marg{string}\marg{substring}\marg{true %part}\marg{false part} %\end{definition} %If \meta{string} ends with \meta{substring}, this does %\meta{true part}, otherwise it does \meta{false part}. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newrobustcmd*{\DTLifEndsWith}{\@ifstar\@sDTLifEndsWith\@DTLifEndsWith} % \end{macrocode} %\end{macro} %\begin{macro}{\@DTLifEndsWith} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand*{\@DTLifEndsWith}[4]{% \tl_set:Nx \l__datatool_tmpa_tl { \text_purify:n { #1 } } \tl_set:Nx \l__datatool_tmpb_tl { \text_purify:n { #2 } } \regex_replace_all:NnN \l_datatool_space_regex { \~ } \l__datatool_tmpa_tl \regex_replace_all:NnN \l_datatool_space_regex { \~ } \l__datatool_tmpb_tl \tl_reverse:N \l__datatool_tmpa_tl \tl_reverse:N \l__datatool_tmpb_tl \exp_args:NVV \__datatool_if_starts_with:nnTF \l__datatool_tmpa_tl \l__datatool_tmpb_tl { #3 } { #4 } } % \end{macrocode} %\end{macro} %\begin{macro}{\@sDTLifEndsWith} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand*{\@sDTLifEndsWith}[2]{% \@DTLifEndsWith{ \text_lowercase:n { #1 } }{ \text_lowercase:n { #2 } }% } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifstringclosedbetween} %\begin{definition} %\cs{DTLifstringclosedbetween}\marg{string}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % String comparison (starred version ignores case) %\changes{1.01}{2007 Aug 17}{added starred version} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLifstringclosedbetween { s m m m m m m } { \IfBooleanTF { #1 } { \@sDTLifstringclosedbetween { #2 } { #3 } { #4 } { #5 } { #6 } } { \@DTLifstringclosedbetween { #2 } { #3 } { #4 } { #5 } { #6 } } } % \end{macrocode} %\end{macro} %\begin{macro}{\@DTLifstringclosedbetween} % Unstarred version (case-sensitive). % \begin{macrocode} \newcommand*{\@DTLifstringclosedbetween}[5]{% \exp_args:NNo \__datatool_get_compare_sort:Nn \l__datatool_tmpa_str { #1 } \exp_args:NNo \__datatool_get_compare_sort:Nn \l__datatool_tmpb_str { #2 } \exp_args:NNo \__datatool_get_compare_sort:Nn \l__datatool_tmpc_str { #3 } \str_compare:eNeTF { \l__datatool_tmpa_str } < { \l__datatool_tmpb_str } { #5 } { \str_compare:eNeTF { \l__datatool_tmpa_str } > { \l__datatool_tmpc_str } { #5 } { #4 } } } % \end{macrocode} %\end{macro} %\begin{macro}{\@sDTLifstringclosedbetween} % Starred version (case-sensitive). % \begin{macrocode} \newcommand*{\@sDTLifstringclosedbetween}[5]{% \exp_args:NNo \__datatool_get_icompare_sort:Nn \l__datatool_tmpa_str { #1 } \exp_args:NNo \__datatool_get_icompare_sort:Nn \l__datatool_tmpb_str { #2 } \exp_args:NNo \__datatool_get_icompare_sort:Nn \l__datatool_tmpc_str { #3 } \str_compare:eNeTF { \l__datatool_tmpa_str } < { \l__datatool_tmpb_str } { #5 } { \str_compare:eNeTF { \l__datatool_tmpa_str } > { \l__datatool_tmpc_str } { #5 } { #4 } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifclosedbetween} %\begin{definition} %\cs{DTLifclosedbetween}\marg{arg}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % Does \cs{DTLifnumclosedbetween} if \marg{arg}, \meta{min} and \meta{max} are % numerical, otherwise do \cs{DTLifstringclosedbetween} % or \cs{DTLifstringclosedbetween*}. %\changes{1.01}{2007 Aug 17}{added starred version} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLifclosedbetween { s m m m m m } { \IfBooleanTF { #1 } { \@sDTLifclosedbetween { #2 } { #3 } { #4 } { #5 } { #6 } } { \@DTLifclosedbetween { #2 } { #3 } { #4 } { #5 } { #6 } } } % \end{macrocode} %\end{macro} %\begin{macro}{\@DTLifclosedbetween} % Unstarred version % \begin{macrocode} \newcommand*{\@DTLifclosedbetween}[5]{% \dtl@testbothnumerical{#2}{#3}% \if@dtl@condition \DTLifnumerical { #1 } { \DTLifnumclosedbetween {#1} {#2} {#3} {#4} {#5} } { \@DTLifstringclosedbetween {#1} {#2} {#3} {#4} {#5} } \else \@DTLifstringclosedbetween {#1} {#2} {#3} {#4} {#5} \fi } % \end{macrocode} %\end{macro} %\begin{macro}{\@sDTLifclosedbetween} % Starred version % \begin{macrocode} \newcommand*{\@sDTLifclosedbetween}[5]{% \dtl@testbothnumerical{#2}{#3}% \if@dtl@condition \DTLifnumerical { #1 } { \DTLifnumclosedbetween {#1} {#2} {#3} {#4} {#5} } { \@sDTLifstringclosedbetween {#1} {#2} {#3} {#4} {#5} } \else \@sDTLifstringclosedbetween {#1} {#2} {#3} {#4} {#5} \fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifstringopenbetween} %\begin{definition} %\cs{DTLifstringopenbetween}\marg{string}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % String comparison (starred version ignores case) %\changes{1.01}{2007 Aug 17}{added starred version} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLifstringopenbetween { s m m m m m m } { \IfBooleanTF { #1 } { \@sDTLifstringopenbetween { #2 } { #3 } { #4 } { #5 } { #6 } } { \@DTLifstringopenbetween { #2 } { #3 } { #4 } { #5 } { #6 } } } % \end{macrocode} % Unstarred version: % \begin{macrocode} \newcommand*{\@DTLifstringopenbetween}[5]{% \exp_args:NNo \__datatool_get_compare_sort:Nn \l__datatool_tmpa_str { #1 } \exp_args:NNo \__datatool_get_compare_sort:Nn \l__datatool_tmpb_str { #2 } \exp_args:NNo \__datatool_get_compare_sort:Nn \l__datatool_tmpc_str { #3 } \str_compare:eNeTF { \l__datatool_tmpa_str } > { \l__datatool_tmpb_str } { \str_compare:eNeTF { \l__datatool_tmpa_str } < { \l__datatool_tmpc_str } { #4 } { #5 } } { #5 } } % \end{macrocode} %\end{macro} %\begin{macro}{\@sDTLifstringopenbetween} % Starred version (case-sensitive). % \begin{macrocode} \newcommand*{\@sDTLifstringopenbetween}[5]{% \exp_args:NNo \__datatool_get_icompare_sort:Nn \l__datatool_tmpa_str { #1 } \exp_args:NNo \__datatool_get_icompare_sort:Nn \l__datatool_tmpb_str { #2 } \exp_args:NNo \__datatool_get_icompare_sort:Nn \l__datatool_tmpc_str { #3 } \str_compare:eNeTF { \l__datatool_tmpa_str } > { \l__datatool_tmpb_str } { \str_compare:eNeTF { \l__datatool_tmpa_str } < { \l__datatool_tmpc_str } { #4 } { #5 } } { #5 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifopenbetween} %\begin{definition} %\cs{DTLifopenbetween}\marg{arg}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % Does \cs{DTLifnumopenbetween} if \marg{arg}, \meta{min} and \meta{max} are % numerical, otherwise do \cs{DTLifstringopenbetween} % or \cs{DTLifstringopenbetween*}. %\changes{1.01}{2007 Aug 17}{added starred version} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \DTLifopenbetween { s m m m m m } { \IfBooleanTF { #1 } { \@sDTLifopenbetween { #2 } { #3 } { #4 } { #5 } { #6 } } { \@DTLifopenbetween { #2 } { #3 } { #4 } { #5 } { #6 } } } % \end{macrocode} %\end{macro} %\begin{macro}{\@DTLifopenbetween} % Unstarred version % \begin{macrocode} \newcommand*{\@DTLifopenbetween}[5]{ \dtl@testbothnumerical {#2} {#3} \if@dtl@condition \DTLifnumerical { #1 } { \DTLifnumopenbetween {#1} {#2} {#3} {#4} {#5} } { \@DTLifstringopenbetween {#1} {#2} {#3} {#4} {#5} } \else \@DTLifstringopenbetween {#1} {#2} {#3} {#4} {#5} \fi } % \end{macrocode} %\end{macro} %\begin{macro}{\@sDTLifopenbetween} % Starred version % \begin{macrocode} \newcommand*{\@sDTLifopenbetween}[5]{ \dtl@testbothnumerical {#2} {#3} \if@dtl@condition \DTLifnumerical { #1 } { \DTLifnumopenbetween {#1} {#2} {#3} {#4} {#5} } { \@sDTLifstringopenbetween {#1} {#2} {#3} {#4} {#5} } \else \@sDTLifstringopenbetween {#1} {#2} {#3} {#4} {#5} \fi } % \end{macrocode} %\end{macro} % % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} % %\begin{macro}{\DTLifFPopenbetween} %\begin{definition} %\cs{DTLifFPopenbetween}\marg{num}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % Determines if \meta{min} $<$ \meta{num} $<$ \meta{max} where % all arguments are in standard fixed point notation. % (Command name maintained for backward compatibility.) % \begin{macrocode} \let\DTLifFPopenbetween\dtlifnumopenbetween % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLifFPclosedbetween} %\begin{definition} %\cs{DTLifFPclosedbetween}\marg{num}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % Determines if \meta{min} $\leq$ \meta{num} $\leq$ \meta{max}. % (Command name maintained for backward compatibility.) % \begin{macrocode} \let\DTLifFPclosedbetween\dtlifnumclosedbetween % \end{macrocode} %\end{macro} % % %\subsubsection{ifthen Conditionals} % The following commands provide conditionals \cs{DTLis}\ldots\ % which can be used in \cs{ifthenelse}. % %\begin{macro}{\dtl@testlt} % Command to test if first argument is less than second argument. % If either argument is a string, a case sensitive string comparison % is used instead. % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testlt}[2]{% \@DTLiflt{#1}{#2}{\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLislt} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLislt}[2]{% \TE@throw\noexpand\dtl@testlt{#1}{#2}\noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testiclt} % Command to test if first argument is less than second argument. % If either argument is a string, a case insensitive string comparison % is used instead. % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testiclt}[2]{% \@sDTLiflt{#1}{#2}{\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisilt} % Provide conditional command for use in \cs{ifthenelse} %\changes{1.01}{2007 Aug 17}{new} % \begin{macrocode} \newcommand*{\DTLisilt}[2]{% \TE@throw\noexpand\dtl@testiclt{#1}{#2}\noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testgt} % Command to test if first argument is greater than second argument. % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testgt}[2]{% \@DTLifgt{#1}{#2}{\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisgt} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLisgt}[2]{% \TE@throw\noexpand\dtl@testgt{#1}{#2}\noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testicgt} % Command to test if first argument is greater than second argument % (ignores case). % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testicgt}[2]{% \@sDTLifgt{#1}{#2}{\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisigt} % Provide conditional command for use in \cs{ifthenelse} %\changes{1.01}{2007 Aug 17}{new} % \begin{macrocode} \newcommand*{\DTLisigt}[2]{% \TE@throw\noexpand\dtl@testicgt{#1}{#2}\noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testeq} % Command to test if first argument is equal to the second argument. % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testeq}[2]{% \@DTLifeq{#1}{#2}{\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLiseq} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLiseq}[2]{% \TE@throw\noexpand\dtl@testeq{#1}{#2}\noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testiceq} % Command to test if first number is equal to the second number % (ignores case). % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testiceq}[2]{% \@sDTLifeq{#1}{#2}{\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisieq} % Provide conditional command for use in \cs{ifthenelse} %\changes{1.01}{2007 Aug 17}{new} % \begin{macrocode} \newcommand*{\DTLisieq}[2]{% \TE@throw\noexpand\dtl@testiceq{#1}{#2}\noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testifsubstring} % Command to test if second argument is a substring of the first argument. % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testifsubstring}[2]{% \@DTLifSubString{#1}{#2}{\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisSubString} % Tests if second argument is contained in first argument. %\changes{1.01}{2007 Aug 17}{new} % \begin{macrocode} \newcommand*{\DTLisSubString}[2]{% \TE@throw\noexpand\dtl@testifsubstring{#1}{#2}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testifisubstring} % Command to test if second argument is a substring of the first argument. % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testifisubstring}[2]{% \@sDTLifSubString{#1}{#2}{\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisiSubString} % Tests if second argument is contained in first argument (no case). %\changes{1.01}{2007 Aug 17}{new} % \begin{macrocode} \newcommand*{\DTLisiSubString}[2]{% \TE@throw\noexpand\dtl@testifisubstring{#1}{#2}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@teststartswith} % Command to test if second argument is a prefix of the first argument. % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@teststartswith}[2]{% \@DTLifStartsWith{#1}{#2}{\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisPrefix} % Tests if first argument starts with second argument. %\changes{1.01}{2007 Aug 17}{new} % \begin{macrocode} \newcommand*{\DTLisPrefix}[2]{% \TE@throw\noexpand\dtl@teststartswith{#1}{#2}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testistartswith} % Command to test if second argument is a prefix of the first argument. % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testistartswith}[2]{% \@sDTLifStartsWith{#1}{#2}{\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisiPrefix} % Tests if first argument starts with second argument. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand*{\DTLisiPrefix}[2]{% \TE@throw\noexpand\dtl@testistartswith{#1}{#2}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testendswith} % Command to test if second argument is a suffix of the first argument. % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testendswith}[2]{% \@DTLifEndsWith{#1}{#2}{\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisSuffix} % Tests if first argument ends with second argument. %\changes{1.01}{2007 Aug 17}{new} % \begin{macrocode} \newcommand*{\DTLisSuffix}[2]{% \TE@throw\noexpand\dtl@testendswith{#1}{#2}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testiendswith} % Command to test if second argument is a suffix of the first argument. % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testiendswith}[2]{% \@sDTLifEndsWith{#1}{#2}{\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisiSuffix} % Tests if first argument ends with second argument. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand*{\DTLisiSuffix}[2]{% \TE@throw\noexpand\dtl@testiendswith{#1}{#2}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisinlist} % Tests if first argument is an element of the comma-separated list given % in the second argument. %\changes{2.23}{2015-07-11}{new} % \begin{macrocode} \newcommand*{\DTLisinlist}[2]{% \TE@throw\noexpand\dtl@testinlist{#1}{#2}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testinlist} %\changes{2.23}{2015-07-11}{new} % \begin{macrocode} \newcommand*{\dtl@testinlist}[2]{% \DTLifinlist{#1}{#2}{\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testnumclosedbetween} % Command to test if first number lies between second and third % numbers. (End points included, all arguments are fixed point % numbers in standard format.) This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testnumclosedbetween}[3]{% \DTLifnumclosedbetween{#1}{#2}{#3}% {\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} % % Provide conditional command for use in \cs{ifthenelse} %\begin{macro}{\DTLisnumclosedbetween} % \begin{macrocode} \newcommand*{\DTLisnumclosedbetween}[3]{% \TE@throw\noexpand\dtl@testnumclosedbetween{#1}{#2}{#3}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisFPclosedbetween} % Keep old command name for backwards compatibility: % \begin{macrocode} \let\DTLisFPclosedbetween\DTLisnumclosedbetween % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testnumopenbetween} % Command to test if first number lies between second and third % numbers. (End points excluded, all arguments are fixed point % numbers in standard format.) This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testnumopenbetween}[3]{% \DTLifnumopenbetween{#1}{#2}{#3}% {\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisnumopenbetween} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLisnumopenbetween}[3]{% \TE@throw\noexpand\dtl@testnumopenbetween{#1}{#2}{#3}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisFPopenbetween} % Keep old command name for backwards compatibility: %\changes{3.0}{2025-03-03}{made synonym of \cs{DTLisnumopenbetween}} % \begin{macrocode} \let\DTLisFPopenbetween\DTLisnumopenbetween % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testclosedbetween} % Command to test if first value lies between second and third % values. (End points included, case sensitive.) % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testclosedbetween}[3]{% \@DTLifclosedbetween{#1}{#2}{#3}% {\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisclosedbetween} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLisclosedbetween}[3]{% \TE@throw\noexpand\dtl@testclosedbetween{#1}{#2}{#3}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testiclosedbetween} % Command to test if first value lies between second and third % values. (End points included, case ignored.) % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testiclosedbetween}[3]{% \@sDTLifclosedbetween{#1}{#2}{#3}% {\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisiclosedbetween} % Provide conditional command for use in \cs{ifthenelse} %\changes{1.01}{2007 Aug 17}{new} % \begin{macrocode} \newcommand*{\DTLisiclosedbetween}[3]{% \TE@throw\noexpand\dtl@testiclosedbetween{#1}{#2}{#3}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testopenbetween} % Command to test if first value lies between second and third % values. (End points excluded, case sensitive.) % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testopenbetween}[3]{% \@DTLifopenbetween{#1}{#2}{#3}% {\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisopenbetween} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLisopenbetween}[3]{% \TE@throw\noexpand\dtl@testopenbetween{#1}{#2}{#3}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testiopenbetween} % Command to test if first value lies between second and third % values. (End points excluded, case ignored.) % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testiopenbetween}[3]{% \@sDTLifopenbetween{#1}{#2}{#3}% {\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisiopenbetween} % Provide conditional command for use in \cs{ifthenelse} %\changes{1.01}{2007 Aug 17}{new} % \begin{macrocode} \newcommand*{\DTLisiopenbetween}[3]{% \TE@throw\noexpand\dtl@testiopenbetween{#1}{#2}{#3}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testFPislt} % Command to test if first number is less than second % number where both numbers are in standard format (dot for decimal % separator and no group separators). % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testFPislt}[2]{% \dtlifnumlt{#1}{#2}% {% \@dtl@conditiontrue }% {% \@dtl@conditionfalse }% } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisnumlt} %\changes{3.0}{2025-03-03}{new} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLisnumlt}[2]{% \TE@throw\noexpand\dtl@testFPislt{#1}{#2}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisFPlt} % Synonym of \cs{DTLisnumlt}. % \begin{macrocode} \let\DTLisFPlt\DTLisnumlt % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testFPisgt} % Command to test if first number is greater than second % number where both numbers are in standard format. % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testFPisgt}[2]{% \dtlifnumgt{#1}{#2}% {% \@dtl@conditiontrue }% {% \@dtl@conditionfalse }% } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisnumgt} % Provide conditional command for use in \cs{ifthenelse} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand*{\DTLisnumgt}[2]{% \TE@throw\noexpand\dtl@testFPisgt{#1}{#2}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisFPgt} % Synonym % \begin{macrocode} \let\DTLisFPgt\DTLisnumgt % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testFPiseq} % Command to test if two numbers are equal, where both numbers % are in standard decimal format % \begin{macrocode} \newcommand*{\dtl@testFPiseq}[2]{% \dtlifnumeq{#1}{#2}% {% \@dtl@conditiontrue }% {% \@dtl@conditionfalse }% } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisnumeq} %\changes{3.0}{2025-03-03}{new} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLisnumeq}[2]{% \TE@throw\noexpand\dtl@testFPiseq{#1}{#2}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisFPeq} % Synonym. % \begin{macrocode} \let\DTLisFPeq\DTLisnumeq % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testFPislteq} % Command to test if first number is less than or equal to second % number where both numbers are in standard format. % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testFPislteq}[2]{% \dtlifnumlt{#1}{#2}% {% \@dtl@conditiontrue }% {% \@dtl@conditionfalse }% \if@dtl@condition \else \dtl@testFPiseq{#1}{#2}% \fi } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisnumlteq} %\changes{3.0}{2025-03-03}{new} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLisnumlteq}[2]{% \TE@throw\noexpand\dtl@testFPislteq{#1}{#2}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisFPlteq} % Synonym. % \begin{macrocode} \let\DTLisFPlteq\DTLisnumlteq % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testFPisgteq} % Command to test if first number is greater than or equal to second % number where both numbers are in standard format. % This sets \cs{if@dtl@condition}. % \begin{macrocode} \newcommand*{\dtl@testFPisgteq}[2]{% \dtlifnumgt{#1}{#2}% {% \@dtl@conditiontrue }% {% \@dtl@conditionfalse }% \if@dtl@condition \else \dtl@testFPiseq{#1}{#2}% \fi } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisnumgteq} % Provide conditional command for use in \cs{ifthenelse} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand*{\DTLisnumgteq}[2]{% \TE@throw\noexpand\dtl@testFPisgteq{#1}{#2}% \noexpand\if@dtl@condition} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLisFPgteq} % Synonym. % \begin{macrocode} \let\DTLisFPgteq\DTLisnumgteq % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@teststring} % Command to test if argument is a string. This sets % \cs{if@dtl@condition} % \begin{macrocode} \newcommand*{\dtl@teststring}[1]{% \DTLifstring{#1}{\@dtl@conditiontrue}{\@dtl@conditionfalse}} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisstring} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLisstring}[1]{% \TE@throw\noexpand\dtl@teststring{#1}\noexpand\if@dtl@condition} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testnumerical} % Command to test if argument is a numerical. This sets % \cs{if@dtl@condition} % \begin{macrocode} \newcommand*{\dtl@testnumerical}[1]{% \DTLifnumerical{#1}{\@dtl@conditiontrue}{\@dtl@conditionfalse}% } % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisnumerical} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLisnumerical}[1]{% \TE@throw\noexpand\dtl@testnumerical{#1}\noexpand\if@dtl@condition} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testint} % Command to test if argument is an integer. This sets % \cs{if@dtl@condition} % \begin{macrocode} \newcommand*{\dtl@testint}[1]{% \DTLifint{#1}{\@dtl@conditiontrue}{\@dtl@conditionfalse}} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisint} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLisint}[1]{% \TE@throw\noexpand\dtl@testint{#1}\noexpand\if@dtl@condition} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testreal} % Command to test if argument is a real. This sets % \cs{if@dtl@condition} % \begin{macrocode} \newcommand*{\dtl@testreal}[1]{% \DTLifreal{#1}{\@dtl@conditiontrue}{\@dtl@conditionfalse}} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLisreal} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLisreal}[1]{% \TE@throw\noexpand\dtl@testreal{#1}\noexpand\if@dtl@condition} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testcurrency} % Command to test if argument is a currency. This sets % \cs{if@dtl@condition} % \begin{macrocode} \newcommand*{\dtl@testcurrency}[1]{% \DTLifcurrency{#1}{\@dtl@conditiontrue}{\@dtl@conditionfalse}} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLiscurrency} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLiscurrency}[1]{% \TE@throw\noexpand\dtl@testcurrency{#1}\noexpand\if@dtl@condition} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@testcurrencyunit} % Command to test if argument is a currency with given unit. This sets % \cs{if@dtl@condition} % \begin{macrocode} \newcommand*{\dtl@testcurrencyunit}[2]{% \DTLifcurrencyunit{#1}{#2}{\@dtl@conditiontrue}{\@dtl@conditionfalse}} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLiscurrencyunit} % Provide conditional command for use in \cs{ifthenelse} % \begin{macrocode} \newcommand*{\DTLiscurrencyunit}[2]{% \TE@throw\noexpand\dtl@testcurrencyunit{#1}{#2}% \noexpand\if@dtl@condition } % \end{macrocode} %\end{macro} % %\subsection{Loops} %\begin{macro}{\dtlbreak} % Break out of loop at the end of current iteration. % \begin{macrocode} \newcommand*{\dtlbreak}{% \PackageError{datatool}{Can't break out of anything}{}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlforint} %NB \LaTeX3 now provides a better integer step function, which should be used instead. This is retained for backward-compatibility. %\begin{definition} %\cs{dtlforint}\meta{ct}=\meta{start}\cs{to}\meta{end}\cs{step}% %\meta{inc}\cs{do}\marg{body} %\end{definition} % \meta{ct} is a count register, \meta{start}, \meta{end} and % \meta{inc} are integers. % Group if nested or use \cs{dtlgforint}. % An infinite loop may result if \meta{inc}$=0$ and % \meta{start} $\le$ \meta{end} and \cs{dtlbreak} isn't used. % \begin{macrocode} \long\def\dtlforint#1=#2\to#3\step#4\do#5{% % \end{macrocode} % Make a copy of old version of break function % \begin{macrocode} \let\@dtl@orgbreak\dtlbreak \def\@dtl@endloophook{}% % \end{macrocode} % Setup break function for the loop (sets \meta{ct} to \meta{end} % at the end of the current iteration). % \begin{macrocode} \def\dtlbreak{\def\@dtl@endloophook{#1=#3}}% % \end{macrocode} % Initialise \meta{ct} % \begin{macrocode} #1=#2\relax % \end{macrocode} % Check if the steps are positive or negative. % \begin{macrocode} \ifnum#4<0\relax % \end{macrocode} % Counting down % \begin{macrocode} \whiledo{\(#1>#3\)\TE@or\(#1=#3\)}% {% #5% \@dtl@endloophook \advance#1 by #4\relax }% \else % \end{macrocode} % Counting up % \begin{macrocode} \whiledo{\(#1<#3\)\TE@or\(#1=#3\)}% {% #5% \@dtl@endloophook \advance#1 by #4\relax }% \fi % \end{macrocode} % Restore break function. % \begin{macrocode} \let\dtlbreak\@dtl@orgbreak } % \end{macrocode} %\end{macro} % %\begin{macro}{\@dtl@foreach@level} % Count register to keep track of global nested loops. % \begin{macrocode} \newcount\@dtl@foreach@level % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlgforint} %\begin{definition} %\cs{dtlgforint}\meta{ct}=\meta{start}\cs{to}\meta{end}\cs{step}% %\meta{inc}\cs{do}\marg{body} %\end{definition} % \meta{ct} is a count register, \meta{start}, \meta{end} and % \meta{inc} are integers. % An infinite loop may result if \meta{inc}=0 and \meta{start} $\le$ % \meta{end} and \cs{dtlbreak} isn't used. % \begin{macrocode} \long\def\dtlgforint#1=#2\to#3\step#4\do#5{% % \end{macrocode} % Initialise % \begin{macrocode} \global#1=#2\relax % \end{macrocode} % Increment level counter to allow for nested loops % \begin{macrocode} \global\advance\@dtl@foreach@level by 1\relax % \end{macrocode} % Set up end loop hook % \begin{macrocode} \expandafter\global\expandafter \let\csname @dtl@endhook@\the\@dtl@foreach@level\endcsname \relax % \end{macrocode} % Set up the break function: % Copy current definition % \begin{macrocode} \expandafter\global\expandafter \let\csname @dtl@break@\the\@dtl@foreach@level\endcsname \dtlbreak % \end{macrocode} % Set up definition for this level (sets to at the end % of the current iteration). % \begin{macrocode} \gdef\dtlbreak{\expandafter \gdef\csname @dtl@endhook@\the\@dtl@foreach@level\endcsname{% #1=#3}}% % \end{macrocode} % check the direction % \begin{macrocode} \ifnum#4<0\relax % \end{macrocode} % Counting down % \begin{macrocode} \whiledo{\(#1>#3\)\TE@or\(#1=#3\)}% {% #5% \csname @dtl@endhook@\the\@dtl@foreach@level\endcsname \global\advance#1 by #4\relax }% \else % \end{macrocode} % Counting up (or 0 increments) % \begin{macrocode} \whiledo{\(#1<#3\)\TE@or\(#1=#3\)}% {% #5% \csname @dtl@endhook@\the\@dtl@foreach@level\endcsname \global\advance#1 by #4\relax }% \fi % \end{macrocode} % Restore break function % \begin{macrocode} \expandafter\global\expandafter\let\expandafter\dtlbreak \csname @dtl@break@\the\@dtl@foreach@level\endcsname % \end{macrocode} % Decrement level counter % \begin{macrocode} \global\advance\@dtl@foreach@level by -1\relax } % \end{macrocode} %\end{macro} % %\begin{environment}{dtlenvgforint} % Environment form (contents are gathered, so verbatim can't be % used): % \begin{macrocode} \NewDocumentEnvironment{dtlenvgforint} { m +b } {\dtlgforint#1\do{#2}} {} % \end{macrocode} %\end{environment} % %\begin{environment}{dtlenvgforint*} % Starred form suppresses space-trimming: % \begin{macrocode} \NewDocumentEnvironment{dtlenvgforint*} { m +!b } {\dtlgforint#1\do{#2}} {} % \end{macrocode} %\end{environment} % %\subsection{Localisation Support} %Load localisation support. None provided with \sty{datatool}, but %support can be added by defining a file called %\texttt{datatool-}\meta{tag}\texttt{.ldf} that adds a redefinition %of \cs{DTLCurrentLocaleWordHandler} and \cs{DTLCurrentLocaleGetInitialLetter} %to the language hook. If a region is supplied, also set the %currency. % % \begin{macrocode} \ExplSyntaxOn % \end{macrocode} % %Provide quick way to reset all localisation support. %\begin{macro}{\DTLresetLanguage} %\changes{3.0}{2025-03-03}{new} %Reset all language (not region) related commands. % \begin{macrocode} \newcommand\DTLresetLanguage { \tl_clear:N \l_datatool_current_language_tl \renewcommand \DTLandname { \& } \renewcommand \DTLdatatypeunsetname { unset } \renewcommand \DTLdatatypestringname { string } \renewcommand \DTLdatatypeintegername { integer } \renewcommand \DTLdatatypedecimalname { decimal } \renewcommand \DTLdatatypecurrencyname { currency } \renewcommand \DTLdatatypedatetimename { date-time } \renewcommand \DTLdatatypedatename { date } \renewcommand \DTLdatatypetimename { time } \renewcommand \DTLdatatypeinvalidname { invalid } \renewcommand \DTLCurrentLocaleWordHandler [1] { } \renewcommand \DTLCurrentLocaleGetInitialLetter [2] { \datatool_get_first_letter:nN { ##1 } ##2 } \renewcommand \DTLCurrentLocaleGetGroupString [3] { \tl_set:Nn ##3 { ##1 } } \renewcommand \DTLCurrentLocaleGetMonthNameMap [1] { \tl_set_eq:NN \l_datatool_monthname_map_value_tl \q_no_value } \renewcommand \DTLCurrentLocaleIfpmTF [ 3 ] { \tl_if_eq:nnTF { ##1 } { pm } { ##2 } { ##3 } } \renewcommand \dtllettergroup [1] { \text_titlecase_first:n { ##1 } } \renewcommand \dtlnonlettergroup [1] { \detokenize { ##1 } } \renewcommand \dtlnumbergroup [1] { ##1 } \renewcommand \dtlcurrencygroup [2] { ##1 } \renewcommand \dtldatetimegroup [1] { ##1 } \renewcommand \dtldategroup [1] { ##1 } \renewcommand \dtltimegroup [1] { ##1 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLresetRegion} %\changes{3.0}{2025-03-03}{new} %Reset all region related commands. % \begin{macrocode} \newcommand\DTLresetRegion { \tl_clear:N \l_datatool_current_region_tl \DTLsetnumberchars { , } { . } \DTLsetdefaultcurrency { XXX } \renewcommand \DTLCurrentLocaleCurrencyDP { 2 } \renewcommand \dtlcurrfmtsymsep { } \renewcommand \DTLCurrentLocaleParseTimeStamp [ 4 ] { ##4 } \renewcommand \DTLCurrentLocaleParseDate [ 4 ] { ##4 } \renewcommand \DTLCurrentLocaleParseTime [ 4 ] { ##4 } \renewcommand \DTLCurrentLocaleGetTimeZoneMap [ 1 ] { \tl_set_eq:NN \l_datatool_timezone_map_value_tl \q_no_value } \renewcommand \DTLCurrentLocaleFormatDate [ 4 ] { \datatool_default_date_fmt:nnnn { ##1 } { ##2 } { ##3 } { ##4 } } \renewcommand \DTLCurrentLocaleFormatTime [ 3 ] { \datatool_default_time_fmt:nnn { ##1 } { ##2 } { ##3 } } \renewcommand \DTLCurrentLocaleFormatTimeZone [ 2 ] { \datatool_default_timezone_fmt:nn { ##1 } { ##2 } } \renewcommand \DTLCurrentLocaleFormatTimeStampNoZone [7] { \datatool_default_timestamp_fmt:nnnnnnn { ##1 } { ##2 } { ##3 } { ##4 } { ##5 } { ##6 } { ##7 } } \renewcommand \DTLCurrentLocaleFormatTimeStampWithZone [9] { \datatool_default_timestamp_fmt:nnnnnnnnn { ##1 } { ##2 } { ##3 } { ##4 } { ##5 } { ##6 } { ##7 } { ##8 } { ##9 } } \renewcommand \DTLCurrentLocaleTimeStampFmtSep { ~ } } % \end{macrocode} %\end{macro} % %\begin{macro}{\RequireDatatoolDialect} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand \RequireDatatoolDialect [1] { \tl_clear:N \l_datatool_current_language_tl \__datatool_require_ldf: [ % \end{macrocode} %Ensure region language file is loaded, if it exists. % \begin{macrocode} \tl_if_empty:NF \CurrentTrackedRegion { \TrackLangRequireResource { \CurrentTrackedRegion } % \end{macrocode} %Note that the region file is only loaded once. This means that if %there are multiple dialects that share a region, the region hook %would only be added to the first dialect with that region. %Therefore, the region file shouldn't add anything to the captions hook %but should instead just define a command called %\cs{DTL}\meta{Region}\texttt{LocaleHook} where \meta{Region} is the two %letter (uppercase) region code. This means that it can be added %here, even if the file wasn't loaded at this point. % \begin{macrocode} \tl_if_exist:cTF { DTL \CurrentTrackedRegion LocaleHook } { \exp_args:Nc \TrackLangAddToCaptions { DTL \CurrentTrackedRegion LocaleHook } } { \tl_new:c { DTL \CurrentTrackedRegion LocaleHook } \datatool_locale_warn:nn { datatool-base } { No ~ locale ~ hook ~ available ~ for ~ region ~ ` \CurrentTrackedRegion ' } } } % \end{macrocode} %Search as usual from the locale tag to pick up any specific % language + region, sub-languages or scripts. % \begin{macrocode} \TrackLangRequireResource { \CurrentTrackedTag } ] { datatool } { #1 } \tl_if_empty:NT \l_datatool_current_language_tl { % \end{macrocode} %No support for this language so clear the token list in the %language hook. % \begin{macrocode} \@TrackLangAddToHook { \tl_clear:N \l_datatool_current_language_tl } { captions } } \tl_if_empty:NF \CurrentTrackedRegion { % \end{macrocode} %If the region isn't empty but the hook hasn't been defined then the %region file wasn't loaded. This may be because there's no language %support. % \begin{macrocode} \tl_if_exist:cF { DTL \CurrentTrackedRegion LocaleHook } { \cs_if_exist:NT \TrackLangRequireDialectOmitDialectLabel { \TrackLangRequireDialectOmitDialectLabel { datatool} { #1 } \csuse { DTL \CurrentTrackedRegion LocaleHook } } } } } % \end{macrocode} %\end{macro} %It's best to omit dialect label and region code from search to %ensure the search isn't prematurely terminated. % \begin{macrocode} \cs_new:Nn \__datatool_require_ldf: { \TrackLangRequireDialectOmitDialectLabelOmitOnlyRegion } % \end{macrocode} %Provide interface for locale options. %Define options using l3keys interface. % \begin{macrocode} \cs_new:Nn \datatool_locale_define_keys:nn { \keys_define:nn { datatool / locale / #1 } { #2 } } % \end{macrocode} %\begin{macro}{\DTLsetLocaleOptions} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{DTLsetLocaleOptions}\oarg{parent module(s)}\marg{module(s)}\marg{key-val list} %\end{definition} %User command for setting options for the given locale. %The starred version ignores unknown keys. % \begin{macrocode} \NewDocumentCommand \DTLsetLocaleOptions { s O{} m m } { \IfBlankTF { #2 } { \IfBooleanTF { #1 } { \datatool_set_known_locale_options:nn { #3 } { #4 } } { \datatool_set_locale_options:nn { #3 } { #4 } } } { \clist_map_inline:nn { #2 } { \IfBooleanTF { #1 } { \datatool_set_known_locale_options:nn { ##1 / #3 } { #4 } } { \datatool_set_locale_options:nn { ##1 / #3 } { #4 } } } } } % \end{macrocode} %\end{macro} % %Set options (error on unknown): % \begin{macrocode} \cs_new:Nn \datatool_set_locale_options:nn { \clist_map_inline:nn { #1 } { \keys_set:nn { datatool / locale / ##1 } { #2 } } } % \end{macrocode} %Set options (ignore unknown): % \begin{macrocode} \cs_new:Nn \datatool_set_known_locale_options:nn { \clist_map_inline:nn { #1 } { \keys_set_known:nn { datatool / locale / ##1 } { #2 } } } % \end{macrocode} %Provided for the ldf files to add to the captions hook to allow the %preferred label for language and region (that may be different from %the dialect or language label). % \begin{macrocode} \tl_new:N \l_datatool_current_language_tl \tl_new:N \l_datatool_current_region_tl % \end{macrocode} %\begin{definition} %\cs{datatool\_if\_current\_lang\_region:nn} \marg{lang} %\marg{region} %\end{definition} %Check if current language and region match \meta{lang} and %\meta{region}. % \begin{macrocode} \prg_new_conditional:Npnn \datatool_if_current_lang_region:nn #1 #2 { T, F, TF } { \tl_if_eq:NnTF \l_datatool_current_language_tl { #1 } { \tl_if_eq:NnTF \l_datatool_current_region_tl { #2 } { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } % \end{macrocode} %Check if empty language. % \begin{macrocode} \cs_new:Nn \datatool_warn_check_language_empty:nnn { \tl_if_empty:NTF \l_datatool_current_language_tl { \cs_if_exist:NTF \TrackLangRequireDialectOmitDialectLabelOmitOnlyRegion { \datatool_locale_warn:nn { #1 } { #3 ~ (check ~ your ~ localisation ~ setting ~ includes ~ language ~ and ~ region) } } { \datatool_locale_warn:nn { #1 } { #2 ~ (try ~ updating ~ tracklang ~ to ~ at ~ least ~ version ~ 1.6.3) } } } { \datatool_locale_warn:nn { #1 } { #3 } } } % \end{macrocode} %Check if token list starts with or is \verb|\l_datatool_current_language_tl| %and issue applicable warning. % \begin{macrocode} \cs_new:Nn \datatool_warn_check_head_language_empty:nnnn { \tl_if_head_eq_meaning:nNTF { #1 } \l_datatool_current_language_tl { \datatool_warn_check_language_empty:nnn { #2 } { #3 } { #4 } } { \datatool_locale_warn:nn { #2 } { #4 } } } \cs_generate_variant:Nn \datatool_warn_check_head_language_empty:nnnn { Vnnn } % \end{macrocode} %Track each additional locale requested in the locales/lang package option. %Note that \cs{TrackLanguageTag} requires the language first but, %for datatool, allow just the region. This needs to test if the %supplied tag has exactly two uppercase characters. If extra %information, such as the script is required, then the language part %must be included. % \begin{macrocode} \clist_map_inline:Nn \l__datatool_extra_locales_clist { \regex_match:nnTF { \A [A-Z]{2} \Z } { #1 } { \tl_set:Nn \l__datatool_tmpa_tl { \dtl@message { Adding ~ tracked ~ locale ~ ` und-#1 ' } \TrackLanguageTag { und-#1 } } \ForEachTrackedDialect{\l__datatool_dialect_tl} { \IfTrackedIsoCode { \TwoLetterIsoCountryCode } { \l__datatool_dialect_tl } { \dtl@message { Dialect ~ ` \l__datatool_dialect_tl' ~ already ~ has ~ region } } { \dtl@message { Adding ~ region ~ `#1' ~ to ~ locale ~ ` \l__datatool_dialect_tl ' } \AddTrackedRegion { #1 } { \l__datatool_dialect_tl } \tl_clear:N \l__datatool_tmpa_tl } } \l__datatool_tmpa_tl } { \TrackIfKnownLanguage { #1 } { \dtl@message { Adding ~ tracked ~ locale ~ ` #1 ' } } { \datatool_locale_warn:nn { datatool-base } { Unrecognised ~ language ~ in ~ locale ~ tag ~ ` #1 '} } } } % \end{macrocode} %\begin{macro}{\datatool@do@load@locales} %The actual ldf loading needs to be after \LaTeX3 syntax has been %switched off, so define a command for use afterwards. % \begin{macrocode} \newcommand{\datatool@do@load@locales}{} \datatool@load@locales { \renewcommand\datatool@do@load@locales { \AnyTrackedLanguages { \cs_if_exist:NF \TrackLangRequireDialectOmitDialectLabelOmitOnlyRegion { \datatool_locale_warn:nn { old ~ version ~ of ~ tracklang: ~ localisation ~ files ~ may ~ not ~ all ~ load. ~ Upgrade ~ to ~ at ~ least ~ tracklang ~ v1.6.3 ~ for ~ better ~ support } \cs_set:Nn \__datatool_require_ldf: { \TrackLangRequireDialect } } \ForEachTrackedDialect { \l__datatool_dialect_tl } { \RequireDatatoolDialect { \l__datatool_dialect_tl } } } { } } } % \end{macrocode} %\end{macro} %Switch off \LaTeX3 syntax: % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} %Now load applicable locale files for each tracked dialect. % \begin{macrocode} \datatool@do@load@locales % \end{macrocode} % %\begin{macro}{\@dtl@gobbletonil} %NB still used by datatool.sty. %May be removed in future. % \begin{macrocode} \def\@dtl@gobbletonil#1\@nil{} % \end{macrocode} %\end{macro} % %\begin{macro}{\@dtl@countdigits} %NB still used by datatool-pgfmath.def %May be removed in future. % \begin{macrocode} \def\@dtl@countdigits#1#2\relax{% \advance\@dtl@tmpcount by 1\relax \ifx.#2\relax \let\@dtl@countnext=\@gobble \else \let\@dtl@countnext=\@dtl@countdigits \fi \@dtl@countnext#2\relax } % \end{macrocode} %\end{macro} % %\subsection{Deprecated} %These commands are being phased out and should not be used. % %\begin{macro}{\dtlenableUTFviii} %\changes{2.24}{2016-01-12}{new} %Deprecated. % \begin{macrocode} \newcommand*{\dtlenableUTFviii}{\booltrue{@dtl@utf8}} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtldisableUTFviii} %\changes{2.24}{2016-01-12}{new} %Deprecated. % \begin{macrocode} \newcommand*{\dtldisableUTFviii}{\boolfalse{@dtl@utf8}} % \end{macrocode} %\end{macro} % %\begin{macro}{\long@collect@body} % Need long versions of \isty{amsmath}'s \cs{collect@body}. These macros are % adapted from the macros defined by \sty{amsmath}. % Use \sty{xparse} instead. % \begin{macrocode} \long\def\long@collect@body#1{% \@envbody{\@xp#1\@xp{\the\@envbody}}% \edef\process@envbody{\the\@envbody\@nx\end{\@currenvir}}% \@envbody\@emptytoks \def\begin@stack{b}% \begingroup \@xp\let\csname\@currenvir\endcsname\long@collect@@body \edef\process@envbody{\@xp\@nx\csname\@currenvir\endcsname}% \process@envbody } % \end{macrocode} %\end{macro} %\begin{macro}{\long@addto@envbody} %\changes{2.10}{2012-07-18}{new} % Adapted from \isty{amsmath}'s \cs{addto@envbody} % \begin{macrocode} \long\def\long@addto@envbody#1{% \toks@{#1}% \edef\@dtl@tmp{\the\@envbody\the\toks@}% \global\@envbody\@xp{\@dtl@tmp}% } % \end{macrocode} %\end{macro} %\begin{macro}{\long@collect@@body} %\changes{2.10}{2012-07-18}{new} % Adapted from \isty{amsmath}'s \cs{collect@body} % \begin{macrocode} \long\def\long@collect@@body#1\end#2{% \protected@edef\begin@stack{% \long@push@begins#1\begin\end \@xp\@gobble\begin@stack }% \ifx\@empty\begin@stack \endgroup \@checkend{#2}% \long@addto@envbody{#1}% \else \long@addto@envbody{#1\end{#2}}% \fi \process@envbody } % \end{macrocode} %\end{macro} %\begin{macro}{\long@push@begins} %\changes{2.10}{2012-07-18}{new} %Adapted from \isty{amsmath}'s \cs{push@begins} % \begin{macrocode} \long\def\long@push@begins#1\begin#2{% \ifx\end#2\else b\@xp\long@push@begins\fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@ifsingle} %\begin{definition} %\cs{dtl@ifsingle}\marg{arg}\marg{true part}\marg{false part} %\end{definition} % If there is only one object in \meta{arg} (without expansion) % do \meta{true part}, otherwise do false part. %This now just uses the new \LaTeX3 command to test for a single %token. Old versions of \sty{glossaries} use \cs{dtl@ifsingle}. %\changes{3.0}{2025-03-03}{now uses LaTeX3} % \begin{macrocode} \ExplSyntaxOn \newcommand \dtl@ifsingle [ 3 ] { \tl_if_single_token:nTF { #1 } { #2 } { #3 } } \ExplSyntaxOff % \end{macrocode} %\end{macro} %\begin{macro}{\@dtl@ifsingle} %\changes{3.0}{2025-03-03}{removed} %\end{macro} % % Count registers to store character codes: % \begin{macrocode} \newcount\dtl@codeA \newcount\dtl@codeB % \end{macrocode} % %\begin{macro}{\@dtl@listelement@outofrange} %\changes{2.31}{2018-12-07}{new} % \begin{macrocode} \newcommand{\@dtl@listelement@outofrange}[1]{% \PackageWarning{datatool-base}{List index `\number#1' out of range}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@sortlist} %I made a bit of a blunder here. \cs{dtl@sortlist} was supposed to %work with commands like \cs{dtlcompare}, but those commands require %three arguments, the first being the register in which to store the %result. This contradicts the requirements of \cs{dtl@sortlist}. The %\qt{bug fix} in v2.26 fixed it to work with commands like %\cs{dtlcompare}, but that broke the documented design (which breaks %the \sty{glossaries} package). The other problem is that %\cs{dtl@insertinto} actually sorts in the reverse order. %So v2.27 undoes the change from v2.26 to ensure backward %compatibility and provides an alternative user-level command %\cs{dtlsortlist}, that's designed to work with the three-argument %handler commands like \cs{dtlcompare}. % %\begin{definition} % \cs{dtl@sortlist}\marg{list}\marg{criteria cmd} %\end{definition} % Performs an insertion sort on \meta{list}, where \meta{criteria cmd} % is a macro which takes two arguments \meta{a} and \meta{b}. % \meta{criteria cmd} must set the count register \cs{dtl@sortresult} % to either $-1$ (\meta{b} less than \meta{a}), 0 (\meta{a} is % equal to \meta{b}) or 1 (\meta{b} is greater than \meta{a}.) % % DEPRECATED. To be removed. (NB used by glossaries.sty) % \begin{macrocode} \newcommand{\dtl@sortlist}[2]{% \def\@dtl@sortedlist{}% \@for\@dtl@currentrow:=#1\do{% \expandafter\dtl@insertinto\expandafter {\@dtl@currentrow}{\@dtl@sortedlist}{#2}% \@endforfalse}% \let#1=\@dtl@sortedlist } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@insertinto} %\begin{definition} % \cs{dtl@insertinto}\marg{element}\marg{sorted-list}\marg{criteria cmd} %\end{definition} % Inserts \meta{element} into the sorted list \meta{sorted-list} % according to the criteria given by \meta{criteria cmd} (see above.) % DEPRECATED. To be removed. NB used by glossaries.sty % \begin{macrocode} \newcommand{\dtl@insertinto}[3]{% \def\@dtl@newsortedlist{}% \@dtl@insertdonefalse \@for\dtl@srtelement:=#2\do{% \if@dtl@insertdone \expandafter\toks@\expandafter{\dtl@srtelement}% \edef\@dtl@newstuff{{\the\toks@}}% \else % \end{macrocode} %\changes{2.26}{2016-07-20}{fixed bug (missing \cs{dtl@sortresult})} %\changes{2.27}{2016-07-28}{undone the incorrect change in v2.26} % \begin{macrocode} \expandafter#3\expandafter{\dtl@srtelement}{#1}% % \end{macrocode} %\changes{2.26}{2016-07-20}{fixed bug (incorrect inequality sign)} %\changes{2.27}{2016-07-28}{undone the incorrect change in v2.26} % \begin{macrocode} \ifnum\dtl@sortresult<0\relax \expandafter\toks@\expandafter{\dtl@srtelement}% \@dtl@toks{#1}% \edef\@dtl@newstuff{{\the\@dtl@toks},{\the\toks@}}% \@dtl@insertdonetrue \else \expandafter\toks@\expandafter{\dtl@srtelement}% \edef\@dtl@newstuff{{\the\toks@}}% \fi \fi \ifdefempty{\@dtl@newsortedlist}% {% \expandafter\toks@\expandafter{\@dtl@newstuff}% \edef\@dtl@newsortedlist{\the\toks@}% }% {% \expandafter\toks@\expandafter{\@dtl@newsortedlist}% \expandafter\@dtl@toks\expandafter{\@dtl@newstuff}% \edef\@dtl@newsortedlist{\the\toks@,\the\@dtl@toks}% }% \@endforfalse }% \ifdefempty{\@dtl@newsortedlist}% {% \@dtl@toks{#1}% \edef\@dtl@newsortedlist{{\the\@dtl@toks}}% }% {% \if@dtl@insertdone \else \expandafter\toks@\expandafter{\@dtl@newsortedlist}% \@dtl@toks{#1}% \edef\@dtl@newsortedlist{\the\toks@,{\the\@dtl@toks}}% \fi }% \global\let#2=\@dtl@newsortedlist } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@ifsingleorUTFviii} %\changes{2.24}{2016-01-12}{new} % Test if the argument is a single token or, if utf8 setting on, % a two-octet UTF-8 character. This only tests for two octet UTF-8 % characters. %\changes{3.0}{2025-03-03}{partially rewritten to use \LaTeX3} % \begin{macrocode} \ExplSyntaxOn \newcommand \dtl@ifsingleorUTFviii [ 3 ] { \tl_if_single_token:nTF { #1 } { #2 } { \ifbool{@dtl@utf8} { \tl_if_head_is_N_type:nTF { #1 } { \__datatool_isheadactivechar:w#1\q_stop { \ifnum\tl_count_tokens:n { #1 } = 2 \expandafter \__datatool_isheadUTFviiitwooctets \expandafter { \__datatool_xpactiveheadchar } { #2 } { #3 } \else #3 \fi } { #3 } } { #3 } } { #3 } } } % \end{macrocode} %\end{macro} % %\changes{3.0}{2025-03-03}{new} %Tests if the argument starts with an active character. % \begin{macrocode} \catcode`\@13\relax \cs_new:Npn \__datatool_isheadactivechar:w #1 #2 \q_stop #3 #4 { \tl_if_head_eq_catcode:nNTF { #1 } @ { \tl_set:No \__datatool_xpactiveheadchar { #1 } #3 } { #4 } } \catcode`\@11\relax % \end{macrocode} %\changes{3.0}{2025-03-03}{new} %Tests if the first argument starts with \cs{UTFviii@two@octets}. % \begin{macrocode} \cs_new:Npn \__datatool_isheadUTFviiitwooctets #1 #2 #3 { \tl_if_head_eq_meaning:nNTF { #1 } \UTFviii@two@octets { #2 } { #3 } } \ExplSyntaxOff % \end{macrocode} %\begin{macro}{\dtl@if@two@octets} %\changes{2.24}{2016-01-12}{new} %Check if argument starts with \cs{UTFviii@two@octets} %Deprecated. % \begin{macrocode} \def\dtl@if@two@octets#1#2\dtl@end@if@two@octets#3#4{% \ifbool{@dtl@utf8} {% \ifx\UTFviii@two@octets#1\relax #3% \else #4% \fi }% {% #4% }% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@getfirst@UTFviii} %\changes{2.24}{2016-01-12}{new} %Deprecated. % \begin{macrocode} \def\dtl@getfirst@UTFviii#1#2#3\end@dtl@getfirst@UTFviii{% \def\dtl@first{#1#2}% \ifx\@nil#3\relax \def\dtl@rest{}% \else \expandafter\def\expandafter\dtl@rest\expandafter{\@dtl@firsttonil#3}% \fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\@dtl@firsttonil} %\changes{2.24}{2016-01-12}{new} % \begin{macrocode} \def\@dtl@firsttonil#1\@nil{#1} % \end{macrocode} %\end{macro} % %\begin{macro}{\@dtlgetfirstchar} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{@dtlgetfirstchar}\marg{text}\marg{cs1}\marg{cs2} %\end{definition} %Stores the first character in \meta{text} in \meta{cs1} and the %remainder in \meta{cs2}. Largely redundant, but can be used to %obtain the letter group of a UTF-8 string. % \begin{macrocode} \ExplSyntaxOn \newcommand{\@dtlgetfirstchar}[3]{ \tl_if_empty:nTF { #1 } { \cs_set:Nn #2 {} \cs_set:Nn #3 {} } { \tl_if_single_token:nTF { #1 } { \cs_set:Nn #2 { #1 } \cs_set:Nn #3 {} } { \edef #2 { \tl_head:n { #1 } } \edef #3 { \tl_tail:n { #1 } } \ifbool{@dtl@utf8} { \tl_if_head_is_N_type:nTF { #1 } { \__datatool_isheadactivechar:w#1\q_stop { \expandafter \__datatool_isheadUTFviiitwooctets \expandafter { \__datatool_xpactiveheadchar } { \tl_put_right:Nx #2 { \tl_head:N #3 } \edef #3 { \tl_tail:N #3 } } { } } { } } { } } { } } } } \ExplSyntaxOff % \end{macrocode} %\end{macro} % %\begin{macro}{\dtl@getfirst} % Gets the first object, and stores in \cs{dtl@first}. The remainder % is stored in \cs{dtl@rest}. %\changes{2.23}{2015-07-11}{changed \cs{end} to \cs{end@dtl@getfirst}} %Deprecated. %\changes{3.0}{2025-03-03}{deprecated} % \begin{macrocode} \def\dtl@getfirst#1#2\end@dtl@getfirst{% \dtlgetfirstchar{#1#2}{\dtl@first}{\dtl@rest}% }% % \end{macrocode} %\end{macro} %\begin{macro}{\dtl@setcharcode} %\begin{definition} %\cs{dtl@setcharcode}\marg{c}\marg{count register} %\end{definition} % Sets \meta{count register} to the character code of \meta{c}, or % to $-1$ if \meta{c} is empty, or to % 0 if \meta{c} is a control sequence, unless \meta{c} is either % \cs{space} or |~| in which case it sets \meta{count register} % to the character code of the space character. %Largely redundant. Likely to be removed in future. % \begin{macrocode} \ExplSyntaxOn \newcommand*{\dtl@setcharcode}[2]{% \exp_args:Nx \__datatool_setcharcode:nN { \text_purify:n { #1 } } #2 } \cs_new:Npn \__datatool_setcharcode:nN #1#2 { \tl_if_empty:nTF { #1 } { #2=-1\relax } { \tl_if_head_is_space:nTF { #1 } { #2=32\relax } { \tl_if_head_eq_catcode:nNTF { #1 } \relax { #2=0\relax } { \tl_if_single_token:nTF { #1 } { \dtlsetcharcode{#1}{#2} } { \exp_args:Nx \dtlsetcharcode { \tl_head:n { #1 } } {#2} \ifbool{@dtl@utf8} { \ifnum#2>127 \dtlsetUTFviiicharcode { #1 }{ #2 } \fi } { } } } } } } \ExplSyntaxOff % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsetcharcode} %\changes{2.24}{2016-01-12}{new} % Set the code for the given character. May be redefined by user % for non-UTF8 encodings (e.g.\ Latin-1). %Deprecated. % \begin{macrocode} \newcommand*{\dtlsetcharcode}[2]{#2=`#1\relax} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsetlccharcode} %\changes{2.24}{2016-01-12}{new} % Set the lowercase code for the given character. May be redefined by user % for non-UTF8 encodings (e.g.\ Latin-1). %Deprecated. % \begin{macrocode} \newcommand*{\dtlsetlccharcode}[2]{#2=\lccode`#1\relax} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsetUTFviiicharcode} % Default behaviour is to set all UTF8 characters to code 64 (before % A). This will need to be redefined according to the relevant alphabet. %\changes{2.24}{2016-01-12}{new} %Deprecated. % \begin{macrocode} \newcommand*\dtlsetUTFviiicharcode[2]{\dtlsetdefaultUTFviiicharcode{#1}{#2}} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsetUTFviiilccharcode} %\changes{2.24}{2016-01-12}{new} % Default behaviour is to set all UTF8 characters to code 96 (before % a). This will need to be redefined according to the relevant alphabet. %Deprecated. % \begin{macrocode} \newcommand*\dtlsetUTFviiilccharcode[2]{\dtlsetdefaultUTFviiilccharcode{#1}{#2}} % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsetdefaultUTFviiicharcode} %\changes{2.24}{2016-01-12}{new} % Default codes for some supplemental Latin characters. %Deprecated. % \begin{macrocode} \newcommand*\dtlsetdefaultUTFviiicharcode[2]{% \ifboolexpr { test {\ifstrequal{#1}{À}} or test {\ifstrequal{#1}{Á}} or test {\ifstrequal{#1}{Á}} or test {\ifstrequal{#1}{Ã}} or test {\ifstrequal{#1}{Ä}} }% {% #2=`A\relax }% {% \ifstrequal{#1}{Ç}% {% #2=`C\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{È}} or test {\ifstrequal{#1}{É}} or test {\ifstrequal{#1}{Ê}} or test {\ifstrequal{#1}{Ë}} }% {% #2=`E\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{Ì}} or test {\ifstrequal{#1}{Í}} or test {\ifstrequal{#1}{Î}} or test {\ifstrequal{#1}{Ï}} }% {% #2=`I\relax }% {% \ifstrequal{#1}{Ñ}% {% #2=`N\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{Ò}} or test {\ifstrequal{#1}{Ó}} or test {\ifstrequal{#1}{Ô}} or test {\ifstrequal{#1}{Õ}} or test {\ifstrequal{#1}{Ö}} }% {% #2=`O\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{Ù}} or test {\ifstrequal{#1}{Ú}} or test {\ifstrequal{#1}{Û}} or test {\ifstrequal{#1}{Ü}} }% {% #2=`U\relax }% {% \ifstrequal{#1}{Ý}% {% #2=`Y\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{à}} or test {\ifstrequal{#1}{á}} or test {\ifstrequal{#1}{á}} or test {\ifstrequal{#1}{ã}} or test {\ifstrequal{#1}{ä}} }% {% #2=`a\relax }% {% \ifstrequal{#1}{ç}% {% #2=`c\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{è}} or test {\ifstrequal{#1}{é}} or test {\ifstrequal{#1}{ê}} or test {\ifstrequal{#1}{ë}} }% {% #2=`e\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{ì}} or test {\ifstrequal{#1}{í}} or test {\ifstrequal{#1}{î}} or test {\ifstrequal{#1}{ï}} }% {% #2=`i\relax }% {% \ifstrequal{#1}{ñ}% {% #2=`n\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{ò}} or test {\ifstrequal{#1}{ó}} or test {\ifstrequal{#1}{ô}} or test {\ifstrequal{#1}{õ}} or test {\ifstrequal{#1}{ö}} }% {% #2=`o\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{ù}} or test {\ifstrequal{#1}{ú}} or test {\ifstrequal{#1}{û}} or test {\ifstrequal{#1}{ü}} }% {% #2=`u\relax }% {% \ifstrequal{#1}{ý}% {% #2=`y\relax }% {% #2=64\relax }% }% }% }% }% }% }% }% }% }% }% }% }% }% }% }% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsetdefaultUTFviiilccharcode} %\changes{2.24}{2016-01-12}{new} % As above but for case-insensitive comparison. %Deprecated. % \begin{macrocode} \newcommand*\dtlsetdefaultUTFviiilccharcode[2]{% \ifboolexpr { test {\ifstrequal{#1}{à}} or test {\ifstrequal{#1}{á}} or test {\ifstrequal{#1}{á}} or test {\ifstrequal{#1}{ã}} or test {\ifstrequal{#1}{ä}} or test {\ifstrequal{#1}{À}} or test {\ifstrequal{#1}{Á}} or test {\ifstrequal{#1}{Á}} or test {\ifstrequal{#1}{Ã}} or test {\ifstrequal{#1}{Ä}} }% {% #2=`a\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{ç}} or test {\ifstrequal{#1}{Ç}} } {% #2=`c\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{è}} or test {\ifstrequal{#1}{é}} or test {\ifstrequal{#1}{ê}} or test {\ifstrequal{#1}{ë}} or test {\ifstrequal{#1}{È}} or test {\ifstrequal{#1}{É}} or test {\ifstrequal{#1}{Ê}} or test {\ifstrequal{#1}{Ë}} }% {% #2=`e\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{ì}} or test {\ifstrequal{#1}{í}} or test {\ifstrequal{#1}{î}} or test {\ifstrequal{#1}{ï}} or test {\ifstrequal{#1}{Ì}} or test {\ifstrequal{#1}{Í}} or test {\ifstrequal{#1}{Î}} or test {\ifstrequal{#1}{Ï}} }% {% #2=`i\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{ñ}} or test {\ifstrequal{#1}{Ñ}} } {% #2=`n\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{ò}} or test {\ifstrequal{#1}{ó}} or test {\ifstrequal{#1}{ô}} or test {\ifstrequal{#1}{õ}} or test {\ifstrequal{#1}{ö}} or test {\ifstrequal{#1}{Ò}} or test {\ifstrequal{#1}{Ó}} or test {\ifstrequal{#1}{Ô}} or test {\ifstrequal{#1}{Õ}} or test {\ifstrequal{#1}{Ö}} }% {% #2=`o\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{ù}} or test {\ifstrequal{#1}{ú}} or test {\ifstrequal{#1}{û}} or test {\ifstrequal{#1}{ü}} or test {\ifstrequal{#1}{Ù}} or test {\ifstrequal{#1}{Ú}} or test {\ifstrequal{#1}{Û}} or test {\ifstrequal{#1}{Ü}} }% {% #2=`u\relax }% {% \ifboolexpr { test {\ifstrequal{#1}{ý}} or test {\ifstrequal{#1}{Ý}} }% {% #2=`y\relax }% {% #2=96\relax }% }% }% }% }% }% }% }% } % \end{macrocode} %\end{macro} % %\iffalse % \begin{macrocode} % % \end{macrocode} %\fi %\iffalse % \begin{macrocode} %<*datatool-undetermined.ldf> % \end{macrocode} %\fi %\section{datatool-undetermined.ldf}\label{sec:code:datatool-undetermined} % Minimal support for undetermined language. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \TrackLangProvidesResource{undetermined}[2025/03/25 v3.3] % \end{macrocode} % Provide hook to customized: % \begin{macrocode} \ExplSyntaxOn \newcommand \DTLundLocaleHook { \DTLresetLanguage \tl_set:Nn \l_datatool_current_language_tl { und } } \ExplSyntaxOff % \end{macrocode} %Add to captions hook. % \begin{macrocode} \TrackLangAddToCaptions{\DTLundLocaleHook} % \end{macrocode} %\iffalse % \begin{macrocode} % % \end{macrocode} %\fi %\iffalse % \begin{macrocode} %<*datatool-latin1.ldf> % \end{macrocode} %\fi %\section{datatool-latin1.ldf}\label{sec:code:datatool-latin1} % ISO-8859-1 (Latin-1) strings. % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} \ProvidesFile{datatool-latin1.ldf}[2025/03/25 v3.3 (NLCT)] \ExplSyntaxOn \datatool_set_currencysign_from_charcode:ne { cent } { "A2 } \datatool_set_currencysign_from_charcode:nn { pound } { "A3 } \datatool_set_currencysign_from_charcode:nn { currency } { "A4 } \datatool_set_currencysign_from_charcode:nn { yen } { "A5 } \datatool_set_symbol_from_charcode:nn { middot } { "B7 } \ExplSyntaxOff % \end{macrocode} % No available ISO-8859-1 characters to support other commands %\iffalse % \begin{macrocode} % % \end{macrocode} %\fi %\iffalse % \begin{macrocode} %<*datatool-utf8.ldf> % \end{macrocode} %\fi %\section{datatool-utf8.ldf}\label{sec:code:datatool-utf8} %UTF-8 strings. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} \ProvidesFile{datatool-utf8.ldf}[2025/03/25 v3.3 (NLCT)] \ExplSyntaxOn % \end{macrocode} %Common numeric and currency formatting symbols. % \begin{macrocode} \datatool_set_currencysign:nn { cent } { ¢ } \datatool_set_currencysign:nn { pound } { £ } \datatool_set_currencysign:nn { currency } { ¤ } \datatool_set_currencysign:nn { yen } { ¥ } \datatool_set_symbol:nn { middot } { · } \datatool_set_currencysign:nn { florin } { ƒ } \datatool_set_currencysign:nn { baht } { ฿ } \datatool_set_currencysign:nn { ecu } { ₠ } \datatool_set_currencysign:nn { colonsign } { ₡ } \datatool_set_currencysign:nn { cruzerio } { ₢ } \datatool_set_currencysign:nn { frenchfranc } { ₣ } \datatool_set_currencysign:nn { lira } { ₤ } \datatool_set_currencysign:nn { mill } { ₥ } \datatool_set_currencysign:nn { naira } { ₦ } \datatool_set_currencysign:nn { peseta } { ₧ } \datatool_set_currencysign:nn { rupee } { ₨ } \datatool_set_currencysign:nn { won } { ₩ } \datatool_set_currencysign:nn { shekel } { ₪ } \datatool_set_currencysign:nn { dong } { ₫ } \datatool_set_currencysign:nn { euro } { € } \datatool_set_currencysign:nn { kip } { ₭ } \datatool_set_currencysign:nn { tugrik } { ₮ } \datatool_set_currencysign:nn { drachma } { ₯ } \datatool_set_currencysign:nn { germanpenny } { ₰ } \datatool_set_currencysign:nn { peso } { ₱ } \datatool_set_currencysign:nn { guarani } { ₲ } \datatool_set_currencysign:nn { austral } { ₳ } \datatool_set_currencysign:nn { hryvnia } { ₴ } \datatool_set_currencysign:nn { cedi } { ₵ } \datatool_set_currencysign:nn { livretournois } { ₶ } \datatool_set_currencysign:nn { spesmilo } { Sm } \datatool_set_currencysign:nn { tenge } { ₸ } \datatool_set_currencysign:nn { indianrupee } { ₹ } \datatool_set_currencysign:nn { turkishlira } { ₺ } \datatool_set_currencysign:nn { nordicmark } { ₻ } \datatool_set_currencysign:nn { manat } { ₼ } \datatool_set_currencysign:nn { ruble } { ₽ } \datatool_set_currencysign:nn { lari } { ₾ } \datatool_set_currencysign:nn { bitcoin } { ₿ } \datatool_set_currencysign:nn { som } { ⃀ } % \end{macrocode} %Regular expression to match apostrophe. % \begin{macrocode} \regex_set:Nn \l_datatool_apos_regex { \' | ’ } \ExplSyntaxOff % \end{macrocode} %\iffalse % \begin{macrocode} % % \end{macrocode} %\fi %\iffalse % \begin{macrocode} %<*datatool-l3fp.def> % \end{macrocode} %\fi %\section{datatool-l3fp.sty}\label{sec:code:datatool-l3fp} % Definitions of fixed-point commands that use LaTeX3 commands. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} \ProvidesFile{datatool-l3fp.def}[2025/03/25 v3.3 (NLCT)] % \end{macrocode} % %This file provides commands that use l3fp interfaces. %The commands defined here match the name and syntax of the commands %in the alternative \texttt{datatool-\meta{processor}.def} files. % %\subsection{Comparisons} %\begin{macro}{\dtlifnumeq} %\begin{definition} %\cs{dtlifnumeq}\marg{num1}\marg{num2}\marg{true part}\marg{false %part} %\end{definition} % Does \meta{true part} if \meta{num1}=\meta{num2}, otherwise does % \meta{false part}. % \begin{macrocode} \ExplSyntaxOn \newcommand*{\dtlifnumeq}[4]{% \fp_compare:nNnTF { #1 } = { #2 } { #3 } { #4 } } % \end{macrocode} %\end{macro} %\begin{macro}{\dtlifnumlt} %\begin{definition} %\cs{dtlifnumlt}\marg{num1}\marg{num2}\marg{true part}\marg{false %part} %\end{definition} % Does \meta{true part} if \meta{num1} \textless \meta{num2}, otherwise does % \meta{false part}. % \begin{macrocode} \newcommand*{\dtlifnumlt}[4]{% \fp_compare:nNnTF { #1 } < { #2 } { #3 } { #4 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlifnumgt} %\begin{definition} %\cs{dtlifnumgt}\marg{num1}\marg{num2}\marg{true part}\marg{false %part} %\end{definition} % Does \meta{true part} if \meta{num1} \textgreater \meta{num2}, otherwise does % \meta{false part}. % \begin{macrocode} \newcommand*{\dtlifnumgt}[4]{% \fp_compare:nNnTF { #1 } > { #2 } { #3 } { #4 } } % \end{macrocode} %\end{macro} %\begin{macro}{\dtlifnumopenbetween} %\begin{definition} %\cs{dtlifnumopenbetween}\marg{num}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % Determines if \meta{min} $<$ \meta{num} $<$ \meta{max}. % \begin{macrocode} \newcommand*{\dtlifnumopenbetween}[5]{% \fp_compare:nTF { #2 < #1 < #3 } { #4 } { #5 } } % \end{macrocode} %\end{macro} %\begin{macro}{\dtlifnumclosedbetween} %\begin{definition} %\cs{dtlifnumclosedbetween}\marg{num}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % Determines if \meta{min} $\leq$ \meta{num} $\leq$ \meta{max}. % \begin{macrocode} \newcommand*{\dtlifnumclosedbetween}[5]{% \fp_compare:nTF { #2 <= #1 <= #3 } { #4 } { #5 } } % \end{macrocode} %\end{macro} % %\subsection{Functions} % %\begin{macro}{\dtladd} %\begin{definition} %\cs{dtladd}\marg{cs}\marg{num1}\marg{num2} %\end{definition} % Adds two numbers and stores the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand \dtladd { m m m } { \fp_set:Nn \l__datatool_tmpa_fp { #2 + #3 } \tl_set:Nx #1 { \fp_use:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtladdall} %\begin{definition} %\cs{dtladdall}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the sum of all the values in the comma-separated list of % numbers. % \begin{macrocode} \NewDocumentCommand \dtladdall { m m } { \fp_zero:N \l__datatool_tmpa_fp \exp_args:Nx \clist_map_inline:nn { #2 } { \fp_add:Nn \l__datatool_tmpa_fp { ##1 } } \tl_set:Nx #1 { \fp_use:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsub} %\begin{definition} %\cs{dtlsub}\marg{cs}\marg{num1}\marg{num2} %\end{definition} % Subtracts two numbers and stores the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand \dtlsub { m m m } { \fp_set:Nn \l__datatool_tmpa_fp { #2 - #3 } \tl_set:Nx #1 { \fp_use:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmul} %\begin{definition} %\cs{dtlmul}\marg{cs}\marg{num1}\marg{num2} %\end{definition} % Multiplies two numbers and stores the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand \dtlmul { m m m } { \fp_set:Nn \l__datatool_tmpa_fp { #2 * #3 } \tl_set:Nx #1 { \fp_use:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtldiv} %\begin{definition} %\cs{dtldiv}\marg{cs}\marg{num1}\marg{num2} %\end{definition} % Divides two numbers and stores the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand \dtldiv { m m m } { \fp_set:Nn \l__datatool_tmpa_fp { #2 / #3 } \tl_set:Nx #1 { \fp_use:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsqrt} %\begin{definition} %\cs{dtlsqrt}\marg{cs}\marg{num} %\end{definition} % Square root number and store the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand \dtlsqrt { m m } { \fp_set:Nn \l__datatool_tmpa_fp { sqrt ( #2 ) } \tl_set:Nx #1 { \fp_use:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlroot} %\begin{definition} %\cs{dtlroot}\marg{cs}\marg{num}\marg{n} %\end{definition} % Nth root number and store the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand \dtlroot { m m m } { \exp_args:Nx \tl_if_eq:nnTF { #3 } { 2 } { \fp_set:Nn \l__datatool_tmpa_fp { sqrt ( #2 ) } \tl_set:Nx #1 { \fp_use:N \l__datatool_tmpa_fp } } { \fp_set:Nn \l__datatool_tmpa_fp { ( #2 ) ^ ( 1 / ( #3 ) ) } \tl_set:Nx #1 { \fp_use:N \l__datatool_tmpa_fp } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlround} %\begin{definition} %\cs{dtlround}\marg{cs}\marg{num}\marg{dp} %\end{definition} % Rounds \meta{num} to \meta{dp} decimal places and stores the % result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand \dtlround { m m m } { \fp_set:Nn \l__datatool_tmpa_fp { round ( #2, #3 ) } \tl_set:Nx #1 { \fp_to_decimal:N \l__datatool_tmpa_fp } \datatool_pad_trailing_zeros:Nn #1 { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtltrunc} %\begin{definition} %\cs{dtltrunc}\marg{cs}\marg{num}\marg{dp} %\end{definition} % Truncates \meta{num} to \meta{dp} decimal places and stores the % result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand \dtltrunc { m m m } { \fp_set:Nn \l__datatool_tmpa_fp { trunc ( #2, #3 ) } \tl_set:Nx #1 { \fp_to_decimal:N \l__datatool_tmpa_fp } \datatool_pad_trailing_zeros:Nn #1 { #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlclip} %\begin{definition} %\cs{dtlclip}\marg{cs}\marg{num} %\end{definition} % Removes redundant trailing zeros from \meta{num} and stores the % result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand \dtlclip { m m } { \fp_set:Nn \l__datatool_tmpa_fp { #2 } \tl_set:Nx #1 { \fp_to_decimal:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmin} %\begin{definition} %\cs{dtlmin}\marg{cs}\marg{num1}\marg{num2} %\end{definition} % Defines \meta{cs} to the smaller of the two numbers. % \begin{macrocode} \NewDocumentCommand \dtlmin { m m m } { \fp_set:Nn \l__datatool_tmpa_fp { min ( #2, #3 ) } \tl_set:Nx #1 { \fp_use:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlminall} %\begin{definition} %\cs{dtlminall}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the minimum in the comma-separated list of % numbers. % \begin{macrocode} \NewDocumentCommand \dtlminall { m m } { \fp_set:Nn \l__datatool_tmpa_fp { min ( #2 ) } \tl_set:Nx #1 { \fp_use:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmax} %\begin{definition} %\cs{dtlmax}\marg{cs}\marg{num1}\marg{num2} %\end{definition} % Defines \meta{cs} to the larger of the two numbers. % \begin{macrocode} \NewDocumentCommand \dtlmax { m m m } { \fp_set:Nn \l__datatool_tmpa_fp { max ( #2, #3 ) } \tl_set:Nx #1 { \fp_use:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmaxall} %\begin{definition} %\cs{dtlmaxall}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the maximum in the comma-separated list of % numbers. % \begin{macrocode} \NewDocumentCommand \dtlmaxall { m m } { \fp_set:Nn \l__datatool_tmpa_fp { max ( #2 ) } \tl_set:Nx #1 { \fp_use:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlabs} %\begin{definition} %\cs{dtlabs}\marg{cs}\marg{num} %\end{definition} % Defines \meta{cs} to the absolute value of \meta{num}. % \begin{macrocode} \NewDocumentCommand \dtlabs { m m } { \fp_set:Nn \l__datatool_tmpa_fp { abs ( #2 ) } \tl_set:Nx #1 { \fp_use:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlneg} %\begin{definition} %\cs{dtlneg}\marg{cs}\marg{num} %\end{definition} % Defines \meta{cs} to the negative of \meta{num}. % \begin{macrocode} \NewDocumentCommand \dtlneg {m m } { \fp_set:Nn \l__datatool_tmpa_fp { - ( #2 ) } \tl_set:Nx #1 { \fp_use:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmeanforall} %\begin{definition} %\cs{dtlmeanforall}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the mean (average) of all the values in the comma-separated list of % numbers. % \begin{macrocode} \NewDocumentCommand \dtlmeanforall { m m } { \fp_zero:N \l__datatool_total_fp \int_zero:N \l__datatool_count_int \exp_args:No \clist_map_inline:nn { #2 } { \int_incr:N \l__datatool_count_int \fp_add:Nn \l__datatool_total_fp { ##1 } } \fp_set:Nn \l__datatool_mean_fp { \l__datatool_total_fp / \l__datatool_count_int } \tl_set:Nx #1 { \fp_use:N \l__datatool_mean_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlvarianceforall} %\begin{definition} %\cs{dtlvarianceforall}\oarg{mean}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the variance of all the values in the comma-separated list of % numbers. If the mean value has already been calculated, it can be % supplied in the optional argument. % \begin{macrocode} \NewDocumentCommand \dtlvarianceforall { o m m } { \IfNoValueTF { #1 } { \fp_zero:N \l__datatool_total_fp \int_zero:N \l__datatool_count_int \exp_args:No \clist_map_inline:nn { #3 } { \int_incr:N \l__datatool_count_int \fp_add:Nn \l__datatool_total_fp { ##1 } } \fp_set:Nn \l__datatool_mean_fp { \l__datatool_total_fp / \l__datatool_count_int } \fp_zero:N \l__datatool_total_fp \exp_args:No \clist_map_inline:nn { #3 } { \fp_set:Nn \l__datatool_tmpa_fp { ##1 - \l__datatool_mean_fp } \fp_add:Nn \l__datatool_total_fp { \l__datatool_tmpa_fp * \l__datatool_tmpa_fp } } } { \fp_set:Nn { \l__datatool_mean_fp } { #1 } \fp_zero:N \l__datatool_total_fp \int_zero:N \l__datatool_count_int \exp_args:No \clist_map_inline:nn { #3 } { \int_incr:N \l__datatool_count_int \fp_set:Nn \l__datatool_tmpa_fp { ##1 - \l__datatool_mean_fp } \fp_add:Nn \l__datatool_total_fp { \l__datatool_tmpa_fp * \l__datatool_tmpa_fp } } } \fp_set:Nn \l__datatool_tmpa_fp { \l__datatool_total_fp / \l__datatool_count_int } \tl_set:Nx #2 { \fp_use:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsdforall} %\begin{definition} %\cs{dtlsdforall}\oarg{mean}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the standard deviation of all the values in the comma-separated list of % numbers. If the mean value has already been calculated, it can be % supplied in the optional argument. % \begin{macrocode} \NewDocumentCommand \dtlsdforall { o m m } { \IfNoValueTF { #1 } { \fp_zero:N \l__datatool_total_fp \int_zero:N \l__datatool_count_int \exp_args:No \clist_map_inline:nn { #3 } { \int_incr:N \l__datatool_count_int \fp_add:Nn \l__datatool_total_fp { ##1 } } \fp_set:Nn \l__datatool_mean_fp { \l__datatool_total_fp / \l__datatool_count_int } \fp_zero:N \l__datatool_total_fp \exp_args:No \clist_map_inline:nn { #3 } { \fp_set:Nn \l__datatool_tmpa_fp { ##1 - \l__datatool_mean_fp } \fp_add:Nn \l__datatool_total_fp { \l__datatool_tmpa_fp * \l__datatool_tmpa_fp } } } { \fp_set:Nn \l__datatool_mean_fp { #1 } \fp_zero:N \l__datatool_total_fp \int_zero:N \l__datatool_count_int \exp_args:No \clist_map_inline:nn { #3 } { \int_incr:N \l__datatool_count_int \fp_set:Nn \l__datatool_tmpa_fp { ##1 - \l__datatool_mean_fp } \fp_add:Nn \l__datatool_total_fp { \l__datatool_tmpa_fp * \l__datatool_tmpa_fp } } } \fp_set:Nn \l__datatool_tmpa_fp { sqrt ( \l__datatool_total_fp / \l__datatool_count_int ) } \tl_set:Nx #2 { \fp_use:N \l__datatool_tmpa_fp } } % \end{macrocode} %\end{macro} % % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} %\iffalse % \begin{macrocode} % % \end{macrocode} %\fi %\iffalse % \begin{macrocode} %<*datatool-lua.def> % \end{macrocode} %\fi %\section{datatool-lua.def}\label{sec:code:datatool-lua} % Definitions of fixed-point commands that use Lua. Only for use % with \LuaLaTeX. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} \ProvidesFile{datatool-lua.def}[2025/03/25 v3.3 (NLCT)] % \end{macrocode} % %This file provides commands that use \cs{directlua}. %The commands defined here match the name and syntax of the commands %in the alternative \texttt{datatool-\meta{processor}.def} files. % %\subsection{Comparisons} %\begin{macro}{\dtlifnumeq} %\begin{definition} %\cs{dtlifnumeq}\marg{num1}\marg{num2}\marg{true part}\marg{false %part} %\end{definition} % Does \meta{true part} if \meta{num1}=\meta{num2}, otherwise does % \meta{false part}. % \begin{macrocode} \newcommand*{\dtlifnumeq}[4]{% \ifnum\directlua{if #1==#2 then tex.print(1) else tex.print(0) end}=1 #3% \else #4% \fi } % \end{macrocode} %\end{macro} %\begin{macro}{\dtlifnumlt} %\begin{definition} %\cs{dtlifnumlt}\marg{num1}\marg{num2}\marg{true part}\marg{false %part} %\end{definition} % Does \meta{true part} if \meta{num1} \textless \meta{num2}, otherwise does % \meta{false part}. % \begin{macrocode} \newcommand*{\dtlifnumlt}[4]{% \ifnum\directlua{if #1<#2 then tex.print(1) else tex.print(0) end}=1 #3% \else #4% \fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlifnumgt} %\begin{definition} %\cs{dtlifnumgt}\marg{num1}\marg{num2}\marg{true part}\marg{false %part} %\end{definition} % Does \meta{true part} if \meta{num1} \textgreater \meta{num2}, otherwise does % \meta{false part}. % \begin{macrocode} \newcommand*{\dtlifnumgt}[4]{% \ifnum\directlua{if #1>#2 then tex.print(1) else tex.print(0) end}=1 #3% \else #4% \fi } % \end{macrocode} %\end{macro} %\begin{macro}{\dtlifnumopenbetween} %\begin{definition} %\cs{dtlifnumopenbetween}\marg{num}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % Determines if \meta{min} $<$ \meta{num} $<$ \meta{max}. % \begin{macrocode} \newcommand*{\dtlifnumopenbetween}[5]{% \ifnum\directlua{if #2 < #1 and #1 < #3 then tex.print(1) else tex.print(0) end}=1 #4% \else #5% \fi } % \end{macrocode} %\end{macro} %\begin{macro}{\dtlifnumclosedbetween} %\begin{definition} %\cs{dtlifnumclosedbetween}\marg{num}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % Determines if \meta{min} $\leq$ \meta{num} $\leq$ \meta{max}. % \begin{macrocode} \newcommand*{\dtlifnumclosedbetween}[5]{% \ifnum\directlua{if #2 <= #1 and #1 <= #3 then tex.print(1) else tex.print(0) end}=1 #4% \else #5% \fi } % \end{macrocode} %\end{macro} % %\subsection{Functions} % %\begin{macro}{\dtladd} %\begin{definition} %\cs{dtladd}\marg{cs}\marg{num1}\marg{num2} %\end{definition} % Adds two numbers and stores the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand\dtladd{mmm}{% \edef#1{\directlua{ tex.print(#2+(#3)) }}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtladdall} %\begin{definition} %\cs{dtladdall}\marg{cs}\marg{num list} %\end{definition} % Adds all numbers and stores the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand\dtladdall{mm}{% \edef#1{\directlua{ x = 0; array = { #2 }; for key,val in ipairs(array) do x = x + val; end tex.print(x); }}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsub} %\begin{definition} %\cs{dtlsub}\marg{cs}\marg{num1}\marg{num2} %\end{definition} % Subtracts two numbers and stores the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand\dtlsub{mmm}{% \edef#1{\directlua{ tex.print(#2-(#3)) }}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmul} %\begin{definition} %\cs{dtlmul}\marg{cs}\marg{num1}\marg{num2} %\end{definition} % Multiplies two numbers and stores the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand\dtlmul{mmm}{% \edef#1{\directlua{ tex.print(#2*#3) }}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtldiv} %\begin{definition} %\cs{dtldiv}\marg{cs}\marg{num1}\marg{num2} %\end{definition} % Divides two numbers and stores the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand\dtldiv{mmm}{% \edef#1{\directlua{ tex.print(#2/#3) }}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsqrt} %\begin{definition} %\cs{dtlsqrt}\marg{cs}\marg{num} %\end{definition} % Square root number and store the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand\dtlsqrt{mm}{% \edef#1{\directlua{ tex.print(math.sqrt(#2)) }}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlroot} %\begin{definition} %\cs{dtlroot}\marg{cs}\marg{num}\marg{n} %\end{definition} % Nth root number and store the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand\dtlroot{mmm}{% \edef#1{\directlua{ tex.print((#2)^(1/(#3))) }}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlround} %\begin{definition} %\cs{dtlround}\marg{cs}\marg{num}\marg{dp} %\end{definition} % Rounds \meta{num} to \meta{dp} decimal places and stores the % result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand\dtlround{mmm}{% \edef#1{"\@percentchar0.\number#3f", #2}% \edef#1{\directlua{ tex.print(string.format(#1)) }}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtltrunc} %\begin{definition} %\cs{dtltrunc}\marg{cs}\marg{num}\marg{dp} %\end{definition} % Truncates \meta{num} to \meta{dp} decimal places and stores the % result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand\dtltrunc{mmm}{% \edef#1{\directlua{ local m = 10^(#3); tex.print(string.format("\@percentchar.#3f", math.floor(#2 * m)/m)) }}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlclip} %\begin{definition} %\cs{dtlclip}\marg{cs}\marg{num} %\end{definition} % Removes redundant trailing zeros from \meta{num} and stores the % result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand\dtlclip{mm}{% \edef#1{\directlua{ local s = "#2"; tex.print(s:match("^(\@percentchar d*\@percentchar.?0?\@percentchar d-)0*$")) }}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmin} %\begin{definition} %\cs{dtlmin}\marg{cs}\marg{num1}\marg{num2} %\end{definition} % Defines \meta{cs} to the smaller of the two numbers. % \begin{macrocode} \NewDocumentCommand\dtlmin{mmm}{% \edef#1{\directlua{tex.print(math.min(#2,#3))}}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlminall} %\begin{definition} %\cs{dtlminall}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the minimum in the comma-separated list of % numbers. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand\dtlminall{mm}{% \edef#1{\directlua{tex.print(math.min(#2))}}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmax} %\begin{definition} %\cs{dtlmax}\marg{cs}\marg{num1}\marg{num2} %\end{definition} % Defines \meta{cs} to the larger of the two numbers. % \begin{macrocode} \NewDocumentCommand\dtlmax{mmm}{% \edef#1{\directlua{tex.print(math.max(#2,#3))}}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmaxall} %\begin{definition} %\cs{dtlmaxall}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the maximum in the comma-separated list of % numbers. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand\dtlmaxall{mm}{% \edef#1{\directlua{tex.print(math.max(#2))}}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlabs} %\begin{definition} %\cs{dtlabs}\marg{cs}\marg{num} %\end{definition} % Defines \meta{cs} to the absolute value of \meta{num}. % \begin{macrocode} \NewDocumentCommand\dtlabs{mm}{% \edef#1{\directlua{tex.print(math.abs(#2))}}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlneg} %\begin{definition} %\cs{dtlneg}\marg{cs}\marg{num} %\end{definition} % Defines \meta{cs} to the negative of \meta{num}. % \begin{macrocode} \NewDocumentCommand\dtlneg{mm}{% \edef#1{\directlua{tex.print(-(#2))}}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmeanforall} %\begin{definition} %\cs{dtlmeanforall}\marg{cs}\marg{num list} %\end{definition} % Computes the mean and stores the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand\dtlmeanforall{mm}{% \edef#1{\directlua{ x = 0; n = 0; array = { #2 }; for key,val in ipairs(array) do n = n + 1; x = x + val; end tex.print(x/n); }}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlvarianceforall} %\begin{definition} %\cs{dtlvarianceforall}\oarg{mean}\marg{cs}\marg{num list} %\end{definition} % Computes the variance and stores the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand \dtlvarianceforall { o m m } {% \IfNoValueTF{#1}% {% \edef#2{\directlua{ n = 0; mean = 0; array = { #3 }; for key,val in ipairs(array) do n = n + 1; mean = mean + val; end mean = mean / n; variance = 0; for key,val in ipairs(array) do x = val - mean; variance = variance + x * x; end variance = variance / n; tex.print(variance); }}% }% {% \edef#2{\directlua{ n = 0; mean = #1; array = { #3 }; variance = 0; for key,val in ipairs(array) do n = n + 1; x = val - mean; variance = variance + x * x; end variance = variance / n; tex.print(variance); }}% }% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsdforall} %\begin{definition} %\cs{dtlsdforall}\oarg{mean}\marg{cs}\marg{num list} %\end{definition} % Computes the standard deviation and stores the result in \meta{cs}. % \begin{macrocode} \NewDocumentCommand \dtlsdforall { o m m } {% \IfNoValueTF{#1}% {% \edef#2{\directlua{ n = 0; mean = 0; array = { #3 }; for key,val in ipairs(array) do n = n + 1; mean = mean + val; end mean = mean / n; variance = 0; for key,val in ipairs(array) do x = val - mean; variance = variance + x * x; end variance = variance / n; tex.print(math.sqrt(variance)); }}% }% {% \edef#2{\directlua{ n = 0; mean = #1; array = { #3 }; variance = 0; for key,val in ipairs(array) do n = n + 1; x = val - mean; variance = variance + x * x; end variance = variance / n; tex.print(math.sqrt(variance)); }}% }% } % \end{macrocode} %\end{macro} % %\iffalse % \begin{macrocode} % % \end{macrocode} %\fi %\iffalse % \begin{macrocode} %<*datatool-fp.def> % \end{macrocode} %\fi %\section{datatool-fp.sty}\label{sec:code:datatool-fp} % Definitions of fixed-point commands that use the \sty{fp} package. % Provided for backward-compatibility. % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} \ProvidesFile{datatool-fp.def}[2025/03/25 v3.3 (NLCT)] % \end{macrocode} % Required package: % \begin{macrocode} \RequirePackage{fp} % \end{macrocode} % % If \sty{fp}'s \pkgopt{verbose} option set, switch on \pkgopt{verbose} for % \sty{datatool-base} as well: % \begin{macrocode} \ifFPmessages \dtlverbosetrue \fi % \end{macrocode} % %\subsection{Comparison Commands} % %\begin{macro}{\dtlifnumeq} %\begin{definition} %\cs{dtlifnumeq}\marg{num1}\marg{num2}\marg{true part}\marg{false %part} %\end{definition} % Does \meta{true part} if \meta{num1}=\meta{num2}, otherwise does % \meta{false part}. The numbers must use a full stop as the decimal % character and no number group separator. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\dtlifnumeq}[4]{% \FPifeq{#1}{#2}% #3% \else #4% \fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlifnumlt} %\begin{definition} %\cs{dtlifnumlt}\marg{num1}\marg{num2}\marg{true part}\marg{false %part} %\end{definition} % Does \meta{true part} if \meta{num1} \textless \meta{num2}, otherwise does % \meta{false part}. The numbers must use a full stop as the decimal % character and no number group separator. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\dtlifnumlt}[4]{% \FPiflt{#1}{#2}% #3% \else #4% \fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlifnumgt} %\begin{definition} %\cs{dtlifnumgt}\marg{num1}\marg{num2}\marg{true part}\marg{false %part} %\end{definition} % Does \meta{true part} if \meta{num1} \textgreater \meta{num2}, otherwise does % \meta{false part}. The numbers must use a full stop as the decimal % character and no number group separator. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\dtlifnumgt}[4]{% \FPifgt{#1}{#2}% #3% \else #4% \fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlifnumopenbetween} %\begin{definition} %\cs{dtlifnumopenbetween}\marg{num}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % Determines if \meta{min} $<$ \meta{num} $<$ \meta{max} where % all numerical arguments are in standard fixed point notation. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\dtlifnumopenbetween}[5]{% \let\@dtl@dovalue\relax \dtlifnumgt{#1}{#2}% {}% {% \def\@dtl@dovalue{#5}% }% \dtlifnumlt{#1}{#3}% {% \ifx\@dtl@dovalue\relax \def\@dtl@dovalue{#4}% \fi }% {% \def\@dtl@dovalue{#5}% }% \@dtl@dovalue } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlifnumclosedbetween} %\begin{definition} %\cs{dtlifnumclosedbetween}\marg{num}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % Determines if \meta{min} $\leq$ \meta{num} $\leq$ \meta{max} where % all numerical arguments are in standard fixed point notation. %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \newrobustcmd*{\dtlifnumclosedbetween}[5]{% \let\@dtl@dovalue\relax \dtlifnumgt{#1}{#2}% {}% {% \dtlifnumeq{#1}{#2}% {% \def\@dtl@dovalue{#4}% }% {% \def\@dtl@dovalue{#5}% }% }% \dtlifnumlt{#1}{#3}% {% \ifx\@dtl@dovalue\relax \def\@dtl@dovalue{#4}% \fi }% {% \dtlifnumeq{#1}{#3}% {% \def\@dtl@dovalue{#4}% }% {% \def\@dtl@dovalue{#5}% }% }% \@dtl@dovalue } % \end{macrocode} %\end{macro} % %\subsection{Functions} % %\begin{macro}{\dtladd} % Adds two numbers using fp. % \begin{macrocode} \NewDocumentCommand{\dtladd}{mmm}{% \FPadd{#1}{#2}{#3}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtladdall} %\begin{definition} %\cs{dtladdall}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the sum of all the numbers in the comma-separated list of % numbers. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand{\dtladdall}{mm}{% \def#1{0}% \@for\@dtl@tmp:=#2\do{% \FPadd{#1}{#1}{\@dtl@tmp}% }% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsub} % Subtracts two numbers using fp. % \begin{macrocode} \NewDocumentCommand{\dtlsub}{mmm}{% \FPsub{#1}{#2}{#3}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmul} % Multiplies two numbers using fp. % \begin{macrocode} \NewDocumentCommand{\dtlmul}{mmm}{% \FPmul{#1}{#2}{#3}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtldiv} % Divides two numbers using fp. % \begin{macrocode} \NewDocumentCommand{\dtldiv}{mmm}{% \FPdiv{#1}{#2}{#3}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsqrt} % Square root using fp. % \begin{macrocode} \NewDocumentCommand{\dtlsqrt}{mm}{% \FProot{#1}{#2}{2}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlroot} % Nth root using fp. % \begin{macrocode} \NewDocumentCommand{\dtlroot}{mmm}{% \FProot{#1}{#2}{#3}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlround} % Rounds using fp. % \begin{macrocode} \NewDocumentCommand{\dtlround}{mmm}{% \FPround{#1}{#2}{#3}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtltrunc} % Truncates using fp. % (Third argument is the number of digits.) % \begin{macrocode} \NewDocumentCommand{\dtltrunc}{mmm}{% \FPtrunc{#1}{#2}{#3}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlclip} % \begin{macrocode} \NewDocumentCommand{\dtlclip}{mm}{% \FPclip{#1}{#2}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmin} % Minimum of two numbers using fp. % \begin{macrocode} \NewDocumentCommand{\dtlmin}{mmm}{% \FPmin{#1}{#2}{#3}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlminall} %\begin{definition} %\cs{dtlminall}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the minimum in the comma-separated list of % numbers. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand{\dtlminall}{mm}{% \let#1\empty \@for\@dtl@tmp:=#2\do{% \ifx\empty \let#1\@dtl@tmp \else \FPmin#1{#1}{\@dtl@tmp}% \fi }% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmax} % Maximum of two numbers using fp. % \begin{macrocode} \NewDocumentCommand{\dtlmax}{mmm}{% \FPmax{#1}{#2}{#3}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmaxall} %\begin{definition} %\cs{dtlmaxall}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the maximum in the comma-separated list of % numbers. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand{\dtlmaxall}{mm}{% \let#1\empty \@for\@dtl@tmp:=#2\do{% \ifx\empty \let#1\@dtl@tmp \else \FPmax#1{#1}{\@dtl@tmp}% \fi }% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlabs} % Absolute value using fp. % \begin{macrocode} \NewDocumentCommand{\dtlabs}{mm}{% \FPabs{#1}{#2}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlneg} % Negative of a value using fp. % \begin{macrocode} \NewDocumentCommand{\dtlneg}{mm}{% \FPneg{#1}{#2}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmeanforall} %\begin{definition} %\cs{dtlmeanforall}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the mean (average) of all the numbers in the comma-separated list of % numbers. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand{\dtlmeanforall}{mm}{% \def#1{0}% \count@=0\relax \@for\@dtl@tmp:=#2\do{% \advance\count@ by \@ne \FPadd{#1}{#1}{\@dtl@tmp}% }% \FPdiv{#1}{#1}{\number\count@}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlvarianceforall} %\begin{definition} %\cs{dtlvarianceforall}\oarg{mean}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the variance of all the numbers in the comma-separated list of % numbers. If the mean has already been calculated it can be % supplied in the optional argument. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand{\dtlvarianceforall}{omm}{% \IfNoValueTF{#1}% {% \def\@dtl@mean{0}% \count@=0\relax \@for\@dtl@tmp:=#3\do{% \advance\count@ by \@ne \FPadd{\@dtl@mean}{\@dtl@mean}{\@dtl@tmp}% }% \FPdiv{\@dtl@mean}{\@dtl@mean}{\number\count@}% \def#2{0}% \@for\@dtl@tmp:=#3\do{% \FPsub{\@dtl@tmp}{\@dtl@tmp}{\@dtl@mean}% \FPmul{\@dtl@tmp}{\@dtl@tmp}{\@dtl@tmp}% \FPadd{#2}{#2}{\@dtl@tmp}% }% \FPdiv{#2}{#2}{\number\count@}% }% {% \def\@dtl@mean{#1}% \count@=0\relax \def#2{0}% \@for\@dtl@tmp:=#3\do{% \advance\count@ by \@ne \FPsub{\@dtl@tmp}{\@dtl@tmp}{\@dtl@mean}% \FPmul{\@dtl@tmp}{\@dtl@tmp}{\@dtl@tmp}% \FPadd{#2}{#2}{\@dtl@tmp}% }% \FPdiv{#2}{#2}{\number\count@}% }% } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsdforall} %\begin{definition} %\cs{dtlsdforall}\oarg{mean}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the standard deviation of all the numbers in the comma-separated list of % numbers. If the mean has already been calculated it can be % supplied in the optional argument. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand{\dtlsdforall}{omm}{% \IfNoValueTF{#1}% {% \def\@dtl@mean{0}% \count@=0\relax \@for\@dtl@tmp:=#3\do{% \advance\count@ by \@ne \FPadd{\@dtl@mean}{\@dtl@mean}{\@dtl@tmp}% }% \FPdiv{\@dtl@mean}{\@dtl@mean}{\number\count@}% \def#2{0}% \@for\@dtl@tmp:=#3\do{% \FPsub{\@dtl@tmp}{\@dtl@tmp}{\@dtl@mean}% \FPmul{\@dtl@tmp}{\@dtl@tmp}{\@dtl@tmp}% \FPadd{#2}{#2}{\@dtl@tmp}% }% }% {% \def\@dtl@mean{#1}% \count@=0\relax \def#2{0}% \@for\@dtl@tmp:=#3\do{% \advance\count@ by \@ne \FPsub{\@dtl@tmp}{\@dtl@tmp}{\@dtl@mean}% \FPmul{\@dtl@tmp}{\@dtl@tmp}{\@dtl@tmp}% \FPadd{#2}{#2}{\@dtl@tmp}% }% }% \FPdiv{#2}{#2}{\number\count@}% \FProot{#2}{#2}{2}% } % \end{macrocode} %\end{macro} %\iffalse % \begin{macrocode} % % \end{macrocode} %\fi %\iffalse % \begin{macrocode} %<*datatool-fp.sty> % \end{macrocode} %\fi %\section{datatool-fp.sty} % Definitions of fixed-point commands that use the \sty{fp} package. % Note that the newer \sty{datatool-l3fp} or \sty{datatool-lua} are % preferable. % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} % \end{macrocode} % Rollback releases: % \begin{macrocode} \DeclareRelease{v2.32}{2019-09-27}{datatool-fp-2019-09-27.sty} \DeclareCurrentRelease{v3.3}{2025-03-25} % \end{macrocode} % Declare package: % \begin{macrocode} \ProvidesPackage{datatool-fp}[2025/03/25 v3.3 (NLCT)] % \end{macrocode} % This package is deprecated. % \begin{macrocode} \PackageWarning{datatool-fp}% {datatool-fp.sty deprecated. Use \string\usepackage[math=fp]{datatool} instead or (better still) \string\usepackage{datatool} } \RequirePackage[math=fp]{datatool} % \end{macrocode} %\iffalse % \begin{macrocode} % % \end{macrocode} %\fi %\iffalse % \begin{macrocode} %<*datatool-pgfmath.def> % \end{macrocode} %\fi %\section{datatool-pgfmath.sty}\label{sec:code:datatool-pgfmath} % Definitions of fixed-point commands that use the \sty{pgfmath} package. % Provided for backward-compatibility. The newer \texttt{datatool-l3fp.def} or % \texttt{datatool-lua.def} are preferable. % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} \ProvidesFile{datatool-pgfmath.def}[2025/03/25 v3.3 (NLCT)] % \end{macrocode} % Required packages: % \begin{macrocode} \RequirePackage{pgfrcs,pgfkeys,pgfmath} % \end{macrocode} % % Old code has only been partially rewritten with \LaTeX3. % \begin{macrocode} \ExplSyntaxOn % \end{macrocode} % %\subsection{Comparison Commands} %\begin{macro}{\dtlifnumeq} %\begin{definition} %\cs{dtlifnumeq}\marg{num1}\marg{num2}\marg{true part}\marg{false %part} %\end{definition} % Does \meta{true part} if \meta{num1}=\meta{num2}, otherwise does % \meta{false part}. The numbers must use a full stop as the decimal % character and no number group separator. The \verb|\number0| part % allows an empty argument to be treated as zero. (\cs{number} % required to prevent a zero prefix indicating an octal number.) %\changes{2.12}{2012-11-30}{fixed bug causing premature expansion} %\changes{2.26}{2016-07-20}{added \cs{number}} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \dtlifnumeq { m m m m } { \def\@dtl@truepart{#3}% \def\@dtl@falsepart{#4}% \exp_args:Nx \pgfmathifthenelse {#1==#2}% {"\exp_not:N \@dtl@truepart"}{"\exp_not:N \@dtl@falsepart"}% \pgfmathresult } % \end{macrocode} %\end{macro} %\begin{macro}{\dtlifnumlt} %\begin{definition} %\cs{dtlifnumlt}\marg{num1}\marg{num2}\marg{true part}\marg{false %part} %\end{definition} % Does \meta{true part} if \meta{num1} \textless \meta{num2}, otherwise does % \meta{false part}. The numbers must use a full stop as the decimal % character and no number group separator. %\changes{2.12}{2012-11-30}{fixed bug causing premature expansion} %\changes{2.26}{2016-07-20}{added \cs{number}} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \dtlifnumlt { m m m m } { \def\@dtl@truepart{#3}% \def\@dtl@falsepart{#4}% \exp_args:Nx \pgfmathifthenelse {#1 < #2}% {"\exp_not:N \@dtl@truepart"}{"\exp_not:N \@dtl@falsepart"}% \pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlifnumgt} %\begin{definition} %\cs{dtlifnumgt}\marg{num1}\marg{num2}\marg{true part}\marg{false %part} %\end{definition} % Does \meta{true part} if \meta{num1} \textgreater \meta{num2}, otherwise does % \meta{false part}. The numbers must use a full stop as the decimal % character and no number group separator. %\changes{2.12}{2012-11-30}{fixed bug causing premature expansion} %\changes{2.26}{2016-07-20}{added \cs{number}} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \dtlifnumgt { m m m m } { \def\@dtl@truepart{#3}% \def\@dtl@falsepart{#4}% \exp_args:Nx \pgfmathifthenelse {#1 > #2}% {"\exp_not:N \@dtl@truepart"}{"\exp_not:N \@dtl@falsepart"}% \pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlifnumopenbetween} %\begin{definition} %\cs{dtlifnumopenbetween}\marg{num}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % Determines if \meta{min} $<$ \meta{num} $<$ \meta{max} where % all numerical arguments are in standard fixed point notation. %\changes{2.12}{2012-11-30}{fixed bug causing premature expansion} %\changes{2.26}{2016-07-20}{added \cs{number}} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \dtlifnumopenbetween { m m m m m } { \dtlifnumlt { #1 } { #3 } { \dtlifnumgt { #1 } { #2 } { #4 } { #5 } } { #5 } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlifnumclosedbetween} %\begin{definition} %\cs{dtlifnumclosedbetween}\marg{num}\marg{min}\marg{max}\marg{true part}\marg{false part} %\end{definition} % Determines if \meta{min} $\leq$ \meta{num} $\leq$ \meta{max} where % all numerical arguments are in standard fixed point notation. %\changes{2.12}{2012-11-30}{fixed bug causing premature expansion} %\changes{2.26}{2016-07-20}{added \cs{number}} %\changes{3.0}{2025-03-03}{made robust} % \begin{macrocode} \NewDocumentCommand \dtlifnumclosedbetween { m m m m m } { \dtlifnumlt { #1 } { #2 } { #5 } { \dtlifnumgt { #1 } { #3 } { #5 } { #4 } } } % \end{macrocode} %\end{macro} % %\subsection{Functions} %\begin{macro}{\dtladd} % Adds two numbers using PGF math engine. % \begin{macrocode} \NewDocumentCommand \dtladd { m m m } { \pgfmathadd{#2}{#3}% \let#1\pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtladdall} %\begin{definition} %\cs{dtladdall}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the sum of all the numbers in the comma-separated list of % numbers. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand \dtladdall { m m } { \tl_set:Nn \pgfmathresult { 0 } \exp_args:No \clist_map_inline:nn { #2 } { \pgfmathadd { \pgfmathresult } { ##1 } } \tl_set_eq:NN #1 \pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsub} % Subtracts two numbers using PGF math engine. % \begin{macrocode} \NewDocumentCommand \dtlsub { m m m } { \pgfmathsubtract{#2}{#3}% \let#1\pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmul} % Multiplies two numbers using PGF math engine. % \begin{macrocode} \NewDocumentCommand \dtlmul { m m m } {% \pgfmathmultiply{#2}{#3}% \let#1\pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtldiv} % Divides two numbers using PGF math engine. % \begin{macrocode} \NewDocumentCommand \dtldiv { m m m } {% \pgfmathdivide{#2}{#3}% \let#1\pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsqrt} % Root using PGF math engine. % \begin{macrocode} \NewDocumentCommand \dtlsqrt { m m } {% \pgfmathsqrt{#2}% \let#1\pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlroot} % Root using PGF math engine. %\changes{3.0}{2025-03-03}{bug fix: added missing third argument} % \begin{macrocode} \NewDocumentCommand \dtlroot { m m m } { \exp_args:Nx \tl_if_eq:nnTF { #3 } { 2 } { \exp_args:Nx \pgfmathsqrt { #2 } \let#1\pgfmathresult } { \exp_args:Nxx \pgfmathpow { #2 } { 1 / ( #3 ) } \let#1\pgfmathresult } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlround} % Rounds using PGF math engine. %\changes{2.26}{2016-07-20}{fixed bug cause by rounding} % \begin{macrocode} \NewDocumentCommand{\dtlround}{mmm}{% \ifnum#3=0\relax \exp_args:Nx \pgfmathparse { int( round ( #2 ) ) } \let#1\pgfmathresult \else \exp_args:Nx \pgfmathparse { int ( 10 \exp_not:N ^ #3 )} \let\dtl@tmpshift\pgfmathresult % \end{macrocode} %Need to be careful not to trigger the dimension too large error, %so this is a bit convoluted. % \begin{macrocode} \exp_args:Nx \pgfmathparse { int ( floor ( #2 ) ) }% \let\dtl@int@round\pgfmathresult \exp_args:Nx \pgfmathparse { int ( round ( ( #2 - \dtl@int@round ) * \dtl@tmpshift ) ) } % \end{macrocode} %\changes{2.26}{2016-07-20}{fixed bug caused by rounding errors} % This bit is awkward because simply dividing by multiples % of 10 in pgfmath can cause rounding errors, so need to % employ another method. % \begin{macrocode} \@dtl@tmpcount=0\relax \expandafter\@dtl@countdigits\pgfmathresult.\relax \advance\@dtl@tmpcount by -#3\relax \def\@dtl@intpart{}% \def\@dtl@fracpart{}% \expandafter\@dtl@gatherintfrac\pgfmathresult\relax \tl_if_empty:NT \@dtl@intpart { \tl_set:Nn \@dtl@intpart { 0 } } \edef\@dtl@intpart{\number\numexpr\dtl@int@round + \@dtl@intpart}% \edef#1{\@dtl@intpart.\@dtl@fracpart}% \fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\@dtl@gatherintfrac} %\changes{2.26}{2016-07-20}{new} % \begin{macrocode} \newcommand*{\@dtl@gatherintfrac}[1]{% \ifx\relax#1\relax \else \advance\@dtl@tmpcount by -1\relax \ifnum\@dtl@tmpcount<0\relax \edef\@dtl@fracpart{\@dtl@fracpart#1}% \else \edef\@dtl@intpart{\@dtl@intpart#1}% \fi \expandafter\@dtl@gatherintfrac \fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtltrunc} % Truncates using PGF math engine. % (Third argument is the number of digits.) %\changes{2.26}{2016-07-20}{new} %This suffers from the same problems as \cs{dtlround}. Can % cause dimension too large error or rounding errors. % \begin{macrocode} \NewDocumentCommand \dtltrunc { m m m } { \ifnum#3=0\relax \exp_args:Nx \pgfmathparse { int ( floor ( #2 ) ) }% \let#1\pgfmathresult \else \exp_args:Nx \pgfmathparse { int ( 10 \exp_not:N ^ #3 ) } \let\dtl@tmpshift\pgfmathresult % \end{macrocode} %Need to be careful not to trigger the dimension too large error, %so this is a bit convoluted. % \begin{macrocode} \exp_args:Nx \pgfmathparse { int ( floor ( #2 ) ) } \let\dtl@int@trunc\pgfmathresult \exp_args:Nx \pgfmathparse { int ( floor ( ( #2 - \dtl@int@trunc ) * \dtl@tmpshift ) ) } % \end{macrocode} %\changes{2.26}{2016-07-20}{fixed bug caused by rounding errors} % This bit is awkward because simply dividing by multiples % of 10 in pgfmath can cause rounding errors, so need to % employ another method. % \begin{macrocode} \@dtl@tmpcount=0\relax \expandafter\@dtl@countdigits\pgfmathresult.\relax \advance\@dtl@tmpcount by -#3\relax \def\@dtl@intpart{}% \def\@dtl@fracpart{}% \expandafter\@dtl@gatherintfrac\pgfmathresult\relax \tl_if_empty:NT \@dtl@intpart { \tl_set:Nn \@dtl@intpart { 0 } } \edef\@dtl@intpart{\number\numexpr\dtl@int@trunc + \@dtl@intpart}% \edef#1{\@dtl@intpart.\@dtl@fracpart}% \fi } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlclip} % There isn't a clip in pgfmath so use \LaTeX3 instead. % \begin{macrocode} \NewDocumentCommand \dtlclip { m m } { \fp_set:Nn { \l__datatool_tmpa_fp } { #2 } \tl_set:Nx #1 { \fp_use:N { \l__datatool_tmpa_fp } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmin} % Minimum of two numbers using PGF math engine. % \begin{macrocode} \NewDocumentCommand \dtlmin { m m m } { \exp_args:Nxx \pgfmathmin { #2 } { #3 } \let#1\pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlminall} %\begin{definition} %\cs{dtlminall}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the minimum in the comma-separated list of % numbers. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand \dtlminall { m m } { \exp_args:Nx \pgfmathmin { #2 } { } \let#1\pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmax} % Maximum of two numbers using PGF math engine. % \begin{macrocode} \NewDocumentCommand \dtlmax { m m m } { \exp_args:Nx \pgfmathmax { #2 } { #3 } \let#1\pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmaxall} %\begin{definition} %\cs{dtlmaxall}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the maximum in the comma-separated list of % numbers. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand \dtlmaxall { m m } { \exp_args:Nx \pgfmathmax { #2 } { } \let#1\pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlabs} % Absolute value using PGF math engine. % \begin{macrocode} \NewDocumentCommand \dtlabs { m m } { \exp_args:Nx \pgfmathabs { #2 } \let#1\pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlneg} % Negative of a value using PGF math engine. % \begin{macrocode} \NewDocumentCommand \dtlneg { m m } { \exp_args:Nx \pgfmathneg { #2 } \let#1\pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlmeanforall} %\begin{definition} %\cs{dtlmeanforall}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the mean of all the numbers in the comma-separated list of % numbers. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand \dtlmeanforall { m m } { \tl_set:Nn \pgfmathresult { 0 } \int_zero:N \l__datatool_count_int \exp_args:No \clist_map_inline:nn { #2 } { \int_incr:N \l__datatool_count_int \exp_args:Nxx \pgfmathadd { \pgfmathresult } { ##1 } } \exp_args:Nxx \pgfmathdivide { \pgfmathresult } { \int_use:N \l__datatool_count_int } \tl_set_eq:NN #1 \pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlvarianceforall} %\begin{definition} %\cs{dtlvarianceforall}\oarg{mean}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the variance of all the numbers in the comma-separated list of % numbers. If the mean has already been computed it can be supplied % in the optional argument. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand {\dtlvarianceforall} { o m m } {% \IfNoValueTF { #1 } { \tl_set:Nn \pgfmathresult { 0 } \int_zero:N \l__datatool_count_int \exp_args:No \clist_map_inline:nn { #3 } { \int_incr:N \l__datatool_count_int \exp_args:Nxx \pgfmathadd { \pgfmathresult } { ##1 } } \exp_args:Nxx \pgfmathdivide { \pgfmathresult } { \int_use:N \l__datatool_count_int } \tl_set_eq:NN \@dtl@mean \pgfmathresult \tl_set:Nn #2 { 0 } \exp_args:No \clist_map_inline:nn { #3 } { \exp_args:Nxx \pgfmathsubtract { ##1 } { \@dtl@mean } \exp_args:Nxx \pgfmathmultiply { \pgfmathresult } { \pgfmathresult } \exp_args:Nxx \pgfmathadd { #2 } { \pgfmathresult } \tl_set_eq:NN #2 \pgfmathresult } } { \tl_set:Nx \@dtl@mean { #1 } \int_zero:N \l__datatool_count_int \tl_set:Nn #2 { 0 } \exp_args:No \clist_map_inline:nn { #3 } { \int_incr:N \l__datatool_count_int \exp_args:Nxx \pgfmathsubtract { ##1 } { \@dtl@mean } \exp_args:Nxx \pgfmathmultiply { \pgfmathresult } { \pgfmathresult } \exp_args:Nxx \pgfmathadd { #2 } { \pgfmathresult } \tl_set_eq:NN #2 \pgfmathresult } } \exp_args:Nxx \pgfmathdivide { #2 } { \int_use:N \l__datatool_count_int } \tl_set_eq:NN #2 \pgfmathresult } % \end{macrocode} %\end{macro} % %\begin{macro}{\dtlsdforall} %\begin{definition} %\cs{dtlsdforall}\oarg{mean}\marg{cs}\marg{num list} %\end{definition} % Defines \meta{cs} to the standard deviation of all the numbers in the comma-separated list of % numbers. If the mean has already been computed it can be supplied % in the optional argument. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand{\dtlsdforall} { o m m } {% \IfNoValueTF{#1}% {% \tl_set:Nn \pgfmathresult { 0 } \int_zero:N \l__datatool_count_int \exp_args:No \clist_map_inline:nn { #3 } { \int_incr:N \l__datatool_count_int \exp_args:Nxx \pgfmathadd { \pgfmathresult } { ##1 } } \exp_args:Nxx \pgfmathdivide { \pgfmathresult } { \int_use:N \l__datatool_count_int } \tl_set_eq:NN \@dtl@mean \pgfmathresult \tl_set:Nn #2 { 0 } \exp_args:No \clist_map_inline:nn { #3 } { \exp_args:Nxx \pgfmathsubtract { ##1 } { \@dtl@mean } \exp_args:Nxx \pgfmathmultiply { \pgfmathresult } { \pgfmathresult } \exp_args:Nxx \pgfmathadd { #2 } { \pgfmathresult } \tl_set_eq:NN #2 \pgfmathresult } } { \tl_set:Nn \@dtl@mean { #1 } \int_zero:N \l__datatool_count_int \tl_set:Nn #2 { 0 } \exp_args:No \clist_map_inline:nn { #3 } { \int_incr:N \l__datatool_count_int \exp_args:Nxx \pgfmathsubtract { ##1 } { \@dtl@mean } \exp_args:Nxx \pgfmathmultiply { \pgfmathresult } { \pgfmathresult } \exp_args:Nxx \pgfmathadd { #2 } { \pgfmathresult } \tl_set_eq:NN #2 \pgfmathresult } } \exp_args:Nxx \pgfmathdivide { #2 } { \int_use:N \l__datatool_count_int } \exp_args:Nx \pgfmathsqrt { \pgfmathresult } \tl_set_eq:NN #2 \pgfmathresult } % \end{macrocode} %\end{macro} % % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} % %\iffalse % \begin{macrocode} % % \end{macrocode} %\fi %\iffalse % \begin{macrocode} %<*datatool-pgfmath.sty> % \end{macrocode} %\fi %\section{datatool-pgfmath.sty} % Definitions of fixed-point commands that use the \sty{pgfmath} package. % Note that the newer \sty{datatool-l3fp} or \sty{datatool-lua} are % preferable. % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} % \end{macrocode} % Rollback releases: % \begin{macrocode} \DeclareRelease{v2.32}{2019-09-27}{datatool-pgfmath-2019-09-27.sty} \DeclareCurrentRelease{v3.3}{2025-03-25} % \end{macrocode} % Declare package: % \begin{macrocode} \ProvidesPackage{datatool-pgfmath}[2025/03/25 v3.3 (NLCT)] % \end{macrocode} % This package is deprecated. % \begin{macrocode} \PackageWarning{datatool-pgfmath}% {datatool-pgfmath.sty deprecated. Use \string\usepackage[math=pgfmath]{datatool} instead or (better still) \string\usepackage{datatool} } \RequirePackage[math=pgfmath]{datatool} % \end{macrocode} %\iffalse % \begin{macrocode} % % \end{macrocode} %\fi %\iffalse % \begin{macrocode} %<*datatool.sty> % \end{macrocode} %\fi %\section{datatool.sty} %\label{sec:code:datatool} %\subsection{Package Declaration} % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} % \end{macrocode} % Rollback releases: % \begin{macrocode} \DeclareRelease{v2.32}{2019-09-27}{datatool-2019-09-27.sty} \DeclareCurrentRelease{v3.3}{2025-03-25} % \end{macrocode} % Declare package: % \begin{macrocode} \ProvidesPackage{datatool}[2025/03/25 v3.3 (NLCT)] % \end{macrocode} %\changes{3.0}{2025-03-03}{dropped \sty{xkeyval}} % Version 3.0: no longer using \sty{xkeyval}. % Load required packages: % \begin{macrocode} \RequirePackage{ifthen} \RequirePackage{xfor} \RequirePackage{etoolbox} \RequirePackage{tracklang} % \end{macrocode} %\changes{2.0}{2009 February 27}{added etex as a required package} %\changes{2.23}{2015-07-11}{removed etex as a required package} % %Version 3.0: partial rewrite using \LaTeX3\ (mainly for the CSV file %parsing) so there's a mix of new and old style commands. %This rewrite needs to take into account the format %of dbtex files, which can be read and written by datatooltk. %There are three file format versions: 1, 2 %and 3. The first two are almost identical. Version 2 has the final %line that defines \cs{dtllastloadeddb}. The internal commands are %defined to allow a fast load that doesn't require any parsing to %to split on separators or to determine data types. This means that %the internal structure of the database needs to remain the same. %Also, it's not possible to enable \LaTeX3 syntax in the dbtex file %because the category code change will affect the data (the space %character in particular). The version 3 file format is new to %datatool v3.0. An undefined command on loading a dbtex will likely %indicate that a newer dbtex is being used with an old datatool %version and so should be considered incompatible. % %\subsection{Package Options} % % \begin{macrocode} \ExplSyntaxOn \cs_new:Nn \__datatool_char_to_hex:nN { \tl_set:Nx #2 { \int_to_hex:n { `#1 } } } % \end{macrocode} % %\begin{macro}{\@dtl@separator} % The data separator character (comma by default) is stored in % \cs{@dtl@separator}. % This is the separator used in external data files, not in the % \LaTeX\ code, which always uses a comma separator. % \begin{macrocode} \newcommand*{\@dtl@separator}{,} % \end{macrocode} %\end{macro} %\begin{macro}{\DTLsetseparator} %\begin{definition} %\cs{DTLsetseparator}\marg{char} %\end{definition} % The sets \cs{@dtl@separator}, and constructs the relevant macros % that require this character to be hardcoded into their definition. %\changes{3.0}{2025-03-03}{changed to new document command and detokenize} % \begin{macrocode} \NewDocumentCommand \DTLsetseparator { m } { \tl_if_single_token:nTF { #1 } { \tl_set:Nx \@dtl@separator { \tl_to_str:n { #1 } } \__datatool_char_to_hex:nN { #1 } \l__datatool_tmpa_tl \exp_args:NNx \regex_set:Nn \l__datatool_blank_row_regex { \exp_not:N \A \exp_not:N \cO (?: \exp_not:N \x { \l__datatool_tmpa_tl } ) * \exp_not:N \Z } } { \PackageError{datatool}{Separator ~ must ~ be ~ a ~ single ~ token ~ (found ~ \tl_count_tokens:n { #1 } ~ tokens) }{} } } \regex_new:N \l__datatool_blank_row_regex \DTLsetseparator{,} \ExplSyntaxOff % \end{macrocode} %\end{macro} % \begin{macrocode} \begingroup \catcode`\^^I12 % \end{macrocode} %\begin{macro}{\DTLsettabseparator} %\cs{DTLsettabseparator} makes it easier to set a tab separator. %\changes{2.10}{2012-07-18}{changed tab character to %\texttt{\textasciicircum\textasciicircum I}} % \begin{macrocode} \gdef\DTLsettabseparator{% \catcode`\^^I12 \DTLsetseparator{^^I}% } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLmaketabspace} %\changes{2.23}{2015-07-11}{restores tab catcode to 10} % \begin{macrocode} \gdef\DTLmaketabspace{% \catcode`\^^I10\relax } \endgroup % \end{macrocode} %\end{macro} % %\begin{macro}{\@dtl@delimiter} % The data delimiter character (double quote by default) is stored % in \cs{@dtl@delimiter}. This is used in external data files, not % in the \LaTeX\ code. % \begin{macrocode} \newcommand{\@dtl@delimiter}{"} % \end{macrocode} %\end{macro} % % \begin{macrocode} \ExplSyntaxOn % \end{macrocode} % This may have already been defined if \sty{datatool} was loaded % first. % \begin{macrocode} \tl_clear_new:N \l__datatool_tmpa_tl % \end{macrocode} % %\begin{macro}{\DTLsetdelimiter} %\begin{definition} %\cs{DTLsetdelimiter}\marg{char} %\end{definition} % This sets the delimiter. %\changes{3.0}{2025-03-03}{changed to new document command and set up reg %exp} % \begin{macrocode} \NewDocumentCommand \DTLsetdelimiter { m } { \tl_if_single_token:nTF { #1 } { \tl_set:Nx \@dtl@delimiter { \tl_to_str:n { #1 } } \__datatool_char_to_hex:nN { #1 } \l__datatool_tmpa_tl \exp_args:NNx \regex_set:Nn \l__datatool_delim_left_regex { \exp_not:N \A \exp_not:N \s * \exp_not:N \cO (?: \exp_not:N \x { \l__datatool_tmpa_tl } ) } \exp_args:NNx \regex_set:Nn \l__datatool_delim_both_regex { \exp_not:N \A \exp_not:N \s * \exp_not:N \cO (?: \exp_not:N \x { \l__datatool_tmpa_tl } ) ( .* ) \exp_not:N \cO (?: \exp_not:N \x { \l__datatool_tmpa_tl } ) \exp_not:N \s * \exp_not:N \Z } \exp_args:NNx \regex_set:Nn \l__datatool_delim_right_regex { \exp_not:N \cO (?: \exp_not:N \x { \l__datatool_tmpa_tl } ) \exp_not:N \s * \exp_not:N \Z } \exp_args:NNx \regex_set:Nn \l__datatool_escape_delim_bksl_regex { ( \exp_not:N \\ | \exp_not:N \cO ( \exp_not:N \x { \l__datatool_tmpa_tl } ) ) } \exp_args:NNx \regex_set:Nn \l__datatool_escape_delim_regex { ( \exp_not:N \cO ( \exp_not:N \x { \l__datatool_tmpa_tl } ) ) } \exp_args:NNx \regex_set:Nn \l__datatool_unescape_str_delim_bksl_regex { \exp_not:N \cO (?: \exp_not:N \x { 5c } ) ( \exp_not:N \cO ( \exp_not:N \x { \l__datatool_tmpa_tl } | \exp_not:N \x { 5c } ) ) } \exp_args:NNx \regex_set:Nn \l__datatool_unescape_str_double_delim_regex { \exp_not:N \cO (?: \exp_not:N \x { \l__datatool_tmpa_tl } ) ( \exp_not:N \cO ( \exp_not:N \x { \l__datatool_tmpa_tl } ) ) } \exp_args:NNx \regex_set:Nn \l__datatool_unescape_cs_double_delim_regex { ( \exp_not:N \c { ( \exp_not:N \x { \l__datatool_tmpa_tl } ) } ) \exp_not:N \x { \l__datatool_tmpa_tl } } \exp_args:NNx \regex_set:Nn \l__datatool_unescape_str_delim_regex { \exp_not:N \cO (?: \exp_not:N \x { 5c } ) ( \exp_not:N \cO ( \exp_not:N \x { \l__datatool_tmpa_tl } )) } \exp_args:NNx \regex_set:Nn \l__datatool_unescape_cs_delim_regex { \exp_not:N \c { ( \exp_not:N \x { \l__datatool_tmpa_tl } ) } } } { \PackageError{datatool}{Delimiter ~ must ~ be ~ a ~ single ~ token ~ (found ~ \tl_count_tokens:n { #1 } ~ tokens) }{} } } \regex_new:N \l__datatool_delim_left_regex \regex_new:N \l__datatool_delim_right_regex \regex_new:N \l__datatool_delim_both_regex \regex_new:N \l__datatool_escape_delim_regex \regex_new:N \l__datatool_escape_delim_bksl_regex \regex_new:N \l__datatool_unescape_str_delim_regex \regex_new:N \l__datatool_unescape_str_delim_bksl_regex \regex_new:N \l__datatool_unescape_cs_delim_regex \regex_new:N \l__datatool_unescape_str_double_delim_regex \regex_new:N \l__datatool_unescape_cs_double_delim_regex % \end{macrocode} %Initialise: % \begin{macrocode} \DTLsetdelimiter{"} % \end{macrocode} %\end{macro} % %Default database name. % \begin{macrocode} \tl_new:N \l__datatool_default_dbname_tl \tl_set:Nn \l__datatool_default_dbname_tl { untitled } % \end{macrocode} %Determine whether to use the local or global function. % \begin{macrocode} \bool_new:N \l__datatool_db_global_bool \bool_set_true:N \l__datatool_db_global_bool % \end{macrocode} % %Determine whether or not to trim spaces around new entries. % \begin{macrocode} \bool_new:N \l__datatool_new_element_trim_bool \bool_set_true:N \l__datatool_new_element_trim_bool % \end{macrocode} % %Version 3.0: provide boolean to check on the current new value %expansion setting: % \begin{macrocode} \bool_new:N \l__datatool_new_element_expand_bool % \end{macrocode} % %Determine whether or not to expand values before adding them to the %database. %\begin{macro}{\dtlexpandnewvalue} % Expand new value before adding to database % \begin{macrocode} \newcommand*{\dtlexpandnewvalue}{ \bool_set_true:N \l__datatool_new_element_expand_bool \def\@dtl@setnewvalue##1{ \protected@edef\@dtl@tmp{ ##1 } \bool_if:NT\l__datatool_new_element_trim_bool { \tl_trim_spaces:N \@dtl@tmp } \expandafter\@dtl@toks\expandafter{\@dtl@tmp} } } % \end{macrocode} %\end{macro} %\begin{macro}{\dtlnoexpandnewvalue} % Don't expand new value before adding to database % \begin{macrocode} \newcommand*{\dtlnoexpandnewvalue}{% \bool_set_false:N \l__datatool_new_element_expand_bool \def\@dtl@setnewvalue##1{ \bool_if:NTF\l__datatool_new_element_trim_bool { \tl_set:Nn \l__datatool_item_value_tl { ##1 } \tl_trim_spaces:N \l__datatool_item_value_tl \exp_args:NV \@dtl@toks \l__datatool_item_value_tl } { \@dtl@toks{##1} } } } % \end{macrocode} %\end{macro} %\begin{macro}{\@dtl@setnewvalue} % Do this by default. This will define \cs{@dtl@setnewvalue}. % \begin{macrocode} \dtlnoexpandnewvalue % \end{macrocode} %\end{macro} %Note that \verb|\__datatool_process_new_value:n| should be used to %preprocess values. That uses \cs{dtl@setnewvalue} to follow the %trim and expansion settings but then checks the store datum %boolean. %TODO remove \cs{@dtl@setnewvalue} and replace with another boolean? % %Determine whether or not to store values in the database in datum %format. % \begin{macrocode} \bool_new:N \l__datatool_db_store_datum_bool % \end{macrocode} % % Define options. The default-name value is expanded for consistency % because it will be expanded if used as a package option so it % should therefore also expand when used in \cs{DTLsetup}. % \begin{macrocode} \keys_define:nn { datatool } { default-name .str_set_x:N = \l__datatool_default_dbname_tl, global .bool_set:N = \l__datatool_db_global_bool, store-datum .bool_set:N = \l__datatool_db_store_datum_bool, separator .code:n = { \DTLsetseparator { #1 } }, delimiter .code:n = { \DTLsetdelimiter { #1 } }, new-value-trim .bool_set:N = \l__datatool_new_element_trim_bool, new-value-expand .choice:, new-value-expand / true .code:n = { \dtlexpandnewvalue }, new-value-expand / false .code:n = { \dtlnoexpandnewvalue }, new-value-expand .default:n = true, } \ExplSyntaxOff % \end{macrocode} % %If {datatool-base} may have already been loaded by another package, %in which case the options need processing now. % \begin{macrocode} \IfPackageLoadedTF{datatool-base} { \ProcessKeyOptions[datatool] } { % \end{macrocode} %The \sty{datatool-base} package hasn't been loaded, so pass all options %to that. % \begin{macrocode} \DeclareOption*{\PassOptionsToPackage{\CurrentOption}{datatool-base}} \ProcessOptions % \end{macrocode} % % Load base package: % \begin{macrocode} \RequirePackage{datatool-base} } % \end{macrocode} % % \begin{macrocode} \ExplSyntaxOn % \end{macrocode} % %Private variables. % %Token lists: % \begin{macrocode} \tl_new:N \l__datatool_item_key_tl \tl_new:N \l__datatool_item_type_tl \tl_new:N \l__datatool_item_head_tl \tl_new:N \l__datatool_item_value_tl \tl_new:N \l__datatool_item_currency_tl \tl_new:N \l__datatool_item_currency_ii_tl \tl_new:N \l__datatool_row_tl \tl_new:N \l__datatool_keydata_tl \tl_new:N \l__datatool_content_tl \tl_new:N \l__datatool_align_tl \tl_new:N \l__datatool_before_tl \tl_new:N \l__datatool_after_tl \tl_new:N \l__datatool_cs_builder_tl % \end{macrocode} %Numeric values that may be set to null to need a token list %variable. % \begin{macrocode} \tl_new:N \l__datatool_item_col_tl % \end{macrocode} %Replaces \cs{dtl@rowidx}: % \begin{macrocode} \tl_new:N \l__datatool_row_idx_tl % \end{macrocode} %Integers: % \begin{macrocode} \int_new:N \l__datatool_max_cols_int \int_new:N \l__datatool_col_idx_int \int_new:N \l__datatool_row_idx_int \int_new:N \l__datatool_item_type_int % \end{macrocode} %Sequences: % \begin{macrocode} \seq_new:N \l__datatool_column_indexes_seq \seq_new:N \l__datatool_column_keys_seq % \end{macrocode} %Floating point: % \begin{macrocode} \fp_new:N \l__datatool_min_fp \fp_new:N \l__datatool_min_ii_fp \fp_new:N \l__datatool_max_fp \fp_new:N \l__datatool_max_ii_fp \fp_new:N \l__datatool_total_ii_fp % \end{macrocode} % Temporary list: % \begin{macrocode} \clist_new:N \l__datatool_tmpa_clist % \end{macrocode} % %\begin{macro}{\DTLpar} % Many of the commands used by this package are short commands. % This means that you can't use \cs{par} % in the data. This command needs to be robust so it doesn't get % expanded when written to a file. We also can't just use a synonym % for \cs{@@par} because it may be used in a context where \cs{par} % has a different meaning to \cs{@@par}. %\changes{2.14}{2013-06-28}{changed to \cs{let}} %\changes{2.18}{2013-09-06}{changed back to a robust command} % \begin{macrocode} \newrobustcmd{\DTLpar}{\par} % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLaction} %\changes{3.0}{2025-03-03}{new} %\begin{definition} %\cs{DTLaction}\oarg{options}\marg{action} %\end{definition} %General purpose action command to avoid typing long command names. %Note that since some commands may only have a local effect, it's %not possible to include grouping in \cs{DTLaction} (although some %of the underlying commands may introduce grouping). Therefore the %options need to be reset at the start, and so there's no provision %for use of those options in \cs{DTLsetup}. % \begin{macrocode} \NewDocumentCommand \DTLaction { o m } { % \end{macrocode} %Initialise settings. %Column index: % \begin{macrocode} \int_zero:N \l__datatool_action_column_int \int_zero:N \l__datatool_action_column_ii_int % \end{macrocode} %Column key: % \begin{macrocode} \tl_clear:N \l__datatool_action_key_tl \tl_clear:N \l__datatool_action_key_ii_tl % \end{macrocode} % Data type: % \begin{macrocode} \int_set_eq:NN \l__datatool_action_type_int \c_datatool_unknown_int % \end{macrocode} %Row index: % \begin{macrocode} \int_zero:N \l__datatool_action_row_int \int_zero:N \l__datatool_action_row_ii_int % \end{macrocode} %Value: % \begin{macrocode} \tl_set:Nn \l__datatool_action_value_tl { \q_no_value } % \end{macrocode} %List settings: % \begin{macrocode} \clist_clear:N \l__datatool_action_options_clist \clist_clear:N \l__datatool_action_assign_clist \clist_clear:N \l__datatool_action_keys_clist \clist_clear:N \l__datatool_action_columns_clist \seq_clear:N \l__datatool_action_columns_seq \seq_clear:N \l__datatool_action_names_seq % \end{macrocode} %Return formatting: % \begin{macrocode} \bool_set_false:N \l__datatool_action_datum_bool \int_set:Nn \l__datatool_action_datum_round_int { -1 } \bool_set_true:N \l__datatool_action_datum_locale_decimal_bool \bool_set_false:N \l__datatool_action_datum_locale_integer_bool \tl_set:Nn \l__datatool_action_datum_currency_tl { \l__datatool_item_currency_tl } % \end{macrocode} %Return Values: % \begin{macrocode} \clist_clear:N \l__datatool_action_return_clist \tl_set_eq:NN \l__datatool_action_return_tl \dtlnovalue \prop_clear:N \l__datatool_action_return_prop \IfValueT { #1 } { \keys_set:nn { datatool/action } { #1 } } % \end{macrocode} %Get database name for actions that require a single name: % \begin{macrocode} \seq_if_empty:NTF \l__datatool_action_names_seq { % \end{macrocode} % Name not set so use default name. % \begin{macrocode} \tl_set_eq:NN \l__datatool_action_name_tl \l__datatool_default_dbname_tl } { % \end{macrocode} % Set name to first one in the list. % \begin{macrocode} \tl_set:Nx \l__datatool_action_name_tl { \seq_item:Nn \l__datatool_action_names_seq { \c_one_int } } } % \end{macrocode} %Save action name: % \begin{macrocode} \tl_set:Nx \l__datatool_action_tl { \tl_trim_spaces:n { #2 } } % \end{macrocode} %Do action: % \begin{macrocode} \cs_if_exist_use:cF { __datatool_action_ \l__datatool_action_tl : } { \PackageError { datatool } { Unknown ~ action ~ ` \l__datatool_action_tl ' } { The ~ action ~ in ~ the ~ mandatory ~ argument ~ of ~ \token_to_str:N \DTLaction \c_space_tl ~ is ~ not ~ recognised } } % \end{macrocode} %Assign return values to provided token list variables, if set. % \begin{macrocode} \clist_if_empty:NF \l__datatool_action_return_clist { \keyval_parse:NNV \__datatool_cskey_missing_val:n \__datatool_action_get:nn \l__datatool_action_return_clist } } % \end{macrocode} %\end{macro} % %Internal variables for \cs{DTLaction}: % \begin{macrocode} \tl_new:N \l__datatool_action_tl \tl_new:N \l__datatool_action_name_tl \tl_new:N \l__datatool_action_key_tl \tl_new:N \l__datatool_action_key_ii_tl \int_new:N \l__datatool_action_column_int \int_new:N \l__datatool_action_column_ii_int \int_new:N \l__datatool_action_row_int \int_new:N \l__datatool_action_row_ii_int \int_new:N \l__datatool_action_type_int \int_set_eq:NN \l__datatool_action_type_int \c_datatool_unknown_int \tl_new:N \l__datatool_action_value_tl \tl_set:Nn \l__datatool_action_value_tl { \q_no_value } \bool_new:N \l__datatool_action_datum_bool \seq_new:N \l__datatool_action_names_seq \clist_new:N \l__datatool_action_options_clist \clist_new:N \l__datatool_action_assign_clist \clist_new:N \l__datatool_action_return_clist \clist_new:N \l__datatool_action_keys_clist \clist_new:N \l__datatool_action_columns_clist \seq_new:N \l__datatool_action_columns_seq % \end{macrocode} %Scratch variables. % \begin{macrocode} \tl_new:N \l__datatool_action_tmpa_tl \tl_new:N \l__datatool_action_tmpb_tl \seq_new:N \l__datatool_action_tmp_options_seq \seq_new:N \l__datatool_action_tmp_data_seq % \end{macrocode} %Define keys. (These can only be set in the optional argument of %\cs{DTLaction}.) % \begin{macrocode} \keys_define:nn { datatool/action } { name .code:n = { \exp_args:NNx \seq_set_from_clist:Nn \l__datatool_action_names_seq { #1 } }, name .value_required:n = true, key .str_set_x:N = \l__datatool_action_key_tl, key .value_required:n = true, key2 .str_set_x:N = \l__datatool_action_key_ii_tl, key2 .value_required:n = true, column .int_set:N = \l__datatool_action_column_int, column .value_required:n = true, column2 .int_set:N = \l__datatool_action_column_ii_int, column2 .value_required:n = true, row .int_set:N = \l__datatool_action_row_int, row .value_required:n = true, row2 .int_set:N = \l__datatool_action_row_ii_int, row2 .value_required:n = true, value .tl_set:N = \l__datatool_action_value_tl, value .value_required:n = true, expand-value .tl_set_x:N = \l__datatool_action_value_tl, expand-value .value_required:n = true, expand-once-value .code:n = { \tl_set:No \l__datatool_action_value_tl { #1 } }, expand-once-value .value_required:n = true, options .clist_set:N = \l__datatool_action_options_clist, options .value_required:n = true, assign .clist_set:N = \l__datatool_action_assign_clist, assign .value_required:n = true, keys .clist_set:N = \l__datatool_action_keys_clist, keys .value_required:n = true, columns .clist_set:N = \l__datatool_action_columns_clist, columns .value_required:n = true, return .clist_set:N = \l__datatool_action_return_clist, return .value_required:n = true, type .choice:, type .value_required:n = true, type / string .code:n = { \int_set_eq:NN \l__datatool_action_type_int \c_datatool_string_int }, type / integer .code:n = { \int_set_eq:NN \l__datatool_action_type_int \c_datatool_integer_int }, % \end{macrocode} %Synonym: % \begin{macrocode} type / int .code:n = { \int_set_eq:NN \l__datatool_action_type_int \c_datatool_integer_int }, type / decimal .code:n = { \int_set_eq:NN \l__datatool_action_type_int \c_datatool_decimal_int }, % \end{macrocode} %Synonym: % \begin{macrocode} type / real .code:n = { \int_set_eq:NN \l__datatool_action_type_int \c_datatool_real_int }, type / currency .code:n = { \int_set_eq:NN \l__datatool_action_type_int \c_datatool_currency_int }, type / datetime .code:n = { \int_set_eq:NN \l__datatool_action_type_int \c_datatool_datetime_int }, % \end{macrocode} %Synonym: % \begin{macrocode} type / timestamp .code:n = { \int_set_eq:NN \l__datatool_action_type_int \c_datatool_datetime_int }, type / date .code:n = { \int_set_eq:NN \l__datatool_action_type_int \c_datatool_date_int }, type / time .code:n = { \int_set_eq:NN \l__datatool_action_type_int \c_datatool_time_int }, datum .code:n = { \tl_if_eq:nnTF { #1 } { false } { \bool_set_false:N \l__datatool_action_datum_bool } { \bool_set_true:N \l__datatool_action_datum_bool \tl_if_eq:nnF { #1 } { true } { \keys_set:nn { datatool/action/datum } { #1 } } } }, datum .default:n = true , } % \end{macrocode} % Sub keys for datum option: % \begin{macrocode} \int_new:N \l__datatool_action_datum_round_int \int_set:Nn \l__datatool_action_datum_round_int { -1 } \bool_new:N \l__datatool_action_datum_locale_decimal_bool \bool_set_true:N \l__datatool_action_datum_locale_decimal_bool \bool_new:N \l__datatool_action_datum_locale_integer_bool \tl_new:N \l__datatool_action_datum_currency_tl \tl_set:Nn \l__datatool_action_datum_currency_tl { \l__datatool_item_currency_tl } % \end{macrocode} %Define keys: % \begin{macrocode} \keys_define:nn { datatool/action/datum } { round .code:n = { \tl_if_eq:nnTF { #1 } { false } { \int_set:Nn \l__datatool_action_datum_round_int { -1 } } { \int_set:Nn \l__datatool_action_datum_round_int { #1 } } } , round .default:n = 0 , locale-decimal .bool_set:N = \l__datatool_action_datum_locale_decimal_bool , locale-integer .bool_set:N = \l__datatool_action_datum_locale_integer_bool , currency .choice: , currency / false .code:n = { \tl_clear:N \l__datatool_action_datum_currency_tl } , currency / match .code:n = { \tl_set:Nn \l__datatool_action_datum_currency_tl { \l__datatool_item_currency_tl } } , currency / default .code:n = { \tl_set:Nn \l__datatool_action_datum_currency_tl { \@dtl@currency } } , currency / unknown .code:n = { \tl_set:Nn \l__datatool_action_datum_currency_tl { #1 } } , currency .default:n = default , } % \end{macrocode} %Return value(s): % \begin{macrocode} \tl_new:N \l__datatool_action_return_tl \tl_set_eq:NN \l__datatool_action_return_tl \dtlnovalue \tl_new:N \l__datatool_action_secondary_tl \prop_new:N \l__datatool_action_return_prop % \end{macrocode} % % The datum option only governs the secondary return results. % The primary return result isn't converted. % % Secondary return result is a string: % \begin{macrocode} \cs_new:Nn \__datatool_put_return_action_string:nn { \tl_if_head_eq_meaning:nNTF { #2 } \__datatool_datum:w { % \end{macrocode} %In weird format. Needs conversion. % \begin{macrocode} \prop_put:Nnx \l__datatool_action_return_prop { #1 } { #2 } } { \tl_if_head_eq_meaning:nNTF { #2 } \__datatool_datum:nnnn { % \end{macrocode} %Already in datum format. Leave it as is. % \begin{macrocode} \prop_put:Nnn \l__datatool_action_return_prop { #1 } { #2 } } { \bool_if:NTF \l__datatool_action_datum_bool { \prop_put:Nnn \l__datatool_action_return_prop { #1 } { \__datatool_datum:nnnn { #2 } { } { } { \c_datatool_string_int } } } { \prop_put:Nnn \l__datatool_action_return_prop { #1 } { #2 } } } } } \cs_generate_variant:Nn \__datatool_put_return_action_string:nn { nV, nv, nx, xV } % \end{macrocode} %Secondary return result is an integer: % \begin{macrocode} \cs_new:Nn \__datatool_put_return_action_int:nn { \bool_if:NTF \l__datatool_action_datum_bool { \bool_if:NTF \l__datatool_action_datum_locale_integer_bool { \DTLdecimaltolocale { #2 } \l__datatool_action_secondary_tl \prop_put:Nnx \l__datatool_action_return_prop { #1 } { \exp_not:N \__datatool_datum:nnnn { \l__datatool_action_secondary_tl } { #2 } { } { \exp_not:N \c_datatool_integer_int } } } { \tl_if_head_eq_meaning:nNTF { #2 } \__datatool_datum:w { % \end{macrocode} %In weird format. Needs conversion. % \begin{macrocode} \prop_put:Nnx \l__datatool_action_return_prop { #1 } { #2 } } { \tl_if_head_eq_meaning:nNTF { #2 } \__datatool_datum:nnnn { % \end{macrocode} %Already in datum format. Leave it as is. % \begin{macrocode} \prop_put:Nnn \l__datatool_action_return_prop { #1 } { #2 } } { \prop_put:Nnn \l__datatool_action_return_prop { #1 } { \__datatool_datum:nnnn { #2 } { #2 } { } { \c_datatool_integer_int } } } } } } { \prop_put:Nnn \l__datatool_action_return_prop { #1 } { #2 } } } \cs_generate_variant:Nn \__datatool_put_return_action_int:nn { nV, nv, nx } % \end{macrocode} % Secondary return result is a decimal. % \begin{macrocode} \cs_new:Nn \__datatool_put_return_action_decimal:nn { \tl_set:Nn \l__datatool_action_secondary_tl { #2 } \__datatool_rm_weird_datum:N \l__datatool_action_secondary_tl \exp_args:NV \tl_if_head_eq_meaning:nNTF \l__datatool_action_secondary_tl \__datatool_datum:nnnn { \tl_set:Nx \l__datatool_action_secondary_tl { \DTLdatumvalue { \l__datatool_action_secondary_tl } } } \bool_if:NTF \l__datatool_action_datum_bool { \int_compare:nNnT { \l__datatool_action_datum_round_int } > { -1 } { \dtlround \l__datatool_action_secondary_tl \l__datatool_action_secondary_tl { \int_use:N \l__datatool_action_datum_round_int } } \bool_if:NT \l__datatool_action_datum_locale_decimal_bool { \exp_args:NV \DTLdecimaltolocale \l__datatool_action_secondary_tl \l__datatool_action_secondary_tl } \tl_if_empty:NF \l__datatool_action_datum_currency_tl { \tl_if_eq:NnTF \l__datatool_action_datum_currency_tl { \l__datatool_item_currency_tl } { \tl_if_empty:NF \l__datatool_item_currency_tl { \tl_set:Nx \l__datatool_action_secondary_tl { \exp_not:N \DTLfmtcurrency { \exp_not:V \l__datatool_item_currency_tl } { \exp_not:V \l__datatool_action_secondary_tl } } } } { \tl_if_eq:NnTF \l__datatool_action_datum_currency_tl { \@dtl@currency } { \tl_set:Nx \l__datatool_action_secondary_tl { \exp_not:N \DTLfmtcurrency { \exp_not:V \@dtl@currency } { \exp_not:V \l__datatool_action_secondary_tl } } } { \tl_set:Nx \l__datatool_action_secondary_tl { \exp_not:N \DTLfmtcurrency { \exp_not:V \l__datatool_action_datum_currency_tl } { \exp_not:V \l__datatool_action_secondary_tl } } } } } \prop_put:Nnx \l__datatool_action_return_prop { #1 } { \exp_not:N \__datatool_datum:nnnn { \exp_not:V \l__datatool_action_secondary_tl } { #2 } { } { \exp_not:N \c_datatool_decimal_int } } } { \prop_put:NnV \l__datatool_action_return_prop { #1 } \l__datatool_action_secondary_tl } } \cs_generate_variant:Nn \__datatool_put_return_action_decimal:nn { nV, nv, nx } % \end{macrocode} % Secondary result should be parsed if applicable. % \begin{macrocode} \cs_new:Nn \__datatool_put_return_action_parse:nn { \bool_if:NTF \l__datatool_action_datum_bool { \__datatool_parse:Nn \l__datatool_action_secondary_tl { #2 } } { \tl_set:Nn \l__datatool_action_secondary_tl { #2 } \__datatool_rm_weird_datum:N \l__datatool_action_secondary_tl } \prop_put:NnV \l__datatool_action_return_prop { #1 } \l__datatool_action_secondary_tl } \cs_generate_variant:Nn \__datatool_put_return_action_parse:nn { VV, xV } % \end{macrocode} % % Assign secondary return properties using column keys as name and value % obtained from corresponding element in the given row specs. % \begin{macrocode} \cs_new:Nn \__datatool_set_return_from_row:nn { \int_step_inline:nn { \DTLcolumncount { #2 } } { \__datatool_get_entry_from_row:Nnn \l__datatool_item_value_tl { ##1 } { #1 } \datatool_if_null:NF \l__datatool_item_value_tl { \@dtl@getkeyforcolumn \l__datatool_item_key_tl { #2 } { ##1 } \__datatool_put_return_action_parse:VV \l__datatool_item_key_tl \l__datatool_item_value_tl } } } \cs_generate_variant:Nn \__datatool_set_return_from_row:nn { Vn } % \end{macrocode} % %Add to options sequence unless already present: % \begin{macrocode} \cs_new:Nn \__datatool_add_action_option:n { \seq_if_in:NnF \l__datatool_action_tmp_options_seq { #1 } { \seq_put_right:Nn \l__datatool_action_tmp_options_seq { #1 } } } % \end{macrocode} %Tests if the given option has been added: % \begin{macrocode} \cs_new:Nn \__datatool_if_action_option:nTF { \seq_if_in:NnTF \l__datatool_action_tmp_options_seq { #1 } { #2 } { #3 } } \cs_new:Nn \__datatool_if_action_option:nT { \seq_if_in:NnT \l__datatool_action_tmp_options_seq { #1 } { #2 } } % \end{macrocode} % %\begin{macro}{\DTLifaction} %\begin{definition} %\cs{DTLifaction}\marg{prop-key}\marg{true}\marg{false} %\end{definition} %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \newcommand{\DTLifaction}[3]{ \tl_if_empty:nTF { #1 } { \tl_if_eq:NNTF \l__datatool_action_return_tl \dtlnovalue { #3 } { #2 } } { \prop_if_in:NnTF \l__datatool_action_return_prop { #1 } { #2 } { #3 } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\DTLget} %Get the return value. The first argument should either be empty or %a property key. The second argument should be a token list in which %to store the return value. %\changes{3.0}{2025-03-03}{new} % \begin{macrocode} \NewDocumentCommand \DTLget { O{} m } { \tl_if_blank:nTF { #1 } { \tl_set_eq:NN #2 \l__datatool_action_return_tl } { \__datatool_action_get:nn { #2 } { #1 } } } % \end{macrocode} %\end{macro} % \begin{macrocode} \cs_new:Nn \__datatool_action_get:nn { \tl_if_single:nTF { #1 } { \prop_get:NnN \l__datatool_action_return_prop { #2 } #1 \quark_if_no_value:NT #1 { \tl_set_eq:NN #1 \dtlnovalue } } { \PackageError { datatool } { Control ~ sequence ~ expected ~ (to ~ store ~ return ~ value): ~ found ~ \tl_to_str:n { #1 } } { To ~ fetch ~ an ~ action ~ return ~ value ~ you ~ need ~ to ~ provide ~ a ~ command ~ to ~ be ~ defined ~ to ~ the ~ returned ~ value } } } % \end{macrocode} % \begin{macrocode} \cs_new:Nn \__datatool_cskey_missing_val:n { \PackageError { datatool} { Invalid ~ cs=key ~ assignment ~ syntax ~ in ~ `\tl_to_str:n { #1 }' : ~ missing ~ key ~ or ~ property ~ name } { Column ~ or ~ property ~ assignment ~ lists ~ need ~ to ~ have ~ each ~ element ~ in ~ the ~ list ~ in ~ the ~ form ~ =