Port of Arduino Timezone library to Mbed for setting daylight saving/summer time
Dependents: WebTimer DISCO-F746NG_light_control_system_tth
Revision 0:38a95c82b08c, committed 2020-11-11
- Comitter:
- hudakz
- Date:
- Wed Nov 11 16:53:04 2020 +0000
- Commit message:
- Port of Arduino Timezone library to set daylight saving/summer time.
Changed in this revision
Timezone.cpp | Show annotated file Show diff for this revision Revisions of this file |
Timezone.h | Show annotated file Show diff for this revision Revisions of this file |
diff -r 000000000000 -r 38a95c82b08c Timezone.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Timezone.cpp Wed Nov 11 16:53:04 2020 +0000 @@ -0,0 +1,244 @@ +/*----------------------------------------------------------------------* + * Arduino Timezone Library * + * Jack Christensen Mar 2012 * + * * + * Arduino Timezone Library Copyright (C) 2018 by Jack Christensen and * + * licensed under GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html * + *----------------------------------------------------------------------*/ +#include "Timezone.h" +#include "mbed_mktime.h" + +/*----------------------------------------------------------------------* + * Create a Timezone object from the given time change rules. * + *----------------------------------------------------------------------*/ +Timezone::Timezone(TimeChangeRule dstStart, TimeChangeRule stdStart) : + m_dst(dstStart), + m_std(stdStart) +{ + initTimeChanges(); +} + +/*----------------------------------------------------------------------* + * Create a Timezone object for a zone that does not observe * + * daylight time. * + *----------------------------------------------------------------------*/ +Timezone::Timezone(TimeChangeRule stdTime) : + m_dst(stdTime), + m_std(stdTime) +{ + initTimeChanges(); +} + +/*----------------------------------------------------------------------* + * Convert the given UTC time to local time, standard or * + * daylight time, as appropriate. * + *----------------------------------------------------------------------*/ +time_t Timezone::toLocal(time_t utc) +{ + // recalculate the time change points if needed + + if (year(utc) != year(m_dstUTC)) + calcTimeChanges(year(utc)); + + if (utcIsDST(utc)) + return utc + m_dst.offset * SECS_PER_MIN; + else + return utc + m_std.offset * SECS_PER_MIN; +} + +/*----------------------------------------------------------------------* + * Convert the given UTC time to local time, standard or * + * daylight time, as appropriate, and return a pointer to the time * + * change rule used to do the conversion. The caller must take care * + * not to alter this rule. * + *----------------------------------------------------------------------*/ +time_t Timezone::toLocal(time_t utc, TimeChangeRule ** tcr) +{ + // recalculate the time change points if needed + + if (year(utc) != year(m_dstUTC)) + calcTimeChanges(year(utc)); + + if (utcIsDST(utc)) { + *tcr = &m_dst; + return utc + m_dst.offset * SECS_PER_MIN; + } + else { + *tcr = &m_std; + return utc + m_std.offset * SECS_PER_MIN; + } +} + +/*----------------------------------------------------------------------* + * Convert the given local time to UTC time. * + * * + * WARNING: * + * This function is provided for completeness, but should seldom be * + * needed and should be used sparingly and carefully. * + * * + * Ambiguous situations occur after the Standard-to-DST and the * + * DST-to-Standard time transitions. When changing to DST, there is * + * one hour of local time that does not exist, since the clock moves * + * forward one hour. Similarly, when changing to standard time, there * + * is one hour of local times that occur twice since the clock moves * + * back one hour. * + * * + * This function does not test whether it is passed an erroneous time * + * value during the Local -> DST transition that does not exist. * + * If passed such a time, an incorrect UTC time value will be returned. * + * * + * If passed a local time value during the DST -> Local transition * + * that occurs twice, it will be treated as the earlier time, i.e. * + * the time that occurs before the transistion. * + * * + * Calling this function with local times during a transition interval * + * should be avoided! * + *----------------------------------------------------------------------*/ +time_t Timezone::toUTC(time_t local) +{ + // recalculate the time change points if needed + + if (year(local) != year(m_dstLoc)) + calcTimeChanges(year(local)); + + if (locIsDST(local)) + return local - m_dst.offset * SECS_PER_MIN; + else + return local - m_std.offset * SECS_PER_MIN; +} + +/*----------------------------------------------------------------------* + * Determine whether the given UTC time_t is within the DST interval * + * or the Standard time interval. * + *----------------------------------------------------------------------*/ +bool Timezone::utcIsDST(time_t utc) +{ + // recalculate the time change points if needed + + if (year(utc) != year(m_dstUTC)) + calcTimeChanges(year(utc)); + + if (m_stdUTC == m_dstUTC) // daylight time not observed in this tz + return false; + else + if (m_stdUTC > m_dstUTC) // northern hemisphere + return(utc >= m_dstUTC && utc < m_stdUTC); + else + // southern hemisphere + return !(utc >= m_stdUTC && utc < m_dstUTC); +} + +/*----------------------------------------------------------------------* + * Determine whether the given Local time_t is within the DST interval * + * or the Standard time interval. * + *----------------------------------------------------------------------*/ +bool Timezone::locIsDST(time_t local) +{ + // recalculate the time change points if needed + + if (year(local) != year(m_dstLoc)) + calcTimeChanges(year(local)); + + if (m_stdUTC == m_dstUTC) // daylight time not observed in this tz + return false; + else + if (m_stdLoc > m_dstLoc) // northern hemisphere + return(local >= m_dstLoc && local < m_stdLoc); + else + // southern hemisphere + return !(local >= m_stdLoc && local < m_dstLoc); +} + +/*----------------------------------------------------------------------* + * Calculate the DST and standard time change points for the given * + * given year as local and UTC time_t values. * + *----------------------------------------------------------------------*/ +void Timezone::calcTimeChanges(int yr) +{ + m_dstLoc = toTime_t(m_dst, yr); + m_stdLoc = toTime_t(m_std, yr); + m_dstUTC = m_dstLoc - m_std.offset * SECS_PER_MIN; + m_stdUTC = m_stdLoc - m_dst.offset * SECS_PER_MIN; +} + +/*----------------------------------------------------------------------* + * Initialize the DST and standard time change points. * + *----------------------------------------------------------------------*/ +void Timezone::initTimeChanges() +{ + m_dstLoc = 0; + m_stdLoc = 0; + m_dstUTC = 0; + m_stdUTC = 0; +} + +/*----------------------------------------------------------------------* + * Convert the given time change rule to a time_t value * + * for the given year. * + *----------------------------------------------------------------------*/ +time_t Timezone::toTime_t(TimeChangeRule r, int yr) +{ + uint8_t m = r.month; // temp copies of r.month and r.week + uint8_t w = r.week; + if (w == 0) { + + // is this a "Last week" rule? + if (++m > 12) { + + // yes, for "Last", go to the next month + m = 1; + ++yr; + } + + w = 1; // and treat as first week of next month, subtract 7 days later + } + + // calculate first day of the month, or for "Last" rules, first day of the next month + tm tmTime; + tmTime.tm_hour = r.hour; + tmTime.tm_min = 0; + tmTime.tm_sec = 0; + tmTime.tm_mday = 1; + tmTime.tm_mon = m; + tmTime.tm_year = yr - 1970; + + time_t t; + _rtc_maketime(&tmTime, &t, RTC_FULL_LEAP_YEAR_SUPPORT); + + // add offset from the first of the month to r.dow, and offset for the given week + tm tmNow; + + _rtc_localtime(t, &tmNow, RTC_FULL_LEAP_YEAR_SUPPORT); + + t += ((r.dow - tmNow.tm_wday + 7) % 7 + (w - 1) * 7) * SECS_PER_DAY; + + // back up a week if this is a "Last" rule + if (r.week == 0) + t -= 7 * SECS_PER_DAY; + return t; +} + +/*----------------------------------------------------------------------* + * Read or update the daylight and standard time rules from RAM. * + *----------------------------------------------------------------------*/ +void Timezone::setRules(TimeChangeRule dstStart, TimeChangeRule stdStart) +{ + m_dst = dstStart; + m_std = stdStart; + initTimeChanges(); // force calcTimeChanges() at next conversion call +} + +/** + * @brief + * @note + * @param + * @retval + */ +int Timezone::year(time_t seconds) +{ + tm tmNow; + + _rtc_localtime(seconds, &tmNow, RTC_FULL_LEAP_YEAR_SUPPORT); + return tmNow.tm_year; +} +
diff -r 000000000000 -r 38a95c82b08c Timezone.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Timezone.h Wed Nov 11 16:53:04 2020 +0000 @@ -0,0 +1,64 @@ +/*----------------------------------------------------------------------* + * Arduino Timezone Library * + * Jack Christensen Mar 2012 * + * * + * Arduino Timezone Library Copyright (C) 2018 by Jack Christensen and * + * licensed under GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html * + *----------------------------------------------------------------------*/ +#ifndef TIMEZONE_H_INCLUDED +#define TIMEZONE_H_INCLUDED + +#include "mbed.h" + +#define SECS_PER_MIN 60 +#define SECS_PER_DAY 24*60*60 + +/*$off*/ +// convenient constants for TimeChangeRules +enum week_t {Last, First, Second, Third, Fourth}; +enum dow_t {Sun=1, Mon, Tue, Wed, Thu, Fri, Sat}; +enum month_t {Jan=1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec}; +/*$on*/ + +// structure to describe rules for when daylight/summer time begins, + +// or when standard time begins. +struct TimeChangeRule +{ + char abbrev[6]; // five chars max + uint8_t week; // First, Second, Third, Fourth, or Last week of the month + uint8_t dow; // day of week, 1=Sun, 2=Mon, ... 7=Sat + uint8_t month; // 1=Jan, 2=Feb, ... 12=Dec + uint8_t hour; // 0-23 + int offset; // offset from UTC in minutes +}; + +class Timezone +{ +public: + Timezone(TimeChangeRule dstStart, TimeChangeRule stdStart); + Timezone(TimeChangeRule stdTime); + Timezone(int address); + time_t toLocal(time_t utc); + time_t toLocal(time_t utc, TimeChangeRule ** tcr); + time_t toUTC(time_t local); + bool utcIsDST(time_t utc); + bool locIsDST(time_t local); + void setRules(TimeChangeRule dstStart, TimeChangeRule stdStart); + void readRules(int address); + void writeRules(int address); +private: + void calcTimeChanges(int yr); + void initTimeChanges(); + time_t toTime_t(TimeChangeRule r, int yr); + int year(time_t seconds); + TimeChangeRule m_dst; // rule for start of dst or summer time for any year + TimeChangeRule m_std; // rule for start of standard time for any year + time_t m_dstUTC; // dst start for given/current year, given in UTC + time_t m_stdUTC; // std time start for given/current year, given in UTC + time_t m_dstLoc; // dst start for given/current year, given in local time + time_t m_stdLoc; // std time start for given/current year, given in local time +}; + +#endif // TIMEZONE_H_INCLUDED +