adasd
All checks were successful
Deploy / deploy-staging (push) Successful in 6s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-04-24 02:32:28 +02:00
parent c73656d895
commit 92c9bed5bb
7 changed files with 115 additions and 12 deletions

View File

@@ -399,6 +399,69 @@ $mm->registerFunction($moduleName, 'bavest_request', static function (
];
});
$mm->registerFunction($moduleName, 'display_timezone', static function (): \DateTimeZone {
return new \DateTimeZone('Europe/Berlin');
});
$mm->registerFunction($moduleName, 'normalize_bavest_timestamp_utc', static function (mixed $value): string {
if (is_numeric($value)) {
return gmdate('Y-m-d H:i:s', (int) $value);
}
$raw = trim((string) $value);
if ($raw === '') {
return gmdate('Y-m-d H:i:s');
}
try {
$date = new \DateTimeImmutable($raw, new \DateTimeZone('UTC'));
return $date->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
} catch (\Throwable) {
$timestamp = strtotime($raw);
return $timestamp !== false ? gmdate('Y-m-d H:i:s', $timestamp) : gmdate('Y-m-d H:i:s');
}
});
$mm->registerFunction($moduleName, 'format_datetime_for_display', static function (
?string $value,
?string $source = null,
string $format = 'Y-m-d H:i:s'
): string {
$raw = trim((string) $value);
if ($raw === '') {
return '';
}
$displayTimezone = new \DateTimeZone('Europe/Berlin');
$source = trim((string) $source);
if (str_starts_with($source, 'bavest:')) {
$date = \DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $raw, new \DateTimeZone('UTC'));
if (!$date instanceof \DateTimeImmutable) {
try {
$date = new \DateTimeImmutable($raw, new \DateTimeZone('UTC'));
} catch (\Throwable) {
return $raw;
}
}
return $date->setTimezone($displayTimezone)->format($format);
}
if (preg_match('/(Z|[+\-]\d{2}:\d{2})$/', $raw) === 1) {
try {
return (new \DateTimeImmutable($raw))->setTimezone($displayTimezone)->format($format);
} catch (\Throwable) {
return $raw;
}
}
return str_replace('T', ' ', $raw);
});
$mm->registerFunction($moduleName, 'local_now_input_value', static function (): string {
return (new \DateTimeImmutable('now', new \DateTimeZone('Europe/Berlin')))->format('Y-m-d\TH:i');
});
$mm->registerFunction($moduleName, 'bavest_extract_quote', static function (array $entry): ?array {
$candidates = [$entry];
foreach (['quote', 'data', 'result', 'security'] as $nestedKey) {
@@ -419,8 +482,11 @@ $mm->registerFunction($moduleName, 'bavest_extract_quote', static function (arra
continue;
}
$timestamp = trim((string) ($candidate['timestamp'] ?? $candidate['time'] ?? $candidate['date'] ?? ''));
$timestamp = $timestamp !== '' ? date('Y-m-d H:i:s', strtotime($timestamp) ?: time()) : date('Y-m-d H:i:s');
$timestamp = module_fn(
'boersenchecker',
'normalize_bavest_timestamp_utc',
$candidate['timestamp'] ?? $candidate['time'] ?? $candidate['date'] ?? null
);
return [
'symbol' => trim((string) ($candidate['symbol'] ?? $candidate['ticker'] ?? $entry['symbol'] ?? '')),

View File

@@ -287,7 +287,7 @@
</label>
<label class="setup-field muted">
<span>Zeitpunkt</span>
<input type="datetime-local" name="quoted_at" value="<?= e(date('Y-m-d\TH:i')) ?>" required>
<input type="datetime-local" name="quoted_at" value="<?= e($localNowInputValue) ?>" required>
</label>
<label class="setup-field muted">
<span>Quelle</span>
@@ -404,7 +404,7 @@
<td>
<?php if ($position['latest_price'] !== null): ?>
<?= e($fmtNumber((float) $position['latest_price'], 4)) ?> <?= e((string) $position['latest_currency']) ?>
<div class="muted"><?= e((string) $position['latest_quoted_at']) ?></div>
<div class="muted"><?= e($fmtDateTime((string) $position['latest_quoted_at'], (string) ($position['latest_source'] ?? ''))) ?></div>
<?php else: ?>
<span class="muted">kein Kurs</span>
<?php endif; ?>
@@ -487,7 +487,7 @@
<tbody>
<?php foreach ($history as $quote): ?>
<tr>
<td><?= e((string) $quote['quoted_at']) ?></td>
<td><?= e($fmtDateTime((string) $quote['quoted_at'], (string) ($quote['source'] ?? ''))) ?></td>
<td><?= e($fmtNumber((float) $quote['price'], 4)) ?> <?= e((string) $quote['currency']) ?></td>
<td><?= e((string) $quote['source']) ?></td>
<td>

View File

@@ -124,7 +124,7 @@
<section class="module-box-soft bc-stat">
<div class="bc-field-label"><?= e((string) $position['instrument_name']) ?></div>
<div class="bc-stat-value"><?= $position['latest_price'] !== null ? e(number_format((float) $position['latest_price'], 2, ',', '.')) . ' ' . e((string) $position['latest_currency']) : 'n/a' ?></div>
<div class="bc-text" style="margin-top:6px;"><?= e((string) ($position['latest_quoted_at'] ?: 'kein Kurs')) ?></div>
<div class="bc-text" style="margin-top:6px;"><?= e((string) (($position['latest_quoted_at'] ?? '') !== '' ? $fmtDateTime((string) $position['latest_quoted_at'], (string) ($position['latest_source'] ?? '')) : 'kein Kurs')) ?></div>
</section>
<?php endforeach; ?>
</div>

View File

@@ -135,7 +135,7 @@
<div class="grid" style="grid-template-columns:repeat(auto-fit, minmax(180px, 1fr)); gap:10px;">
<label class="setup-field muted"><span>Kurs</span><input type="number" name="quote_price" min="0" step="0.00000001" required></label>
<label class="setup-field muted"><span>Waehrung</span><input type="text" name="quote_currency" value="<?= e((string) ($selectedInstrument['quote_currency'] ?? $defaultReportCurrency)) ?>" required></label>
<label class="setup-field muted"><span>Zeitpunkt</span><input type="datetime-local" name="quoted_at" value="<?= e(date('Y-m-d\TH:i')) ?>" required></label>
<label class="setup-field muted"><span>Zeitpunkt</span><input type="datetime-local" name="quoted_at" value="<?= e($localNowInputValue) ?>" required></label>
<label class="setup-field muted"><span>Quelle</span><input type="text" name="quote_source" value="manual"></label>
</div>
<div class="bc-actions">
@@ -169,7 +169,7 @@
<tbody>
<?php foreach ($quotes as $quote): ?>
<tr>
<td><?= e((string) $quote['quoted_at']) ?></td>
<td><?= e($fmtDateTime((string) $quote['quoted_at'], (string) ($quote['source'] ?? ''))) ?></td>
<td><?= e(number_format((float) $quote['price'], 4, ',', '.')) ?> <?= e((string) $quote['currency']) ?></td>
<td><?= e((string) $quote['source']) ?></td>
<td>

View File

@@ -114,6 +114,8 @@ final class DashboardPage
'selectedInstrumentForQuote' => $state['selectedInstrumentForQuote'],
'selectedInstrumentQuoteCurrency' => $state['selectedInstrumentQuoteCurrency'],
'fmtNumber' => fn (?float $value, int $scale = 2): string => $this->formatNumber($value, $scale),
'fmtDateTime' => fn (?string $value, ?string $source = null): string => (string) \module_fn('boersenchecker', 'format_datetime_for_display', $value, $source),
'localNowInputValue' => (string) \module_fn('boersenchecker', 'local_now_input_value'),
];
}
@@ -674,11 +676,13 @@ final class DashboardPage
$position['latest_price'] = (float) $latestQuote['price'];
$position['latest_currency'] = (string) $latestQuote['currency'];
$position['latest_quoted_at'] = (string) $latestQuote['quoted_at'];
$position['latest_source'] = (string) ($latestQuote['source'] ?? '');
$position['current_total_base'] = $currentTotalBase;
} else {
$position['latest_price'] = null;
$position['latest_currency'] = null;
$position['latest_quoted_at'] = null;
$position['latest_source'] = null;
$position['current_total_base'] = null;
}
@@ -784,13 +788,22 @@ final class DashboardPage
private function normalizeDateTimeLocal(?string $value): string
{
$timezone = new \DateTimeZone('Europe/Berlin');
$value = trim((string) $value);
if ($value === '') {
return date('Y-m-d H:i:s');
return (new \DateTimeImmutable('now', $timezone))->format('Y-m-d H:i:s');
}
$timestamp = strtotime($value);
return $timestamp !== false ? date('Y-m-d H:i:s', $timestamp) : date('Y-m-d H:i:s');
$date = \DateTimeImmutable::createFromFormat('Y-m-d\TH:i', $value, $timezone);
if ($date instanceof \DateTimeImmutable) {
return $date->format('Y-m-d H:i:s');
}
try {
return (new \DateTimeImmutable($value, $timezone))->format('Y-m-d H:i:s');
} catch (\Throwable) {
return (new \DateTimeImmutable('now', $timezone))->format('Y-m-d H:i:s');
}
}
private function formatNumber(?float $value, int $scale = 2): string

View File

@@ -68,6 +68,7 @@ final class HomePage
$position['latest_price'] = is_array($latestQuote) && is_numeric($latestQuote['price'] ?? null) ? (float) $latestQuote['price'] : null;
$position['latest_currency'] = is_array($latestQuote) ? (string) ($latestQuote['currency'] ?? '') : '';
$position['latest_quoted_at'] = is_array($latestQuote) ? (string) ($latestQuote['quoted_at'] ?? '') : '';
$position['latest_source'] = is_array($latestQuote) ? (string) ($latestQuote['source'] ?? '') : '';
$position['current_total_report'] = null;
$position['gain_report'] = null;
$position['gain_percent'] = null;
@@ -108,6 +109,7 @@ final class HomePage
'summary' => $this->buildSummary($positions),
'defaultReportCurrency' => $this->defaultReportCurrency,
'chartEndpoint' => '/module/boersenchecker/chart_data',
'fmtDateTime' => fn (?string $value, ?string $source = null): string => (string) \module_fn('boersenchecker', 'format_datetime_for_display', $value, $source),
];
}

View File

@@ -97,6 +97,8 @@ final class InstrumentPage
'searchKeywords' => $this->searchKeywords,
'searchResults' => $this->searchResults,
'defaultReportCurrency' => $this->defaultReportCurrency,
'fmtDateTime' => fn (?string $value, ?string $source = null): string => (string) \module_fn('boersenchecker', 'format_datetime_for_display', $value, $source),
'localNowInputValue' => (string) \module_fn('boersenchecker', 'local_now_input_value'),
];
}
@@ -180,7 +182,7 @@ final class InstrumentPage
'instrument_id' => $instrumentId,
'price' => $price,
'currency' => strtoupper(trim((string) ($_POST['quote_currency'] ?? $this->defaultReportCurrency))) ?: $this->defaultReportCurrency,
'quoted_at' => date('Y-m-d H:i:s', strtotime((string) ($_POST['quoted_at'] ?? 'now')) ?: time()),
'quoted_at' => $this->normalizeDateTimeLocal((string) ($_POST['quoted_at'] ?? '')),
'source' => trim((string) ($_POST['quote_source'] ?? 'manual')) ?: 'manual',
]);
return 'Kurs gespeichert.';
@@ -272,4 +274,24 @@ final class InstrumentPage
return $instrument;
}
private function normalizeDateTimeLocal(?string $value): string
{
$timezone = new \DateTimeZone('Europe/Berlin');
$value = trim((string) $value);
if ($value === '') {
return (new \DateTimeImmutable('now', $timezone))->format('Y-m-d H:i:s');
}
$date = \DateTimeImmutable::createFromFormat('Y-m-d\TH:i', $value, $timezone);
if ($date instanceof \DateTimeImmutable) {
return $date->format('Y-m-d H:i:s');
}
try {
return (new \DateTimeImmutable($value, $timezone))->format('Y-m-d H:i:s');
} catch (\Throwable) {
return (new \DateTimeImmutable('now', $timezone))->format('Y-m-d H:i:s');
}
}
}