Skip to content
This repository was archived by the owner on Aug 3, 2026. It is now read-only.

Commit 4db288d

Browse files
committed
added three ntp server support
1 parent 81e4c5c commit 4db288d

8 files changed

Lines changed: 116 additions & 24 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ The format follows Keep a Changelog and the project adheres to Semantic Versioni
77
## [Unreleased]
88
### Added
99
- `ESPDateConfig::usePSRAMBuffers` to prefer PSRAM-backed allocation for ESPDate-owned text/state buffers (timezone, NTP server, scoped TZ restore state) through `ESPBufferManager`, with automatic fallback to normal heap.
10+
- Multi-server NTP config support in `ESPDateConfig` via `ntpServer` (primary), `ntpServer2` (secondary), and `ntpServer3` (tertiary), with empty values ignored and compacted before calling `configTzTime`.
1011
- `isDstActive` helper to detect whether daylight saving time is in effect using a provided POSIX TZ string, the stored TZ config, or the current system TZ.
1112
- Moon phase calculation helpers returning phase angle and illumination for any `DateTime` (or `now()`).
1213
- Local time helpers: `nowLocal()` plus `toLocal(DateTime[, tz])` expose broken-out local components and UTC offset for debugging sunrise/sunset and DST handling.
1314
- Documented the recommended UTC storage + local UI workflow (convert user-picked local times back to UTC with `fromLocal`/`parseDateTimeLocal`).
1415
- `setNtpSyncCallback(...)` so applications can optionally react when SNTP reports a successful sync.
1516
- `setNtpSyncCallback(const NtpSyncCallable&)` overload so member methods can be registered via `std::bind`.
16-
- `syncNTP()` to immediately trigger a new SNTP sync using the configured NTP server.
17+
- `syncNTP()` to immediately trigger a new SNTP sync using the configured NTP server list.
1718
- `ntpSyncIntervalMs` config field plus `setNtpSyncIntervalMs(...)` runtime setter to override SNTP sync interval when runtime support is available.
1819
- Internal last-sync tracking plus `hasLastNtpSync()` / `lastNtpSync()` getters.
1920
- String formatting helpers for `DateTime`/`LocalDateTime` with buffer-based APIs plus `std::string` convenience wrappers (`nowUtcString`, `nowLocalString`, etc.).
@@ -23,7 +24,7 @@ The format follows Keep a Changelog and the project adheres to Semantic Versioni
2324

2425
### Changed
2526
- Replaced the `ESPDateConfig` constructor with an explicit `init(const ESPDateConfig&)` so configuration happens after the Arduino runtime is alive, avoiding early SNTP watchdog resets on some boards.
26-
- `ESPDateConfig` now accepts an `ntpServer`; when provided alongside `timeZone`, `init` calls `configTzTime` to set the TZ and bootstrap SNTP automatically.
27+
- `ESPDateConfig` now accepts up to three NTP servers; when at least one is provided alongside `timeZone`, `init` calls `configTzTime` to set the TZ and bootstrap SNTP automatically.
2728

2829
### Fixed
2930
- Restored builds by adding the missing internal `utils.h` helpers referenced by the sun/scheduler code paths.

README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ ESPDate is a tiny C++17 helper for ESP32 projects that makes working with dates
1919
- **Sunrise / sunset**: compute daily sun times from lat/lon using numeric offsets or POSIX TZ strings (auto-DST aware).
2020
- **DST detection**: `isDstActive` reports whether daylight saving time applies using the stored TZ, an explicit POSIX TZ string, or the current system TZ.
2121
- **Moon phase**: `moonPhase` returns the current lunar phase angle and illumination fraction for any moment.
22-
- **Optional NTP bootstrap**: call `init` with `ESPDateConfig` containing both `timeZone` and `ntpServer` to set TZ and start SNTP after Arduino/WiFi is ready.
22+
- **Optional NTP bootstrap**: call `init` with `ESPDateConfig` containing `timeZone` and at least one NTP server (`ntpServer`, optional `ntpServer2`/`ntpServer3`) to set TZ and start SNTP after Arduino/WiFi is ready.
2323
- **NTP sync callback + manual re-sync**: register `setNtpSyncCallback(...)` with a function, lambda, or `std::bind`, call `syncNTP()` anytime to trigger an immediate refresh, and optionally override SNTP interval via `ntpSyncIntervalMs` / `setNtpSyncIntervalMs(...)`.
2424
- **Optional PSRAM-backed config/state buffers**: `ESPDateConfig::usePSRAMBuffers` routes ESPDate-owned text state (timezone/NTP/scoped TZ restore buffers) through `ESPBufferManager` with automatic fallback.
2525
- **Explicit lifecycle cleanup**: `deinit()` unregisters ESPDate-owned SNTP callback hooks, clears runtime config buffers, and is safe to call repeatedly; the destructor calls it automatically.
@@ -31,8 +31,8 @@ ESPDate is a tiny C++17 helper for ESP32 projects that makes working with dates
3131
- **Class-based API**: everything hangs off a single `ESPDate` instance; no global namespace clutter.
3232
- **Lightweight & portable**: C++17, header-first public API; relies only on standard C time functions and the system clock (`time()`).
3333

34-
ESPDate does not configure SNTP by default. Call `init` with a POSIX TZ string plus an `ntpServer` to have ESPDate call `configTzTime` for you—do this after the Arduino runtime and WiFi are up to avoid early watchdog resets. Otherwise you remain in control of time-zone setup and system clock sync.
35-
`syncNTP()` returns `true` only when an NTP server is configured and the runtime supports `configTzTime`.
34+
ESPDate does not configure SNTP by default. Call `init` with a POSIX TZ string plus at least one NTP server (`ntpServer`, optional `ntpServer2`/`ntpServer3`) to have ESPDate call `configTzTime` for you. Do this after the Arduino runtime and WiFi are up to avoid early watchdog resets. Otherwise you remain in control of time-zone setup and system clock sync.
35+
`syncNTP()` returns `true` only when one or more NTP servers are configured and the runtime supports `configTzTime`.
3636
SNTP exposes a system-level sync hook, so the last `setNtpSyncCallback(...)` registration is the active callback.
3737
For the same reason, `lastNtpSync()` is tracked on the currently active `ESPDate` instance.
3838
Example member-method binding style:
@@ -63,9 +63,12 @@ void setup() {
6363

6464
// Configure TZ + NTP after WiFi is connected if you want ESPDate to call configTzTime
6565
ESPDateConfig dateCfg{0.0f, 0.0f, "CET-1CEST,M3.5.0/2,M10.5.0/3", "pool.ntp.org", 15 * 60 * 1000};
66+
dateCfg.ntpServer2 = "time.google.com";
67+
dateCfg.ntpServer3 = "time.cloudflare.com";
6668
dateCfg.usePSRAMBuffers = true; // optional: best effort, falls back automatically on non-PSRAM boards
6769
date.init(dateCfg);
6870

71+
// Single-server config remains valid as before.
6972
ESPDateConfig solarCfg{47.4979f, 19.0402f, "CET-1CEST,M3.5.0/2,M10.5.0/3", "pool.ntp.org", 15 * 60 * 1000};
7073
solarCfg.usePSRAMBuffers = true;
7174
solar.init(solarCfg);
@@ -386,7 +389,10 @@ Bind your coordinates and TZ once via `init`, then fetch today’s sun cycle (au
386389

387390
```cpp
388391
ESPDate solar;
389-
solar.init(ESPDateConfig{47.4979f, 19.0402f, "CET-1CEST,M3.5.0/2,M10.5.0/3", "pool.ntp.org"});
392+
ESPDateConfig cfg{47.4979f, 19.0402f, "CET-1CEST,M3.5.0/2,M10.5.0/3", "pool.ntp.org"};
393+
cfg.ntpServer2 = "time.google.com";
394+
cfg.ntpServer3 = "time.cloudflare.com";
395+
solar.init(cfg);
390396

391397
SunCycleResult rise = solar.sunrise(); // today, using stored config
392398
SunCycleResult setToday = solar.sunset(); // today, using stored config
@@ -456,7 +462,10 @@ See `examples/sun_cycle/sun_cycle.ino` for a full sketch. Key bits:
456462

457463
```cpp
458464
ESPDate solar;
459-
solar.init(ESPDateConfig{47.4979f, 19.0402f, "CET-1CEST,M3.5.0/2,M10.5.0/3", "pool.ntp.org"}); // call in setup after WiFi
465+
ESPDateConfig cfg{47.4979f, 19.0402f, "CET-1CEST,M3.5.0/2,M10.5.0/3", "pool.ntp.org"};
466+
cfg.ntpServer2 = "time.google.com";
467+
cfg.ntpServer3 = "time.cloudflare.com";
468+
solar.init(cfg); // call in setup after WiFi
460469
DateTime today = solar.now();
461470

462471
SunCycleResult rise = solar.sunrise(today);
@@ -472,7 +481,7 @@ if (rise.ok && set.ok) {
472481
```
473482

474483
## Gotchas
475-
- ESPDate configures SNTP only when you call `init` with both `timeZone` and `ntpServer` in `ESPDateConfig` (it calls `configTzTime`). Call it after WiFi is up, or ensure the device clock is set before calling `now()`. Sunrise/sunset use either the stored TZ string (if provided) or the current process TZmake sure it matches the coordinates you pass.
484+
- ESPDate configures SNTP only when you call `init` with `timeZone` and at least one configured NTP server (`ntpServer`, `ntpServer2`, or `ntpServer3`) in `ESPDateConfig` (it calls `configTzTime`). Empty server strings are ignored and compacted. Call it after WiFi is up, or ensure the device clock is set before calling `now()`. Sunrise/sunset use either the stored TZ string (if provided) or the current process TZ; make sure it matches the coordinates you pass.
476485
- All arithmetic and comparisons are UTC-first. Local helpers rely on the current process TZ (`setenv("TZ", ...)`, `tzset()`); make sure that matches your deployment.
477486
- Month/year arithmetic clamps to the last valid day of the target month (e.g., Jan 31 + 1 month → Feb 28/29; Feb 29 - 1 year → Feb 28).
478487
- `differenceInDays` is purely `seconds / 86400` truncated toward zero, not a calendar-boundary delta.

examples/basic_date/basic_date.ino

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ void setup() {
2727
delay(200);
2828
Serial.println("ESPDate basic example");
2929
Serial.println("Connect WiFi so configTzTime can sync time, or set the system clock manually before using date.now().");
30-
date.init(ESPDateConfig{0.0f, 0.0f, "CET-1CEST,M3.5.0/2,M10.5.0/3", "pool.ntp.org", 15 * 60 * 1000});
30+
ESPDateConfig cfg{0.0f, 0.0f, "CET-1CEST,M3.5.0/2,M10.5.0/3", "pool.ntp.org", 15 * 60 * 1000};
31+
cfg.ntpServer2 = "time.google.com";
32+
cfg.ntpServer3 = "time.cloudflare.com";
33+
date.init(cfg);
3134
date.setNtpSyncCallback(std::bind(&SyncObserver::onNtpSync, &syncObserver, std::placeholders::_1));
3235
date.setNtpSyncIntervalMs(10 * 60 * 1000); // optional runtime update
3336
date.syncNTP(); // force an immediate refresh using configured NTP

examples/ntp_sync_tracking/ntp_sync_tracking.ino

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@ void setup() {
2626
Serial.begin(115200);
2727
delay(200);
2828
Serial.println("ESPDate NTP sync tracking example");
29-
Serial.println("Connect WiFi before this sketch runs so SNTP can reach the server.");
29+
Serial.println("Connect WiFi before this sketch runs so SNTP can reach the configured servers.");
3030

31-
date.init(ESPDateConfig{0.0f, 0.0f, "CET-1CEST,M3.5.0/2,M10.5.0/3", "pool.ntp.org", 15 * 60 * 1000});
31+
ESPDateConfig cfg{0.0f, 0.0f, "CET-1CEST,M3.5.0/2,M10.5.0/3", "pool.ntp.org", 15 * 60 * 1000};
32+
cfg.ntpServer2 = "time.google.com";
33+
cfg.ntpServer3 = "time.cloudflare.com";
34+
date.init(cfg);
3235
date.setNtpSyncCallback([](const DateTime &syncedAtUtc) {
3336
char buf[32];
3437
if (syncedAtUtc.localString(buf, sizeof(buf))) {

examples/sun_cycle/sun_cycle.ino

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ void setup() {
1717
delay(250);
1818
Serial.println("ESPDate sun cycle example");
1919
Serial.println("Connect WiFi so configTzTime can sync time, or set system clock/TZ manually before running.");
20-
solar.init(ESPDateConfig{47.4979f, 19.0402f, "CET-1CEST,M3.5.0/2,M10.5.0/3", "pool.ntp.org"});
20+
ESPDateConfig cfg{47.4979f, 19.0402f, "CET-1CEST,M3.5.0/2,M10.5.0/3", "pool.ntp.org"};
21+
cfg.ntpServer2 = "time.google.com";
22+
cfg.ntpServer3 = "time.cloudflare.com";
23+
solar.init(cfg);
2124

2225
DateTime today = solar.now();
2326

src/esp_date/date.cpp

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,9 @@ void ESPDate::deinit() {
199199
ntpSyncIntervalMs_ = 0;
200200
const bool usePSRAM = usePSRAMBuffers_;
201201
timeZone_ = DateString(DateAllocator<char>(usePSRAM));
202-
ntpServer_ = DateString(DateAllocator<char>(usePSRAM));
202+
for (size_t i = 0; i < kMaxNtpServers; ++i) {
203+
ntpServers_[i] = DateString(DateAllocator<char>(usePSRAM));
204+
}
203205
usePSRAMBuffers_ = false;
204206
initialized_ = false;
205207

@@ -219,18 +221,25 @@ void ESPDate::init(const ESPDateConfig& config) {
219221
hasLocation_ = true;
220222
usePSRAMBuffers_ = config.usePSRAMBuffers;
221223
timeZone_ = DateString(DateAllocator<char>(usePSRAMBuffers_));
222-
ntpServer_ = DateString(DateAllocator<char>(usePSRAMBuffers_));
224+
for (size_t i = 0; i < kMaxNtpServers; ++i) {
225+
ntpServers_[i] = DateString(DateAllocator<char>(usePSRAMBuffers_));
226+
}
223227
ntpSyncIntervalMs_ = config.ntpSyncIntervalMs;
224228
hasLastNtpSync_ = false;
225229
lastNtpSync_ = DateTime{};
226230

227231
const bool hasTz = config.timeZone && config.timeZone[0] != '\0';
228-
const bool hasNtp = config.ntpServer && config.ntpServer[0] != '\0';
232+
const char* configuredNtpServers[kMaxNtpServers] = {config.ntpServer, config.ntpServer2, config.ntpServer3};
233+
size_t ntpServerCount = 0;
229234
if (hasTz) {
230235
timeZone_ = config.timeZone;
231236
}
232-
if (hasNtp) {
233-
ntpServer_ = config.ntpServer;
237+
for (size_t i = 0; i < kMaxNtpServers; ++i) {
238+
const char* server = configuredNtpServers[i];
239+
if (!server || server[0] == '\0') {
240+
continue;
241+
}
242+
ntpServers_[ntpServerCount++] = server;
234243
}
235244

236245
if (!applyNtpConfig() && hasTz) {
@@ -247,7 +256,7 @@ void ESPDate::setNtpSyncCallback(NtpSyncCallback callback) {
247256
activeNtpSyncCallback_ = callback;
248257
activeNtpSyncCallbackCallable_ = NtpSyncCallable{};
249258
#if ESPDATE_HAS_SNTP_NOTIFICATION_CB
250-
const bool keepTrackingEnabled = !ntpServer_.empty();
259+
const bool keepTrackingEnabled = hasAnyNtpServerConfigured();
251260
sntp_set_time_sync_notification_cb((callback || keepTrackingEnabled) ? &ESPDate::handleSntpSync : nullptr);
252261
#endif
253262
}
@@ -259,7 +268,7 @@ void ESPDate::setNtpSyncCallbackCallable(const NtpSyncCallable& callback) {
259268
activeNtpSyncCallback_ = nullptr;
260269
activeNtpSyncCallbackCallable_ = callback;
261270
#if ESPDATE_HAS_SNTP_NOTIFICATION_CB
262-
const bool keepTrackingEnabled = !ntpServer_.empty();
271+
const bool keepTrackingEnabled = hasAnyNtpServerConfigured();
263272
sntp_set_time_sync_notification_cb((static_cast<bool>(callback) || keepTrackingEnabled) ? &ESPDate::handleSntpSync
264273
: nullptr);
265274
#endif
@@ -289,9 +298,18 @@ bool ESPDate::syncNTP() {
289298
return applyNtpConfig();
290299
}
291300

301+
bool ESPDate::hasAnyNtpServerConfigured() const {
302+
for (size_t i = 0; i < kMaxNtpServers; ++i) {
303+
if (!ntpServers_[i].empty()) {
304+
return true;
305+
}
306+
}
307+
return false;
308+
}
309+
292310
bool ESPDate::applyNtpConfig() const {
293311
#if ESPDATE_HAS_CONFIG_TZ_TIME
294-
if (ntpServer_.empty()) {
312+
if (!hasAnyNtpServerConfigured()) {
295313
return false;
296314
}
297315
activeNtpSyncOwner_ = const_cast<ESPDate*>(this);
@@ -310,7 +328,10 @@ bool ESPDate::applyNtpConfig() const {
310328
#endif
311329

312330
const char* tz = timeZone_.empty() ? "UTC0" : timeZone_.c_str();
313-
configTzTime(tz, ntpServer_.c_str(), nullptr, nullptr);
331+
const char* ntpServer1 = ntpServers_[0].empty() ? nullptr : ntpServers_[0].c_str();
332+
const char* ntpServer2 = ntpServers_[1].empty() ? nullptr : ntpServers_[1].c_str();
333+
const char* ntpServer3 = ntpServers_[2].empty() ? nullptr : ntpServers_[2].c_str();
334+
configTzTime(tz, ntpServer1, ntpServer2, ntpServer3);
314335
return true;
315336
#else
316337
return false;

src/esp_date/date.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,11 @@ struct ESPDateConfig {
5454
float latitude = 0.0f;
5555
float longitude = 0.0f;
5656
const char* timeZone = nullptr; // POSIX TZ string, e.g. "CET-1CEST,M3.5.0/2,M10.5.0/3"
57-
const char* ntpServer = nullptr; // optional NTP server; used with timeZone to call configTzTime
57+
const char* ntpServer = nullptr; // optional primary NTP server; used with timeZone to call configTzTime
5858
uint32_t ntpSyncIntervalMs = 0; // optional SNTP sync interval override; 0 keeps runtime default
5959
bool usePSRAMBuffers = false; // prefer PSRAM for ESPDate-owned config/state text buffers
60+
const char* ntpServer2 = nullptr; // optional secondary NTP server
61+
const char* ntpServer3 = nullptr; // optional tertiary NTP server
6062
};
6163

6264
struct SunCycleResult {
@@ -101,7 +103,7 @@ class ESPDate {
101103
// Returns the last SNTP sync timestamp (UTC epoch-backed DateTime).
102104
// When hasLastNtpSync() is false this returns DateTime{}.
103105
DateTime lastNtpSync() const;
104-
// Triggers an immediate NTP sync with the configured server.
106+
// Triggers an immediate NTP sync with the configured server list.
105107
// Returns false when no NTP server is configured or SNTP runtime support is unavailable.
106108
bool syncNTP();
107109

@@ -282,6 +284,7 @@ class ESPDate {
282284
#endif
283285
void setNtpSyncCallbackCallable(const NtpSyncCallable& callback);
284286
bool applyNtpConfig() const;
287+
bool hasAnyNtpServerConfigured() const;
285288

286289
SunCycleResult sunriseFromConfig(const DateTime& day) const;
287290
SunCycleResult sunsetFromConfig(const DateTime& day) const;
@@ -290,7 +293,8 @@ class ESPDate {
290293
float latitude_ = 0.0f;
291294
float longitude_ = 0.0f;
292295
DateString timeZone_;
293-
DateString ntpServer_;
296+
static constexpr size_t kMaxNtpServers = 3;
297+
DateString ntpServers_[kMaxNtpServers];
294298
uint32_t ntpSyncIntervalMs_ = 0;
295299
bool usePSRAMBuffers_ = false;
296300
DateTime lastNtpSync_{};

test/test_esp_date/test_esp_date.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,28 @@
77
#include <functional>
88
#include <string>
99

10+
#if defined(__has_include)
11+
# if __has_include(<esp_sntp.h>) || __has_include(<esp_netif_sntp.h>)
12+
# define TEST_ESPDATE_HAS_CONFIG_TZ_TIME 1
13+
# else
14+
# define TEST_ESPDATE_HAS_CONFIG_TZ_TIME 0
15+
# endif
16+
#else
17+
# define TEST_ESPDATE_HAS_CONFIG_TZ_TIME 0
18+
#endif
19+
1020
ESPDate date;
1121
static const float kBudapestLat = 47.4979f;
1222
static const float kBudapestLon = 19.0402f;
1323

24+
static bool expected_sync_result_with_any_ntp_server() {
25+
#if TEST_ESPDATE_HAS_CONFIG_TZ_TIME
26+
return true;
27+
#else
28+
return false;
29+
#endif
30+
}
31+
1432
static void test_deinit_is_safe_before_init() {
1533
ESPDate monitor;
1634
TEST_ASSERT_FALSE(monitor.isInitialized());
@@ -267,6 +285,34 @@ static void test_sync_ntp_requires_server_config() {
267285
TEST_ASSERT_FALSE(onlyTimezone.syncNTP());
268286
}
269287

288+
static void test_sync_ntp_accepts_secondary_or_tertiary_server_only() {
289+
ESPDate secondaryOnly;
290+
ESPDateConfig secondaryCfg{0.0f, 0.0f, "UTC0", nullptr};
291+
secondaryCfg.ntpServer2 = "time.google.com";
292+
secondaryOnly.init(secondaryCfg);
293+
TEST_ASSERT_EQUAL(expected_sync_result_with_any_ntp_server(), secondaryOnly.syncNTP());
294+
295+
ESPDate tertiaryOnly;
296+
ESPDateConfig tertiaryCfg{0.0f, 0.0f, "UTC0", nullptr};
297+
tertiaryCfg.ntpServer3 = "time.cloudflare.com";
298+
tertiaryOnly.init(tertiaryCfg);
299+
TEST_ASSERT_EQUAL(expected_sync_result_with_any_ntp_server(), tertiaryOnly.syncNTP());
300+
}
301+
302+
static void test_sync_ntp_with_three_servers_matches_single_server_behavior() {
303+
ESPDate singleServer;
304+
singleServer.init(ESPDateConfig{0.0f, 0.0f, "UTC0", "pool.ntp.org"});
305+
const bool singleResult = singleServer.syncNTP();
306+
TEST_ASSERT_EQUAL(expected_sync_result_with_any_ntp_server(), singleResult);
307+
308+
ESPDate threeServers;
309+
ESPDateConfig threeServerCfg{0.0f, 0.0f, "UTC0", "pool.ntp.org"};
310+
threeServerCfg.ntpServer2 = "time.google.com";
311+
threeServerCfg.ntpServer3 = "time.cloudflare.com";
312+
threeServers.init(threeServerCfg);
313+
TEST_ASSERT_EQUAL(singleResult, threeServers.syncNTP());
314+
}
315+
270316
struct NtpSyncTestObserver {
271317
int callCount = 0;
272318
int64_t lastEpoch = 0;
@@ -385,6 +431,8 @@ void setup() {
385431
RUN_TEST(test_to_local_breakdown);
386432
RUN_TEST(test_moon_phase_full_and_new_moon);
387433
RUN_TEST(test_sync_ntp_requires_server_config);
434+
RUN_TEST(test_sync_ntp_accepts_secondary_or_tertiary_server_only);
435+
RUN_TEST(test_sync_ntp_with_three_servers_matches_single_server_behavior);
388436
RUN_TEST(test_ntp_callback_registration_supports_member_binding);
389437
RUN_TEST(test_ntp_sync_interval_setter_accepts_default);
390438
RUN_TEST(test_last_ntp_sync_defaults_to_empty);

0 commit comments

Comments
 (0)