Timezone.cpp
#include &Timezone.h>
timezone toLocal toUtc utcIsDST
locIsDST calcTimeChanges initTimeChanges toTime_t
setRules readRules writeRules
/*----------------------------------------------------------------------*
* 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"
#ifdef __AVR__
#include <avr/eeprom.h>
#endif


/*----------------------------------------------------------------------* * 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(); } #ifdef __AVR__ /*----------------------------------------------------------------------* * Create a Timezone object from time change rules stored in EEPROM * * at the given address. * *----------------------------------------------------------------------*/ Timezone:: Timezone( int address ) { readRules( address ); } #endif
/*----------------------------------------------------------------------* * Convert the given UTC time to local time, standard or * * daylight time, as appropriate. * *----------------------------------------------------------------------*/ time_t Timezone:: toLocal( time_t utc) { // RECALCULATE 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 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 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 * 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 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 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 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 1st WEEK OF NEXT MONTH, SUBTRACT 7 DAYS LATER } // CALCULATE FIRST DAY OF MONTH, OR FOR "lAST" RULES, FIRST DAY OF NEXT MONTH tmElements_t tm; tm.Hour = r.hour; tm.Minute = 0; tm.Second = 0; tm.Day = 1; tm.Month = m; tm.Year = yr - 1970; time_t t = makeTime( tm ); // ADD OFFSET FROM FIRST OF MONTH TO R.DOW, AND OFFSET FOR GIVEN WEEK t += ( (r.dow - weekday(t) + 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 } #ifdef __AVR__
/*----------------------------------------------------------------------* * Read the daylight and standard time rules from EEPROM at * * the given address. * *----------------------------------------------------------------------*/ void Timezone:: readRules( int address ) { eeprom_read_block( (void*)&m_dst, (void*)address, sizeof( m_dst )); address += sizeof( m_dst ); eeprom_read_block( (void*)&m_std, (void*)address, sizeof( m_std )); initTimeChanges(); // FORCE calcTimeChanges() AT NEXT CONVERSION CALL }
/*----------------------------------------------------------------------* * Write the daylight and standard time rules to EEPROM at * * the given address. * *----------------------------------------------------------------------*/ void Timezone:: writeRules( int address ) { eeprom_write_block( (void*)&m_dst, (void*)address, sizeof( m_dst )); address += sizeof( m_dst ); eeprom_write_block( (void*)&m_std, (void*)address, sizeof( m_std )); } #endif