{ }codelper
加法逻辑运算符P0170

几天后、几天前的日期

做一个计算器,求出基准日期几天后或几天前的日期。
第一行以 YYYY-MM-DD 的形式给出基准日期。
第二行给出询问数 n,接下来 n 行每行给出一个整数 k。
k 为正表示基准日期的 k 天后,为负表示 |k| 天前,为 0 表示基准日期本身。
历法是现在使用的公历(格里高利历),不会出现 1582 年以前的日期。
1、3、5、7、8、10、12 月有 31 天,4、6、9、11 月有 30 天,2 月有 28 天,只有闰年的 2 月有 29 天。
年份能被 4 整除但不能被 100 整除,或者能被 400 整除,就是闰年。
所以 2000 年是闰年,而 1900 年和 2100 年不是。
按输入顺序,每个询问输出一行答案日期,形式为 YYYY-MM-DD(年份四位,月和日两位)。

输入
1900-01-01 ≤ 基准日期 ≤ 2100-12-31(日历上存在的日期),1 ≤ n ≤ 1,000,-100,000 ≤ k ≤ 100,000
输出
每个询问一行:答案日期(YYYY-MM-DD)
样例 1 · stdin
2024-02-28
6
1
2
-59
366
-1
0
stdout
2024-02-29
2024-03-01
2023-12-31
2025-02-28
2024-02-27
2024-02-28
样例 2 · stdin
1900-02-28
4
1
36525
-100000
100000
stdout
1900-03-01
2000-02-29
1626-05-15
2173-12-13
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

把日期变成「从 1600 年 1 月 1 日起的第几天」这一个数,k 天后就是给这个数加上 k 的一次加法,再把它变回日期即可。
第一个陷阱就是加法本身。
读进来的行是字符串,所以在 JS、TS、Craykon 里 数 + "1" 不是加法而是拼接("1540001")。
Java、C# 只要有一边是 String,+ 也是拼接;Python 的 int + str 会因 TypeError 停下。
C、C++ 用 scanf、cin 一开始就按整数读,没有这个问题。
所以要先转换:JS/TS Number(行),Craykon 转数字(),Java Integer.parseInt,C# int.Parse,Python int。
第二个陷阱是闰年条件:七种语言写 (y % 4 == 0 && y % 100 != 0) || y % 400 == 0,Python 用 and、or。
八种语言里 && 都比 || 先结合,所以不加括号意思也一样,但 gcc 在 -Wall 下会警告要求加括号。
如果只写「每 4 年一个闰年」,1900-02-28 的下一天就会变成 1900-02-29。
第三个陷阱是结转剩余天数时的比较。
剩余天数恰好等于那一年的天数时,答案是下一年的 1 月 1 日,所以要用 >= 判断;用 > 会得到 12 月 32 日这样的日期。
也可以用库:Python date + timedelta(days=k),Java LocalDate.plusDays,C# DateTime.AddDays,C++20 std::chrono::sys_days。
JS 的 Date 要小心:new Date("2024-02-28") 被当作 UTC 零点读入,而 getDate() 用的是本地时间,所以在比 UTC 晚的地区(如美国)会得到前一天;在本地时间里每次加 86,400,000 毫秒,到夏令时结束那天(一天有 25 小时)日期不会前进。
所以只用 UTC 计算,比如 getUTCDate、setUTCDate。
C 和 Craykon 没有日期库,要自己数(C 的 mktime 也是本地时间)。

Pythonmain.py · 45 行
BASE = 1600  # 날짜를 1600-01-01부터 센 날 수로 바꾼다(답의 연도는 1626 ~ 2374)MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]  def is_leap(y):    # 4로 나누어떨어지고 100으로는 안 나누어떨어지거나, 400으로 나누어떨어지면 윤년    return (y % 4 == 0 and y % 100 != 0) or y % 400 == 0  def year_days(y):    return 366 if is_leap(y) else 365  def month_days(y, m):    return 29 if m == 2 and is_leap(y) else MONTH_DAYS[m - 1]  def to_count(y, m, d):    total = d - 1    for yy in range(BASE, y):        total += year_days(yy)    for mm in range(1, m):        total += month_days(y, mm)    return total  def from_count(c):    y = BASE    while c >= year_days(y):  # 남은 날이 그해 날 수와 같으면 다음 해 1월 1일이다        c -= year_days(y)        y += 1    m = 1    while c >= month_days(y, m):        c -= month_days(y, m)        m += 1    return y, m, c + 1  y, m, d = map(int, input().strip().split("-"))start = to_count(y, m, d)n = int(input())for _ in range(n):    k = int(input())  # 수로 바꾼 다음에 더한다    ry, rm, rd = from_count(start + k)    print(f"{ry:04d}-{rm:02d}-{rd:02d}")
JavaScriptmain.js · 36 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const BASE = 1600; // 날짜를 1600-01-01부터 센 날 수로 바꾼다(답의 연도는 1626 ~ 2374)const MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];// 4로 나누어떨어지고 100으로는 안 나누어떨어지거나, 400으로 나누어떨어지면 윤년const isLeap = (y) => (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0;const yearDays = (y) => (isLeap(y) ? 366 : 365);const monthDays = (y, m) => (m === 2 && isLeap(y) ? 29 : MONTH_DAYS[m - 1]);const toCount = (y, m, d) => {  let total = d - 1;  for (let yy = BASE; yy < y; yy++) total += yearDays(yy);  for (let mm = 1; mm < m; mm++) total += monthDays(y, mm);  return total;};const fromCount = (c) => {  let y = BASE;  while (c >= yearDays(y)) {    // 남은 날이 그해 날 수와 같으면 다음 해 1월 1일이다    c -= yearDays(y);    y++;  }  let m = 1;  while (c >= monthDays(y, m)) {    c -= monthDays(y, m);    m++;  }  return `${String(y).padStart(4, "0")}-${String(m).padStart(2, "0")}-${String(c + 1).padStart(2, "0")}`;};const [y, m, d] = lines[0].trim().split("-").map(Number);const start = toCount(y, m, d);const n = Number(lines[1]);const out = [];for (let i = 0; i < n; i++) {  const k = Number(lines[2 + i]); // 문자열 그대로 +하면 이어 붙이기가 된다 → 수로 바꾼 다음에 더한다  out.push(fromCount(start + k));}console.log(out.join("\n"));
TypeScriptmain.ts · 37 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const BASE = 1600; // 날짜를 1600-01-01부터 센 날 수로 바꾼다(답의 연도는 1626 ~ 2374)const MONTH_DAYS: number[] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];// 4로 나누어떨어지고 100으로는 안 나누어떨어지거나, 400으로 나누어떨어지면 윤년const isLeap = (y: number): boolean => (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0;const yearDays = (y: number): number => (isLeap(y) ? 366 : 365);const monthDays = (y: number, m: number): number => (m === 2 && isLeap(y) ? 29 : MONTH_DAYS[m - 1]);const toCount = (y: number, m: number, d: number): number => {  let total = d - 1;  for (let yy = BASE; yy < y; yy++) total += yearDays(yy);  for (let mm = 1; mm < m; mm++) total += monthDays(y, mm);  return total;};const fromCount = (c: number): string => {  let y = BASE;  while (c >= yearDays(y)) {    // 남은 날이 그해 날 수와 같으면 다음 해 1월 1일이다    c -= yearDays(y);    y++;  }  let m = 1;  while (c >= monthDays(y, m)) {    c -= monthDays(y, m);    m++;  }  return `${String(y).padStart(4, "0")}-${String(m).padStart(2, "0")}-${String(c + 1).padStart(2, "0")}`;};const [y, m, d] = lines[0].trim().split("-").map(Number);const start = toCount(y, m, d);const n = Number(lines[1]);const out: string[] = [];for (let i = 0; i < n; i++) {  const k = Number(lines[2 + i]); // 문자열 그대로 +하면 이어 붙이기가 된다 → 수로 바꾼 다음에 더한다  out.push(fromCount(start + k));}console.log(out.join("\n"));
JavaMain.java · 53 行
import java.io.BufferedReader;import java.io.InputStreamReader; public class Main {    static final int BASE = 1600; // 날짜를 1600-01-01부터 센 날 수로 바꾼다(답의 연도는 1626 ~ 2374)    static final int[] MONTH_DAYS = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};     // 4로 나누어떨어지고 100으로는 안 나누어떨어지거나, 400으로 나누어떨어지면 윤년    static boolean isLeap(int y) {        return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;    }     static int yearDays(int y) {        return isLeap(y) ? 366 : 365;    }     static int monthDays(int y, int m) {        return (m == 2 && isLeap(y)) ? 29 : MONTH_DAYS[m - 1];    }     static int toCount(int y, int m, int d) {        int total = d - 1;        for (int yy = BASE; yy < y; yy++) total += yearDays(yy);        for (int mm = 1; mm < m; mm++) total += monthDays(y, mm);        return total;    }     static String fromCount(int c) {        int y = BASE, m = 1;        while (c >= yearDays(y)) { // 남은 날이 그해 날 수와 같으면 다음 해 1월 1일이다            c -= yearDays(y);            y++;        }        while (c >= monthDays(y, m)) {            c -= monthDays(y, m);            m++;        }        return String.format("%04d-%02d-%02d", y, m, c + 1);    }     public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        String[] p = in.readLine().trim().split("-");        int start = toCount(Integer.parseInt(p[0]), Integer.parseInt(p[1]), Integer.parseInt(p[2]));        int n = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        for (int i = 0; i < n; i++) {            int k = Integer.parseInt(in.readLine().trim()); // 문자열과 +하면 이어 붙이기가 된다 → 수로 바꾼다            out.append(fromCount(start + k)).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 51 行
using System;using System.Text; class Program{    const int Base = 1600; // 날짜를 1600-01-01부터 센 날 수로 바꾼다(답의 연도는 1626 ~ 2374)    static readonly int[] MonthDaysTable = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };     // 4로 나누어떨어지고 100으로는 안 나누어떨어지거나, 400으로 나누어떨어지면 윤년    static bool IsLeap(int y) => (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;    static int YearDays(int y) => IsLeap(y) ? 366 : 365;    static int MonthDays(int y, int m) => (m == 2 && IsLeap(y)) ? 29 : MonthDaysTable[m - 1];     static int ToCount(int y, int m, int d)    {        int total = d - 1;        for (int yy = Base; yy < y; yy++) total += YearDays(yy);        for (int mm = 1; mm < m; mm++) total += MonthDays(y, mm);        return total;    }     static string FromCount(int c)    {        int y = Base, m = 1;        while (c >= YearDays(y)) // 남은 날이 그해 날 수와 같으면 다음 해 1월 1일이다        {            c -= YearDays(y);            y++;        }        while (c >= MonthDays(y, m))        {            c -= MonthDays(y, m);            m++;        }        return $"{y:D4}-{m:D2}-{c + 1:D2}";    }     static void Main()    {        string[] p = Console.ReadLine().Trim().Split('-');        int start = ToCount(int.Parse(p[0]), int.Parse(p[1]), int.Parse(p[2]));        int n = int.Parse(Console.ReadLine());        var sb = new StringBuilder();        for (int i = 0; i < n; i++)        {            int k = int.Parse(Console.ReadLine()); // 문자열과 +하면 이어 붙이기가 된다 → 수로 바꾼다            sb.Append(FromCount(start + k)).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 43 行
#include <array>#include <cstdio>#include <iostream> constexpr int BASE = 1600; // 날짜를 1600-01-01부터 센 날 수로 바꾼다(답의 연도는 1626 ~ 2374)constexpr std::array<int, 12> MONTH_DAYS{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 4로 나누어떨어지고 100으로는 안 나누어떨어지거나, 400으로 나누어떨어지면 윤년bool isLeap(int y) { return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0; }int yearDays(int y) { return isLeap(y) ? 366 : 365; }int monthDays(int y, int m) { return (m == 2 && isLeap(y)) ? 29 : MONTH_DAYS[m - 1]; } int toCount(int y, int m, int d) {    int total = d - 1;    for (int yy = BASE; yy < y; yy++) total += yearDays(yy);    for (int mm = 1; mm < m; mm++) total += monthDays(y, mm);    return total;} void printDate(int c) {    int y = BASE, m = 1;    while (c >= yearDays(y)) { // 남은 날이 그해 날 수와 같으면 다음 해 1월 1일이다        c -= yearDays(y);        y++;    }    while (c >= monthDays(y, m)) {        c -= monthDays(y, m);        m++;    }    std::printf("%04d-%02d-%02d\n", y, m, c + 1);} int main() {    int y, m, d, n;    char dash;    std::cin >> y >> dash >> m >> dash >> d >> n;    int start = toCount(y, m, d);    for (int i = 0; i < n; i++) {        int k;        std::cin >> k;        printDate(start + k);    }}
Cmain.c · 43 行
#include <stdio.h> #define BASE 1600 /* 날짜를 1600-01-01부터 센 날 수로 바꾼다(답의 연도는 1626 ~ 2374) */ static const int MONTH_DAYS[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /* 4로 나누어떨어지고 100으로는 안 나누어떨어지거나, 400으로 나누어떨어지면 윤년 */int is_leap(int y) { return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0; }int year_days(int y) { return is_leap(y) ? 366 : 365; }int month_days(int y, int m) { return (m == 2 && is_leap(y)) ? 29 : MONTH_DAYS[m - 1]; } int to_count(int y, int m, int d) {    int total = d - 1;    for (int yy = BASE; yy < y; yy++) total += year_days(yy);    for (int mm = 1; mm < m; mm++) total += month_days(y, mm);    return total;} void print_date(int c) {    int y = BASE, m = 1;    while (c >= year_days(y)) { /* 남은 날이 그해 날 수와 같으면 다음 해 1월 1일이다 */        c -= year_days(y);        y++;    }    while (c >= month_days(y, m)) {        c -= month_days(y, m);        m++;    }    printf("%04d-%02d-%02d\n", y, m, c + 1);} int main(void) {    int y, m, d, n;    scanf("%d-%d-%d", &y, &m, &d);    scanf("%d", &n);    int start = to_count(y, m, d);    for (int i = 0; i < n; i++) {        int k;        scanf("%d", &k);        print_date(start + k);    }    return 0;}
Craykonmain.cray · 66 行
BASE = 1600; // 날짜를 1600-01-01부터 센 날 수로 바꾼다(답의 연도는 1626 ~ 2374)MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; // 4로 나누어떨어지고 100으로는 안 나누어떨어지거나, 400으로 나누어떨어지면 윤년윤년인가(y){    返回 y % 4 == 0 && y % 100 != 0 || y % 400 == 0;} 해의날수(y){    如果 (윤년인가(y))    {        返回 366;    }    返回 365;} 달의날수(y, m){    如果 (m == 2 && 윤년인가(y))    {        返回 29;    }    返回 MONTH_DAYS[m - 1];} 센날수(y, m, d){    total = d - 1;    循环 (yy = BASE; yy < y; yy++)    {        total += 해의날수(yy);    }    循环 (mm = 1; mm < m; mm++)    {        total += 달의날수(y, mm);    }    返回 total;} 날짜로(c){    y = BASE;    当 (c >= 해의날수(y))    {        c -= 해의날수(y);        y += 1;    }    m = 1;    当 (c >= 달의날수(y, m))    {        c -= 달의날수(y, m);        m += 1;    }    返回 ("" + y).左填充(4, "0") + "-" + ("" + m).左填充(2, "0") + "-" + ("" + (c + 1)).左填充(2, "0");} 칸 = 控制台.读行().去空白().分割("-");start = 센날수(칸[0].转数字(), 칸[1].转数字(), 칸[2].转数字());n = 控制台.读行().转数字();循环 (i = 0; i < n; i++){    k = 控制台.读行().转数字(); // 문자열 그대로 +하면 이어 붙이기가 된다 → 수로 바꾼 다음에 더한다    控制台.输出(날짜로(start + k));}