如何使用 Java、Python、PHP、Golang、Swift 和 Ruby 获取当前是一年里的第几周

在统计报表、排期与调度中,计算“第几周”常见却易错,尤其是跨年边界。统一采用 ISO 8601 周历并成对获取“ISO 年份 + ISO 周数”,可避免绝大多数问题。

对比表:各语言获取 ISO 年份与周数

语言/库获取 ISO 年份获取 ISO 周数必要设置备注
Got.ISOWeek() 返回 (year, week)t.ISOWeek() 返回 (year, week)一次调用即得两者
Pythond.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 依赖的周字段
PHPformat('o')format('W')o 为 ISO 年份,W 为 ISO 周数
Swift.component(.yearForWeekOfYear, from:).component(.weekOfYear, from:)Calendar(identifier: .iso8601)必须用 ISO 日历
Rubyd.cwyeard.cweekcwyear/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、重点测试跨年边界,以获得一致、可预测的结果。