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

Dependents:   WebTimer DISCO-F746NG_light_control_system_tth

Revision:
0:38a95c82b08c
--- /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;
+}
+