r_timezone.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       r_timezone.cc
00003 ///             Record parsing class for the timezone database.
00004 ///
00005 
00006 /*
00007     Copyright (C) 2005-2012, Net Direct Inc. (http://www.netdirect.ca/)
00008     Copyright (C) 2008, Brian Edginton
00009 
00010     This program is free software; you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation; either version 2 of the License, or
00013     (at your option) any later version.
00014 
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00018 
00019     See the GNU General Public License in the COPYING file at the
00020     root directory of this project for more details.
00021 */
00022 
00023 #include "r_timezone.h"
00024 #include "record-internal.h"
00025 #include "protostructs.h"
00026 #include "data.h"
00027 #include "time.h"
00028 #include "iconv.h"
00029 #include "debug.h"
00030 #include <iostream>
00031 #include <sstream>
00032 #include <iomanip>
00033 #include "ios_state.h"
00034 #include <algorithm>
00035 #include "m_desktop.h"
00036 
00037 using namespace std;
00038 using namespace Barry::Protocol;
00039 
00040 namespace Barry
00041 {
00042 
00043 ///////////////////////////////////////////////////////////////////////////////
00044 // TimeZone Class
00045 
00046 // TimeZone Field Codes
00047 #define TZFC_INDEX              0x01
00048 #define TZFC_NAME               0x02
00049 #define TZFC_OFFSET             0x03
00050 #define TZFC_DST                0x04
00051 #define TZFC_STARTMONTH         0x06
00052 #define TZFC_ENDMONTH           0x0B
00053 #define TZFC_TZTYPE             0x64
00054 
00055 #define TZFC_END                0xffff
00056 
00057 static FieldLink<TimeZone> TimeZoneFieldLinks[] = {
00058    { TZFC_NAME,   "Name",        0, 0, &TimeZone::Name, 0, 0, 0, 0, true },
00059    { TZFC_END,    "End of List", 0, 0, 0, 0, 0, 0, 0, false },
00060 };
00061 
00062 TimeZone::TimeZone()
00063 {
00064         Clear();
00065 }
00066 
00067 TimeZone::TimeZone(int utc_offset)
00068 {
00069         Clear();
00070 
00071         UTCOffset = utc_offset;
00072 }
00073 
00074 TimeZone::TimeZone(int hours, int minutes)
00075 {
00076         Clear();
00077 
00078         // make sure that minutes are always positive, for our logic
00079         if( minutes < 0 )
00080                 minutes = -minutes;
00081 
00082         // calculate offset in total minutes
00083         UTCOffset = hours * 60;
00084         if( hours < 0 )
00085                 UTCOffset -= minutes;
00086         else
00087                 UTCOffset += minutes;
00088 }
00089 
00090 TimeZone::~TimeZone()
00091 {
00092 }
00093 
00094 const unsigned char* TimeZone::ParseField(const unsigned char *begin,
00095                                           const unsigned char *end,
00096                                           const IConverter *ic)
00097 {
00098         const CommonField *field = (const CommonField *) begin;
00099 
00100         // advance and check size
00101         begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
00102         if( begin > end )       // if begin==end, we are ok
00103                 return begin;
00104 
00105         if( !btohs(field->size) )   // if field has no size, something's up
00106                 return begin;
00107 
00108         if( field->type == TZFC_TZTYPE ) {
00109                 if( ( TZType = field->u.uint32 ) != 1 ) {
00110                         throw Error("TimeZone::ParseField: TimeZone Type is not valid");
00111                 }
00112                 return begin;
00113         }
00114 
00115         // cycle through the type table
00116         for(    FieldLink<TimeZone> *b = TimeZoneFieldLinks;
00117                 b->type != TZFC_END;
00118                 b++ )
00119         {
00120                 if( b->type == field->type ) {
00121                         if( b->strMember ) {
00122                                 std::string &s = this->*(b->strMember);
00123                                 s = ParseFieldString(field);
00124                                 if( b->iconvNeeded && ic )
00125                                         s = ic->FromBB(s);
00126                                 return begin;   // done!
00127                         }
00128                 }
00129         }
00130 
00131         switch( field->type )
00132         {
00133         case TZFC_INDEX:
00134                 Index = btohl(field->u.uint32);
00135                 return begin;
00136 
00137         case TZFC_OFFSET:
00138                 UTCOffset = btohs(field->u.int16);
00139                 return begin;
00140 
00141         case TZFC_DST:
00142                 DSTOffset = btohl(field->u.uint32);
00143                 if (DSTOffset) {
00144                         UseDST = true;
00145                 }
00146                 return begin;
00147 
00148         case TZFC_STARTMONTH:
00149                 StartMonth = btohl(field->u.uint32);
00150                 return begin;
00151 
00152         case TZFC_ENDMONTH:
00153                 EndMonth = btohl(field->u.uint32);
00154                 return begin;
00155         }
00156 
00157         // if still not handled, add to the Unknowns list
00158         UnknownField uf;
00159         uf.type = field->type;
00160         uf.data.assign((const char*)field->u.raw, btohs(field->size));
00161         Unknowns.push_back(uf);
00162 
00163         // return new pointer for next field
00164         return begin;
00165 }
00166 
00167 void TimeZone::ParseHeader(const Data &data, size_t &offset)
00168 {
00169         // no header in Task records
00170 }
00171 
00172 void TimeZone::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
00173 {
00174         const unsigned char *finish = ParseCommonFields(*this,
00175                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
00176         offset += finish - (data.GetData() + offset);
00177 }
00178 
00179 void TimeZone::Validate() const
00180 {
00181 }
00182 
00183 void TimeZone::BuildHeader(Data &data, size_t &offset) const
00184 {
00185         // not yet implemented
00186 }
00187 
00188 void TimeZone::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
00189 {
00190         // not yet implemented
00191 }
00192 
00193 void TimeZone::Clear()
00194 {
00195         RecType = GetDefaultRecType();
00196         RecordId = 0;
00197 
00198         Name.clear();
00199         Index = 0;
00200         UTCOffset = 0;
00201 
00202         UseDST = false;
00203         DSTOffset = 0;
00204         StartMonth = -1;
00205         EndMonth = -1;
00206 
00207         TZType = 0;
00208 
00209         Unknowns.clear();
00210 }
00211 
00212 const FieldHandle<TimeZone>::ListT& TimeZone::GetFieldHandles()
00213 {
00214         static FieldHandle<TimeZone>::ListT fhv;
00215 
00216         if( fhv.size() )
00217                 return fhv;
00218 
00219 #undef CONTAINER_OBJECT_NAME
00220 #define CONTAINER_OBJECT_NAME fhv
00221 
00222 #undef RECORD_CLASS_NAME
00223 #define RECORD_CLASS_NAME TimeZone
00224 
00225         FHP(RecType, "Record Type Code");
00226         FHP(RecordId, "Unique Record ID");
00227 
00228         FHD(Name, "TimeZone Name", TZFC_NAME, true);
00229         FHD(Index, "Index", TZFC_INDEX, false);
00230         FHD(UTCOffset, "TimeZone Offset in Minutes", TZFC_OFFSET, false);
00231         FHD(UseDST, "Use DST?", TZFC_DST, false);
00232         FHD(DSTOffset, "DST Offset", TZFC_DST, false);
00233         FHD(StartMonth, "Start Month", TZFC_STARTMONTH, false);
00234         FHD(EndMonth, "End Month", TZFC_ENDMONTH, false);
00235         FHD(TZType, "TimeZone Type", TZFC_TZTYPE, false);
00236 
00237         FHP(Unknowns, "Unknown Fields");
00238 
00239         return fhv;
00240 }
00241 
00242 std::string TimeZone::GetDescription() const
00243 {
00244         ostringstream oss;
00245         oss << Name <<
00246                 " (" << (UTCOffset > 0 ? "+" : "")
00247                 << dec << (UTCOffset / 60.0) << ")";
00248         return oss.str();
00249 }
00250 
00251 void TimeZone::Dump(std::ostream &os) const
00252 {
00253         ios_format_state state(os);
00254 
00255         static const char *month[] = {
00256                         "Jan", "Feb", "Mar", "Apr", "May",
00257                         "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
00258         };
00259 
00260         os << "TimeZone entry: 0x" << setbase(16) << RecordId
00261            << " (" << (unsigned int)RecType << ")\n";
00262 
00263         // cycle through the type table
00264         for(    const FieldLink<TimeZone> *b = TimeZoneFieldLinks;
00265                 b->type != TZFC_END;
00266                 b++ )
00267         {
00268                 if( b->strMember ) {
00269                         const std::string &s = this->*(b->strMember);
00270                         if( s.size() )
00271                                 os << "       " << b->name << ": " << s << "\n";
00272                 }
00273         }
00274 
00275         int hours, minutes;
00276         Split(&hours, &minutes);
00277 
00278         os << "       Desc: " << GetDescription() << "\n";
00279         os << "      Index: 0x" <<setw(2) << Index << "\n";
00280         os << "       Type: 0x" <<setw(2) << (unsigned int)TZType << "\n";
00281         os << "     Offset: " << setbase(10) << UTCOffset << " minutes ("
00282                 << dec << (UTCOffset / 60.0) << ")\n";
00283         os << "            Split Offset: hours: "
00284                 << dec << hours << ", minutes: " << minutes << "\n";
00285         os << "  Sample TZ: " << GetTz("E") << "\n";
00286         os << "    Use DST: " << (UseDST ? "true" : "false") << "\n";
00287         if (UseDST) {
00288                 os << " DST Offset: " << setbase(10) << DSTOffset << "\n";
00289                 if ((StartMonth > 0) && (StartMonth < 11))
00290                                 os << "Start Month: " << month[StartMonth] << "\n";
00291                 else
00292                                 os << "Start Month: unknown (" << setbase(10) << StartMonth << ")\n";
00293                 if ((EndMonth > 0) && (EndMonth < 11))
00294                         os << "  End Month: " << month[EndMonth] << "\n";
00295                 else
00296                         os << "  End Month: unknown (" << setbase(10) << EndMonth << ")\n";
00297         }
00298 
00299         os << Unknowns;
00300         os << "\n\n";
00301 }
00302 
00303 
00304 void TimeZone::Split(int *hours, int *minutes) const
00305 {
00306         *hours = UTCOffset / 60;
00307         *minutes = UTCOffset % 60;
00308         if( *minutes < 0 )
00309                 *minutes = -*minutes;
00310 }
00311 
00312 void TimeZone::SplitAbsolute(bool *west,
00313                                 unsigned int *hours,
00314                                 unsigned int *minutes) const
00315 {
00316         int tmphours, tmpminutes;
00317         Split(&tmphours, &tmpminutes);
00318 
00319         if( tmphours < 0 ) {
00320                 *west = true;
00321                 tmphours = -tmphours;
00322         }
00323         else {
00324                 *west = false;
00325         }
00326 
00327         *hours = tmphours;
00328         *minutes = tmpminutes;
00329 }
00330 
00331 std::string TimeZone::GetTz(const std::string &prefix) const
00332 {
00333         int hours, minutes;
00334         Split(&hours, &minutes);
00335 
00336         // TZ variable uses positive for west, and negative for east,
00337         // so swap the hour value.  Minutes is always positive for
00338         // our purposes, so leave it alone.
00339         hours = -hours;
00340 
00341         // standard time first
00342         ostringstream oss;
00343         oss << prefix << "ST"
00344                 << hours << ":"
00345                 << setfill('0') << setw(2) << minutes
00346                 << ":00";
00347 
00348         // daylight saving time next, if available
00349         if( UseDST ) {
00350                 TimeZone dst(UTCOffset + DSTOffset);
00351 
00352                 dst.Split(&hours, &minutes);
00353 
00354                 // swap again for TZ semantics...
00355                 hours = -hours;
00356 
00357                 oss << prefix << "DT"
00358                         << hours << ":"
00359                         << setfill('0') << setw(2) << minutes
00360                         << ":00";
00361 
00362                 // assume second Sunday of start month to first Sunday of
00363                 // end month, since we don't have enough data to be more
00364                 // precise
00365                 oss << ",M" << (StartMonth + 1) << ".2.0";
00366                 oss << ",M" << (EndMonth + 1) << ".1.0";
00367         }
00368 
00369         return oss.str();
00370 }
00371 
00372 
00373 ///////////////////////////////////////////////////////////////////////////////
00374 // TimeZones Class
00375 
00376 TimeZones::TimeZones()
00377 {
00378         const StaticTimeZone *zones = GetStaticTimeZoneTable();
00379         for( ; zones->Name; zones++ ) {
00380                 TimeZone tz(zones->HourOffset, zones->MinOffset);
00381                 tz.Index = zones->Code;
00382                 tz.Name = zones->Name;
00383                 m_list.push_back(tz);
00384         }
00385 
00386         sort(begin(), end(), &TimeZone::SortByZone);
00387 }
00388 
00389 struct TimeZoneStore
00390 {
00391         TimeZones::ListType &m_list;
00392 
00393         TimeZoneStore(TimeZones::ListType &list) : m_list(list)
00394         {
00395         }
00396 
00397         void operator()(const TimeZone &rec)
00398         {
00399                 m_list.push_back(rec);
00400         }
00401 };
00402 
00403 TimeZones::TimeZones(Barry::Mode::Desktop &desktop)
00404 {
00405         unsigned int dbId = desktop.GetDBID( TimeZone::GetDBName() );
00406         TimeZoneStore store(m_list);
00407         RecordParser<TimeZone, TimeZoneStore> parser(store);
00408 
00409         desktop.LoadDatabase(dbId, parser);
00410 
00411         sort(begin(), end(), &TimeZone::SortByZone);
00412 }
00413 
00414 bool TimeZones::IsLoadable(Barry::Mode::Desktop &desktop)
00415 {
00416         unsigned int num;
00417         return desktop.GetDBDB().GetDBNumber(TimeZone::GetDBName(), num);
00418 }
00419 
00420 TimeZones::iterator TimeZones::Find(int index)
00421 {
00422         for( iterator i = begin(), e = end(); i != e; ++i )
00423                 if( i->Index == index )
00424                         return i;
00425         return end();
00426 }
00427 
00428 TimeZones::const_iterator TimeZones::Find(int index) const
00429 {
00430         return const_cast<TimeZones*>(this)->Find(index);
00431 }
00432 
00433 TimeZones::iterator TimeZones::FindByOffset(int utc_offset)
00434 {
00435         for( iterator i = begin(), e = end(); i != e; ++i )
00436                 if( i->UTCOffset == utc_offset )
00437                         return i;
00438         return end();
00439 }
00440 
00441 TimeZones::const_iterator TimeZones::FindByOffset(int utc_offset) const
00442 {
00443         return const_cast<TimeZones*>(this)->FindByOffset(utc_offset);
00444 }
00445 
00446 void TimeZones::Dump(std::ostream &os) const
00447 {
00448         const_iterator b = begin(), e = end();
00449         for( ; b != e; ++b ) {
00450                 os << *b << endl;
00451         }
00452 }
00453 
00454 } // namespace Barry
00455