From efb2dcdb165755213dc5778eea8bcba19f3330fe Mon Sep 17 00:00:00 2001 From: Leonardo Rivera Date: Thu, 9 May 2024 18:08:48 -0300 Subject: [PATCH 1/6] Added method getFormattedDateTime and keywords --- NTPClient.cpp | 190 ++++++++++++++++++++++++++++++++++++++++++++++---- NTPClient.h | 34 ++++++++- keywords.txt | 8 ++- 3 files changed, 216 insertions(+), 16 deletions(-) diff --git a/NTPClient.cpp b/NTPClient.cpp index b435855..2ece8d2 100755 --- a/NTPClient.cpp +++ b/NTPClient.cpp @@ -21,6 +21,49 @@ #include "NTPClient.h" +const DateLanguageData EnglishData = { + {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}, + {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}, + {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} +}; + +const DateLanguageData SpanishData = { + {"Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb"}, + {"Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"}, + {"Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"}, + {"Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"} +}; + +const DateLanguageData PortugueseData = { + {"Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"}, + {"Domingo", "Segunda-feira", "Terça-feira", "Quarta-feira", "Quinta-feira", "Sexta-feira", "Sábado"}, + {"Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"}, + {"Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"} +}; + +// Language map definition +const struct LanguageMap { + const char* code; + const DateLanguageData* data; +} languageMap[] = { + {"en", &EnglishData}, + {"es", &SpanishData}, + {"pt", &PortugueseData} + // Add new languages here +}; +const int languageMapSize = sizeof(languageMap) / sizeof(LanguageMap); + +// Function to find language data by code +const DateLanguageData* findLanguageData(const String& code) { + for (int i = 0; i < languageMapSize; ++i) { + if (code == languageMap[i].code) { + return languageMap[i].data; + } + } + return &EnglishData; // Default to English if not found +} + NTPClient::NTPClient(UDP& udp) { this->_udp = &udp; } @@ -136,7 +179,7 @@ unsigned long NTPClient::getEpochTime() const { ((millis() - this->_lastUpdate) / 1000); // Time since last update } -int NTPClient::getDay() const { +int NTPClient::getDayOfWeek() const { return (((this->getEpochTime() / 86400L) + 4 ) % 7); //0 is Sunday } int NTPClient::getHours() const { @@ -149,18 +192,137 @@ int NTPClient::getSeconds() const { return (this->getEpochTime() % 60); } -String NTPClient::getFormattedTime() const { - unsigned long rawTime = this->getEpochTime(); - unsigned long hours = (rawTime % 86400L) / 3600; - String hoursStr = hours < 10 ? "0" + String(hours) : String(hours); +int NTPClient::getDay() const { + long days = this->getEpochTime() / 86400L; + int fullYears = days / 365; + int overDays = days % 365; + + // Adjust for leap years + int leapYears = (fullYears + 1) / 4; // +1 because year 0 (1970) is not a leap year + if (leapYears > overDays) { + fullYears--; + } + + int currentYear = 1970 + fullYears; + + // Check if current year is a leap year + bool thisYearIsLeap = (currentYear % 4 == 0 && (currentYear % 100 != 0 || currentYear % 400 == 0)); + + // Calculate day of the year + int dayOfYear = (days - ((fullYears * 365) + leapYears)) + 1; // +1 to convert from 0-based to 1-based + + int daysInMonth[12] = {31, 28 + thisYearIsLeap, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - unsigned long minutes = (rawTime % 3600) / 60; - String minuteStr = minutes < 10 ? "0" + String(minutes) : String(minutes); + int monthDay = dayOfYear; + for (int month = 0; month < 12; month++) { + if (monthDay <= daysInMonth[month]) { + return monthDay; // Correct day of the month + } + monthDay -= daysInMonth[month]; + } - unsigned long seconds = rawTime % 60; - String secondStr = seconds < 10 ? "0" + String(seconds) : String(seconds); + return -1; // Error case, should not happen +} + +int NTPClient::getMonth() const { + long days = this->getEpochTime() / 86400L; // Total days since epoch + int fullYears = 0; + int year = 1970; + + // Correctly account for leap years in the loop + while (days > 365) { + if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { + if (days > 366) { + days -= 366; + year++; + } else { + break; // Leap year but not enough days to complete the year + } + } else { + days -= 365; + year++; + } + } + + bool thisYearIsLeap = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); + int daysInMonth[12] = {31, 28 + thisYearIsLeap, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + int month = 0; + for (month = 0; month < 12; month++) { + if (days < daysInMonth[month]) { + break; // Found the current month + } + days -= daysInMonth[month]; + } + + return month + 1; // Month is 1-based +} + +int NTPClient::getYear() const { + long days = this->getEpochTime() / 86400L; // Total days since epoch + int year = 1970; + + while (days > 365) { + if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { + // Leap year + if (days > 366) { + days -= 366; + year += 1; + } else { + // If days <= 366 in a leap year, then we've found the current year + break; + } + } else { + // Not a leap year + days -= 365; + year += 1; + } + } + + return year; +} - return hoursStr + ":" + minuteStr + ":" + secondStr; +String NTPClient::getFormattedDateTime(const String& format) { + String result; + bool escape = false; + + const DateLanguageData* langData = findLanguageData(this->_dateLanguage); + + for (char c : format) { + if (c == '%') { + if (escape) { + result += c; // Literal '%' character + escape = false; + } else { + escape = true; + } + continue; + } + + if (escape) { + switch (c) { + case 'Y': result += String(this->getYear()); break; + case 'y': result += String(this->getYear()).substring(2); break; + case 'm': result += (this->getMonth() < 10 ? "0" : "") + String(this->getMonth()); break; + case 'd': result += (this->getDay() < 10 ? "0" : "") + String(this->getDay()); break; + case 'H': result += (this->getHours() < 10 ? "0" : "") + String(this->getHours()); break; + case 'M': result += (this->getMinutes() < 10 ? "0" : "") + String(this->getMinutes()); break; + case 'S': result += (this->getSeconds() < 10 ? "0" : "") + String(this->getSeconds()); break; + case 'a': result += langData->shortWeekDays[this->getDayOfWeek()]; break; + case 'A': result += langData->longWeekDays[this->getDayOfWeek()]; break; + case 'w': result += String(this->getDayOfWeek()); break; + case 'b': result += langData->shortMonths[this->getMonth() - 1]; break; + case 'B': result += langData->longMonths[this->getMonth() - 1]; break; + case 'p': result += (this->getHours() < 12 ? "AM" : "PM"); break; // Note: Consider locale for AM/PM + default: result += "%" + String(c); // Unsupported format code + } + escape = false; + } else { + result += c; + } + } + + return result; } void NTPClient::end() { @@ -170,15 +332,19 @@ void NTPClient::end() { } void NTPClient::setTimeOffset(int timeOffset) { - this->_timeOffset = timeOffset; + this->_timeOffset = timeOffset; } void NTPClient::setUpdateInterval(unsigned long updateInterval) { this->_updateInterval = updateInterval; } +void NTPClient::setDateLanguage(const String &dateLanguage) { + this->_dateLanguage = dateLanguage; +} + void NTPClient::setPoolServerName(const char* poolServerName) { - this->_poolServerName = poolServerName; + this->_poolServerName = poolServerName; } void NTPClient::sendNTPPacket() { diff --git a/NTPClient.h b/NTPClient.h index a31d32f..ee5388b 100755 --- a/NTPClient.h +++ b/NTPClient.h @@ -8,9 +8,17 @@ #define NTP_PACKET_SIZE 48 #define NTP_DEFAULT_LOCAL_PORT 1337 +struct DateLanguageData { + const char* shortWeekDays[7]; + const char* longWeekDays[7]; + const char* shortMonths[12]; + const char* longMonths[12]; +}; + class NTPClient { private: UDP* _udp; + String _dateLanguage = "en"; // Default language bool _udpSetup = false; const char* _poolServerName = "pool.ntp.org"; // Default time server @@ -81,7 +89,10 @@ class NTPClient { */ bool isTimeSet() const; + int getDayOfWeek() const; int getDay() const; + int getMonth() const; + int getYear() const; int getHours() const; int getMinutes() const; int getSeconds() const; @@ -98,9 +109,28 @@ class NTPClient { void setUpdateInterval(unsigned long updateInterval); /** - * @return time formatted like `hh:mm:ss` + * @return Date Time string formated. The available format codes are: + %Y: Full year (e.g., 2023) + %y: Last two digits of the year (e.g., 23 for 2023) + %m: Month as a zero-padded decimal number (01 to 12) + %d: Day of the month as a zero-padded decimal number (01 to 31) + %H: Hour (00 to 23) as a zero-padded decimal number + %M: Minute as a zero-padded decimal number (00 to 59) + %S: Second as a zero-padded decimal number (00 to 59) + %a: Abbreviated weekday name according to the current locale + %A: Full weekday name according to the current locale + %w: Weekday as a decimal number (0 for Sunday through 6 for Saturday) + %b: Abbreviated month name according to the current locale + %B: Full month name according to the current locale + %p: "AM" or "PM" based on the hour (Note: This is locale-sensitive and might not be applicable in all languages) + */ + String getFormattedDateTime(const String &format); + + /** + * Set language for displaying date. Available languages are 'pt', 'es' and 'en' (default) + * @param dateLanguage */ - String getFormattedTime() const; + void setDateLanguage(const String &dateLanguage); /** * @return time in seconds since Jan. 1, 1970 diff --git a/keywords.txt b/keywords.txt index edce989..1423bd2 100644 --- a/keywords.txt +++ b/keywords.txt @@ -14,11 +14,15 @@ update KEYWORD2 forceUpdate KEYWORD2 isTimeSet KEYWORD2 getDay KEYWORD2 +getDayOfWeek KEYWORD2 +getDay KEYWORD2 +getMonth KEYWORD2 +getYear KEYWORD2 getHours KEYWORD2 getMinutes KEYWORD2 getSeconds KEYWORD2 -getFormattedTime KEYWORD2 +getFormattedDateTime KEYWORD2 getEpochTime KEYWORD2 setTimeOffset KEYWORD2 setUpdateInterval KEYWORD2 -setPoolServerName KEYWORD2 +setPoolServerName KEYWORD2 \ No newline at end of file From 98d57da5ef3d17209cbdb51b63fb3be1fcad3918 Mon Sep 17 00:00:00 2001 From: Leonardo Rivera Date: Thu, 9 May 2024 19:21:03 -0300 Subject: [PATCH 2/6] Adjust spelling --- .codespellrc | 2 +- NTPClient.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.codespellrc b/.codespellrc index 101edae..20cd0f8 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,7 +1,7 @@ # See: https://github.com/codespell-project/codespell#using-a-config-file [codespell] # In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: -ignore-words-list = , +ignore-words-list = leapyears,vie check-filenames = check-hidden = skip = ./.git diff --git a/NTPClient.h b/NTPClient.h index ee5388b..b13a51b 100755 --- a/NTPClient.h +++ b/NTPClient.h @@ -109,7 +109,7 @@ class NTPClient { void setUpdateInterval(unsigned long updateInterval); /** - * @return Date Time string formated. The available format codes are: + * @return Date Time string formatted. The available format codes are: %Y: Full year (e.g., 2023) %y: Last two digits of the year (e.g., 23 for 2023) %m: Month as a zero-padded decimal number (01 to 12) From 6ae0d8835dbd0a88fb79d312b418356e31af87a6 Mon Sep 17 00:00:00 2001 From: Leonardo Rivera Date: Fri, 10 May 2024 00:44:05 -0300 Subject: [PATCH 3/6] Adjust DateLanguageData --- .codespellrc | 2 +- NTPClient.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.codespellrc b/.codespellrc index 20cd0f8..22d70c2 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,7 +1,7 @@ # See: https://github.com/codespell-project/codespell#using-a-config-file [codespell] # In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: -ignore-words-list = leapyears,vie +ignore-words-list = leapyears, check-filenames = check-hidden = skip = ./.git diff --git a/NTPClient.cpp b/NTPClient.cpp index 2ece8d2..38013ed 100755 --- a/NTPClient.cpp +++ b/NTPClient.cpp @@ -24,22 +24,22 @@ const DateLanguageData EnglishData = { {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}, {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, - {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}, + {"Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec"}, {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} }; const DateLanguageData SpanishData = { - {"Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb"}, + {"Dom", "Lun", "Mart", "Miérc", "Juev", "Vier", "Sáb"}, {"Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"}, - {"Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"}, - {"Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"} + {"ene", "feb", "mar", "abr", "mayo", "jun", "jul", "ago", "sept", "oct", "nov", "dic"}, + {"enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"} }; const DateLanguageData PortugueseData = { {"Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"}, {"Domingo", "Segunda-feira", "Terça-feira", "Quarta-feira", "Quinta-feira", "Sexta-feira", "Sábado"}, - {"Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"}, - {"Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"} + {"jan", "fev", "mar", "abr", "maio", "jun", "jul", "ago", "set", "out", "nov", "dez"}, + {"janeiro", "fevereiro", "março", "abril", "maio", "junho", "julho", "agosto", "setembro", "outubro", "novembro", "dezembro"} }; // Language map definition From 3b359ebe1848a887e7947339ce2e992f20a40d52 Mon Sep 17 00:00:00 2001 From: Leonardo Rivera Date: Fri, 10 May 2024 00:50:59 -0300 Subject: [PATCH 4/6] Examples --- examples/Advanced/Advanced.ino | 5 +- examples/Basic/Basic.ino | 2 +- .../FormattedDateTime/FormattedDateTime.ino | 62 +++++++++++++++++++ examples/IsTimeSet/IsTimeSet.ino | 2 +- 4 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 examples/FormattedDateTime/FormattedDateTime.ino diff --git a/examples/Advanced/Advanced.ino b/examples/Advanced/Advanced.ino index 18a6a97..bb21b16 100644 --- a/examples/Advanced/Advanced.ino +++ b/examples/Advanced/Advanced.ino @@ -30,8 +30,7 @@ void setup(){ void loop() { timeClient.update(); - - Serial.println(timeClient.getFormattedTime()); - + Serial.println(timeClient.getFormattedDateTime("%d %B %Y")); + Serial.println(timeClient.getFormattedDateTime("%Y-%m-%d %H:%M:%S")); delay(1000); } diff --git a/examples/Basic/Basic.ino b/examples/Basic/Basic.ino index f0a2a7c..71d1857 100644 --- a/examples/Basic/Basic.ino +++ b/examples/Basic/Basic.ino @@ -27,7 +27,7 @@ void setup(){ void loop() { timeClient.update(); - Serial.println(timeClient.getFormattedTime()); + Serial.println(timeClient.getFormattedDateTime("%H:%M:%S")); delay(1000); } diff --git a/examples/FormattedDateTime/FormattedDateTime.ino b/examples/FormattedDateTime/FormattedDateTime.ino new file mode 100644 index 0000000..c513626 --- /dev/null +++ b/examples/FormattedDateTime/FormattedDateTime.ino @@ -0,0 +1,62 @@ +#include +// change next line to use with another board/shield +#include +//#include // for WiFi shield +//#include // for WiFi 101 shield or MKR1000 +#include + +const char *ssid = ""; +const char *password = ""; + +WiFiUDP ntpUDP; + +// You can specify the time server pool and the offset (in seconds, can be +// changed later with setTimeOffset() ). Additionally you can specify the +// update interval (in milliseconds, can be changed using setUpdateInterval() ). +NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000); + +void setup(){ + Serial.begin(115200); + + WiFi.begin(ssid, password); + + while ( WiFi.status() != WL_CONNECTED ) { + delay ( 500 ); + Serial.print ( "." ); + } + + timeClient.begin(); + timeClient.setDateLanguage("pt"); // Available languages: "pt", "es" and "en" (default) +} + +void loop() { + timeClient.update(); + Serial.print("%Y: "); + Serial.println(timeClient.getFormattedDateTime("%Y")); // Full year (e.g., 2023) + Serial.print("%y: "); + Serial.println(timeClient.getFormattedDateTime("%y")); // Last two digits of the year (e.g., 23 for 2023) + Serial.print("%m: "); + Serial.println(timeClient.getFormattedDateTime("%m")); // Month as a zero-padded decimal number (01 to 12) + Serial.print("%d: "); + Serial.println(timeClient.getFormattedDateTime("%d")); // Day of the month as a zero-padded decimal number (01 to 31) + Serial.print("%H: "); + Serial.println(timeClient.getFormattedDateTime("%H")); // Hour (00 to 23) as a zero-padded decimal number + Serial.print("%M: "); + Serial.println(timeClient.getFormattedDateTime("%M")); // Minute as a zero-padded decimal number (00 to 59) + Serial.print("%S: "); + Serial.println(timeClient.getFormattedDateTime("%S")); // Second as a zero-padded decimal number (00 to 59) + Serial.print("%a: "); + Serial.println(timeClient.getFormattedDateTime("%a")); // Abbreviated weekday name according to the current locale + Serial.print("%A: "); + Serial.println(timeClient.getFormattedDateTime("%A")); // Full weekday name according to the current locale + Serial.print("%w: "); + Serial.println(timeClient.getFormattedDateTime("%w")); // Weekday as a decimal number (0 for Sunday through 6 for Saturday) + Serial.print("%b: "); + Serial.println(timeClient.getFormattedDateTime("%b")); // Abbreviated month name according to the current locale + Serial.print("%B: "); + Serial.println(timeClient.getFormattedDateTime("%B")); // Full month name according to the current locale + Serial.print("%p: "); + Serial.println(timeClient.getFormattedDateTime("%p")); // "AM" or "PM" based on the hour (Note: This is locale-sensitive and might not be applicable in all languages) + Serial.println("-------------------"); + delay(1000); +} diff --git a/examples/IsTimeSet/IsTimeSet.ino b/examples/IsTimeSet/IsTimeSet.ino index 619bfde..b8c2492 100644 --- a/examples/IsTimeSet/IsTimeSet.ino +++ b/examples/IsTimeSet/IsTimeSet.ino @@ -42,7 +42,7 @@ void setup(){ void loop() { timeClient.update(); - Serial.println(timeClient.getFormattedTime()); + Serial.println(timeClient.getFormattedDateTime("%H:%M:%S")); if(timeClient.isTimeSet()) { if (hour == timeClient.getHours() && minute == timeClient.getMinutes()) { digitalWrite(led, 0); From 5b5c3178e554f9b75ac8983a92a0e03b128a6247 Mon Sep 17 00:00:00 2001 From: Leonardo Rivera Date: Thu, 23 May 2024 01:36:26 -0300 Subject: [PATCH 5/6] Implement the calculateFullDateComponents and other improvements --- NTPClient.cpp | 142 ++++++++++++++++---------------------------------- NTPClient.h | 40 +++++++++++--- 2 files changed, 79 insertions(+), 103 deletions(-) diff --git a/NTPClient.cpp b/NTPClient.cpp index 38013ed..10ed5e9 100755 --- a/NTPClient.cpp +++ b/NTPClient.cpp @@ -21,49 +21,27 @@ #include "NTPClient.h" -const DateLanguageData EnglishData = { +const NTPClient::DateLanguageData NTPClient::EnglishData = { {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}, {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, {"Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec"}, {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} }; -const DateLanguageData SpanishData = { +const NTPClient::DateLanguageData NTPClient::SpanishData = { {"Dom", "Lun", "Mart", "Miérc", "Juev", "Vier", "Sáb"}, {"Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"}, {"ene", "feb", "mar", "abr", "mayo", "jun", "jul", "ago", "sept", "oct", "nov", "dic"}, {"enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"} }; -const DateLanguageData PortugueseData = { +const NTPClient::DateLanguageData NTPClient::PortugueseData = { {"Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"}, {"Domingo", "Segunda-feira", "Terça-feira", "Quarta-feira", "Quinta-feira", "Sexta-feira", "Sábado"}, {"jan", "fev", "mar", "abr", "maio", "jun", "jul", "ago", "set", "out", "nov", "dez"}, {"janeiro", "fevereiro", "março", "abril", "maio", "junho", "julho", "agosto", "setembro", "outubro", "novembro", "dezembro"} }; -// Language map definition -const struct LanguageMap { - const char* code; - const DateLanguageData* data; -} languageMap[] = { - {"en", &EnglishData}, - {"es", &SpanishData}, - {"pt", &PortugueseData} - // Add new languages here -}; -const int languageMapSize = sizeof(languageMap) / sizeof(LanguageMap); - -// Function to find language data by code -const DateLanguageData* findLanguageData(const String& code) { - for (int i = 0; i < languageMapSize; ++i) { - if (code == languageMap[i].code) { - return languageMap[i].data; - } - } - return &EnglishData; // Default to English if not found -} - NTPClient::NTPClient(UDP& udp) { this->_udp = &udp; } @@ -169,6 +147,16 @@ bool NTPClient::update() { return false; // return false if update does not occur } +// Function to find language data by code +const NTPClient::DateLanguageData* NTPClient::findLanguageData(const String& code) const { + for (int i = 0; i < languageMapSize; ++i) { + if (code == languageMap[i].code) { + return languageMap[i].data; + } + } + return &EnglishData; // Default to English if not found +} + bool NTPClient::isTimeSet() const { return (this->_lastUpdate != 0); // returns true if the time has been set, else false } @@ -179,57 +167,11 @@ unsigned long NTPClient::getEpochTime() const { ((millis() - this->_lastUpdate) / 1000); // Time since last update } -int NTPClient::getDayOfWeek() const { - return (((this->getEpochTime() / 86400L) + 4 ) % 7); //0 is Sunday -} -int NTPClient::getHours() const { - return ((this->getEpochTime() % 86400L) / 3600); -} -int NTPClient::getMinutes() const { - return ((this->getEpochTime() % 3600) / 60); -} -int NTPClient::getSeconds() const { - return (this->getEpochTime() % 60); -} - -int NTPClient::getDay() const { - long days = this->getEpochTime() / 86400L; - int fullYears = days / 365; - int overDays = days % 365; - - // Adjust for leap years - int leapYears = (fullYears + 1) / 4; // +1 because year 0 (1970) is not a leap year - if (leapYears > overDays) { - fullYears--; - } - - int currentYear = 1970 + fullYears; - - // Check if current year is a leap year - bool thisYearIsLeap = (currentYear % 4 == 0 && (currentYear % 100 != 0 || currentYear % 400 == 0)); - - // Calculate day of the year - int dayOfYear = (days - ((fullYears * 365) + leapYears)) + 1; // +1 to convert from 0-based to 1-based - - int daysInMonth[12] = {31, 28 + thisYearIsLeap, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - - int monthDay = dayOfYear; - for (int month = 0; month < 12; month++) { - if (monthDay <= daysInMonth[month]) { - return monthDay; // Correct day of the month - } - monthDay -= daysInMonth[month]; - } - - return -1; // Error case, should not happen -} - -int NTPClient::getMonth() const { - long days = this->getEpochTime() / 86400L; // Total days since epoch - int fullYears = 0; +NTPClient::FullDateComponents NTPClient::calculateFullDateComponents() const { + unsigned long epochTime = this->getEpochTime(); + long days = epochTime / 86400L; // Total days since epoch int year = 1970; - // Correctly account for leap years in the loop while (days > 365) { if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { if (days > 366) { @@ -244,42 +186,50 @@ int NTPClient::getMonth() const { } } + int dayOfYear = static_cast(days) + 1; // +1 to convert from 0-based to 1-based bool thisYearIsLeap = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); int daysInMonth[12] = {31, 28 + thisYearIsLeap, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int month = 0; for (month = 0; month < 12; month++) { - if (days < daysInMonth[month]) { + if (dayOfYear <= daysInMonth[month]) { break; // Found the current month } - days -= daysInMonth[month]; + dayOfYear -= daysInMonth[month]; } - return month + 1; // Month is 1-based + return {year, month + 1, dayOfYear}; // Month is 1-based } int NTPClient::getYear() const { - long days = this->getEpochTime() / 86400L; // Total days since epoch - int year = 1970; + FullDateComponents dateComponents = calculateFullDateComponents(); + return dateComponents.year; +} - while (days > 365) { - if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { - // Leap year - if (days > 366) { - days -= 366; - year += 1; - } else { - // If days <= 366 in a leap year, then we've found the current year - break; - } - } else { - // Not a leap year - days -= 365; - year += 1; - } - } +int NTPClient::getMonth() const { + FullDateComponents dateComponents = calculateFullDateComponents(); + return dateComponents.month; +} + +int NTPClient::getDay() const { + FullDateComponents dateComponents = calculateFullDateComponents(); + return dateComponents.day; +} - return year; +int NTPClient::getDayOfWeek() const { + return (((this->getEpochTime() / 86400L) + 4 ) % 7); // 0 is Sunday +} + +int NTPClient::getHours() const { + return ((this->getEpochTime() % 86400L) / 3600); +} + +int NTPClient::getMinutes() const { + return ((this->getEpochTime() % 3600) / 60); +} + +int NTPClient::getSeconds() const { + return (this->getEpochTime() % 60); } String NTPClient::getFormattedDateTime(const String& format) { diff --git a/NTPClient.h b/NTPClient.h index b13a51b..14cf488 100755 --- a/NTPClient.h +++ b/NTPClient.h @@ -8,13 +8,6 @@ #define NTP_PACKET_SIZE 48 #define NTP_DEFAULT_LOCAL_PORT 1337 -struct DateLanguageData { - const char* shortWeekDays[7]; - const char* longWeekDays[7]; - const char* shortMonths[12]; - const char* longMonths[12]; -}; - class NTPClient { private: UDP* _udp; @@ -35,6 +28,39 @@ class NTPClient { void sendNTPPacket(); + struct DateLanguageData { + const char* shortWeekDays[7]; + const char* longWeekDays[7]; + const char* shortMonths[12]; + const char* longMonths[12]; + }; + + struct FullDateComponents { + int year; + int month; + int day; + }; + + // Language map + struct LanguageMap { + const char* code; + const DateLanguageData* data; + }; + + static const DateLanguageData EnglishData; + static const DateLanguageData SpanishData; + static const DateLanguageData PortugueseData; + + const LanguageMap languageMap[3] = { + {"en", &EnglishData}, + {"es", &SpanishData}, + {"pt", &PortugueseData} + }; + + const int languageMapSize = sizeof(languageMap) / sizeof(languageMap[0]); + const DateLanguageData* findLanguageData(const String& code) const; + FullDateComponents calculateFullDateComponents() const; + public: NTPClient(UDP& udp); NTPClient(UDP& udp, long timeOffset); From 8ab65a0cd038cb191b30a38db6035195372b777e Mon Sep 17 00:00:00 2001 From: Leonardo Rivera Date: Wed, 1 Jan 2025 23:52:50 -0300 Subject: [PATCH 6/6] Adjust in leap year logic --- NTPClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NTPClient.cpp b/NTPClient.cpp index 10ed5e9..43eaf3f 100755 --- a/NTPClient.cpp +++ b/NTPClient.cpp @@ -174,7 +174,7 @@ NTPClient::FullDateComponents NTPClient::calculateFullDateComponents() const { while (days > 365) { if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { - if (days > 366) { + if (days >= 366) { days -= 366; year++; } else {