ISO-8601 周编号与 PHP 中的“Outlook”编号
Posted
技术标签:
【中文标题】ISO-8601 周编号与 PHP 中的“Outlook”编号【英文标题】:ISO-8601 week numbering vs "Outlook" numbering in PHP 【发布时间】:2010-02-04 18:11:02 【问题描述】:我最近遇到了一个大问题,因为我有一个每周向客户付款的系统。
众所周知,一年有 52 周,并且有标准。我正在使用 php aka date('W') 从日期中获取周数,该日期根据标准 ISO-8601 计算得出。
以下是一些参考资料:
http://www.iso.org/iso/date_and_time_format 和 http://en.wikipedia.org/wiki/ISO_week_date但问题是:2009 年有 53 周。似乎通过公历 400 年内有 71 年有 53 周。这是我不知道的一件事,可能很多人也不知道。
根据***:
2009-12-31 是 2009-W53-4(ISO 2009 年有 53 周,延长了公历 2009 年,从星期四开始和结束,两端各三天)。
PHP 中的日期函数完全尊重它。
如果您查看 MS Outlook,并在日历视图中显示星期几,它将显示为 52 周 考虑 2009 年 12 月 28 日至 2010 年 1 月 3 日第 1 周。这是另一个标准吗?美国标准还是什么?
如果是这样,那为什么 PHP 不能支持呢?有没有人制作了支持这个的函数?
53 周是否正确?我们有欧洲和美国的客户。
【问题讨论】:
有趣的问题。我注意到 Outlook 在 2011 年有第 53 周。看起来它将“第 1 周”定义为包含 1 月 1 日的第一周。由于根据设置,周可以从周日或周一开始,实际上可能因不同而有所不同用户。 我刚试过。 Outlook 将允许您选择任何一天作为一周的开始日,并且周编号仅从包含 1 月 1 日的那一周开始。因此 Outlook 中有七种不同的编号方案可用。 【参考方案1】:Outlook 不遵循任何类型的周编号标准。它有两个设置来确定周编号,称为“一周的第一天”和“一年的第一周”。
通过将“一周的第一天”设置为星期一,将“一年中的第一周”设置为“第一个 4 天的一周”,可以模拟 ISO 标准。
每个用户都必须进行此调整以遵循 ISO 标准。
我知道没有单独的美国标准,显然 Outlook 也不知道。
【讨论】:
【参考方案2】:我认为您不会在这里遇到问题。事实上,您正在遵循日期和时间格式和计数的国际标准 (ISO8601
)。如果您有任何抱怨的客户,只需让他们参考标准即可。
Outlook 的周编号有点相当于以下内容:
$dummyWeek = floor((date('z') + (date('N') - 1)) / 7) + 1;
出于计费目的,您最好使用ISO8601
作为标准。事实上,如果您查看今年要缴纳的税款,他们会将上一个财政年度描述为长达 53 周。
Outlook 计算方式的问题是一周不能保证是 7 天。例如,OW01-2010 被破坏了只有 2 天:1 月 1 日星期五,1 月 2 日星期六。这是一个非常短的一周计费周期。
ISO8601
周保证为 7 天,这就是为什么我们需要每 4/5/6 年有一个闰周。
您更喜欢哪一个选项:
ISO8601
:偶尔有53周,但每一个都是7天。
Outlook
: 一年有 52/53 周随机,但“半周”必须每年支付两次。
【讨论】:
【参考方案3】:这是我解决它的方法。 注意:我用 PHP 编写代码,但在 SQL 中实现的算法是我下面代码的相关部分。另请注意,我单独处理了“2028-12-31”,因为它是一个特殊的日期,它的周数为 54。
// generate an sql that calculates the outlook week of $date (until 2033)
function sqlWeek ($date)
$week = "if( ". $date . " = '2028-12-31', 54, (week( " . $date . ", 0) + if( (year( " . $date . ") - 1) in (2011, 2016, 2022, 2033), 0, 1)) MOD (if (year( ". $date .") = 2028, 54, if( (year( ".$date." ) - 1) in (2011, 2016, 2022, 2033), 53, 52))))";
return "concat( if( char_length( ".$week." ) < 2, concat( '0', ".$week." ), ".$week." ), '/', year($date) )";
【讨论】:
另外,请注意我使用了“Week('2011-11-09', 0)”,这意味着一周从星期日开始。如果从星期一开始,则应使用“Week('2011-11-09', 1)”。【参考方案4】:美国以外的世界比美国国内的要多 - ISO 8601 在美国以外的应用可能比在美国国内更广泛。
正如您现在发现的那样,最初的陈述“一年有 52 周”只是部分正确。如 Wikipedia 中所述,就 ISO 周数而言,您可以在第 53 周结束。在 N 年的第 52 周或 N 年的第 53 周,您最多可以有 N+1 年的三天。在 N 年的第 1 周,您最多可以有 N-1 年的三天。并且您需要确定 ISO 'week-year' 和 'week' - 因为当公历年为 N 时,ISO 周年可以是 N-1 年或 N+1 年(取决于您正在使用的年份)。这就是为什么在 strftime() 中有单独的格式说明符('%Y'、'%y'、'%g'、'%G'),例如(但请注意,POSIX 标准认为在第一个星期一之前的几天一月是在当年的第 0 周,而不是在上一年的第 52 周或第 53 周)。
“星期”似乎没有任何可靠的“美国标准”。根据您使用的软件,您会得到不同的结果。如果您查看Oracle 对周的定义,那么一年中的前 7 天是第 1 周;在年底的第 53 周,您有 1 或 2 天。
另请参阅SO 274861,了解 ISO 周数代码的实现。即使您不使用该语言编写代码,该算法也应该易于理解。
【讨论】:
【参考方案5】:请注意,如果您使用的是 PHP,您可能还会使用 mysql。 MySQL 确实不遵循 ISO-8601 周惯例,但它是否足够接近你可能认为的那样。正如其他人注意到 Outlook 一样,试图模仿一种不稳定的非标准计算周数的方法是不值得的!
【讨论】:
以上是关于ISO-8601 周编号与 PHP 中的“Outlook”编号的主要内容,如果未能解决你的问题,请参考以下文章