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

Dependents:   WebTimer DISCO-F746NG_light_control_system_tth

Committer:
hudakz
Date:
Wed Nov 11 16:53:04 2020 +0000
Revision:
0:38a95c82b08c
Port of Arduino Timezone library to set daylight saving/summer time.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
hudakz 0:38a95c82b08c 1 /*----------------------------------------------------------------------*
hudakz 0:38a95c82b08c 2 * Arduino Timezone Library *
hudakz 0:38a95c82b08c 3 * Jack Christensen Mar 2012 *
hudakz 0:38a95c82b08c 4 * *
hudakz 0:38a95c82b08c 5 * Arduino Timezone Library Copyright (C) 2018 by Jack Christensen and *
hudakz 0:38a95c82b08c 6 * licensed under GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html *
hudakz 0:38a95c82b08c 7 *----------------------------------------------------------------------*/
hudakz 0:38a95c82b08c 8 #include "Timezone.h"
hudakz 0:38a95c82b08c 9 #include "mbed_mktime.h"
hudakz 0:38a95c82b08c 10
hudakz 0:38a95c82b08c 11 /*----------------------------------------------------------------------*
hudakz 0:38a95c82b08c 12 * Create a Timezone object from the given time change rules. *
hudakz 0:38a95c82b08c 13 *----------------------------------------------------------------------*/
hudakz 0:38a95c82b08c 14 Timezone::Timezone(TimeChangeRule dstStart, TimeChangeRule stdStart) :
hudakz 0:38a95c82b08c 15 m_dst(dstStart),
hudakz 0:38a95c82b08c 16 m_std(stdStart)
hudakz 0:38a95c82b08c 17 {
hudakz 0:38a95c82b08c 18 initTimeChanges();
hudakz 0:38a95c82b08c 19 }
hudakz 0:38a95c82b08c 20
hudakz 0:38a95c82b08c 21 /*----------------------------------------------------------------------*
hudakz 0:38a95c82b08c 22 * Create a Timezone object for a zone that does not observe *
hudakz 0:38a95c82b08c 23 * daylight time. *
hudakz 0:38a95c82b08c 24 *----------------------------------------------------------------------*/
hudakz 0:38a95c82b08c 25 Timezone::Timezone(TimeChangeRule stdTime) :
hudakz 0:38a95c82b08c 26 m_dst(stdTime),
hudakz 0:38a95c82b08c 27 m_std(stdTime)
hudakz 0:38a95c82b08c 28 {
hudakz 0:38a95c82b08c 29 initTimeChanges();
hudakz 0:38a95c82b08c 30 }
hudakz 0:38a95c82b08c 31
hudakz 0:38a95c82b08c 32 /*----------------------------------------------------------------------*
hudakz 0:38a95c82b08c 33 * Convert the given UTC time to local time, standard or *
hudakz 0:38a95c82b08c 34 * daylight time, as appropriate. *
hudakz 0:38a95c82b08c 35 *----------------------------------------------------------------------*/
hudakz 0:38a95c82b08c 36 time_t Timezone::toLocal(time_t utc)
hudakz 0:38a95c82b08c 37 {
hudakz 0:38a95c82b08c 38 // recalculate the time change points if needed
hudakz 0:38a95c82b08c 39
hudakz 0:38a95c82b08c 40 if (year(utc) != year(m_dstUTC))
hudakz 0:38a95c82b08c 41 calcTimeChanges(year(utc));
hudakz 0:38a95c82b08c 42
hudakz 0:38a95c82b08c 43 if (utcIsDST(utc))
hudakz 0:38a95c82b08c 44 return utc + m_dst.offset * SECS_PER_MIN;
hudakz 0:38a95c82b08c 45 else
hudakz 0:38a95c82b08c 46 return utc + m_std.offset * SECS_PER_MIN;
hudakz 0:38a95c82b08c 47 }
hudakz 0:38a95c82b08c 48
hudakz 0:38a95c82b08c 49 /*----------------------------------------------------------------------*
hudakz 0:38a95c82b08c 50 * Convert the given UTC time to local time, standard or *
hudakz 0:38a95c82b08c 51 * daylight time, as appropriate, and return a pointer to the time *
hudakz 0:38a95c82b08c 52 * change rule used to do the conversion. The caller must take care *
hudakz 0:38a95c82b08c 53 * not to alter this rule. *
hudakz 0:38a95c82b08c 54 *----------------------------------------------------------------------*/
hudakz 0:38a95c82b08c 55 time_t Timezone::toLocal(time_t utc, TimeChangeRule ** tcr)
hudakz 0:38a95c82b08c 56 {
hudakz 0:38a95c82b08c 57 // recalculate the time change points if needed
hudakz 0:38a95c82b08c 58
hudakz 0:38a95c82b08c 59 if (year(utc) != year(m_dstUTC))
hudakz 0:38a95c82b08c 60 calcTimeChanges(year(utc));
hudakz 0:38a95c82b08c 61
hudakz 0:38a95c82b08c 62 if (utcIsDST(utc)) {
hudakz 0:38a95c82b08c 63 *tcr = &m_dst;
hudakz 0:38a95c82b08c 64 return utc + m_dst.offset * SECS_PER_MIN;
hudakz 0:38a95c82b08c 65 }
hudakz 0:38a95c82b08c 66 else {
hudakz 0:38a95c82b08c 67 *tcr = &m_std;
hudakz 0:38a95c82b08c 68 return utc + m_std.offset * SECS_PER_MIN;
hudakz 0:38a95c82b08c 69 }
hudakz 0:38a95c82b08c 70 }
hudakz 0:38a95c82b08c 71
hudakz 0:38a95c82b08c 72 /*----------------------------------------------------------------------*
hudakz 0:38a95c82b08c 73 * Convert the given local time to UTC time. *
hudakz 0:38a95c82b08c 74 * *
hudakz 0:38a95c82b08c 75 * WARNING: *
hudakz 0:38a95c82b08c 76 * This function is provided for completeness, but should seldom be *
hudakz 0:38a95c82b08c 77 * needed and should be used sparingly and carefully. *
hudakz 0:38a95c82b08c 78 * *
hudakz 0:38a95c82b08c 79 * Ambiguous situations occur after the Standard-to-DST and the *
hudakz 0:38a95c82b08c 80 * DST-to-Standard time transitions. When changing to DST, there is *
hudakz 0:38a95c82b08c 81 * one hour of local time that does not exist, since the clock moves *
hudakz 0:38a95c82b08c 82 * forward one hour. Similarly, when changing to standard time, there *
hudakz 0:38a95c82b08c 83 * is one hour of local times that occur twice since the clock moves *
hudakz 0:38a95c82b08c 84 * back one hour. *
hudakz 0:38a95c82b08c 85 * *
hudakz 0:38a95c82b08c 86 * This function does not test whether it is passed an erroneous time *
hudakz 0:38a95c82b08c 87 * value during the Local -> DST transition that does not exist. *
hudakz 0:38a95c82b08c 88 * If passed such a time, an incorrect UTC time value will be returned. *
hudakz 0:38a95c82b08c 89 * *
hudakz 0:38a95c82b08c 90 * If passed a local time value during the DST -> Local transition *
hudakz 0:38a95c82b08c 91 * that occurs twice, it will be treated as the earlier time, i.e. *
hudakz 0:38a95c82b08c 92 * the time that occurs before the transistion. *
hudakz 0:38a95c82b08c 93 * *
hudakz 0:38a95c82b08c 94 * Calling this function with local times during a transition interval *
hudakz 0:38a95c82b08c 95 * should be avoided! *
hudakz 0:38a95c82b08c 96 *----------------------------------------------------------------------*/
hudakz 0:38a95c82b08c 97 time_t Timezone::toUTC(time_t local)
hudakz 0:38a95c82b08c 98 {
hudakz 0:38a95c82b08c 99 // recalculate the time change points if needed
hudakz 0:38a95c82b08c 100
hudakz 0:38a95c82b08c 101 if (year(local) != year(m_dstLoc))
hudakz 0:38a95c82b08c 102 calcTimeChanges(year(local));
hudakz 0:38a95c82b08c 103
hudakz 0:38a95c82b08c 104 if (locIsDST(local))
hudakz 0:38a95c82b08c 105 return local - m_dst.offset * SECS_PER_MIN;
hudakz 0:38a95c82b08c 106 else
hudakz 0:38a95c82b08c 107 return local - m_std.offset * SECS_PER_MIN;
hudakz 0:38a95c82b08c 108 }
hudakz 0:38a95c82b08c 109
hudakz 0:38a95c82b08c 110 /*----------------------------------------------------------------------*
hudakz 0:38a95c82b08c 111 * Determine whether the given UTC time_t is within the DST interval *
hudakz 0:38a95c82b08c 112 * or the Standard time interval. *
hudakz 0:38a95c82b08c 113 *----------------------------------------------------------------------*/
hudakz 0:38a95c82b08c 114 bool Timezone::utcIsDST(time_t utc)
hudakz 0:38a95c82b08c 115 {
hudakz 0:38a95c82b08c 116 // recalculate the time change points if needed
hudakz 0:38a95c82b08c 117
hudakz 0:38a95c82b08c 118 if (year(utc) != year(m_dstUTC))
hudakz 0:38a95c82b08c 119 calcTimeChanges(year(utc));
hudakz 0:38a95c82b08c 120
hudakz 0:38a95c82b08c 121 if (m_stdUTC == m_dstUTC) // daylight time not observed in this tz
hudakz 0:38a95c82b08c 122 return false;
hudakz 0:38a95c82b08c 123 else
hudakz 0:38a95c82b08c 124 if (m_stdUTC > m_dstUTC) // northern hemisphere
hudakz 0:38a95c82b08c 125 return(utc >= m_dstUTC && utc < m_stdUTC);
hudakz 0:38a95c82b08c 126 else
hudakz 0:38a95c82b08c 127 // southern hemisphere
hudakz 0:38a95c82b08c 128 return !(utc >= m_stdUTC && utc < m_dstUTC);
hudakz 0:38a95c82b08c 129 }
hudakz 0:38a95c82b08c 130
hudakz 0:38a95c82b08c 131 /*----------------------------------------------------------------------*
hudakz 0:38a95c82b08c 132 * Determine whether the given Local time_t is within the DST interval *
hudakz 0:38a95c82b08c 133 * or the Standard time interval. *
hudakz 0:38a95c82b08c 134 *----------------------------------------------------------------------*/
hudakz 0:38a95c82b08c 135 bool Timezone::locIsDST(time_t local)
hudakz 0:38a95c82b08c 136 {
hudakz 0:38a95c82b08c 137 // recalculate the time change points if needed
hudakz 0:38a95c82b08c 138
hudakz 0:38a95c82b08c 139 if (year(local) != year(m_dstLoc))
hudakz 0:38a95c82b08c 140 calcTimeChanges(year(local));
hudakz 0:38a95c82b08c 141
hudakz 0:38a95c82b08c 142 if (m_stdUTC == m_dstUTC) // daylight time not observed in this tz
hudakz 0:38a95c82b08c 143 return false;
hudakz 0:38a95c82b08c 144 else
hudakz 0:38a95c82b08c 145 if (m_stdLoc > m_dstLoc) // northern hemisphere
hudakz 0:38a95c82b08c 146 return(local >= m_dstLoc && local < m_stdLoc);
hudakz 0:38a95c82b08c 147 else
hudakz 0:38a95c82b08c 148 // southern hemisphere
hudakz 0:38a95c82b08c 149 return !(local >= m_stdLoc && local < m_dstLoc);
hudakz 0:38a95c82b08c 150 }
hudakz 0:38a95c82b08c 151
hudakz 0:38a95c82b08c 152 /*----------------------------------------------------------------------*
hudakz 0:38a95c82b08c 153 * Calculate the DST and standard time change points for the given *
hudakz 0:38a95c82b08c 154 * given year as local and UTC time_t values. *
hudakz 0:38a95c82b08c 155 *----------------------------------------------------------------------*/
hudakz 0:38a95c82b08c 156 void Timezone::calcTimeChanges(int yr)
hudakz 0:38a95c82b08c 157 {
hudakz 0:38a95c82b08c 158 m_dstLoc = toTime_t(m_dst, yr);
hudakz 0:38a95c82b08c 159 m_stdLoc = toTime_t(m_std, yr);
hudakz 0:38a95c82b08c 160 m_dstUTC = m_dstLoc - m_std.offset * SECS_PER_MIN;
hudakz 0:38a95c82b08c 161 m_stdUTC = m_stdLoc - m_dst.offset * SECS_PER_MIN;
hudakz 0:38a95c82b08c 162 }
hudakz 0:38a95c82b08c 163
hudakz 0:38a95c82b08c 164 /*----------------------------------------------------------------------*
hudakz 0:38a95c82b08c 165 * Initialize the DST and standard time change points. *
hudakz 0:38a95c82b08c 166 *----------------------------------------------------------------------*/
hudakz 0:38a95c82b08c 167 void Timezone::initTimeChanges()
hudakz 0:38a95c82b08c 168 {
hudakz 0:38a95c82b08c 169 m_dstLoc = 0;
hudakz 0:38a95c82b08c 170 m_stdLoc = 0;
hudakz 0:38a95c82b08c 171 m_dstUTC = 0;
hudakz 0:38a95c82b08c 172 m_stdUTC = 0;
hudakz 0:38a95c82b08c 173 }
hudakz 0:38a95c82b08c 174
hudakz 0:38a95c82b08c 175 /*----------------------------------------------------------------------*
hudakz 0:38a95c82b08c 176 * Convert the given time change rule to a time_t value *
hudakz 0:38a95c82b08c 177 * for the given year. *
hudakz 0:38a95c82b08c 178 *----------------------------------------------------------------------*/
hudakz 0:38a95c82b08c 179 time_t Timezone::toTime_t(TimeChangeRule r, int yr)
hudakz 0:38a95c82b08c 180 {
hudakz 0:38a95c82b08c 181 uint8_t m = r.month; // temp copies of r.month and r.week
hudakz 0:38a95c82b08c 182 uint8_t w = r.week;
hudakz 0:38a95c82b08c 183 if (w == 0) {
hudakz 0:38a95c82b08c 184
hudakz 0:38a95c82b08c 185 // is this a "Last week" rule?
hudakz 0:38a95c82b08c 186 if (++m > 12) {
hudakz 0:38a95c82b08c 187
hudakz 0:38a95c82b08c 188 // yes, for "Last", go to the next month
hudakz 0:38a95c82b08c 189 m = 1;
hudakz 0:38a95c82b08c 190 ++yr;
hudakz 0:38a95c82b08c 191 }
hudakz 0:38a95c82b08c 192
hudakz 0:38a95c82b08c 193 w = 1; // and treat as first week of next month, subtract 7 days later
hudakz 0:38a95c82b08c 194 }
hudakz 0:38a95c82b08c 195
hudakz 0:38a95c82b08c 196 // calculate first day of the month, or for "Last" rules, first day of the next month
hudakz 0:38a95c82b08c 197 tm tmTime;
hudakz 0:38a95c82b08c 198 tmTime.tm_hour = r.hour;
hudakz 0:38a95c82b08c 199 tmTime.tm_min = 0;
hudakz 0:38a95c82b08c 200 tmTime.tm_sec = 0;
hudakz 0:38a95c82b08c 201 tmTime.tm_mday = 1;
hudakz 0:38a95c82b08c 202 tmTime.tm_mon = m;
hudakz 0:38a95c82b08c 203 tmTime.tm_year = yr - 1970;
hudakz 0:38a95c82b08c 204
hudakz 0:38a95c82b08c 205 time_t t;
hudakz 0:38a95c82b08c 206 _rtc_maketime(&tmTime, &t, RTC_FULL_LEAP_YEAR_SUPPORT);
hudakz 0:38a95c82b08c 207
hudakz 0:38a95c82b08c 208 // add offset from the first of the month to r.dow, and offset for the given week
hudakz 0:38a95c82b08c 209 tm tmNow;
hudakz 0:38a95c82b08c 210
hudakz 0:38a95c82b08c 211 _rtc_localtime(t, &tmNow, RTC_FULL_LEAP_YEAR_SUPPORT);
hudakz 0:38a95c82b08c 212
hudakz 0:38a95c82b08c 213 t += ((r.dow - tmNow.tm_wday + 7) % 7 + (w - 1) * 7) * SECS_PER_DAY;
hudakz 0:38a95c82b08c 214
hudakz 0:38a95c82b08c 215 // back up a week if this is a "Last" rule
hudakz 0:38a95c82b08c 216 if (r.week == 0)
hudakz 0:38a95c82b08c 217 t -= 7 * SECS_PER_DAY;
hudakz 0:38a95c82b08c 218 return t;
hudakz 0:38a95c82b08c 219 }
hudakz 0:38a95c82b08c 220
hudakz 0:38a95c82b08c 221 /*----------------------------------------------------------------------*
hudakz 0:38a95c82b08c 222 * Read or update the daylight and standard time rules from RAM. *
hudakz 0:38a95c82b08c 223 *----------------------------------------------------------------------*/
hudakz 0:38a95c82b08c 224 void Timezone::setRules(TimeChangeRule dstStart, TimeChangeRule stdStart)
hudakz 0:38a95c82b08c 225 {
hudakz 0:38a95c82b08c 226 m_dst = dstStart;
hudakz 0:38a95c82b08c 227 m_std = stdStart;
hudakz 0:38a95c82b08c 228 initTimeChanges(); // force calcTimeChanges() at next conversion call
hudakz 0:38a95c82b08c 229 }
hudakz 0:38a95c82b08c 230
hudakz 0:38a95c82b08c 231 /**
hudakz 0:38a95c82b08c 232 * @brief
hudakz 0:38a95c82b08c 233 * @note
hudakz 0:38a95c82b08c 234 * @param
hudakz 0:38a95c82b08c 235 * @retval
hudakz 0:38a95c82b08c 236 */
hudakz 0:38a95c82b08c 237 int Timezone::year(time_t seconds)
hudakz 0:38a95c82b08c 238 {
hudakz 0:38a95c82b08c 239 tm tmNow;
hudakz 0:38a95c82b08c 240
hudakz 0:38a95c82b08c 241 _rtc_localtime(seconds, &tmNow, RTC_FULL_LEAP_YEAR_SUPPORT);
hudakz 0:38a95c82b08c 242 return tmNow.tm_year;
hudakz 0:38a95c82b08c 243 }
hudakz 0:38a95c82b08c 244