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

A suggestion... for getDate, getMonth, getYear, isLeapYear #113

Open
marmat123 opened this issue Jul 31, 2020 · 11 comments
Open

A suggestion... for getDate, getMonth, getYear, isLeapYear #113

marmat123 opened this issue Jul 31, 2020 · 11 comments
Labels
topic: code Related to content of the project itself type: enhancement Proposed improvement

Comments

@marmat123
Copy link

marmat123 commented Jul 31, 2020

I don't want to fork, so here:


Since the NTP servers should not provide data before 2020:

Add in NTPClient.h:

#define DAYSOF4YEARS 1461UL		// 4*year plus leap day
#define DAYSOFAYEAR  365UL
#define SEC20200101  1577750400UL	// 2020/01/01 - 1 day (86400s) because the result of the integer division is rounded off

class NTPClient {
  private:
       ...
       uint16_t    _date	= 0;
       uint16_t    _month	= 0;
       uint16_t    _year	= 0;
       bool        _isLeapYear	= false;
       ...
  public:
       ...
       bool isLeapYear() const;
       int getYear() const;
       int getMonth() const;
       int getDate() const; 
      ...

Add in NTPClient.cpp:

bool NTPClient::forceUpdate() {

[...]

// at the end add: 

	uint32_t    _days2k = (_currentEpoc - SEC20200101)/86400;	// distance to today in days
	
	if (_days2k > DAYSOF4YEARS) {					// how many four-year packages + remaining days, from 2050 faster than subtraction on an esp8266 ;)
		_year	= _days2k / DAYSOF4YEARS;
		_days2k	= _days2k - _year * DAYSOF4YEARS;
		_year	*= 4;
	}
	
	if (_days2k > DAYSOFAYEAR + 1) {				//2020 was a leap year, how many years + remaining days
		_days2k--;
		while (_days2k > DAYSOFAYEAR) {				//three times at most
			_days2k -= DAYSOFAYEAR;
			_year++;
		}
	} else _isLeapYear = true;
	
	_year += 20;							// + 2020
	
	if (_isLeapYear && _days2k > 60) _days2k--;			// subtract the possible leap day when February passed
	
	if	(_days2k > 334)	{ _month = 12;	_date = _days2k - 334;	}    //boring but quick
	else if	(_days2k > 304)	{ _month = 11;	_date = _days2k - 304;	}
	else if	(_days2k > 273)	{ _month = 10;	_date = _days2k - 273;	}
	else if	(_days2k > 243)	{ _month = 9;	_date = _days2k - 243;	}
	else if	(_days2k > 212)	{ _month = 8;	_date = _days2k - 212;	}
	else if	(_days2k > 181)	{ _month = 7;	_date = _days2k - 181;	}
	else if	(_days2k > 151)	{ _month = 6;	_date = _days2k - 151;	}
	else if	(_days2k > 120)	{ _month = 5;	_date = _days2k - 120;	}
	else if	(_days2k > 90)	{ _month = 4;	_date = _days2k - 90;	}
	else if	(_days2k > 59)	{ _month = 3;	_date = _days2k - 59;	}
	else if	(_days2k > 31)	{ _month = 2;	_date = _days2k - 31;	}
	else			{ _month = 1;	_date = _days2k;	}

	return true;
}


// and then you can add:

bool NTPClient::isLeapYear() const {
	return this->_isLeapYear;
}
int NTPClient::getYear() const {
	return this->_year;
}
int NTPClient::getMonth() const { 
	return this->_month; 
}
int NTPClient::getDate() const {
	return this->_date;
} 

it works to 2099/12/31 ;-)

You can now use the methods

  • isLeapYear()
  • getYear()
  • getMonth()
  • getDay()

which makes it easier to set an RTC like the DS3231 without time.h - because for some stupid reason it (DS3231) does not understand a timestamp, but has actually stored the "normal" western date format in its registers.

Remark
There is still potential for optimization, because some divisions are redundant now, if you could evaluate temporary variables for getDay, getHours, getMinutes and getSeconds.

@bilame
Copy link

bilame commented Nov 12, 2020

if (_days2k > DAYSOFAYEAR + 1) { //2020 was a leap year, how many years + remaining days
_days2k--;
while (_days2k > DAYSOFAYEAR) { //three times at most
_days2k -= DAYSOFAYEAR;
_year++;
}
} else _isLeapYear = true;

_year += 20; // + 2020

if (_isLeapYear && _days2k > 60) _days2k--; // subtract the possible leap day when February passed

Thanks marmat123 for this work.
I added your lines.
But it didn't work as planned.
Every update, Year advance 20.
So i erased the faulty line, but i guess i added your lines not at the right place.
the +=20, is it correct ?
what am i missing ?
Thanks

@marmat123
Copy link
Author

Runs here with me. But I won't get a chance to watch it until next week.

The +20 is ok, because the constant #define SEC20200101 1577750400UL starts with the year 2020 (no NTP server will give you data before 2020) and you have 20 years difference otherwise.

You actually inserted the code in both files?

@bilame
Copy link

bilame commented Nov 14, 2020

Yes, i inserted both files. the added part in .h seems ok.
here is the part of the .cpp with the inserted lines and in comment the "faulty line".
i added it with the functions (unchanged) : update and getepochtime
to show where exactly i added your lines.
thanks for your help.

include "NTPClient.h"

NTPClient::NTPClient(UDP& udp) {
  this->_udp            = &udp;
}

NTPClient::NTPClient(UDP& udp, long timeOffset) {
  this->_udp            = &udp;
  this->_timeOffset     = timeOffset;
}

NTPClient::NTPClient(UDP& udp, const char* poolServerName) {
  this->_udp            = &udp;
  this->_poolServerName = poolServerName;
}

NTPClient::NTPClient(UDP& udp, const char* poolServerName, long timeOffset) {
  this->_udp            = &udp;
  this->_timeOffset     = timeOffset;
  this->_poolServerName = poolServerName;
}

NTPClient::NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval) {
  this->_udp            = &udp;
  this->_timeOffset     = timeOffset;
  this->_poolServerName = poolServerName;
  this->_updateInterval = updateInterval;
}

void NTPClient::begin() {
  this->begin(NTP_DEFAULT_LOCAL_PORT);
}

void NTPClient::begin(int port) {
  this->_port = port;

  this->_udp->begin(this->_port);

  this->_udpSetup = true;
}

bool NTPClient::forceUpdate() {
  #ifdef DEBUG_NTPClient
    Serial.println("Update from NTP Server");
  #endif

  this->sendNTPPacket();

  // Wait till data is there or timeout...
  byte timeout = 0;
  int cb = 0;
  do {
    delay ( 10 );
    cb = this->_udp->parsePacket();
    if (timeout > 100) return false; // timeout after 1000 ms
    timeout++;
  } while (cb == 0);

  this->_lastUpdate = millis() - (10 * (timeout + 1)); // Account for delay in reading the time

  this->_udp->read(this->_packetBuffer, NTP_PACKET_SIZE);

  unsigned long highWord = word(this->_packetBuffer[40], this->_packetBuffer[41]);
  unsigned long lowWord = word(this->_packetBuffer[42], this->_packetBuffer[43]);
  // combine the four bytes (two words) into a long integer
  // this is NTP time (seconds since Jan 1 1900):
  unsigned long secsSince1900 = highWord << 16 | lowWord;

  this->_currentEpoc = secsSince1900 - SEVENZYYEARS;

  // at the end add: 

  uint32_t    _days2k = (_currentEpoc - SEC20200101) / 86400;	// distance to today in days

  if (_days2k > DAYSOF4YEARS) {					// how many four-year packages + remaining days, from 2050 faster than subtraction on an esp8266 ;)
	  _year = _days2k / DAYSOF4YEARS;
	  _days2k = _days2k - _year * DAYSOF4YEARS;
	  _year *= 4;
  }

  if (_days2k > DAYSOFAYEAR + 1) {				//2020 was a leap year, how many years + remaining days
	  _days2k--;
	  while (_days2k > DAYSOFAYEAR) {				//three times at most
		  _days2k -= DAYSOFAYEAR;
		  _year++;
	  }
  }
  else _isLeapYear = true;

  //_year += 20;							// + 2020

  if (_isLeapYear && _days2k > 60) _days2k--;			// subtract the possible leap day when February passed

  if (_days2k > 334) { _month = 12;	_date = _days2k - 334; }    //boring but quick
  else if (_days2k > 304) { _month = 11;	_date = _days2k - 304; }
  else if (_days2k > 273) { _month = 10;	_date = _days2k - 273; }
  else if (_days2k > 243) { _month = 9;	_date = _days2k - 243; }
  else if (_days2k > 212) { _month = 8;	_date = _days2k - 212; }
  else if (_days2k > 181) { _month = 7;	_date = _days2k - 181; }
  else if (_days2k > 151) { _month = 6;	_date = _days2k - 151; }
  else if (_days2k > 120) { _month = 5;	_date = _days2k - 120; }
  else if (_days2k > 90) { _month = 4;	_date = _days2k - 90; }
  else if (_days2k > 59) { _month = 3;	_date = _days2k - 59; }
  else if (_days2k > 31) { _month = 2;	_date = _days2k - 31; }
  else { _month = 1;	_date = _days2k; }

  return true;
}

// and then you can add:

bool NTPClient::isLeapYear() const {
	return this->_isLeapYear;
}
int NTPClient::getYear() const {
	return this->_year;
}
int NTPClient::getMonth() const {
	return this->_month;
}
int NTPClient::getDate() const {
	return this->_date;
}

bool NTPClient::update() {
  if ((millis() - this->_lastUpdate >= this->_updateInterval)     // Update after _updateInterval
    || this->_lastUpdate == 0) {                                // Update if there was no update yet.
    if (!this->_udpSetup) this->begin();                         // setup the UDP client if needed
    return this->forceUpdate();
  }
  return true;
}

unsigned long NTPClient::getEpochTime() const {
  return this->_timeOffset + // User offset
         this->_currentEpoc + // Epoc returned by the NTP server
         ((millis() - this->_lastUpdate) / 1000); // Time since last update
}

@marmat123
Copy link
Author

marmat123 commented Nov 14, 2020

I'll post the library here as it runs on my ESP8266. The data that is generated is correct here.

I add 20 years to it, because I consider the year 2020 as the year zero and use the difference to compensate for the error. Otherwise, today would be the year 0 and not 20. There is no need, because 2000 was also a leap year (2000 mod 400 = 0) but it saves some unnecessary computing time, because no year before 2020 is possible as result. It is "only" about the conversion of current and later timestamps.

If you have changed the library in any other way, you have to check if maybe this is the reason. This is not my construction site. If you have a difference of 20 years that can be corrected by omitting lines, that's ok for you too.

NTPClient.h

#pragma once

#include "Arduino.h"

#include <Udp.h>

#define SEVENZYYEARS 2208988800UL
#define DAYSOF4YEARS 1461UL
#define DAYSOFAYEAR  365UL
#define SEC20200101  1577750400UL
#define NTP_PACKET_SIZE 48
#define NTP_DEFAULT_LOCAL_PORT 1337

class NTPClient {
  private:
    UDP*          _udp;
    bool          _udpSetup       = false;

    const char*   _poolServerName = "pool.ntp.org"; // Default time server
    int           _port           = NTP_DEFAULT_LOCAL_PORT;
    long          _timeOffset     = 0;

    unsigned long _updateInterval = 60000;  // In ms

    unsigned long _currentEpoc    = 0;      // In s
    unsigned long _lastUpdate     = 0;      // In ms
	uint16_t      _daysSincs2020  = 0;      // In d

    byte          _packetBuffer[NTP_PACKET_SIZE];

    uint16_t	  _date			   = 0;
	uint16_t	  _month		   = 0;
	uint16_t	  _year			   = 0;
	bool		  _isLeapYear	   = false;
	
	void          sendNTPPacket();
	
	

  public:
    NTPClient(UDP& udp);
    NTPClient(UDP& udp, long timeOffset);
    NTPClient(UDP& udp, const char* poolServerName);
    NTPClient(UDP& udp, const char* poolServerName, long timeOffset);
    NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval);

    /**
     * Set time server name
     *
     * @param poolServerName
     */
    void setPoolServerName(const char* poolServerName);

    /**
     * Starts the underlying UDP client with the default local port
     */
    void begin();

    /**
     * Starts the underlying UDP client with the specified local port
     */
    void begin(int port);

    /**
     * This should be called in the main loop of your application. By default an update from the NTP Server is only
     * made every 60 seconds. This can be configured in the NTPClient constructor.
     *
     * @return true on success, false on failure
     */
    bool update();

    /**
     * This will force the update from the NTP Server.
     *
     * @return true on success, false on failure
     */
    bool forceUpdate();

	/**
	 * the following methods are reasonably available after the call of forceUpdate and are explained by the name
	 */
    bool isLeapYear() const;
    int  getYear() const;
    int  getMonth() const;
    int  getDate() const;
    int  getDay() const;		// Day of the Week, Sunday = 0
    int  getHours() const;
    int  getMinutes() const;
    int  getSeconds() const;

    /**
     * Changes the time offset. Useful for changing timezones dynamically
     */
    void setTimeOffset(int timeOffset);

    /**
     * Set the update interval to another frequency. E.g. useful when the
     * timeOffset should not be set in the constructor
     */
    void setUpdateInterval(unsigned long updateInterval);

    /**
     * @return time formatted like `hh:mm:ss`
     */
    String getFormattedTime() const;

    /**
     * @return time in seconds since Jan. 1, 1970
     */
    unsigned long getEpochTime() const;

    /**
     * Stops the underlying UDP client
     */
    void end();
};

NTPClient.cpp

/**
 * The MIT License (MIT)
 * Copyright (c) 2015 by Fabrice Weinberg
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "NTPClient.h"

NTPClient::NTPClient(UDP& udp) {
	this->_udp            = &udp;
}

NTPClient::NTPClient(UDP& udp, long timeOffset) {
	this->_udp            = &udp;
	this->_timeOffset     = timeOffset;
}

NTPClient::NTPClient(UDP& udp, const char* poolServerName) {
	this->_udp            = &udp;
	this->_poolServerName = poolServerName;
}

NTPClient::NTPClient(UDP& udp, const char* poolServerName, long timeOffset) {
	this->_udp            = &udp;
	this->_timeOffset     = timeOffset;
	this->_poolServerName = poolServerName;
}

NTPClient::NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval) {
	this->_udp            = &udp;
	this->_timeOffset     = timeOffset;
	this->_poolServerName = poolServerName;
	this->_updateInterval = updateInterval;
}

void NTPClient::begin() {
	this->begin(NTP_DEFAULT_LOCAL_PORT);
}

void NTPClient::begin(int port) {
	this->_port = port;
	this->_udp->begin(this->_port);
	this->_udpSetup = true;
}

bool NTPClient::forceUpdate() {
	#ifdef DEBUG_NTPClient
		Serial.println("Update from NTP Server");
	#endif

	this->sendNTPPacket();

	// Wait till data is there or timeout...
	byte timeout = 0;
	int cb = 0;
	do {
		delay ( 10 );
		cb = this->_udp->parsePacket();
		if (timeout > 100) return false; 														// timeout after 1000 ms
		timeout++;
	} while (cb == 0);

	this->_lastUpdate = millis() - (10 * (timeout + 1)); 										// Account for delay in reading the time

	this->_udp->read(this->_packetBuffer, NTP_PACKET_SIZE);

	unsigned long highWord = word(this->_packetBuffer[40], this->_packetBuffer[41]);
	unsigned long lowWord = word(this->_packetBuffer[42], this->_packetBuffer[43]);
	unsigned long secsSince1900 = highWord << 16 | lowWord;										// combine the four bytes (two words) into a long integer

	this->_currentEpoc = secsSince1900 - SEVENZYYEARS;
	
	/* new Code for Date */
	uint32_t	_days2k = (_currentEpoc - SEC20200101)/86400;									// distance to today in days
	
	if (_days2k > DAYSOF4YEARS) {																// how many four-year packages + remaining days
		_year	= _days2k / DAYSOF4YEARS;
		_days2k	= _days2k - _year * DAYSOF4YEARS;
		_year	*= 4;
	}
	
	if (_days2k > DAYSOFAYEAR + 1) {															//2020 was a leap year, how many years + remaining days
		_days2k--;
		while (_days2k > DAYSOFAYEAR) {															//three times at most
			_days2k -= DAYSOFAYEAR;
			_year++;
		}
	} else _isLeapYear = true;
	
	_year += 20;																				// + 2020
	
	if (_isLeapYear && _days2k > 60) _days2k--;													// subtract the possible leap day when February passed
	
	if		(_days2k > 334)	{ _month = 12;	_date = _days2k - 334;	}   						//boring but quick
	else if	(_days2k > 304)	{ _month = 11;	_date = _days2k - 304;	}
	else if	(_days2k > 273)	{ _month = 10;	_date = _days2k - 273;	}
	else if	(_days2k > 243)	{ _month = 9;	_date = _days2k - 243;	}
	else if	(_days2k > 212)	{ _month = 8;	_date = _days2k - 212;	}
	else if	(_days2k > 181)	{ _month = 7;	_date = _days2k - 181;	}
	else if	(_days2k > 151)	{ _month = 6;	_date = _days2k - 151;	}
	else if	(_days2k > 120)	{ _month = 5;	_date = _days2k - 120;	}
	else if	(_days2k > 90)	{ _month = 4;	_date = _days2k - 90;	}
	else if	(_days2k > 59)	{ _month = 3;	_date = _days2k - 59;	}
	else if	(_days2k > 31)	{ _month = 2;	_date = _days2k - 31;	}
	else					{ _month = 1;	_date = _days2k;	}	
	
	return true;
}

bool NTPClient::update() {
	if ((millis() - this->_lastUpdate >= this->_updateInterval)     // Update after _updateInterval
		|| this->_lastUpdate == 0) {                                // Update if there was no update yet.
		if (!this->_udpSetup) this->begin();                         // setup the UDP client if needed
		return this->forceUpdate();
	}
	return true;
}

unsigned long NTPClient::getEpochTime() const {
	return this->_timeOffset + // User offset
		     this->_currentEpoc + // Epoc returned by the NTP server
		     ((millis() - this->_lastUpdate) / 1000); // Time since last update
}

bool NTPClient::isLeapYear() const 	{ return this->_isLeapYear; }
int  NTPClient::getYear() const 	{ return this->_year; }
int  NTPClient::getMonth() const 	{ return this->_month; }
int  NTPClient::getDate() const 	{ return this->_date; }
int  NTPClient::getDay() 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;
}

void NTPClient::end() {
	this->_udp->stop();
	this->_udpSetup = false;
}

void NTPClient::setTimeOffset(int timeOffset) {
	this->_timeOffset     = timeOffset;
}

void NTPClient::setUpdateInterval(unsigned long updateInterval) {
	this->_updateInterval = updateInterval;
}

void NTPClient::setPoolServerName(const char* poolServerName) {
		this->_poolServerName = poolServerName;
}

void NTPClient::sendNTPPacket() {
	// set all bytes in the buffer to 0
	memset(this->_packetBuffer, 0, NTP_PACKET_SIZE);
	// Initialize values needed to form NTP request
	// (see URL above for details on the packets)
	this->_packetBuffer[0] = 0b11100011;   // LI, Version, Mode
	this->_packetBuffer[1] = 0;     // Stratum, or type of clock
	this->_packetBuffer[2] = 6;     // Polling Interval
	this->_packetBuffer[3] = 0xEC;  // Peer Clock Precision
	// 8 bytes of zero for Root Delay & Root Dispersion
	this->_packetBuffer[12]  = 49;
	this->_packetBuffer[13]  = 0x4E;
	this->_packetBuffer[14]  = 49;
	this->_packetBuffer[15]  = 52;

	// all NTP fields have been given values, now
	// you can send a packet requesting a timestamp:
	this->_udp->beginPacket(this->_poolServerName, 123); //NTP requests are to port 123
	this->_udp->write(this->_packetBuffer, NTP_PACKET_SIZE);
	this->_udp->endPacket();
}

and finally the added keywords.txt

#######################################
# Datatypes (KEYWORD1)
#######################################

NTPClient		KEYWORD1

#######################################
# Methods and Functions (KEYWORD2)
#######################################

begin			KEYWORD2
end			KEYWORD2
update			KEYWORD2
forceUpdate		KEYWORD2
getDay			KEYWORD2
getHours		KEYWORD2
getMinutes		KEYWORD2
getSeconds		KEYWORD2
getFormattedTime	KEYWORD2
getEpochTime		KEYWORD2
setTimeOffset		KEYWORD2
setUpdateInterval	KEYWORD2
setPoolServerName	KEYWORD2

One request
Please try to use the Markdown syntax for source code. It is quite simple. Put the source code in three backticks with the keyword cpp:

```cpp
[source code]
```

@bilame
Copy link

bilame commented Nov 15, 2020

Ok for the request. i didn't know about it. it will be more readable. thanks.

well, i compared your code and mine. they are similar.

I think i got it.

your code start to count days from 2020 01 01.
So the test : _days2k > DAYSOF4YEARS is false
so the following
_year = _days2k / DAYSOF4YEARS;
doesn't "reset" the value of 20 added from the previous update.

am i right ?

i added _year=0; at the beginning of your code and i uncommented _year +=20; as you wrote.
everything works fine now.

@marmat123
Copy link
Author

marmat123 commented Nov 15, 2020

One more time:

I set the year zero to 2020 (actually 31.12.2019) and divide by the days (integer division without remainder):

_days2k = (_currentEpoc - SEC20200101)/86400;

Then I look if four years have already passed (saves the division with result 0 and is faster but in four years the code will be obsolete):

if (_days2k > DAYSOF4YEARS) { }

Then multiples of the four-year interval (1 day more than four one-year intervals) are subtracted and then multiples of the one-year interval and counting how many years this corresponds to. However, only from 2020.

Example: 2057 --> difference are 37 years, that is 9*4 + 1 year gives 37 and so you have to add 20 years again.

The only advantage is that there is no division for the next three years. But since the units (ESPs) will not run until 2030, that is enough for me. And a saved division is a good division.

You could just as well have written the routine like this:

while (_days2k > DAYSOF4YEARS) { 
			_days2k -= DAYSOF4YEARS;
			_year++;
		}

without the if-condition, that would have been more consistent. And then you see that you save five loops when you start in 2020.

The declaration and initialization of _year is done in the header file:

uint16_t _year = 0;

@bilame
Copy link

bilame commented Nov 15, 2020

i agree with you at 100%

uint16_t  _year = 0 ;

is it called only when you create a new time variable like :

NTPClient time(.......);  

?

let's assume _year=20 (from the previous update since it's 2020)
what would be _year at the end of a new call of update function if we use the date today ?

@marmat123
Copy link
Author

marmat123 commented Nov 15, 2020

Ah yes, well, then separate declaration (in the header file) and initialization (in the routine). That makes sense then. This is the more secure variant.

I myself do not use the object globally, but only within a routine (and then cpp discards it again) and the need to query the year several times in the net does not arise for my problem.

@bilame
Copy link

bilame commented Nov 15, 2020

Ok, i understand too. Thanks for your help anyway.

@LRagji
Copy link

LRagji commented Apr 18, 2021

Can someone raise Pull Request for this? Or was it rejected This is needed feature...

@per1234 per1234 added topic: code Related to content of the project itself type: enhancement Proposed improvement labels Jan 15, 2022
@DE-cr
Copy link

DE-cr commented Jun 23, 2022

First of all: thanks to the NTPClient author(s) for your helpful library!

Of course ;) I also had missed date info (in addition to the time info provided), so I rolled my own. (I had not looked into this repository's issues and pull requests before). The only thing I needed was an ISO date string ("yyyy-mm-dd"), so this is the funtion(s) I came up with for my https://github.com/DE-cr/BSBmonCR v0.4.0 (that version has yet to be uploaded to github - after more tests of its functionality):

int leapyear( int year ) {
  return !(year % 4) && (year % 100 || !(year % 400)) ? 1 : 0;
}

char* epoch2date( unsigned long epoch, char* date ) {
  // date must be at least char[11]!
  epoch /= 24 * 60 * 60;
  int year = 1970;
  while ( epoch >= 365 + leapyear( year ) )
    epoch -= 365 + leapyear( year ++ );
  int days_in_month[] =
    //Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov
    { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 };
  days_in_month[ 1 ] += leapyear( year ); // fix Feb?
  int* m_days = days_in_month;
  int month;
  for ( month = 0; epoch >= *m_days; ++ month )
    epoch -= *( m_days ++);
  sprintf( date, "%04d-%02d-%02d", year, month+1, (int)epoch+1 );
  return date;
}

Feel free to use/adapt this piece of code should you be interested.

FYI: I couldn't get it to fail with the following tests (no price here for beauty/readability, sorry):

// epoch2date() tests:
#define X(str,sec) {char s[99];sprintf(s,"%s?: %s (%d)",str,epoch2date(sec,date_now),sec);Serial.println(s);}while(0)
#define Y(sec) (24*60*60*(sec))
#define Z(str,day) X(str,Y(day))
Z("1970-01-01",0);Z("1970-01-31",30);Z("1970-02-01",31);Z("1970-02-28",31+27);Z("1970-03-01",31+28);
Z("1970-12-31",364);Z("1971-01-01",365);
Z("1972-01-01",365+365);
Z("1973-01-01",365+365+366);
Z("1974-01-01",365+365+366+365);
for(int i=0;i<3;++i)Z("1972-Feb/Mar",365+365+31+27+i);
X("1970-01-01",Y(1)-1);X("1970-01-01",1);X("1970-01-02",Y(1)+1);X("1970-12-31",Y(365)-1);
#define Y2K ((2000-1970)*365+(2000-1970)/4)
Z("1999-12-31",Y2K-1);Z("2000-01-01",Y2K);for(int i=0;i<3;++i)Z("2000-Feb/Mar",Y2K+31+27+i);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: code Related to content of the project itself type: enhancement Proposed improvement
Projects
None yet
Development

No branches or pull requests

5 participants