Port of Arduino Timezone library to Mbed for setting daylight saving/summer time
Dependents: WebTimer DISCO-F746NG_light_control_system_tth
Timezone.cpp@0:38a95c82b08c, 2020-11-11 (annotated)
- 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?
User | Revision | Line number | New 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 |