Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added getFormattedDateTime method #207

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codespellrc
Original file line number Diff line number Diff line change
@@ -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,
check-filenames =
check-hidden =
skip = ./.git
146 changes: 131 additions & 15 deletions NTPClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,27 @@

#include "NTPClient.h"

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 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 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"}
};

NTPClient::NTPClient(UDP& udp) {
this->_udp = &udp;
}
Expand Down Expand Up @@ -126,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
}
Expand All @@ -136,31 +167,112 @@ unsigned long NTPClient::getEpochTime() const {
((millis() - this->_lastUpdate) / 1000); // Time since last update
}

NTPClient::FullDateComponents NTPClient::calculateFullDateComponents() const {
unsigned long epochTime = this->getEpochTime();
long days = epochTime / 86400L; // Total days since epoch
int year = 1970;

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++;
}
}

int dayOfYear = static_cast<int>(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 (dayOfYear <= daysInMonth[month]) {
break; // Found the current month
}
dayOfYear -= daysInMonth[month];
}

return {year, month + 1, dayOfYear}; // Month is 1-based
}

int NTPClient::getYear() const {
FullDateComponents dateComponents = calculateFullDateComponents();
return dateComponents.year;
}

int NTPClient::getMonth() const {
FullDateComponents dateComponents = calculateFullDateComponents();
return dateComponents.month;
}

int NTPClient::getDay() const {
return (((this->getEpochTime() / 86400L) + 4 ) % 7); //0 is Sunday
FullDateComponents dateComponents = calculateFullDateComponents();
return dateComponents.day;
}

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::getFormattedTime() const {
unsigned long rawTime = this->getEpochTime();
unsigned long hours = (rawTime % 86400L) / 3600;
String hoursStr = hours < 10 ? "0" + String(hours) : String(hours);

unsigned long minutes = (rawTime % 3600) / 60;
String minuteStr = minutes < 10 ? "0" + String(minutes) : String(minutes);

unsigned long seconds = rawTime % 60;
String secondStr = seconds < 10 ? "0" + String(seconds) : String(seconds);

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() {
Expand All @@ -170,15 +282,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() {
Expand Down
60 changes: 58 additions & 2 deletions NTPClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
class NTPClient {
private:
UDP* _udp;
String _dateLanguage = "en"; // Default language
bool _udpSetup = false;

const char* _poolServerName = "pool.ntp.org"; // Default time server
Expand All @@ -27,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);
Expand Down Expand Up @@ -81,7 +115,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;
Expand All @@ -98,9 +135,28 @@ class NTPClient {
void setUpdateInterval(unsigned long updateInterval);

/**
* @return time formatted like `hh:mm:ss`
* @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)
%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
Expand Down
5 changes: 2 additions & 3 deletions examples/Advanced/Advanced.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
2 changes: 1 addition & 1 deletion examples/Basic/Basic.ino
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void setup(){
void loop() {
timeClient.update();

Serial.println(timeClient.getFormattedTime());
Serial.println(timeClient.getFormattedDateTime("%H:%M:%S"));

delay(1000);
}
62 changes: 62 additions & 0 deletions examples/FormattedDateTime/FormattedDateTime.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#include <NTPClient.h>
// change next line to use with another board/shield
#include <ESP8266WiFi.h>
//#include <WiFi.h> // for WiFi shield
//#include <WiFi101.h> // for WiFi 101 shield or MKR1000
#include <WiFiUdp.h>

const char *ssid = "<SSID>";
const char *password = "<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);
}
2 changes: 1 addition & 1 deletion examples/IsTimeSet/IsTimeSet.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading
Loading