如何使用 Java、Python、PHP、Golang、Swift 和 Ruby 获取当前是一年里的第几周
在统计报表、排期与调度中,计算“第几周”常见却易错,尤其是跨年边界。统一采用 ISO 8601 周历并成对获取“ISO 年份 + ISO 周数”,可避免绝大多数问题。
对比表:各语言获取 ISO 年份与周数
| 语言/库 | 获取 ISO 年份 | 获取 ISO 周数 | 必要设置 | 备注 |
|---|---|---|---|---|
| Go | t.ISOWeek() 返回 (year, week) |
t.ISOWeek() 返回 (year, week) |
无 | 一次调用即得两者 |
| Python | d.isocalendar()[0] |
d.isocalendar()[1] |
无 | isocalendar() 返回 (year, week, weekday) |
| Java (java.time) | date.get(WeekFields.ISO.weekBasedYear()) |
date.get(WeekFields.ISO.weekOfWeekBasedYear()) |
使用 WeekFields.ISO |
不要用 locale 依赖的周字段 |
| PHP | format('o') |
format('W') |
无 | o 为 ISO 年份,W 为 ISO 周数 |
| Swift | .component(.yearForWeekOfYear, from:) |
.component(.weekOfYear, from:) |
Calendar(identifier: .iso8601) |
必须用 ISO 日历 |
| Ruby | d.cwyear |
d.cweek |
无 | cwyear/cweek 即 ISO 年/周 |
ISO 8601 要点
- 周起始日:周一
- 第 1 周:当年中第一个包含“周四”的那一周
- 周数范围:52 或 53
- 关键:同一日期的“ISO 年份”可能不同于其公历年份,必须成对使用
(isoYear, isoWeek)
边界示例:
- 2015-12-31(周四)→ 2015 年第 53 周
- 2016-01-01(周五)→ 2015 年第 53 周(公历属 2016)
- 2018-12-31(周一)→ 2019 年第 1 周
各语言实现
Go
package iso
import "time"
// 返回 ISO 年份与 ISO 周数
func GetISOWeek(t time.Time) (int, int) {
year, week := t.ISOWeek()
return year, week
}
Python
from datetime import date
# 返回 (ISO 年份, ISO 周数)
def get_iso_week(d: date) -> tuple[int, int]:
iso_year, iso_week, _ = d.isocalendar()
return iso_year, iso_week
Java (java.time)
import java.time.LocalDate;
import java.time.temporal.WeekFields;
public final class IsoWeek {
private IsoWeek() {}
public static int getIsoWeek(LocalDate date) {
return date.get(WeekFields.ISO.weekOfWeekBasedYear());
}
public static int getIsoWeekYear(LocalDate date) {
return date.get(WeekFields.ISO.weekBasedYear());
}
}
PHP
<?php
// 返回 [ISO 年份, ISO 周数]
function get_iso_week(DateTimeInterface $dt): array {
$isoYear = (int)$dt->format('o'); // ISO 年份
$isoWeek = (int)$dt->format('W'); // ISO 周数
return [$isoYear, $isoWeek];
}
Swift
import Foundation
// 返回 (ISO 年份, ISO 周数)
func getIsoWeek(from date: Date,
calendar: Calendar = Calendar(identifier: .iso8601)) -> (year: Int, week: Int) {
let year = calendar.component(.yearForWeekOfYear, from: date)
let week = calendar.component(.weekOfYear, from: date)
return (year, week)
}
Ruby
require 'date'
# 返回 [ISO 年份, ISO 周数]
def get_iso_week(d)
[d.cwyear, d.cweek]
end
常见陷阱与规避
- 时区:同一时刻在不同时区对应的本地日期不同,进而影响周数。计算前明确所用时区(服务器/用户本地),不要依赖默认值。
- API 差异:避免使用受地区化设置影响的“周起始日/周数”接口;优先使用明确支持 ISO 的 API(如上所列)。
- 年份配对:统计/归档时必须存储并显示
(ISO 年份, ISO 周数),切勿用公历年与 ISO 周数混配。 - 展示/序列化:选择语言中与 ISO 兼容的格式化字段(例如 PHP
o/W;Java 使用 week-based 年与周的字段)。
测试建议(覆盖边界)
- 覆盖跨年边界:12 月末与次年 1 月初(如 2015-12-28 至 2016-01-03,2018-12-31,2020-12-31/2021-01-01)
- 覆盖 53 周年份:如 2015、2020
- 固定时区与固定日期进行断言,避免
now()带来的不确定性 - 多语言/多服务对同一组样例比对
(isoYear, isoWeek)一致性
结论
- 统一遵循 ISO 8601,并始终成对获取与使用
(ISO 年份, ISO 周数)。 - 明确时区、选对 API、重点测试跨年边界,以获得一致、可预测的结果。