Skip to content

Commit

Permalink
Bugfix: Temporary time zone switching not working correctly
Browse files Browse the repository at this point in the history
- On Linux/glibc, must call tzset() explicitly to pick up change of TZ.
- On macOS (possibly elsewhere), must save the return value from
  getenv(); the pointed-to string may change if the environment is
  updated.
- Must check if time zone correction is needed at actual run date/time
  • Loading branch information
hansenjo committed Nov 29, 2023
1 parent c00260d commit 5188881
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 52 deletions.
92 changes: 46 additions & 46 deletions Database/Database.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -78,52 +78,6 @@ const char* Here( const char* method, const char* prefix )
//=============================================================================
namespace Podd {

namespace {

const char* gDefaultTZString = "US/Eastern";

//_____________________________________________________________________________
Long64_t GetOffsetToLocal(const char* tz)
{
// Returns the offset in seconds between the given time zone 'tz' and
// the time zone of the local machine
time_t tloc = time(nullptr);
struct tm tml{}, tmd{};
localtime_r(&tloc, &tml);

const char* cur_tz = gSystem->Getenv("TZ");
gSystem->Setenv("TZ", tz);
localtime_r(&tloc, &tmd);
if( cur_tz )
gSystem->Setenv("TZ", cur_tz);
else
gSystem->Unsetenv("TZ");

return tmd.tm_gmtoff - tml.tm_gmtoff;
}

//_____________________________________________________________________________
TString MkDefaultTZ()
{
gNeedTZCorrection = (GetOffsetToLocal(gDefaultTZString) != 0);
return gDefaultTZString;
}

} // namespace

TString gDefaultTZ = MkDefaultTZ();
Bool_t gNeedTZCorrection = true;

//_____________________________________________________________________________
void SetDefaultTZ( const char* tz )
{
if( !tz || !*tz ) {
tz = gDefaultTZString;
}
gNeedTZCorrection = (GetOffsetToLocal(tz) != 0);
gDefaultTZ = tz;
}

//_____________________________________________________________________________
TString& GetObjArrayString( const TObjArray* array, Int_t i )
{
Expand Down Expand Up @@ -1385,6 +1339,52 @@ Bool_t IsDBtimestamp( const string& line, TDatime& date )
return IsDBdate(line, ldate, true);
}

//_____________________________________________________________________________
// Timezone handling
const char* const gDefaultTZString = "US/Eastern";

TString gDefaultTZ = gDefaultTZString;
Bool_t gNeedTZCorrection = true;

//_____________________________________________________________________________
// Set time zone to assume for legacy database time stamps without time zone
// offsets. The default is "US/Eastern".
void SetDefaultTZ( const char* tz )
{
if( !tz || !*tz ) {
tz = gDefaultTZString;
}
gDefaultTZ = tz;
}

//_____________________________________________________________________________
// Helper function for time zone calculations. Determine offset between
// the default time zone and the local time zone at Unix time 'tloc'.
Long64_t GetTZOffsetToLocal( UInt_t tloc )
{
// Returns the offset in seconds between the given time zone 'tz' and
// the time zone of the local machine
auto timet = static_cast<time_t>(tloc);
struct tm tml{}, tmd{};
localtime_r(&timet, &tml);

TString cur_tz = gSystem->Getenv("TZ");
gSystem->Setenv("TZ", gDefaultTZ);
#ifdef __GLIBC__
tzset();
#endif
localtime_r(&timet, &tmd);
if( !cur_tz.IsNull() )
gSystem->Setenv("TZ", cur_tz);
else
gSystem->Unsetenv("TZ");

#ifdef __GLIBC__
tzset();
#endif
return tmd.tm_gmtoff - tml.tm_gmtoff;
}

//_____________________________________________________________________________
#ifdef __clang__
// Clang appears to make implicitly instantiated template functions private
Expand Down
31 changes: 26 additions & 5 deletions Database/Database.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@
// For backward compatibility with existing client code
#include "Textvars.h"
#include "TDatime.h"
#include "TString.h"

class TObjArray;
//class TDatime;
class TString;

// Helper function for error messages
const char* Here( const char* here, const char* prefix = nullptr );
Expand Down Expand Up @@ -66,24 +65,46 @@ Bool_t IsDBtimestamp( const std::string& line, TDatime& keydate );

// Time zone to assume for legacy database time stamps without time zone offsets.
// The default is "US/Eastern".
void SetDefaultTZ(const char* tz = nullptr);
void SetDefaultTZ( const char* tz = nullptr );
Long64_t GetTZOffsetToLocal( UInt_t tloc );
extern TString gDefaultTZ;
extern Bool_t gNeedTZCorrection;

} // namespace Podd

// Macro for running arbitrary code ("cmd") with TZ set to gDefaultTZ.
#ifdef __GLIBC__
// glibc seems to require an explicit call to tzset() to pick up a change of TZ
#include <ctime>
#define WithDefaultTZ(cmd) \
const char* cur_tz = nullptr; \
TString cur_tz; \
if( Podd::gNeedTZCorrection ) { \
cur_tz = gSystem->Getenv("TZ"); \
gSystem->Setenv("TZ", Podd::gDefaultTZ); \
tzset(); \
} \
cmd; \
if( Podd::gNeedTZCorrection ) { \
if( cur_tz ) \
if( !cur_tz.IsNull() ) \
gSystem->Setenv("TZ", cur_tz ); \
else \
gSystem->Unsetenv("TZ"); \
tzset(); \
}
#else
#define WithDefaultTZ(cmd) \
TString cur_tz; \
if( Podd::gNeedTZCorrection ) { \
cur_tz = gSystem->Getenv("TZ"); \
gSystem->Setenv("TZ", Podd::gDefaultTZ); \
} \
cmd; \
if( Podd::gNeedTZCorrection ) { \
if( !cur_tz.IsNull() ) \
gSystem->Setenv("TZ", cur_tz ); \
else \
gSystem->Unsetenv("TZ"); \
}
#endif

#endif //Podd_Database_h_
6 changes: 5 additions & 1 deletion Podd/THaRunBase.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <iterator> // std::begin, std::end

using namespace std;
using namespace Podd;

static const int UNDEFDATE = 19950101;
static const char* NOTINIT = "uninitialized run";
Expand Down Expand Up @@ -144,7 +145,9 @@ Int_t THaRunBase::Update( const THaEvData* evdata )
if( evdata->IsPrestartEvent() ) {
fDataRead |= kDate|kRunNumber|kRunType;
if( !fAssumeDate ) {
WithDefaultTZ(fDate.Set( evdata->GetRunTime() ));
UInt_t tloc = evdata->GetRunTime();
gNeedTZCorrection = (GetTZOffsetToLocal(tloc) != 0);
WithDefaultTZ(fDate.Set(tloc));
fDataSet |= kDate;
}
SetNumber( evdata->GetRunNum() );
Expand Down Expand Up @@ -512,6 +515,7 @@ void THaRunBase::SetDate( UInt_t tloc )
// Set timestamp of this run to 'tloc' which is in Unix time
// format (number of seconds since 01 Jan 1970).

gNeedTZCorrection = (GetTZOffsetToLocal(tloc) != 0);
WithDefaultTZ(TDatime date( tloc ));
SetDate( date );
}
Expand Down

0 comments on commit 5188881

Please sign in to comment.