Port of Arduino Timezone library to Mbed for setting daylight saving/summer time

Dependents:   WebTimer DISCO-F746NG_light_control_system_tth

Files at this revision

API Documentation at this revision

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
+