lib: fixed parsing of recurring VEVENTS: DAILY and interval support
[barry/progweb.git] / src / r_timezone.cc
blobc9920d6352377b18a20b03f75b45f9690af7006b
1 ///
2 /// \file r_timezone.cc
3 /// Record parsing class for the timezone database.
4 ///
6 /*
7 Copyright (C) 2005-2012, Net Direct Inc. (http://www.netdirect.ca/)
8 Copyright (C) 2008, Brian Edginton
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 See the GNU General Public License in the COPYING file at the
20 root directory of this project for more details.
23 #include "r_timezone.h"
24 #include "record-internal.h"
25 #include "protostructs.h"
26 #include "data.h"
27 #include "time.h"
28 #include "iconv.h"
29 #include "debug.h"
30 #include <iostream>
31 #include <sstream>
32 #include <iomanip>
33 #include "ios_state.h"
34 #include <algorithm>
35 #include "m_desktop.h"
37 using namespace std;
38 using namespace Barry::Protocol;
40 namespace Barry
43 ///////////////////////////////////////////////////////////////////////////////
44 // TimeZone Class
46 // TimeZone Field Codes
47 #define TZFC_INDEX 0x01
48 #define TZFC_NAME 0x02
49 #define TZFC_OFFSET 0x03
50 #define TZFC_DST 0x04
51 #define TZFC_STARTMONTH 0x06
52 #define TZFC_ENDMONTH 0x0B
53 #define TZFC_TZTYPE 0x64
55 #define TZFC_END 0xffff
57 static FieldLink<TimeZone> TimeZoneFieldLinks[] = {
58 { TZFC_NAME, "Name", 0, 0, &TimeZone::Name, 0, 0, 0, 0, true },
59 { TZFC_END, "End of List", 0, 0, 0, 0, 0, 0, 0, false },
62 TimeZone::TimeZone()
64 Clear();
67 TimeZone::TimeZone(int utc_offset)
69 Clear();
71 UTCOffset = utc_offset;
74 TimeZone::TimeZone(int hours, int minutes)
76 Clear();
78 // make sure that minutes are always positive, for our logic
79 if( minutes < 0 )
80 minutes = -minutes;
82 // calculate offset in total minutes
83 UTCOffset = hours * 60;
84 if( hours < 0 )
85 UTCOffset -= minutes;
86 else
87 UTCOffset += minutes;
90 TimeZone::~TimeZone()
94 const unsigned char* TimeZone::ParseField(const unsigned char *begin,
95 const unsigned char *end,
96 const IConverter *ic)
98 const CommonField *field = (const CommonField *) begin;
100 // advance and check size
101 begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
102 if( begin > end ) // if begin==end, we are ok
103 return begin;
105 if( !btohs(field->size) ) // if field has no size, something's up
106 return begin;
108 if( field->type == TZFC_TZTYPE ) {
109 if( ( TZType = field->u.uint32 ) != 1 ) {
110 throw Error("TimeZone::ParseField: TimeZone Type is not valid");
112 return begin;
115 // cycle through the type table
116 for( FieldLink<TimeZone> *b = TimeZoneFieldLinks;
117 b->type != TZFC_END;
118 b++ )
120 if( b->type == field->type ) {
121 if( b->strMember ) {
122 std::string &s = this->*(b->strMember);
123 s = ParseFieldString(field);
124 if( b->iconvNeeded && ic )
125 s = ic->FromBB(s);
126 return begin; // done!
131 switch( field->type )
133 case TZFC_INDEX:
134 Index = btohl(field->u.uint32);
135 return begin;
137 case TZFC_OFFSET:
138 UTCOffset = btohs(field->u.int16);
139 return begin;
141 case TZFC_DST:
142 DSTOffset = btohl(field->u.uint32);
143 if (DSTOffset) {
144 UseDST = true;
146 return begin;
148 case TZFC_STARTMONTH:
149 StartMonth = btohl(field->u.uint32);
150 return begin;
152 case TZFC_ENDMONTH:
153 EndMonth = btohl(field->u.uint32);
154 return begin;
157 // if still not handled, add to the Unknowns list
158 UnknownField uf;
159 uf.type = field->type;
160 uf.data.assign((const char*)field->u.raw, btohs(field->size));
161 Unknowns.push_back(uf);
163 // return new pointer for next field
164 return begin;
167 void TimeZone::ParseHeader(const Data &data, size_t &offset)
169 // no header in Task records
172 void TimeZone::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
174 const unsigned char *finish = ParseCommonFields(*this,
175 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
176 offset += finish - (data.GetData() + offset);
179 void TimeZone::Validate() const
183 void TimeZone::BuildHeader(Data &data, size_t &offset) const
185 // not yet implemented
188 void TimeZone::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
190 // not yet implemented
193 void TimeZone::Clear()
195 RecType = GetDefaultRecType();
196 RecordId = 0;
198 Name.clear();
199 Index = 0;
200 UTCOffset = 0;
202 UseDST = false;
203 DSTOffset = 0;
204 StartMonth = -1;
205 EndMonth = -1;
207 TZType = 0;
209 Unknowns.clear();
212 const FieldHandle<TimeZone>::ListT& TimeZone::GetFieldHandles()
214 static FieldHandle<TimeZone>::ListT fhv;
216 if( fhv.size() )
217 return fhv;
219 #undef CONTAINER_OBJECT_NAME
220 #define CONTAINER_OBJECT_NAME fhv
222 #undef RECORD_CLASS_NAME
223 #define RECORD_CLASS_NAME TimeZone
225 FHP(RecType, "Record Type Code");
226 FHP(RecordId, "Unique Record ID");
228 FHD(Name, "TimeZone Name", TZFC_NAME, true);
229 FHD(Index, "Index", TZFC_INDEX, false);
230 FHD(UTCOffset, "TimeZone Offset in Minutes", TZFC_OFFSET, false);
231 FHD(UseDST, "Use DST?", TZFC_DST, false);
232 FHD(DSTOffset, "DST Offset", TZFC_DST, false);
233 FHD(StartMonth, "Start Month", TZFC_STARTMONTH, false);
234 FHD(EndMonth, "End Month", TZFC_ENDMONTH, false);
235 FHD(TZType, "TimeZone Type", TZFC_TZTYPE, false);
237 FHP(Unknowns, "Unknown Fields");
239 return fhv;
242 std::string TimeZone::GetDescription() const
244 ostringstream oss;
245 oss << Name <<
246 " (" << (UTCOffset > 0 ? "+" : "")
247 << dec << (UTCOffset / 60.0) << ")";
248 return oss.str();
251 void TimeZone::Dump(std::ostream &os) const
253 ios_format_state state(os);
255 static const char *month[] = {
256 "Jan", "Feb", "Mar", "Apr", "May",
257 "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
260 os << "TimeZone entry: 0x" << setbase(16) << RecordId
261 << " (" << (unsigned int)RecType << ")\n";
263 // cycle through the type table
264 for( const FieldLink<TimeZone> *b = TimeZoneFieldLinks;
265 b->type != TZFC_END;
266 b++ )
268 if( b->strMember ) {
269 const std::string &s = this->*(b->strMember);
270 if( s.size() )
271 os << " " << b->name << ": " << s << "\n";
275 int hours, minutes;
276 Split(&hours, &minutes);
278 os << " Desc: " << GetDescription() << "\n";
279 os << " Index: 0x" <<setw(2) << Index << "\n";
280 os << " Type: 0x" <<setw(2) << (unsigned int)TZType << "\n";
281 os << " Offset: " << setbase(10) << UTCOffset << " minutes ("
282 << dec << (UTCOffset / 60.0) << ")\n";
283 os << " Split Offset: hours: "
284 << dec << hours << ", minutes: " << minutes << "\n";
285 os << " Sample TZ: " << GetTz("E") << "\n";
286 os << " Use DST: " << (UseDST ? "true" : "false") << "\n";
287 if (UseDST) {
288 os << " DST Offset: " << setbase(10) << DSTOffset << "\n";
289 if ((StartMonth > 0) && (StartMonth < 11))
290 os << "Start Month: " << month[StartMonth] << "\n";
291 else
292 os << "Start Month: unknown (" << setbase(10) << StartMonth << ")\n";
293 if ((EndMonth > 0) && (EndMonth < 11))
294 os << " End Month: " << month[EndMonth] << "\n";
295 else
296 os << " End Month: unknown (" << setbase(10) << EndMonth << ")\n";
299 os << Unknowns;
300 os << "\n\n";
304 void TimeZone::Split(int *hours, int *minutes) const
306 *hours = UTCOffset / 60;
307 *minutes = UTCOffset % 60;
308 if( *minutes < 0 )
309 *minutes = -*minutes;
312 void TimeZone::SplitAbsolute(bool *west,
313 unsigned int *hours,
314 unsigned int *minutes) const
316 int tmphours, tmpminutes;
317 Split(&tmphours, &tmpminutes);
319 if( tmphours < 0 ) {
320 *west = true;
321 tmphours = -tmphours;
323 else {
324 *west = false;
327 *hours = tmphours;
328 *minutes = tmpminutes;
331 std::string TimeZone::GetTz(const std::string &prefix) const
333 int hours, minutes;
334 Split(&hours, &minutes);
336 // TZ variable uses positive for west, and negative for east,
337 // so swap the hour value. Minutes is always positive for
338 // our purposes, so leave it alone.
339 hours = -hours;
341 // standard time first
342 ostringstream oss;
343 oss << prefix << "ST"
344 << hours << ":"
345 << setfill('0') << setw(2) << minutes
346 << ":00";
348 // daylight saving time next, if available
349 if( UseDST ) {
350 TimeZone dst(UTCOffset + DSTOffset);
352 dst.Split(&hours, &minutes);
354 // swap again for TZ semantics...
355 hours = -hours;
357 oss << prefix << "DT"
358 << hours << ":"
359 << setfill('0') << setw(2) << minutes
360 << ":00";
362 // assume second Sunday of start month to first Sunday of
363 // end month, since we don't have enough data to be more
364 // precise
365 oss << ",M" << (StartMonth + 1) << ".2.0";
366 oss << ",M" << (EndMonth + 1) << ".1.0";
369 return oss.str();
373 ///////////////////////////////////////////////////////////////////////////////
374 // TimeZones Class
376 TimeZones::TimeZones()
378 const StaticTimeZone *zones = GetStaticTimeZoneTable();
379 for( ; zones->Name; zones++ ) {
380 TimeZone tz(zones->HourOffset, zones->MinOffset);
381 tz.Index = zones->Code;
382 tz.Name = zones->Name;
383 m_list.push_back(tz);
386 sort(begin(), end(), &TimeZone::SortByZone);
389 struct TimeZoneStore
391 TimeZones::ListType &m_list;
393 TimeZoneStore(TimeZones::ListType &list) : m_list(list)
397 void operator()(const TimeZone &rec)
399 m_list.push_back(rec);
403 TimeZones::TimeZones(Barry::Mode::Desktop &desktop)
405 unsigned int dbId = desktop.GetDBID( TimeZone::GetDBName() );
406 TimeZoneStore store(m_list);
407 RecordParser<TimeZone, TimeZoneStore> parser(store);
409 desktop.LoadDatabase(dbId, parser);
411 sort(begin(), end(), &TimeZone::SortByZone);
414 bool TimeZones::IsLoadable(Barry::Mode::Desktop &desktop)
416 unsigned int num;
417 return desktop.GetDBDB().GetDBNumber(TimeZone::GetDBName(), num);
420 TimeZones::iterator TimeZones::Find(int index)
422 for( iterator i = begin(), e = end(); i != e; ++i )
423 if( i->Index == index )
424 return i;
425 return end();
428 TimeZones::const_iterator TimeZones::Find(int index) const
430 return const_cast<TimeZones*>(this)->Find(index);
433 TimeZones::iterator TimeZones::FindByOffset(int utc_offset)
435 for( iterator i = begin(), e = end(); i != e; ++i )
436 if( i->UTCOffset == utc_offset )
437 return i;
438 return end();
441 TimeZones::const_iterator TimeZones::FindByOffset(int utc_offset) const
443 return const_cast<TimeZones*>(this)->FindByOffset(utc_offset);
446 void TimeZones::Dump(std::ostream &os) const
448 const_iterator b = begin(), e = end();
449 for( ; b != e; ++b ) {
450 os << *b << endl;
454 } // namespace Barry