是否有宏或包来计算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}
结果:
答案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),但你明白我的意思。