Medio · calendario

Días entre dos fechas

Medir directamente entre dos fechas es un laberinto de llevadas y préstamos. En cambio, dale a cada fecha un número de serie — días transcurridos desde una época fija — y la diferencia se vuelve una resta. Toda la fealdad del calendario se esconde dentro de la conversión, que es donde pertenece.

En una fraseEscribe <code>serial(y, m, d)</code> = años desde la época + días de los meses completos antes de m + d. La respuesta es <code>serial(y2,m2,d2) - serial(y1,m1,d1)</code>.

El problema

Entrada: Dos líneas: y1 m1 d1 y y2 m2 d2 (1900 ≤ y ≤ 2100), la primera fecha no posterior a la segunda.

Salida: El número de días de la primera fecha a la segunda (mismo día → 0).

Ejemplos:

EntradaSalida
2000 1 1
2000 12 31
365
1900 2 28
1900 3 1
1

El segundo ejemplo es la trampa de siglo en una línea: 1900 es divisible entre 100 pero no entre 400, así que NO es bisiesto — febrero tuvo 28 días y el hueco es 1. Con las mismas fechas en 2000, el hueco sería 2.

Tu solución

main.cpp — tu solución
Entrada personalizada para «Ejecutar mi entrada»

La corrección corre en tu navegador contra pruebas ocultas. Un bucle infinito se corta a los 3 segundos — aun así, asegúrate de que todo bucle tenga salida. Tu código se guarda solo mientras escribes.

¿Atascado? Pistas

Pista 1 — Tres capas en un número de serie

serial(y, m, d) suma tres bloques: los años completos antes de y (365 o 366 cada uno — un bucle desde el año época), los meses completos antes de m en el año y (un bucle que llama a monthDays), y la d. Cada bloque es un bucle pequeño; ninguno exige astucia.

Pista 2 — La regla bisiesta son tres condiciones

Divisible entre 4 → bisiesto, salvo entre 100 → no, salvo entre 400 → sí. Una línea codifica las tres: (y % 4 == 0 && y % 100 != 0) || y % 400 == 0. Pruébala con 1900 (no), 2000 (sí), 2100 (no), 2004 (sí) antes de confiar.

Pista 3 — Diferencia, no dos casi-aciertos

Los principiantes suelen intentar caminar de fecha a fecha mes a mes, gestionando fines de mes a mano: ese camino tiene cinco oportunidades de error de uno. El diseño de números de serie las elimina todas: convierte ambas fechas, resta, listo — y los mismos números de serie responden gratis «¿qué día de la semana es?».

Solución (¡inténtalo primero!)
#include <iostream>
using namespace std;

bool isLeap(int y) {
    return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
}

int monthDays(int y, int m) {
    int d[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    if (m == 2 && isLeap(y)) return 29;
    return d[m];
}

long long serial(int y, int m, int d) {
    long long s = 0;
    for (int yy = 1900; yy < y; yy++) s += isLeap(yy) ? 366 : 365;
    for (int mm = 1; mm < m; mm++) s += monthDays(y, mm);
    return s + d;
}

int main() {
    int y1, m1, d1, y2, m2, d2;
    cin >> y1 >> m1 >> d1 >> y2 >> m2 >> d2;
    cout << serial(y2, m2, d2) - serial(y1, m1, d1) << endl;
    return 0;
}

El truco del número de serie es una de las ideas más viejas de la computación — las marcas de tiempo de Unix son exactamente esto, contado en segundos desde 1970. Todo lo que construiste aquí (época, tabla de meses, regla bisiesta) se traslada: añade el día de la semana (serial % 7), añade husos horarios, y tienes el esqueleto de una biblioteca de fechas. Las reglas son viejas amigas de Año bisiesto — este problema es donde empiezan a ganar dinero.

Análisis profundo

Por qué funciona

Dale a cada fecha un número de serie — días desde una época fija — y las diferencias se vuelven restas. El serie se construye en tres capas aburridas (años completos, meses completos, día del mes), cada una un bucle pequeño; toda la fealdad del calendario vive dentro de la conversión, donde se prueba una vez, en vez de estar regada por la lógica de la diferencia.

El coste

O(años) por conversión con la versión de bucle — 200 iteraciones para el rango 1900–2100 — y O(1) para la resta final. Existen versiones de tiempo constante (una fórmula con correcciones bisiestas), pero el bucle es demostrablemente correcto y sobra rápido.

Dónde falla

  • La regla de siglo: 1900 y 2100 NO son bisiestos (÷100 pero no ÷400), 2000 SÍ. Probar solo con 2000 esconde el bug para siempre.
  • La longitud de febrero debe consultar el isLeap del año en curso dentro de monthDays — una tabla fija 28/29 indexada solo por mes falla en los bisiestos.
  • Conteo inclusivo: de una fecha a sí misma la respuesta es 0, y del 28 de febrero al 1 de marzo es 1 en años comunes. Un error de uno aquí dobla o parte respuestas enteras.

Siguiente paso

Las reglas bisiestas vienen de Año bisiesto; el truco del número de serie es el tiempo Unix en miniatura — añade serial % 7 y tienes los días de la semana gratis.

Dos cosas para recordar

  • Los números de serie convierten la aritmética de fechas en restas — convierte, resta, listo.
  • Regla bisiesta en una línea: (y%4==0 && y%100!=0) || y%400==0 — pruébala con 1900, 2000 y 2100.
🐞 Esta versión falla: ábrela y pruébala

La prueba bisiesta es solo y % 4 == 0 — 1900 y 2100 pasan por bisiestos

#include <iostream>
using namespace std;

bool isLeap(int y) {
    return y % 4 == 0;   // bug: century rule missing
}

int monthDays(int y, int m) {
    int d[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    if (m == 2 && isLeap(y)) return 29;
    return d[m];
}

long long serial(int y, int m, int d) {
    long long s = 0;
    for (int yy = 1900; yy < y; yy++) s += isLeap(yy) ? 366 : 365;
    for (int mm = 1; mm < m; mm++) s += monthDays(y, mm);
    return s + d;
}

int main() {
    int y1, m1, d1, y2, m2, d2;
    cin >> y1 >> m1 >> d1 >> y2 >> m2 >> d2;
    cout << serial(y2, m2, d2) - serial(y1, m1, d1) << endl;
    return 0;
}