Initialer Commit: Datumsrechner-App mit allen Funktionen, modernem Design und Template-Trennung

This commit is contained in:
2025-07-22 16:36:22 +02:00
commit 241552fb6d
10 changed files with 556 additions and 0 deletions

29
.gitignore vendored Normal file
View File

@@ -0,0 +1,29 @@
# Python
__pycache__/
*.py[cod]
*.pyo
*.pyd
# Virtualenv
.venv/
venv/
ENV/
# VS Code
.vscode/
# Sonstige
.DS_Store
Thumbs.db
# Logdateien
*.log
# Test-/Coverage-Dateien
htmlcov/
.coverage
.tox/
.pytest_cache/
# Betriebssystem
*.swp

24
README.md Normal file
View File

@@ -0,0 +1,24 @@
# Datumsberechnungen Web-App
Diese kleine Python-Webanwendung ermöglicht folgende Datumsberechnungen:
- Anzahl der Tage zwischen zwei Daten
- Anzahl der Werktage zwischen zwei Daten
- Anzeige des Wochentags eines Datums
## Installation
1. Python 3.8+ installieren
2. Abhängigkeiten installieren:
```
pip install -r requirements.txt
```
## Starten der App
```
python app.py
```
Die App ist dann unter http://localhost:5000 erreichbar.

105
app.py Normal file
View File

@@ -0,0 +1,105 @@
from flask import Flask, render_template, request
from datetime import datetime, timedelta
import numpy as np
app = Flask(__name__)
# HTML-Template wird jetzt aus templates/index.html geladen
WOCHENTAGE = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"]
@app.route('/', methods=['GET', 'POST'])
def index():
tage = werktage = wochentag = datumsrechnung = werktagsrechnung = kw_berechnen = kw_datum = None
active_idx = 0
if request.method == 'POST':
action = request.form.get('action')
if action == 'tage':
active_idx = 0
start = request.form.get('start1')
end = request.form.get('end1')
try:
d1 = datetime.strptime(start, '%Y-%m-%d')
d2 = datetime.strptime(end, '%Y-%m-%d')
tage = abs((d2 - d1).days)
except Exception:
tage = 'Ungültige Eingabe'
elif action == 'werktage':
active_idx = 1
start = request.form.get('start2')
end = request.form.get('end2')
try:
d1 = datetime.strptime(start, '%Y-%m-%d')
d2 = datetime.strptime(end, '%Y-%m-%d')
if d1 > d2:
d1, d2 = d2, d1
werktage = np.busday_count(d1.date(), (d2 + timedelta(days=1)).date())
except Exception:
werktage = 'Ungültige Eingabe'
elif action == 'wochentag':
active_idx = 2
datum = request.form.get('datum3')
try:
d = datetime.strptime(datum, '%Y-%m-%d')
wochentag = WOCHENTAGE[d.weekday()]
except Exception:
wochentag = 'Ungültige Eingabe'
elif action == 'datumsrechnung':
active_idx = 3
datum = request.form.get('datum4')
tage_input = request.form.get('tage4')
richtung = request.form.get('richtung4')
try:
d = datetime.strptime(datum, '%Y-%m-%d')
tage_int = int(tage_input)
if richtung == 'add':
result = d + timedelta(days=tage_int)
else:
result = d - timedelta(days=tage_int)
datumsrechnung = result.strftime('%d.%m.%Y')
except Exception:
datumsrechnung = 'Ungültige Eingabe'
elif action == 'werktagsrechnung':
active_idx = 4
datum = request.form.get('datum5')
tage_input = request.form.get('tage5')
richtung = request.form.get('richtung5')
try:
d = datetime.strptime(datum, '%Y-%m-%d').date()
tage_int = int(tage_input)
if richtung == 'add':
result = np.busday_offset(d, tage_int, roll='forward')
else:
result = np.busday_offset(d, -tage_int, roll='backward')
werktagsrechnung = np.datetime_as_string(result, unit='D')
# Formatierung auf deutsch
dt = datetime.strptime(werktagsrechnung, '%Y-%m-%d')
werktagsrechnung = dt.strftime('%d.%m.%Y')
except Exception:
werktagsrechnung = 'Ungültige Eingabe'
elif action == 'kw_berechnen':
active_idx = 5
datum = request.form.get('datum6')
try:
d = datetime.strptime(datum, '%Y-%m-%d')
kw = d.isocalendar().week
kw_berechnen = f"KW {kw} ({d.year})"
except Exception:
kw_berechnen = 'Ungültige Eingabe'
elif action == 'kw_datum':
active_idx = 6
jahr = request.form.get('jahr7')
kw = request.form.get('kw7')
try:
jahr = int(jahr)
kw = int(kw)
# Montag der KW
start = datetime.fromisocalendar(jahr, kw, 1)
end = datetime.fromisocalendar(jahr, kw, 7)
kw_datum = f"{start.strftime('%d.%m.%Y')} bis {end.strftime('%d.%m.%Y')}"
except Exception:
kw_datum = 'Ungültige Eingabe'
return render_template('index.html', tage=tage, werktage=werktage, wochentag=wochentag, datumsrechnung=datumsrechnung, werktagsrechnung=werktagsrechnung, kw_berechnen=kw_berechnen, kw_datum=kw_datum, active_idx=active_idx)
if __name__ == '__main__':
app.run(debug=True)

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

6
favicon.svg Normal file
View File

@@ -0,0 +1,6 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="6" width="26" height="22" rx="5" fill="#2563eb"/>
<rect x="3" y="6" width="26" height="6" rx="2" fill="#1e40af"/>
<rect x="8" y="14" width="16" height="10" rx="3" fill="#fff"/>
<text x="16" y="22" text-anchor="middle" font-size="10" font-family="Segoe UI, Arial, sans-serif" fill="#2563eb" font-weight="bold">15</text>
</svg>

After

Width:  |  Height:  |  Size: 445 B

8
idea.txt Normal file
View File

@@ -0,0 +1,8 @@
erstelle eine python web app, die verschiedene datumsberechnungen durchführt:
- Berechnung der Anzahl der Tage zwischen zwei Daten
- Berechnung der Anzahl der Werktage zwischen zwei Daten
- Anzeige des Wochentags eines Datums
Beachte:
- Virtual Environment unter ./.venv
- Wir entwickeln unter Windows

8
logo.svg Normal file
View File

@@ -0,0 +1,8 @@
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="15" y="25" width="90" height="80" rx="16" fill="#2563eb"/>
<rect x="15" y="25" width="90" height="20" rx="6" fill="#1e40af"/>
<rect x="30" y="55" width="60" height="35" rx="8" fill="#fff"/>
<text x="60" y="80" text-anchor="middle" font-size="28" font-family="Segoe UI, Arial, sans-serif" fill="#2563eb" font-weight="bold">15</text>
<rect x="35" y="15" width="8" height="18" rx="4" fill="#fff"/>
<rect x="77" y="15" width="8" height="18" rx="4" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 585 B

2
requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
Flask==3.0.3
numpy==1.26.4

374
templates/index.html Normal file
View File

@@ -0,0 +1,374 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Elpatrons Datumsrechner</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<style>
:root {
--primary: #2563eb;
--primary-dark: #1e40af;
--background: #f8fafc;
--surface: #fff;
--border: #e5e7eb;
--text: #1e293b;
--shadow: 0 2px 8px rgba(30,41,59,0.07);
}
body {
background: var(--background);
color: var(--text);
font-family: 'Segoe UI', Arial, sans-serif;
margin: 0;
padding: 0;
}
.container {
max-width: 480px;
margin: 3em auto;
background: var(--surface);
border-radius: 16px;
box-shadow: var(--shadow);
padding: 2.5em 2em 2em 2em;
border: 1px solid var(--border);
}
h1 {
text-align: center;
margin-bottom: 2em;
font-size: 2.1em;
letter-spacing: 0.01em;
}
form {
margin-bottom: 2.2em;
padding-bottom: 1.2em;
border-bottom: 1px solid var(--border);
}
form:last-of-type {
border-bottom: none;
margin-bottom: 0;
}
h2 {
font-size: 1.15em;
margin-bottom: 0.7em;
color: var(--primary-dark);
}
label {
display: block;
margin-bottom: 0.7em;
font-weight: 500;
}
.date-row {
display: flex;
align-items: center;
gap: 0.5em;
margin-top: 0.2em;
}
.date-calc-row {
display: flex;
align-items: center;
gap: 0.5em;
margin-top: 0.2em;
}
input[type="date"] {
padding: 0.45em 0.7em;
border: 1px solid var(--border);
border-radius: 6px;
font-size: 1em;
background: #f1f5f9;
color: var(--text);
}
.today-btn {
padding: 0.35em 0.9em;
background: var(--primary-dark);
color: #fff;
border: none;
border-radius: 6px;
font-size: 0.95em;
font-weight: 500;
cursor: pointer;
transition: background 0.2s;
}
.today-btn:hover {
background: var(--primary);
}
button {
margin-top: 0.7em;
padding: 0.55em 1.3em;
background: var(--primary);
color: #fff;
border: none;
border-radius: 6px;
font-size: 1em;
font-weight: 600;
cursor: pointer;
box-shadow: 0 1px 3px rgba(30,41,59,0.05);
transition: background 0.2s;
}
button:hover {
background: var(--primary-dark);
}
.result {
margin-top: 1em;
font-weight: bold;
background: #e0e7ff;
color: #1e293b;
border-radius: 6px;
padding: 0.7em 1em;
box-shadow: 0 1px 2px rgba(30,41,59,0.04);
}
.accordion {
border-radius: 12px;
overflow: hidden;
box-shadow: var(--shadow);
background: var(--surface);
margin-bottom: 2em;
}
.accordion-item + .accordion-item {
border-top: 1px solid var(--border);
}
.accordion-header {
background: var(--primary-dark);
color: #fff;
cursor: pointer;
padding: 1em 1.2em;
font-size: 1.1em;
font-weight: 600;
border: none;
outline: none;
width: 100%;
text-align: left;
transition: background 0.2s;
}
.accordion-header.active, .accordion-header:hover {
background: var(--primary);
}
.accordion-content {
display: none;
padding: 1.2em 1.2em 1em 1.2em;
background: var(--surface);
}
.accordion-content.active {
display: block;
}
@media (max-width: 600px) {
.container {
margin: 1em;
padding: 1.2em 0.7em 1em 0.7em;
}
h1 {
font-size: 1.3em;
}
}
</style>
<script>
function setToday(id) {
const today = new Date().toISOString().split('T')[0];
document.getElementById(id).value = today;
}
function openAccordion(idx) {
document.querySelectorAll('.accordion-header').forEach((btn, i) => {
btn.classList.toggle('active', i === idx);
});
document.querySelectorAll('.accordion-content').forEach((el, i) => {
el.classList.toggle('active', i === idx);
});
}
document.addEventListener('DOMContentLoaded', function() {
openAccordion({{ active_idx|default(0) }});
});
</script>
</head>
<body>
<div class="container">
<h1>Datumsberechnungen</h1>
<div class="accordion">
<div class="accordion-item">
<button type="button" class="accordion-header" onclick="openAccordion(0)">
<span style="vertical-align:middle;display:inline-block;width:1.5em;">
<!-- Kalender mit Doppelpfeil -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><path d="M8 15h8M8 15l2-2M8 15l2 2M16 15l-2-2M16 15l-2 2" stroke="#2563eb" stroke-width="1.5" stroke-linecap="round"/></svg>
</span>
Anzahl der Tage zwischen zwei Daten
</button>
<div class="accordion-content">
<form method="post">
<label>Startdatum:<br>
<span class="date-row">
<input type="date" name="start1" id="start1">
<button type="button" class="today-btn" onclick="setToday('start1')">Heute</button>
</span>
</label>
<label>Enddatum:<br>
<span class="date-row">
<input type="date" name="end1" id="end1">
<button type="button" class="today-btn" onclick="setToday('end1')">Heute</button>
</span>
</label>
<button name="action" value="tage" type="submit">Berechnen</button>
</form>
{% if tage is not none %}
<div class="result">Anzahl der Tage: {{ tage }}</div>
{% endif %}
</div>
</div>
<div class="accordion-item">
<button type="button" class="accordion-header" onclick="openAccordion(1)">
<span style="vertical-align:middle;display:inline-block;width:1.5em;">
<!-- Kalender mit Mo-Fr Symbol -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><text x="12" y="17" text-anchor="middle" font-size="8" font-family="Segoe UI, Arial, sans-serif" fill="#2563eb" font-weight="bold">Mo-Fr</text></svg>
</span>
Anzahl der Werktage zwischen zwei Daten
</button>
<div class="accordion-content">
<form method="post">
<label>Startdatum:<br>
<span class="date-row">
<input type="date" name="start2" id="start2">
<button type="button" class="today-btn" onclick="setToday('start2')">Heute</button>
</span>
</label>
<label>Enddatum:<br>
<span class="date-row">
<input type="date" name="end2" id="end2">
<button type="button" class="today-btn" onclick="setToday('end2')">Heute</button>
</span>
</label>
<button name="action" value="werktage" type="submit">Berechnen</button>
</form>
{% if werktage is not none %}
<div class="result">Anzahl der Werktage: {{ werktage }}</div>
{% endif %}
</div>
</div>
<div class="accordion-item">
<button type="button" class="accordion-header" onclick="openAccordion(2)">
<span style="vertical-align:middle;display:inline-block;width:1.5em;">
<!-- Kalender mit W -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><text x="12" y="17" text-anchor="middle" font-size="12" font-family="Segoe UI, Arial, sans-serif" fill="#2563eb" font-weight="bold">W</text></svg>
</span>
Wochentag eines Datums
</button>
<div class="accordion-content">
<form method="post">
<label>Datum:<br>
<span class="date-row">
<input type="date" name="datum3" id="datum3">
<button type="button" class="today-btn" onclick="setToday('datum3')">Heute</button>
</span>
</label>
<button name="action" value="wochentag" type="submit">Anzeigen</button>
</form>
{% if wochentag is not none %}
<div class="result">Wochentag: {{ wochentag }}</div>
{% endif %}
</div>
</div>
<div class="accordion-item">
<button type="button" class="accordion-header" onclick="openAccordion(3)">
<span style="vertical-align:middle;display:inline-block;width:1.5em;">
<!-- Kalender mit ± -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><text x="12" y="17" text-anchor="middle" font-size="16" font-family="Segoe UI, Arial, sans-serif" fill="#2563eb" font-weight="bold">±</text></svg>
</span>
Datum plus/minus X Tage
</button>
<div class="accordion-content">
<form method="post">
<label>Datum:<br>
<span class="date-row">
<input type="date" name="datum4" id="datum4">
<button type="button" class="today-btn" onclick="setToday('datum4')">Heute</button>
</span>
</label>
<label>Anzahl Tage:<br>
<input type="number" name="tage4" id="tage4" style="width: 6em;">
</label>
<span class="date-calc-row">
<label><input type="radio" name="richtung4" value="add" checked> addieren</label>
<label><input type="radio" name="richtung4" value="sub"> subtrahieren</label>
</span>
<button name="action" value="datumsrechnung" type="submit">Berechnen</button>
</form>
{% if datumsrechnung is not none %}
<div class="result">Ergebnis: {{ datumsrechnung }}</div>
{% endif %}
</div>
</div>
<div class="accordion-item">
<button type="button" class="accordion-header" onclick="openAccordion(4)">
<span style="vertical-align:middle;display:inline-block;width:1.5em;">
<!-- Kalender mit ± und Mo-Fr -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><text x="12" y="14" text-anchor="middle" font-size="10" font-family="Segoe UI, Arial, sans-serif" fill="#2563eb" font-weight="bold">±</text><text x="12" y="20" text-anchor="middle" font-size="7" font-family="Segoe UI, Arial, sans-serif" fill="#2563eb" font-weight="bold">Mo-Fr</text></svg>
</span>
Datum plus/minus X Werktage
</button>
<div class="accordion-content">
<form method="post">
<label>Datum:<br>
<span class="date-row">
<input type="date" name="datum5" id="datum5">
<button type="button" class="today-btn" onclick="setToday('datum5')">Heute</button>
</span>
</label>
<label>Anzahl Werktage:<br>
<input type="number" name="tage5" id="tage5" style="width: 6em;">
</label>
<span class="date-calc-row">
<label><input type="radio" name="richtung5" value="add" checked> addieren</label>
<label><input type="radio" name="richtung5" value="sub"> subtrahieren</label>
</span>
<button name="action" value="werktagsrechnung" type="submit">Berechnen</button>
</form>
{% if werktagsrechnung is not none %}
<div class="result">Ergebnis: {{ werktagsrechnung }}</div>
{% endif %}
</div>
</div>
<div class="accordion-item">
<button type="button" class="accordion-header" onclick="openAccordion(5)">
<span style="vertical-align:middle;display:inline-block;width:1.5em;">
<!-- Kalender mit # -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><text x="12" y="17" text-anchor="middle" font-size="13" font-family="Segoe UI, Arial, sans-serif" fill="#2563eb" font-weight="bold">#</text></svg>
</span>
Kalenderwoche zu Datum
</button>
<div class="accordion-content">
<form method="post">
<label>Datum:<br>
<span class="date-row">
<input type="date" name="datum6" id="datum6">
<button type="button" class="today-btn" onclick="setToday('datum6')">Heute</button>
</span>
</label>
<button name="action" value="kw_berechnen" type="submit">Kalenderwoche berechnen</button>
</form>
{% if kw_berechnen is not none %}
<div class="result">Kalenderwoche: {{ kw_berechnen }}</div>
{% endif %}
</div>
</div>
<div class="accordion-item">
<button type="button" class="accordion-header" onclick="openAccordion(6)">
<span style="vertical-align:middle;display:inline-block;width:1.5em;">
<!-- Kalender mit Pfeil nach außen -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><path d="M7 17l5-5 5 5" stroke="#2563eb" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><text x="12" y="12" text-anchor="middle" font-size="8" font-family="Segoe UI, Arial, sans-serif" fill="#2563eb" font-weight="bold">KW</text></svg>
</span>
Start-/Enddatum zu Kalenderwoche
</button>
<div class="accordion-content">
<form method="post">
<label>Jahr:<br>
<input type="number" name="jahr7" id="jahr7" min="1900" max="2100" style="width: 7em;">
</label>
<label>Kalenderwoche:<br>
<input type="number" name="kw7" id="kw7" min="1" max="53" style="width: 5em;">
</label>
<button name="action" value="kw_datum" type="submit">Start-/Enddatum berechnen</button>
</form>
{% if kw_datum is not none %}
<div class="result">{{ kw_datum }}</div>
{% endif %}
</div>
</div>
</div>
</div>
</body>
</html>