题目描述

✅ 1154. 一年中的第几天

image-20260928232024981

题意分析

给定合法日期字符串 YYYY-MM-DD,返回它在这一年中是第几天。天数从 $1$ 开始计数,因此只需累计当年已经完整经过的月份,再加上当前月的日号,不需要累计之前的年份。

解法:月份天数累加

核心思路

[!blue]

设日期的月、日分别为 month、day,答案就是前 month - 1 个月的总天数加 day。当前月尚未完整经过,不能把它的整个月长也加进去。

先用一个长度为 $12$ 的数组保存平年每个月的天数,数组下标比月份小一。只有二月会受年份影响:能被 $400$ 整除,或能被 $4$ 整除但不能被 $100$ 整除的年份是闰年,将 days[1] 改为 $29$。其余月份保持不变。

日期格式固定,因此年份读取下标区间 [0, 4),月份读取 [5, 7),日号读取 [8, 10),自然跳过两个连字符。parseNumber 从左到右读数字,每次用“已有值乘十,再加当前数字”累积成整数,月份和日号的前导零也会自然处理。

将 answer 初始化为 day,再累加 days[0] 到 days[month - 2]。一月时循环不执行,答案就是日号;日期在二月时,闰日已由合法日号表示,不需另外加一;只有月份超过二月,累加二月整月时才会用到调整后的 $29$ 天。

解题步骤

  1. 按固定下标区间解析 year、month、day。
  2. 初始化平年月长数组;若为闰年,将二月天数改为 $29$。
  3. 令 answer = day,遍历 i < month - 1 的月份下标并累加天数。
  4. 返回 answer。

代码实现

class Solution {
    public int dayOfYear(String date) {
        int year = parseNumber(date, 0, 4);
        int month = parseNumber(date, 5, 7);
        int day = parseNumber(date, 8, 10);
        int[] days = {
            31,
            28,
            31,
            30,
            31,
            30,
            31,
            31,
            30,
            31,
            30,
            31
        };

        // 先确定二月月长,后面的累加不再区分闰年。
        if (isLeapYear(year)) {
            days[1] = 29;
        }

        int answer = day;

        // 当前月只算已经过的日期,循环仅累加前面的整月。
        for (int i = 0; i < month - 1; i++) {
            answer += days[i];
        }

        return answer;
    }

    private int parseNumber(String date, int start, int end) {
        int value = 0;

        for (int i = start; i < end; i++) {
            value = value * 10 + date.charAt(i) - '0';
        }

        return value;
    }

    private boolean isLeapYear(int year) {
        return year % 400 == 0 || year % 4 == 0 && year % 100 != 0;
    }
}
func dayOfYear(date string) int {
    year := parseNumber(date, 0, 4)
    month := parseNumber(date, 5, 7)
    day := parseNumber(date, 8, 10)
    days := []int{
        31,
        28,
        31,
        30,
        31,
        30,
        31,
        31,
        30,
        31,
        30,
        31,
    }

    // 先确定二月月长,后面的累加不再区分闰年。
    if isLeapYear(year) {
        days[1] = 29
    }

    answer := day
    // 当前月只算已经过的日期,循环仅累加前面的整月。
    for i := 0; i < month-1; i++ {
        answer += days[i]
    }

    return answer
}

func parseNumber(date string, start int, end int) int {
    value := 0
    for i := start; i < end; i++ {
        value = value*10 + int(date[i]-'0')
    }

    return value
}

func isLeapYear(year int) bool {
    return year%400 == 0 || year%4 == 0 && year%100 != 0
}

复杂度分析

  • 时间复杂度:$O(1)$,固定长度输入,最多累计11个月。
  • 空间复杂度:$O(1)$,12个月长与解析变量。

关键点总结

[!green]

  • 当前月只加已经经过的日期,不加完整月长。
  • 闰年的额外一天仅在二月之后影响累加结果。

易错点总结

[!yellow]

  • 只判断能被 $4$ 整除,会把不能被 $400$ 整除的整百年份误判为闰年。
  • 累加到当前月,会多算整月。
  • 共享并修改二月月长却不恢复,会让下次平年计算沿用29天。

相似题目

题目 难度 关联与区别
1118. 一月有多少天 简单 先确定各月天数及闰年二月长度,再累加当前月份之前的天数。
1360. 日期之间隔几天 简单 原题计算两个日期间隔,可先把每个日期映射成统一的累计天数。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/84554593
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!