{ }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 の午前 0 時として読まれますが getDate() は地方時なので、UTC より遅れた地域(アメリカなど)では前日の日付が出ます。
また地方時で 86,400,000 ミリ秒ずつ足すと、夏時間が終わる日(一日が 25 時間)に日付が進みません。
だから getUTCDate・setUTCDate のように UTC だけで計算します。
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));}