#ifndef _EZTIME_H_ #ifdef __cplusplus #define _EZTIME_H_ // Whether or not to be compatible with Paul Stoffregen's Arduino Time Library. // (which makes a lot of functions available in your root namespace, additionally to through the // objects created by ezTime.) #define ARDUINO_TIMELIB_COMPATIBILITY // Compiles in NTP updating and timezoneapi.io fetching #define EZTIME_NETWORK_ENABLE // Arduino Ethernet shields // #define EZTIME_ETHERNET // Uncomment one of the below to only put only messages up to a certain level in the compiled code // (You still need to turn them on with ezTime.debugLevel(someLevel) to see them) // #define EZTIME_MAX_DEBUGLEVEL_NONE // #define EZTIME_MAX_DEBUGLEVEL_ERROR // #define EZTIME_MAX_DEBUGLEVEL_INFO // Cache mechanism, either EEPROM or NVS, not both. (See README) #define EZTIME_CACHE_EEPROM // #define EZTIME_CACHE_NVS #if !defined(__time_t_defined) // avoid conflict with newlib or other posix libc typedef unsigned long time_t; #endif #include #ifndef __AVR__ #include // for __time_t_defined, but avr libc lacks sys/types.h #endif #if !defined(__time_t_defined) // avoid conflict with newlib or other posix libc typedef unsigned long time_t; #endif extern "C++" { ////////// Error handing typedef enum { NO_ERROR, LAST_ERROR, // Pseudo-error: replaced by last error NO_NETWORK, TIMEOUT, CONNECT_FAILED, DATA_NOT_FOUND, LOCKED_TO_UTC, NO_CACHE_SET, CACHE_TOO_SMALL } ezError_t; typedef enum { NONE, ERROR, INFO, DEBUG } ezDebugLevel_t; typedef enum { LOCAL_TIME, UTC_TIME } ezLocalOrUTC_t; // Defines that can make your code more readable. For example, if you are looking for the first // Thursday in a year, you could write: ezTime.makeOrdinalTime(0, 0, 0, JANUARY, FIRST, THURSDAY, year) // (As is done within ezTime to calculate ISO weeks) #define SUNDAY 1 #define MONDAY 2 #define TUESDAY 3 #define WEDNESDAY 4 #define THURSDAY 5 #define FRIDAY 6 #define SATURDAY 7 #define JANUARY 1 #define FEBRUARI 2 #define MARCH 3 #define APRIL 4 #define MAY 5 #define JUNE 6 #define JULY 7 #define AUGUST 8 #define SEPTEMBER 9 #define OCTOBER 10 #define NOVEMBER 11 #define DECEMBER 12 #define FIRST 1 #define SECOND 2 #define THIRD 3 #define FOURTH 4 #define LAST 5 #if defined(EZTIME_MAX_DEBUGLEVEL_NONE) #define err(args...) "" #define errln(args...) "" #define info(args...) "" #define infoln(args...) "" #define debug(args...) "" #define debugln(args...) "" #elif defined(EZTIME_MAX_DEBUGLEVEL_ERROR) #define err(args...) if (ezTime._debug_level >= ERROR) Serial.print(args) #define errln(args...) if (ezTime._debug_level >= ERROR) Serial.println(args) #define info(args...) "" #define infoln(args...) "" #define debug(args...) "" #define debugln(args...) "" #elif defined(EZTIME_MAX_DEBUGLELEL_INFO) #define err(args...) if (ezTime._debug_level >= ERROR) Serial.print(args) #define errln(args...) if (ezTime._debug_level >= ERROR) Serial.println(args) #define info(args...) if (ezTime._debug_level >= INFO) Serial.print(args) #define infoln(args...) if (ezTime._debug_level >= INFO) Serial.println(args) #define debug(args...) "" #define debugln(args...) "" #else // nothing specified compiles everything in. #define err(args...) if (ezTime._debug_level >= ERROR) Serial.print(args) #define errln(args...) if (ezTime._debug_level >= ERROR) Serial.println(args) #define info(args...) if (ezTime._debug_level >= INFO) Serial.print(args) #define infoln(args...) if (ezTime._debug_level >= INFO) Serial.println(args) #define debug(args...) if (ezTime._debug_level >= DEBUG) Serial.print(args) #define debugln(args...) if (ezTime._debug_level >= DEBUG) Serial.println(args) #endif //////////////////////// #define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) ) #define SECS_PER_DAY (86400UL) typedef struct { uint8_t Second; uint8_t Minute; uint8_t Hour; uint8_t Wday; // day of week, sunday is day 1 uint8_t Day; uint8_t Month; uint8_t Year; // offset from 1970; } tmElements_t; typedef enum { timeNotSet, timeNeedsSync, timeSet } timeStatus_t; typedef struct { uint8_t stdname_end; //Positions in _posix String. stdname_begin fixed at 0 uint8_t dstname_begin; uint8_t dstname_end; bool is_dst; int16_t offset; // offset from UTC in minutes } tzTimeData_t; #define TIME_NOW 0xFFFFFFFF #define LAST_READ 0xFFFFFFFE #define NTP_PACKET_SIZE 48 #define NTP_LOCAL_TIME_PORT 2342 #define NTP_SERVER "pool.ntp.org" #define NTP_TIMEOUT 1500 // milliseconds #define NTP_INTERVAL 600 // default update interval in seconds #define NTP_RETRY 5 // Retry after this many seconds on failed NTP #define NTP_STALE_AFTER 3600 // If update due for this many seconds, set timeStatus to timeNeedsSync #define TIMEZONEAPI_TIMEOUT 2000 // milliseconds #define EEPROM_CACHE_LEN 50 #define MAX_CACHE_PAYLOAD ((EEPROM_CACHE_LEN - 3) / 3) * 4 + ( (EEPROM_CACHE_LEN - 3) % 3) // 2 bytes for len and date, then 4 to 3 (6-bit) compression on rest #define MAX_CACHE_AGE_MONTHS 2 // Various date-time formats #define ATOM "Y-m-d\\TH:i:sP" #define COOKIE "l, d-M-Y H:i:s T" #define ISO8601 "Y-m-d\\TH:i:sO" #define RFC822 "D, d M y H:i:s O" #define RFC850 COOKIE #define RFC1036 RFC822 #define RFC1123 RFC822 #define RFC2822 RFC822 #define RFC3339 ATOM #define RFC3339_EXT "Y-m-d\\TH:i:s.vP" #define RSS RFC822 #define W3C ATOM #define ISO8601_YWD "X-\\Ww-N" // Note that ISO-8601 Year/Week/Day notation may be one year + or - one at beginning or end of year #define DEFAULT_TIMEFORMAT COOKIE // // E Z t i m e c l a s s // class EZtime { friend class Timezone; // Allow methods from Timezone class to access private methods of this class ////////// Error handing public: static ezError_t error(); // Returns ezError_t enumeration of last error, resets _last_error to OK static String errorString(ezError_t err = LAST_ERROR); // Human-readable form of last error. static void debugLevel(ezDebugLevel_t level); // Sets serial printing of debug info to specified ezDebugLevel_t enumeration private: static void error(ezError_t err); // Used to set an error static String debugLevelString(ezDebugLevel_t level); // Human-readable form of debug level. static ezError_t _last_error; static ezDebugLevel_t _debug_level; /////////// public: EZtime(); static timeStatus_t timeStatus(); static time_t now(bool update_last_read = true); static void breakTime(time_t time, tmElements_t &tm); // break time_t into elements static time_t makeTime(tmElements_t &tm); // convert time elements into time_t static time_t makeTime(uint8_t hour, uint8_t minute, uint8_t second, uint8_t day, uint8_t month, uint16_t year); static time_t makeOrdinalTime(uint8_t hour, uint8_t minute, uint8_t second, uint8_t ordinal, uint8_t wday, uint8_t month, uint16_t year); static time_t compileTime(String compile_date = __DATE__, String compile_time = __TIME__); static String monthString(uint8_t month); static String dayString(uint8_t day); static bool secondChanged(); static bool minuteChanged(); private: static time_t _last_sync_time, _last_read_t; static uint32_t _last_sync_millis; static bool _ntp_enabled; static uint16_t _last_read_ms; static timeStatus_t _time_status; #ifdef EZTIME_NETWORK_ENABLE public: static bool queryNTP(String server, time_t &t, unsigned long &measured_at); // measured_at: millis() at measurement, t is converted to secs since 1970 static void updateNow(); static void setServer(String ntp_server = NTP_SERVER); static void setInterval(uint16_t seconds = 0); // 0 = no NTP updates static bool waitForSync(uint16_t timeout = 0); // timeout in seconds private: static void updateIfNeeded(); static uint16_t _update_interval; // in seconds static uint32_t _update_due; static uint16_t _update_backoff; // seconds to add to _update_due to retry NTP static String _ntp_server; #endif // EZTIME_NETWORK_ENABLE ////////// Free extras ... public: static String urlEncode(String str); // Does what you think it does static String zeropad(uint32_t number, uint8_t length); // Returns number as string of given length, zero-padded on left if needed ////////// }; extern EZtime ezTime; // declares the "ezTime" instance of the "EZtime" class // // T i m e z o n e c l a s s // class Timezone { public: Timezone(bool locked_to_UTC = false); bool setPosix(String posix); String getPosix(); time_t tzTime(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME, tzTimeData_t *tztd = NULL); void setDefault(); bool isDST(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME); String getTimezoneName(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME); int16_t getOffset(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME); time_t now(); void setTime(time_t t, uint16_t ms = 0); void setTime(const uint8_t hr, const uint8_t min, const uint8_t sec, const uint8_t day, const uint8_t mnth, uint16_t yr); String dateTime(String format = DEFAULT_TIMEFORMAT); String dateTime(time_t t, String format = DEFAULT_TIMEFORMAT); String dateTime(time_t t, ezLocalOrUTC_t local_or_utc, String format = DEFAULT_TIMEFORMAT); uint8_t hour(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME); // 0-23 uint8_t minute(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME); // 0-59 uint8_t second(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME); // 0-59 uint16_t ms(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME); // 0-999 uint8_t day(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME); // 1-31 uint8_t weekday(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME); // Day of the week (1-7), Sunday is day 1 uint8_t month(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME); // 1-12 uint16_t year(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME); // four digit year uint16_t dayOfYear(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME); // days from start of year, jan 1st = 0 uint8_t weekISO(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME); // ISO-8601 week number (weeks starting on Monday) uint16_t yearISO(time_t t = TIME_NOW, ezLocalOrUTC_t local_or_utc = LOCAL_TIME); // ISO-8601 year, can differ from actual year, plus or minus one private: String _posix; bool _locked_to_UTC; #ifdef EZTIME_NETWORK_ENABLE public: bool setLocation(String location = ""); #ifdef EZTIME_CACHE_EEPROM public: bool setCache(const int16_t address); private: int16_t _eeprom_address; #endif #ifdef EZTIME_CACHE_NVS public: bool setCache(const String name, const String key); private: String _nvs_name, _nvs_key; #endif #if defined(EZTIME_CACHE_EEPROM) || defined(EZTIME_CACHE_NVS) public: void clearCache(bool delete_section = false); String getOlsen(); private: bool setCache(); bool writeCache(const String &str); bool readCache(String &olsen, String &posix, uint8_t &months_since_jan_2018); uint8_t _cache_month; #endif #endif }; extern Timezone UTC; extern Timezone& defaultTZ; #ifdef ARDUINO_TIMELIB_COMPATIBILITY /* Useful Constants */ #define SECS_PER_MIN (60UL) #define SECS_PER_HOUR (3600UL) #define DAYS_PER_WEEK (7UL) #define SECS_PER_WEEK (SECS_PER_DAY * DAYS_PER_WEEK) #define SECS_PER_YEAR (SECS_PER_WEEK * 52UL) #define SECS_YR_2000 (946684800UL) // the time at the start of y2k /* Useful Macros for getting elapsed time */ #define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN) #define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN) #define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR) #define dayOfWeek(_time_) ((( _time_ / SECS_PER_DAY + 4) % DAYS_PER_WEEK)+1) // 1 = Sunday #define elapsedDays(_time_) ( _time_ / SECS_PER_DAY) // this is number of days since Jan 1 1970 #define elapsedSecsToday(_time_) (_time_ % SECS_PER_DAY) // the number of seconds since last midnight // The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971 // Always set the correct time before settting alarms #define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY) // time at the start of the given day #define nextMidnight(_time_) ( previousMidnight(_time_) + SECS_PER_DAY ) // time at the end of the given day #define elapsedSecsThisWeek(_time_) (elapsedSecsToday(_time_) + ((dayOfWeek(_time_)-1) * SECS_PER_DAY) ) // note that week starts on day 1 #define previousSunday(_time_) (_time_ - elapsedSecsThisWeek(_time_)) // time at the start of the week for the given time #define nextSunday(_time_) ( previousSunday(_time_)+SECS_PER_WEEK) // time at the end of the week for the given time /* Useful Macros for converting elapsed time to a time_t */ #define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN) #define hoursToTime_t ((H)) ( (H) * SECS_PER_HOUR) #define daysToTime_t ((D)) ( (D) * SECS_PER_DAY) // fixed on Jul 22 2011 #define weeksToTime_t ((W)) ( (W) * SECS_PER_WEEK) time_t now(); uint8_t second(time_t t /* = TIME_NOW */); uint8_t minute(time_t t /* = TIME_NOW */); uint8_t hour(time_t t /* = TIME_NOW */); uint8_t day(time_t t /* = TIME_NOW */); uint8_t weekday(time_t t /* = TIME_NOW */); uint8_t month(time_t t /* = TIME_NOW */); uint16_t year(time_t t /* = TIME_NOW */); uint8_t hourFormat12(time_t t /* = TIME_NOW */); bool isAM(time_t t /* = TIME_NOW */); bool isPM(time_t t /* = TIME_NOW */); String monthStr(const uint8_t month); String monthShortStr(const uint8_t month); String dayStr(const uint8_t month); String dayShortStr(const uint8_t month); void setTime(time_t t); void setTime(const uint8_t hr, const uint8_t min, const uint8_t sec, const uint8_t day, const uint8_t month, const uint16_t yr); void breakTime(time_t t, tmElements_t &tm); time_t makeTime(tmElements_t &tm); time_t makeTime(uint8_t hour, uint8_t minute, uint8_t second, uint8_t day, uint8_t month, int16_t year); timeStatus_t timeStatus(); #endif //ARDUINO_TIMELIB_COMPATIBILITY } // extern "C++" #endif // __cplusplus #endif //_EZTIME_H_