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”编号的主要内容,如果未能解决你的问题,请参考以下文章

ISO 8601:时间和日期的表示标准

如何使用 PHP 将日期显示为 iso 8601 格式

从Python中的ISO周数获取日期[重复]

如何将 ISO8601 TSQL DATETIME 参数与 PDO 绑定?

如何在php中将ISO8601转换为日期格式

如何在 PHP 中将日期时间转换为 ISO 8601