Skip to content

week 周输入

<input type="week"> 是 HTML5 引入的周数选择控件,允许用户选择一个具体的年份和周数。该控件提交值格式为 YYYY-Www(例如 2025-W28),适用于按周规划的场景,如课程安排、项目排期、周报管理等。

前置知识

阅读本节前,建议先了解:month 月份输入

基础概念

什么是 week 输入

type="week" 输入框提供了一个年份和 ISO 周数的组合选择界面。主要特点:

  • 年周一体化:年份和周数在同一个控件中选择
  • ISO 8601 标准:遵循 ISO 周数定义(周一为一周的第一天)
  • 格式统一:提交值始终为 YYYY-Www 格式
  • 适合周期规划:自然适配按周安排的业务场景

ISO 8601 周数规则

ISO 8601 标准定义了周数的计算方式:

  1. 每周从周一开始,周日为第七天
  2. 每年的第一周是包含该年 1 月 4 日的那一周
  3. 一年最多 52 或 53 周
  4. 某年的第一周可能从前一年的 12 月就开始了
  5. 某年的最后一周可能延续到下一年的 1 月

浏览器支持

浏览器支持情况控件外观
Chrome✅ 支持年周选择器
Firefox⚠️ 有限支持(93+)简单输入框
Safari⚠️ 不支持降级为文本输入
Edge✅ 支持年周选择器
Opera✅ 支持年周选择器

重要提示type="week" 的浏览器兼容性是所有日期时间类型中最差的,Safari 目前不提供原生周选择器。在生产环境中使用时需要提供降级方案。

提交值格式

YYYY-Www

其中 ww 是两位数的 ISO 周数(01-53)。

例如:2025-W28 表示 2025 年第 28 周。

语法

基本用法

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>week 周输入示例</title>
</head>
<body>
    <h2>选择周</h2>
    <form>
        <label for="work-week">工作周:</label>
        <input type="week" id="work-week" name="work_week">
        <button type="submit">提交</button>
    </form>
</body>
</html>

设置默认值

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>week 默认值示例</title>
</head>
<body>
    <h2>默认选中当前周</h2>
    <form>
        <label for="current-week">当前周:</label>
        <input type="week" id="current-week" name="current_week" value="2025-W28">
        <button type="submit">查询</button>
    </form>

    <script>
        // 通过 JavaScript 获取当前 ISO 周数
        function getISOWeek(date) {
            const d = new Date(date.getTime());
            d.setHours(0, 0, 0, 0);
            // 周四在当前周中则该周属于当前年
            d.setDate(d.getDate() + 3 - ((d.getDay() + 6) % 7));
            const yearStart = new Date(d.getFullYear(), 0, 4);
            const weekNumber = Math.ceil(((d - yearStart) / 86400000 + 1) / 7);
            return { year: d.getFullYear(), week: weekNumber };
        }

        const now = new Date();
        const isoWeek = getISOWeek(now);
        const weekStr = `${isoWeek.year}-W${isoWeek.week.toString().padStart(2, '0')}`;
        document.getElementById('current-week').value = weekStr;
    </script>
</body>
</html>

min 和 max 属性

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>week 范围限制</title>
</head>
<body>
    <h2>选择学期周</h2>
    <form>
        <label for="term-week">学期周次:</label>
        <!-- 只能选择 2025 年的周 -->
        <input
            type="week"
            id="term-week"
            name="term_week"
            min="2025-W01"
            max="2025-W52"
            required
        >
        <button type="submit">提交</button>
    </form>
</body>
</html>
属性说明格式
min最早可选周YYYY-Www
max最晚可选周YYYY-Www

详细说明

step 步进控制

step 属性控制周选择的粒度:

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>week step 示例</title>
</head>
<body>
    <h2>周步进</h2>
    <form>
        <div>
            <label for="every-week">每周可选(默认 step=1):</label>
            <input type="week" id="every-week" name="every_week">
        </div>
        <div>
            <label for="biweekly">每两周可选(step=2):</label>
            <input type="week" id="biweekly" name="biweekly" step="2" min="2025-W01" value="2025-W01">
        </div>
        <div>
            <label for="monthly-week">每四周(约每月):</label>
            <input type="week" id="monthly-week" name="monthly_week" step="4" min="2025-W01" value="2025-W01">
        </div>
    </form>
</body>
</html>
step 值(周)含义
1(默认)每周可选
2隔周可选
4约每月可选
any不限制

valueAsDate 和 valueAsNumber

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>week 值属性</title>
</head>
<body>
    <h2>获取周值</h2>
    <label for="my-week">选择周:</label>
    <input type="week" id="my-week" name="my_week" value="2025-W28">

    <div id="output"></div>

    <script>
        const weekInput = document.getElementById('my-week');
        const output = document.getElementById('output');

        weekInput.addEventListener('input', function () {
            const str = this.value; // "2025-W28"
            const date = this.valueAsDate; // 该周的周一
            const num = this.valueAsNumber; // Unix 时间戳

            if (date) {
                // 计算该周的周日
                const sunday = new Date(date);
                sunday.setDate(sunday.getDate() + 6);

                output.innerHTML = `
                    <p>value: ${str}</p>
                    <p>周一: ${date.toLocaleDateString('zh-CN')}</p>
                    <p>周日: ${sunday.toLocaleDateString('zh-CN')}</p>
                    <p>Unix 时间戳: ${num}</p>
                `;
            }
        });
    </script>
</body>
</html>

ISO 周数计算工具函数

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>ISO 周计算</title>
</head>
<body>
    <h2>ISO 周数计算工具</h2>

    <label for="date-input">输入日期:</label>
    <input type="date" id="date-input" name="date">

    <p id="week-result"></p>

    <script>
        /**
         * 获取日期的 ISO 周数
         * @param {Date} date - 日期对象
         * @returns {{ year: number, week: number }} ISO 年份和周数
         */
        function getISOWeek(date) {
            const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
            const dayNum = d.getUTCDay() || 7;
            d.setUTCDate(d.getUTCDate() + 4 - dayNum);
            const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
            return {
                year: d.getUTCFullYear(),
                week: Math.ceil((((d - yearStart) / 86400000) + 1) / 7)
            };
        }

        /**
         * 根据年份和周数获取该周的日期范围
         * @param {number} year - 年份
         * @param {number} week - 周数
         * @returns {{ monday: Date, sunday: Date }} 该周的周一和周日
         */
        function getWeekDates(year, week) {
            // 找到该年的 1 月 4 日(ISO 第一周必须包含 1 月 4 日)
            const jan4 = new Date(year, 0, 4);
            const dayOfWeek = jan4.getDay() || 7; // 周日 = 7
            const monday = new Date(year, 0, 4 - (dayOfWeek - 1) + (week - 1) * 7);
            const sunday = new Date(monday);
            sunday.setDate(sunday.getDate() + 6);
            return { monday, sunday };
        }

        // 使用示例
        document.getElementById('date-input').addEventListener('change', function () {
            if (this.value) {
                const date = new Date(this.value + 'T00:00:00');
                const iso = getISOWeek(date);
                const weekStr = `${iso.year}-W${iso.week.toString().padStart(2, '0')}`;
                const dates = getWeekDates(iso.year, iso.week);

                document.getElementById('week-result').textContent =
                    `${this.value} 属于 ${weekStr}` +
                    `(${dates.monday.toLocaleDateString('zh-CN')} ~ ${dates.sunday.toLocaleDateString('zh-CN')})`;
            }
        });
    </script>
</body>
</html>

实战示例

项目周报选择器

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>项目周报</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
            max-width: 700px;
            margin: 40px auto;
            padding: 0 20px;
        }
        .controls { display: flex; gap: 12px; align-items: center; margin-bottom: 16px; }
        input[type="week"] {
            padding: 8px 12px;
            border: 2px solid #ddd;
            border-radius: 6px;
            font-size: 16px;
        }
        input[type="week"]:focus { border-color: #e74c3c; outline: none; }
        button {
            padding: 8px 16px;
            background: #e74c3c;
            color: #fff;
            border: none;
            border-radius: 6px;
            cursor: pointer;
        }
        .report-card {
            background: #fff;
            border: 1px solid #eee;
            border-radius: 8px;
            padding: 20px;
            margin-top: 16px;
            display: none;
        }
        .report-card h3 { color: #e74c3c; margin-top: 0; }
        textarea {
            width: 100%;
            min-height: 100px;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 14px;
            box-sizing: border-box;
        }
    </style>
</head>
<body>
    <h2>项目周报</h2>
    <div class="controls">
        <label for="report-week">选择周次:</label>
        <input type="week" id="report-week" name="report_week">
        <button type="button" id="load-btn">加载周报</button>
    </div>

    <div class="report-card" id="report-card">
        <h3 id="week-title"></h3>
        <div>
            <label for="progress">本周进展:</label>
            <textarea id="progress" placeholder="请填写本周工作进展..."></textarea>
        </div>
        <div>
            <label for="plan">下周计划:</label>
            <textarea id="plan" placeholder="请填写下周工作计划..."></textarea>
        </div>
        <button type="button" id="save-btn">保存周报</button>
    </div>

    <script>
        const weekInput = document.getElementById('report-week');
        const reportCard = document.getElementById('report-card');

        // 设置当前 ISO 周为默认值
        function getISOWeek(date) {
            const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
            const dayNum = d.getUTCDay() || 7;
            d.setUTCDate(d.getUTCDate() + 4 - dayNum);
            const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
            return { year: d.getUTCFullYear(), week: Math.ceil((((d - yearStart) / 86400000) + 1) / 7) };
        }

        const iso = getISOWeek(new Date());
        weekInput.value = `${iso.year}-W${iso.week.toString().padStart(2, '0')}`;

        // 加载周报
        document.getElementById('load-btn').addEventListener('click', function () {
            const [year, weekPart] = weekInput.value.split('-');
            const week = parseInt(weekPart.replace('W', ''));

            // 计算该周的日期范围
            const jan4 = new Date(parseInt(year), 0, 4);
            const dayOfWeek = jan4.getDay() || 7;
            const monday = new Date(parseInt(year), 0, 4 - (dayOfWeek - 1) + (week - 1) * 7);
            const sunday = new Date(monday);
            sunday.setDate(sunday.getDate() + 6);

            reportCard.style.display = 'block';
            document.getElementById('week-title').textContent =
                `第 ${week} 周(${monday.toLocaleDateString('zh-CN')} ~ ${sunday.toLocaleDateString('zh-CN')})`;
        });
    </script>
</body>
</html>

注意事项

浏览器兼容性有限

type="week" 在 Safari 中不被支持,会降级为普通文本输入框。建议提供降级方案:

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>week 降级方案</title>
</head>
<body>
    <h2>周选择器(含降级)</h2>
    <form>
        <label for="select-week">选择周:</label>
        <input type="week" id="select-week" name="select_week">

        <p id="fallback-hint" style="color: #999; font-size: 14px; display: none;">
            请输入格式:YYYY-Www(例如:2025-W28)
        </p>

        <!-- 降级方案:年份+周数分离选择 -->
        <div id="fallback-selects" style="display: none; margin-top: 12px;">
            <label for="fallback-year">年份:</label>
            <select id="fallback-year">
                <option>2024</option>
                <option selected>2025</option>
                <option>2026</option>
            </select>
            <label for="fallback-week">周数:</label>
            <select id="fallback-week">
                <!-- 动态生成 1-53 -->
            </select>
        </div>
    </form>

    <script>
        // 检测浏览器是否支持 type="week"
        const testInput = document.createElement('input');
        testInput.setAttribute('type', 'week');
        const isWeekSupported = testInput.type === 'week';

        if (!isWeekSupported) {
            document.getElementById('fallback-hint').style.display = 'block';
            document.getElementById('fallback-selects').style.display = 'block';

            // 生成周数选项
            const weekSelect = document.getElementById('fallback-week');
            for (let i = 1; i <= 53; i++) {
                const opt = document.createElement('option');
                opt.value = i.toString().padStart(2, '0');
                opt.textContent = `第 ${i} 周`;
                weekSelect.appendChild(opt);
            }
        }
    </script>
</body>
</html>

ISO 周与自然月的错位

ISO 周数可能与自然月份不完全对齐。例如,2025 年 1 月 1 日(周三)属于 2025-W01,但 2024 年 12 月 30 日(周一)也属于 2025-W01。

step 与 min 的配合

使用 step 限制时,必须配合 min 属性使用才能确保周数对齐正确:

html
<!-- 必须设置 min,否则隔周选择可能从任意周开始 -->
<input type="week" name="biweekly" step="2" min="2025-W01" value="2025-W01">

最佳实践

  1. 评估兼容性需求:如果目标用户包含 Safari 用户,建议提供降级方案
  2. 提供日期范围提示:由于周数不直观,应在旁边显示对应的日期范围
  3. 使用 ISO 周计算函数:确保前后端使用相同的 ISO 周数计算规则
  4. 结合可视化日历:考虑在周选择器下方显示当周的日历,增强可读性
  5. 合理设置 step:对于隔周报告等场景,使用 step="2" 限制可选周数
  6. 格式验证:在服务端验证提交的周数格式和有效性

下一节

继续学习:range 滑块输入

参考链接