周数宏

周数宏

是否有宏或包来计算ISO 周数或者有人能帮我解决一下吗?

答案1

没有这样的包,但是 Taco Hoekwater 编写了一个名为的宏,calendarweek您可以使用它。您可以在 CTAN 上找到它平日

备注。该宏在 2015 年的前 4 天返回 0,而第 5 天返回第 2 周。由于未解释算法,因此似乎没有简单的方法可以找出问题所在。不过大多数星期都有正确的数字。

这是一个关于如何使用它的最小示例:

\documentclass{article}
%% calendarweek.tex
%% 2006 (C) Taco Hoekwater, public domain
%% Watch out: Dec 29 can be week 1 of the next year; and Jan 3 can be
%% week 53 of the previous year.

\def\Expr#1{\the\numexpr #1\relax}

\def\Modulonumber#1#2{\Expr{#2-((((#2+(#1/2))/#1)-1)*#1)}}
\def\Divisionnumber#1#2{\Expr{(2*#2-#1)/(2*#1)}}

\def\Mod#1#2{\Modulonumber{\Expr{#2}}{\Expr{#1}}}
\def\Div#1#2{\Divisionnumber{\Expr{#2}}{\Expr{#1}}}

\def\Jday#1#2#3%
    {\Expr{#1+\Div{((153*(\Expr{#2+(12*(\Div{14-#2}{12}))-3}))+2)}
     {5}+365*(\Expr{#3+4800-(\Div{14-#2}{12})})+
      (\Div{\Expr{#3+4800-(\Div{14-#2}{12})}}{4})-
      (\Div{\Expr{#3+4800-(\Div{14-#2}{12})}}{100})+
      (\Div{\Expr{#3+4800-(\Div{14-#2}{12})}}{400})-32045 }}

\def\cwhlp#1#2#3%
    {\Expr{\Mod {\Mod {\Mod {\Expr
     {\Jday{#1}{#2}{#3}+31741-\Mod{\Jday{#1}{#2}{#3}}{7}}}%
     {146097}}{36524}}{1461}}}

\def\calendarweek#1#2#3%
  {\Expr{\Expr{\Div{\Expr{\Mod{\cwhlp{#1}{#2}{#3}-
    \Expr{\Div{\cwhlp{#1}{#2}{#3}}{1460}}}{365}+
         \Expr{\Div{\cwhlp{#1}{#2}{#3}}{1460}}}}{7} +1}}}

\begin{document}

\calendarweek{28}{2}{2011}

\end{document}

答案2

我仔细研究了 Yiannis Lazarides 的回答中发布的 Taco Hoekwater 的上述代码,并试图理解它。结果是一个没有上述错误的注释版本,该错误发生在从星期四开始的所有年份(例如 2004 年、2015 年……),导致第一周的值为 -1 而不是 1。

最开始的整数除法导致了这个问题。(不幸的是,我只在最后看了它 :/)然而,现在几乎到处都有评论。只有 2 个魔法数字我还无法解释。

我还删除了一些不必要的\Expr括号。它只是一个没有条件或临时变量的算术计算。两者都可以提高效率。但我现在没有这样做,因为我不想对代码进行太多更改。

这是一个很好的例子,说明了为什么你应该创建其他人可读且易懂的代码。这使得调整和更正变得更容易。

代码:

\documentclass{article}

\usepackage{pgffor}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% https://tex.stackexchange.com/questions/10949/macro-for-week-number
%% calendarweek.tex
%% 2006 (C) Taco Hoekwater, public domain
%% Watch out: Dec 29 can be week 1 of the next year; and Jan 3 can be
%% week 53 of the previous year.
%%
%% 2021 (C) dexteritas
%% corrected and commented
%% Years that begin with a thursday will now give week 1 instead of -1 for the first week.

\def\Expr#1{\the\numexpr #1\relax}

% modulo
\def\Modulonumber#1#2{\Expr{#2 - \Divisionnumber{#1}{#2} * #1}}
% integer division (reminder is discarded)
\def\Divisionnumber#1#2{\Expr{(#2 + #1/2) / #1 - 1}} % <- NEW: works also for 0/x=0

% modulo with Expr
% #1 modulo #2
\def\Mod#1#2{\Modulonumber{\Expr{#2}}{\Expr{#1}}}
% integer division with Expr
% floor(#1 / #2)
\def\Div#1#2{\Divisionnumber{\Expr{#2}}{\Expr{#1}}}

\def\Jday#1#2#3{%
    \Expr{%
        % day
        #1%
        %
        % Offset day of year from 1st of march for given month
        % returns for given month: 1: 306, 2: 337, 3: 0, 4: 31, 5: 61, 6: 92, 7: 122, 8: 153, 9: 184, 10: 214, 11: 245, 12: 275
        % magic number 153: 153/5=30.6 (leads to the sequence above (adds 30 or 31 for each month)
        +\Div{%
            (153*(%
                % month + (12 if month is jan or feb else 0) - 3 returns: jan = 10, feb = 11, mar = 0, apr = 1, ...
                #2 +(12*(%
                    % if month (#2) is 1 (= january) or 2 (= february) than 1 else 0
                    % \Div{14-#2}{12}
                    \Div{14-#2}{12}%
                ))-3%
            )+2)%
        }{%
            5%
        }%
        %
        % Sum 365 for each year:
        % 365 * (year + 4800 - (1 if month is jan or feb else 0))
        % 4800 (= 12 * 400) is a cycle of same years
        +365*(#3+4800-(\Div{14-#2}{12}))%
        %
        % calculate leapyear if dividable by: 4 yes (1), 100 no (-1), 400 yes (1)
        % add 1 for every 4th year (see leapyear definition)
        +(\Div{#3+4800-(\Div{14-#2}{12})}{4})%
        % subtract 1 for every 100th year (see leapyear definition)
        -(\Div{#3+4800-(\Div{14-#2}{12})}{100})%
        % add 1 for every 400th year (see leapyear definition)
        +(\Div{#3+4800-(\Div{14-#2}{12})}{400})%
        %
        % magic number? 32045
        -32045%
    }
}

\def\cwhlp#1#2#3{%
    % returns wednesday of the current week counted within a period of 4 years
    % return value is in [0, 1460]
    \Expr{%
        \Mod{%
            \Mod{%
                \Mod{%
                    \Jday{#1}{#2}{#3}%
                    % magic number? 31741
                    +31741%
                    % day of week 0=monday, 1=tuesday, ...
                    -\Mod{\Jday{#1}{#2}{#3}}{7}%
                }{%
                    % days of 400 years: 146097 = 365 * 400 + 100 - 4 + 1
                    % +100=400/4 (for dividable by 4 is leapyear)
                    % -4=400/-100 (for dividable by 100 is not a leapyear)
                    % +1=400/400 (for dividable by 400 is a leapyear)
                    146097%
                }%
            }{%
                % days of 100 years: 36524 = 365 * 100 + 25 - 1
                % +25 (for dividable by 4 is leapyear)
                % -1 (for dividable by 100 is not a leapyear)
                36524%
            }%
         }{%
             % days of 4 years: 1461 = 365 * 4 + 1 (day for 1 leapyear)
             1461%
         }%
     }%
}

\def\calendarweek#1#2#3{%
    \Expr{%
        % 1460 => 364 + 1 => 365
        % 364 / 7 = 52 (weeks)
        % 365 / 7 = 53 (weeks)
        \Div{%
            % wednesday of current week within the year (?)
            % value is in [0, 364]
            \Mod{%
                \cwhlp{#1}{#2}{#3}%
                % subtract 1 if \cwhlp is 1460
                -\Div{%
                    \cwhlp{#1}{#2}{#3}%
                }{%
                    % days of 4 years without leapyear days: 1460 = 365 * 4
                    1460%
                }%
            }{%
                % days of 1 years without leapyear day: 365
                365%
            }%
            %
            % adds 1 if \cwhlp is 1460 again (?)
            +\Div{%
                \cwhlp{#1}{#2}{#3}%
            }{%
                % days of 4 years without leapyear days: 1460 = 365 * 4
                1460%
            }%
        }{%
            7%
        }%
        % to start with week 1 instead of 0
        +1%
    }%
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}

2021-06-21 is week \calendarweek{21}{06}{2021}.

\medskip
\textbf{The week of the first 7 days of a year:}

\foreach \xyear in {2000,...,2030}{
    \xyear:
    \foreach \xday in {1,...,7}{%
        \calendarweek{\xday}{01}{\xyear}
    }
    
}

\end{document}

结果:

latex 代码的结果

答案3

以下是解释3:

\documentclass{minimal}

\usepackage{expl3}

\ExplSyntaxOn

%% http://www.tondering.dk/claus/cal/week.php#calcweekno
\int_new:N \l_week_number_year_int
\int_new:N \l_week_number_month_int
\int_new:N \l_week_number_day_int
\int_new:N \l_week_number_a_int
\int_new:N \l_week_number_b_int
\int_new:N \l_week_number_c_int
\int_new:N \l_week_number_s_int
\int_new:N \l_week_number_e_int
\int_new:N \l_week_number_f_int
\int_new:N \l_week_number_g_int
\int_new:N \l_week_number_d_int
\int_new:N \l_week_number_n_int
\int_new:N \l_week_number_W_int

\cs_new:Nn \week_number:nnn {

  \int_set:Nn \l_week_number_year_int { #1 }
  \int_set:Nn \l_week_number_month_int { #2 }
  \int_set:Nn \l_week_number_day_int { #3 }

  \int_compare:nNnTF { \l_week_number_month_int } < { 3 } % jan or feb
  { % true

    \int_set:Nn \l_week_number_a_int { \l_week_number_year_int - 1 }

    \int_set:Nn \l_week_number_b_int {
      \int_div_truncate:nn { \l_week_number_a_int } { 4 }
      - \int_div_truncate:nn { \l_week_number_a_int } { 100 }
      + \int_div_truncate:nn { \l_week_number_a_int } { 400 }
    }

    \int_set:Nn \l_week_number_c_int {
      \int_div_truncate:nn { \l_week_number_a_int - 1 } { 4 }
      - \int_div_truncate:nn { \l_week_number_a_int - 1 } { 100 }
      + \int_div_truncate:nn { \l_week_number_a_int - 1 } { 400 }
    }

    \int_set:Nn \l_week_number_s_int {
      \l_week_number_b_int - \l_week_number_c_int }

    \int_zero:N \l_week_number_e_int

    \int_set:Nn \l_week_number_f_int { \l_week_number_day_int - 1
      + 31 * ( \l_week_number_month_int - 1 ) }

  } % end true
  { % false

    \int_set_eq:NN \l_week_number_a_int \l_week_number_year_int

    \int_set:Nn \l_week_number_b_int {
      \int_div_truncate:nn { \l_week_number_a_int } { 4 }
      - \int_div_truncate:nn { \l_week_number_a_int } { 100 }
      + \int_div_truncate:nn { \l_week_number_a_int } { 400 }
    }

    \int_set:Nn \l_week_number_c_int {
      \int_div_truncate:nn { \l_week_number_a_int - 1 } { 4 }
      - \int_div_truncate:nn { \l_week_number_a_int - 1 } { 100 }
      + \int_div_truncate:nn { \l_week_number_a_int - 1 } { 400 }
    }

    \int_set:Nn \l_week_number_s_int {
      \l_week_number_b_int - \l_week_number_c_int }

    \int_set:Nn \l_week_number_e_int { \l_week_number_s_int + 1 }

    \int_set:Nn \l_week_number_f_int { \l_week_number_day_int
      + \int_div_truncate:nn {
        153 * ( \l_week_number_month_int - 3 ) + 2 } { 5 }
      + 58 + \l_week_number_s_int }

  } % end false

  \int_set:Nn \l_week_number_g_int {
    \int_mod:nn { \l_week_number_a_int + \l_week_number_b_int } { 7 }  }

  \int_set:Nn \l_week_number_d_int {
    \int_mod:nn { \l_week_number_f_int + \l_week_number_g_int
      - \l_week_number_e_int } { 7 }  }

  \int_set:Nn \l_week_number_n_int {
    \l_week_number_f_int + 3 - \l_week_number_d_int }

  \int_compare:nNnTF { \l_week_number_n_int } < { 0 }
  { %true

    \int_set:Nn \l_week_number_W_int { 53
      - \int_div_truncate:nn { \l_week_number_g_int
        - \l_week_number_s_int } { 5 } }

  } % end true
  { % false

    \int_compare:nNnTF { \l_week_number_n_int } > { 364
      + \l_week_number_s_int }
    { % true

      \int_set:Nn \l_week_number_W_int { 1 }

    } % end true
    { % false

      \int_set:Nn \l_week_number_W_int { \int_div_truncate:nn {
          \l_week_number_n_int } { 7 } + 1 }

    } % end false

  } % end false

}
\ExplSyntaxOff

\begin{document}

\ExplSyntaxOn
\week_number:nnn { 2010 } { 01 } { 01 }
\int_to_arabic:n { \l_week_number_W_int }
\ExplSyntaxOff

\end{document}

答案4

只是一个想法:

datenumber包可以为您提供数字形式的日期。通过减去同一年 1 月 1 日的数字并加一,您可以得到一年中的日期。然后,星期数约为该数字除以七。

因为 ISO 周按照定义是从星期一开始的,所以您必须添加偏移量:

[ISO week number] = 
 floor( ( [number of selected date] - [number of first Monday of the year] + 1 ) / 7 ) + 1 

该软件包还使您能够检查星期几,从而找到一年中的第一个星期一。

我不确定我是否正确理解了 ISO 周数,并且我可能对公式有误解(现在是凌晨 1:30),但你明白我的意思。

相关内容