Zoltan Hudak / Timezone

Dependents:   WebTimer DISCO-F746NG_light_control_system_tth

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Timezone.cpp Source File

Timezone.cpp

00001 /*----------------------------------------------------------------------*
00002  * Arduino Timezone Library                                             *
00003  * Jack Christensen Mar 2012                                            *
00004  *                                                                      *
00005  * Arduino Timezone Library Copyright (C) 2018 by Jack Christensen and  *
00006  * licensed under GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html   *
00007  *----------------------------------------------------------------------*/
00008 #include "Timezone.h"
00009 #include "mbed_mktime.h"
00010 
00011 /*----------------------------------------------------------------------*
00012  * Create a Timezone object from the given time change rules.           *
00013  *----------------------------------------------------------------------*/
00014 Timezone::Timezone(TimeChangeRule dstStart, TimeChangeRule stdStart) :
00015     m_dst(dstStart),
00016     m_std(stdStart)
00017 {
00018     initTimeChanges();
00019 }
00020 
00021 /*----------------------------------------------------------------------*
00022  * Create a Timezone object for a zone that does not observe            *
00023  * daylight time.                                                       *
00024  *----------------------------------------------------------------------*/
00025 Timezone::Timezone(TimeChangeRule stdTime) :
00026     m_dst(stdTime),
00027     m_std(stdTime)
00028 {
00029     initTimeChanges();
00030 }
00031 
00032 /*----------------------------------------------------------------------*
00033  * Convert the given UTC time to local time, standard or                *
00034  * daylight time, as appropriate.                                       *
00035  *----------------------------------------------------------------------*/
00036 time_t Timezone::toLocal(time_t utc)
00037 {
00038     // recalculate the time change points if needed
00039 
00040     if (year(utc) != year(m_dstUTC))
00041         calcTimeChanges(year(utc));
00042 
00043     if (utcIsDST(utc))
00044         return utc + m_dst.offset * SECS_PER_MIN;
00045     else
00046         return utc + m_std.offset * SECS_PER_MIN;
00047 }
00048 
00049 /*----------------------------------------------------------------------*
00050  * Convert the given UTC time to local time, standard or                *
00051  * daylight time, as appropriate, and return a pointer to the time      *
00052  * change rule used to do the conversion. The caller must take care     *
00053  * not to alter this rule.                                              *
00054  *----------------------------------------------------------------------*/
00055 time_t Timezone::toLocal(time_t utc, TimeChangeRule ** tcr)
00056 {
00057     // recalculate the time change points if needed
00058 
00059     if (year(utc) != year(m_dstUTC))
00060         calcTimeChanges(year(utc));
00061 
00062     if (utcIsDST(utc)) {
00063         *tcr = &m_dst;
00064         return utc + m_dst.offset * SECS_PER_MIN;
00065     }
00066     else {
00067         *tcr = &m_std;
00068         return utc + m_std.offset * SECS_PER_MIN;
00069     }
00070 }
00071 
00072 /*----------------------------------------------------------------------*
00073  * Convert the given local time to UTC time.                            *
00074  *                                                                      *
00075  * WARNING:                                                             *
00076  * This function is provided for completeness, but should seldom be     *
00077  * needed and should be used sparingly and carefully.                   *
00078  *                                                                      *
00079  * Ambiguous situations occur after the Standard-to-DST and the         *
00080  * DST-to-Standard time transitions. When changing to DST, there is     *
00081  * one hour of local time that does not exist, since the clock moves    *
00082  * forward one hour. Similarly, when changing to standard time, there   *
00083  * is one hour of local times that occur twice since the clock moves    *
00084  * back one hour.                                                       *
00085  *                                                                      *
00086  * This function does not test whether it is passed an erroneous time   *
00087  * value during the Local -> DST transition that does not exist.        *
00088  * If passed such a time, an incorrect UTC time value will be returned. *
00089  *                                                                      *
00090  * If passed a local time value during the DST -> Local transition      *
00091  * that occurs twice, it will be treated as the earlier time, i.e.      *
00092  * the time that occurs before the transistion.                         *
00093  *                                                                      *
00094  * Calling this function with local times during a transition interval  *
00095  * should be avoided!                                                   *
00096  *----------------------------------------------------------------------*/
00097 time_t Timezone::toUTC(time_t local)
00098 {
00099     // recalculate the time change points if needed
00100 
00101     if (year(local) != year(m_dstLoc))
00102         calcTimeChanges(year(local));
00103 
00104     if (locIsDST(local))
00105         return local - m_dst.offset * SECS_PER_MIN;
00106     else
00107         return local - m_std.offset * SECS_PER_MIN;
00108 }
00109 
00110 /*----------------------------------------------------------------------*
00111  * Determine whether the given UTC time_t is within the DST interval    *
00112  * or the Standard time interval.                                       *
00113  *----------------------------------------------------------------------*/
00114 bool Timezone::utcIsDST(time_t utc)
00115 {
00116     // recalculate the time change points if needed
00117 
00118     if (year(utc) != year(m_dstUTC))
00119         calcTimeChanges(year(utc));
00120 
00121     if (m_stdUTC == m_dstUTC)   // daylight time not observed in this tz
00122         return false;
00123     else
00124     if (m_stdUTC > m_dstUTC)    // northern hemisphere
00125         return(utc >= m_dstUTC && utc < m_stdUTC);
00126     else
00127         // southern hemisphere
00128         return !(utc >= m_stdUTC && utc < m_dstUTC);
00129 }
00130 
00131 /*----------------------------------------------------------------------*
00132  * Determine whether the given Local time_t is within the DST interval  *
00133  * or the Standard time interval.                                       *
00134  *----------------------------------------------------------------------*/
00135 bool Timezone::locIsDST(time_t local)
00136 {
00137     // recalculate the time change points if needed
00138 
00139     if (year(local) != year(m_dstLoc))
00140         calcTimeChanges(year(local));
00141 
00142     if (m_stdUTC == m_dstUTC)   // daylight time not observed in this tz
00143         return false;
00144     else
00145     if (m_stdLoc > m_dstLoc)    // northern hemisphere
00146         return(local >= m_dstLoc && local < m_stdLoc);
00147     else
00148         // southern hemisphere
00149         return !(local >= m_stdLoc && local < m_dstLoc);
00150 }
00151 
00152 /*----------------------------------------------------------------------*
00153  * Calculate the DST and standard time change points for the given      *
00154  * given year as local and UTC time_t values.                           *
00155  *----------------------------------------------------------------------*/
00156 void Timezone::calcTimeChanges(int yr)
00157 {
00158     m_dstLoc = toTime_t(m_dst, yr);
00159     m_stdLoc = toTime_t(m_std, yr);
00160     m_dstUTC = m_dstLoc - m_std.offset * SECS_PER_MIN;
00161     m_stdUTC = m_stdLoc - m_dst.offset * SECS_PER_MIN;
00162 }
00163 
00164 /*----------------------------------------------------------------------*
00165  * Initialize the DST and standard time change points.                  *
00166  *----------------------------------------------------------------------*/
00167 void Timezone::initTimeChanges()
00168 {
00169     m_dstLoc = 0;
00170     m_stdLoc = 0;
00171     m_dstUTC = 0;
00172     m_stdUTC = 0;
00173 }
00174 
00175 /*----------------------------------------------------------------------*
00176  * Convert the given time change rule to a time_t value                 *
00177  * for the given year.                                                  *
00178  *----------------------------------------------------------------------*/
00179 time_t Timezone::toTime_t(TimeChangeRule r, int yr)
00180 {
00181     uint8_t m = r.month;    // temp copies of r.month and r.week
00182     uint8_t w = r.week;
00183     if (w == 0) {
00184 
00185         // is this a "Last week" rule?
00186         if (++m > 12) {
00187 
00188             // yes, for "Last", go to the next month
00189             m = 1;
00190             ++yr;
00191         }
00192 
00193         w = 1;              // and treat as first week of next month, subtract 7 days later
00194     }
00195 
00196     // calculate first day of the month, or for "Last" rules, first day of the next month
00197     tm  tmTime;
00198     tmTime.tm_hour = r.hour;
00199     tmTime.tm_min = 0;
00200     tmTime.tm_sec = 0;
00201     tmTime.tm_mday = 1;
00202     tmTime.tm_mon = m;
00203     tmTime.tm_year = yr - 1970;
00204 
00205     time_t  t;
00206     _rtc_maketime(&tmTime, &t, RTC_FULL_LEAP_YEAR_SUPPORT);
00207 
00208     // add offset from the first of the month to r.dow, and offset for the given week
00209     tm  tmNow;
00210 
00211     _rtc_localtime(t, &tmNow, RTC_FULL_LEAP_YEAR_SUPPORT);
00212 
00213     t += ((r.dow - tmNow.tm_wday + 7) % 7 + (w - 1) * 7) * SECS_PER_DAY;
00214 
00215     // back up a week if this is a "Last" rule
00216     if (r.week == 0)
00217         t -= 7 * SECS_PER_DAY;
00218     return t;
00219 }
00220 
00221 /*----------------------------------------------------------------------*
00222  * Read or update the daylight and standard time rules from RAM.        *
00223  *----------------------------------------------------------------------*/
00224 void Timezone::setRules(TimeChangeRule dstStart, TimeChangeRule stdStart)
00225 {
00226     m_dst = dstStart;
00227     m_std = stdStart;
00228     initTimeChanges();  // force calcTimeChanges() at next conversion call
00229 }
00230 
00231 /**
00232  * @brief
00233  * @note
00234  * @param
00235  * @retval
00236  */
00237 int Timezone::year(time_t seconds)
00238 {
00239     tm  tmNow;
00240 
00241     _rtc_localtime(seconds, &tmNow, RTC_FULL_LEAP_YEAR_SUPPORT);
00242     return tmNow.tm_year;
00243 }
00244