TOC

周数问题:今天是今年的第几周

2021-01-01

TLDR:

  1. ISO 8601 标准:国际标准规定周一为一周的开始,包含 1 月 4 日的那一周为第一周
  2. Java: 使用 WeekFields.ISO 获取 ISO 标准周数,或使用本地化设置
  3. Python: 使用 strftime%W (本地化) 或 %V (ISO) 参数获取周数
  4. Go: 使用 ISOWeek() 方法获取 ISO 标准周数
  5. MySQL: 使用 WEEK() 函数的 mode 3 或 7 获取 ISO 标准周数

Java

import java.time.LocalDate;
import java.time.temporal.WeekFields;
import java.util.Locale;

public class WeekNumberExample {
    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2021, 1, 1);
        // 使用 ISO 标准计算周数(周一为一周开始)
        int weekNumber = date.get(WeekFields.ISO.weekOfYear());
        System.out.println("ISO week number: " + weekNumber);

        // 使用本地化周数计算
        WeekFields weekFields = WeekFields.of(Locale.getDefault());
        int localizedWeek = date.get(weekFields.weekOfYear());
        System.out.println("Localized week number: " + localizedWeek);
    }
}

Python

import datetime

# 说明不同格式化参数的含义:
# %Y - 年份 (基于日历年度)
# %W - 周数 (周一作为一周的开始,第一周是包含第一个周一的周)
# %G - 年份 (基于ISO周数)
# %V - 周数 (ISO周数,周一作为一周开始,第一周是包含1月4日的那一周)

for i in range(10):
    d = datetime.date(2021 - i, 1, 1)
    print((str(d), d.weekday(), d.strftime('%Y %W / %G %V')))
('2021-01-01', 4, '2021 00 / 2020 53')
('2020-01-01', 2, '2020 00 / 2020 01')
('2019-01-01', 1, '2019 00 / 2019 01')
('2018-01-01', 0, '2018 01 / 2018 01')
('2017-01-01', 6, '2017 00 / 2016 52')
('2016-01-01', 4, '2016 00 / 2015 53')
('2015-01-01', 3, '2015 00 / 2015 01')
('2014-01-01', 2, '2014 00 / 2014 01')
('2013-01-01', 1, '2013 00 / 2013 01')
('2012-01-01', 6, '2012 00 / 2011 52')

Go

package main

import (
    "fmt"
    "time"
)

func main() {
    // Go 中计算周数
    t := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)

    // 使用 ISO 标准计算周数
    _, week := t.ISOWeek()
    fmt.Printf("ISO week number: %d\n", week)

    // 使用 YearDay 计算基于年的周数
    yearDay := t.YearDay()
    weekFromYearDay := (yearDay + 6) / 7
    fmt.Printf("Week from year day: %d\n", weekFromYearDay)
}

MySQL 中的实现

WEEK(date[, mode]) 函数:

Mode First day of week Range Week 1 is the first week …
0 Sunday 0-53 with a Sunday in this year
1 Monday 0-53 with 4 or more days this year
2 Sunday 1-53 with a Sunday in this year
3 Monday 1-53 with 4 or more days this year
4 Sunday 0-53 with 4 or more days this year
5 Monday 0-53 with a Monday in this year
6 Sunday 1-53 with 4 or more days this year
7 Monday 1-53 with a Monday in this year

总结一下,就是三个维度:

  1. 周数从 0 开始还是从 1 开始
  2. 以周一算每周的第一天还是周日
  3. 按每周第一天开始算,还是按每周四天开始算
0 周日 每周第一天 0
2 周日 每周第一天 1
4 周日 每周四天 0
6 周日 每周四天 1

1 周一 每周四天 0
3 周一 每周四天 1
5 周一 每周第一天 0
7 周一 每周第一天 1
SELECT NULL AS Mode, WEEK('2021-01-01'), WEEK('2021-01-04')
UNION SELECT 0 AS Mode, WEEK('2021-01-01', 0), WEEK('2021-01-04', 0)
UNION SELECT 1 AS Mode, WEEK('2021-01-01', 1), WEEK('2021-01-04', 1)
UNION SELECT 2 AS Mode, WEEK('2021-01-01', 2), WEEK('2021-01-04', 2)
UNION SELECT 3 AS Mode, WEEK('2021-01-01', 3), WEEK('2021-01-04', 3)
UNION SELECT 4 AS Mode, WEEK('2021-01-01', 4), WEEK('2021-01-04', 4)
UNION SELECT 5 AS Mode, WEEK('2021-01-01', 5), WEEK('2021-01-04', 5)
UNION SELECT 6 AS Mode, WEEK('2021-01-01', 6), WEEK('2021-01-04', 6)
UNION SELECT 7 AS Mode, WEEK('2021-01-01', 7), WEEK('2021-01-04', 7);
Mode WEEK('2021-01-01') WEEK('2021-01-04')
NULL 0 1
0 0 1
1 0 1
2 52 1
3 53 1
4 0 1
5 0 1
6 53 1
7 52 1

我定义的每周迭代名称(例如:2020-01A)

  1. 每周第一天为周一。
  2. 按照周一属于哪个月计算。
import datetime
from datetime import timedelta

def get_week_iteration_name(date):
    """
    根据周一所属月份计算周迭代名称
    格式:YYYY-MM{A-Z}
    返回:str, date, date (迭代名称,周一日期,周日日期)
    """
    weekday = date.weekday()
    monday = date - timedelta(days=weekday)
    sunday = monday + timedelta(days=6)

    year = monday.year
    month = monday.month

    # 计算该月第几个周一
    # 找到该月第一个周一
    first_day = datetime.date(year, month, 1)
    first_monday_offset = (7 - first_day.weekday()) % 7
    first_monday = first_day + timedelta(days=first_monday_offset)

    # 如果第一个周一不在本月,说明该月第一天就是周一
    if first_monday.month != month:
        first_monday = first_day

    # 计算是第几个周一
    week_index = ((monday - first_monday).days // 7) + 1
    # 转换为字母标识 (A=1, B=2, ...)
    week_letter = chr(ord('A') + week_index - 1)

    return f"{year}-{month:02d}{week_letter}", monday, sunday

test_dates = [
    datetime.date(2021, 1, 1),   # Friday
    datetime.date(2021, 1, 4),   # Monday
    datetime.date(2021, 1, 8),   # Friday
    datetime.date(2020, 12, 28), # Monday
]
for d in test_dates:
    weekday = d.weekday()
    name, monday, sunday = get_week_iteration_name(d)
    print(f"{d} -> {name} {weekday + 1} (Mon: {monday}, Sun: {sunday})")
如果你有魔法,你可以看到一个评论框~