menu: added new Keywords tag to .desktop files
[barry.git] / src / r_timezone.cc
blob9ab0c90feabc2cdb8ceb85332677e64df72573fb
1 ///
2 /// \file r_timezone.cc
3 /// Record parsing class for the timezone database.
4 ///
6 /*
7 Copyright (C) 2005-2013, 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 "i18n.h"
24 #include "r_timezone.h"
25 #include "record-internal.h"
26 #include "protostructs.h"
27 #include "data.h"
28 #include "time.h"
29 #include "iconv.h"
30 #include "debug.h"
31 #include <iostream>
32 #include <sstream>
33 #include <iomanip>
34 #include "ios_state.h"
35 #include <algorithm>
36 #include "m_desktop.h"
38 using namespace std;
39 using namespace Barry::Protocol;
41 namespace Barry
44 ///////////////////////////////////////////////////////////////////////////////
45 // TimeZone Class
47 // TimeZone Field Codes
48 #define TZFC_INDEX 0x01
49 #define TZFC_NAME 0x02
50 #define TZFC_OFFSET 0x03
51 #define TZFC_DST 0x04
52 #define TZFC_STARTMONTH 0x06
53 #define TZFC_ENDMONTH 0x0B
54 #define TZFC_TZTYPE 0x64
56 #define TZFC_END 0xffff
58 static FieldLink<TimeZone> TimeZoneFieldLinks[] = {
59 { TZFC_NAME, N_("Name"), 0, 0, &TimeZone::Name, 0, 0, 0, 0, true },
60 { TZFC_END, N_("End of List"), 0, 0, 0, 0, 0, 0, 0, false },
63 TimeZone::TimeZone()
65 Clear();
68 TimeZone::TimeZone(int utc_offset)
70 Clear();
72 UTCOffset = utc_offset;
75 TimeZone::TimeZone(int hours, int minutes)
77 Clear();
79 // make sure that minutes are always positive, for our logic
80 if( minutes < 0 )
81 minutes = -minutes;
83 // calculate offset in total minutes
84 UTCOffset = hours * 60;
85 if( hours < 0 )
86 UTCOffset -= minutes;
87 else
88 UTCOffset += minutes;
91 TimeZone::~TimeZone()
95 const unsigned char* TimeZone::ParseField(const unsigned char *begin,
96 const unsigned char *end,
97 const IConverter *ic)
99 const CommonField *field = (const CommonField *) begin;
101 // advance and check size
102 begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
103 if( begin > end ) // if begin==end, we are ok
104 return begin;
106 if( !btohs(field->size) ) // if field has no size, something's up
107 return begin;
109 if( field->type == TZFC_TZTYPE ) {
110 if( ( TZType = field->u.uint32 ) != 1 ) {
111 throw Error(_("TimeZone::ParseField: TimeZone Type is not valid"));
113 return begin;
116 // cycle through the type table
117 for( FieldLink<TimeZone> *b = TimeZoneFieldLinks;
118 b->type != TZFC_END;
119 b++ )
121 if( b->type == field->type ) {
122 if( b->strMember ) {
123 std::string &s = this->*(b->strMember);
124 s = ParseFieldString(field);
125 if( b->iconvNeeded && ic )
126 s = ic->FromBB(s);
127 return begin; // done!
132 switch( field->type )
134 case TZFC_INDEX:
135 Index = btohl(field->u.uint32);
136 return begin;
138 case TZFC_OFFSET:
139 UTCOffset = btohs(field->u.int16);
140 return begin;
142 case TZFC_DST:
143 DSTOffset = btohl(field->u.uint32);
144 if (DSTOffset) {
145 UseDST = true;
147 return begin;
149 case TZFC_STARTMONTH:
150 StartMonth = btohl(field->u.uint32);
151 return begin;
153 case TZFC_ENDMONTH:
154 EndMonth = btohl(field->u.uint32);
155 return begin;
158 // if still not handled, add to the Unknowns list
159 UnknownField uf;
160 uf.type = field->type;
161 uf.data.assign((const char*)field->u.raw, btohs(field->size));
162 Unknowns.push_back(uf);
164 // return new pointer for next field
165 return begin;
168 void TimeZone::ParseHeader(const Data &data, size_t &offset)
170 // no header in Task records
173 void TimeZone::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
175 const unsigned char *finish = ParseCommonFields(*this,
176 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
177 offset += finish - (data.GetData() + offset);
180 void TimeZone::Validate() const
184 void TimeZone::BuildHeader(Data &data, size_t &offset) const
186 // not yet implemented
189 void TimeZone::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
191 // not yet implemented
194 void TimeZone::Clear()
196 RecType = GetDefaultRecType();
197 RecordId = 0;
199 Name.clear();
200 Index = 0;
201 UTCOffset = 0;
203 UseDST = false;
204 DSTOffset = 0;
205 StartMonth = -1;
206 EndMonth = -1;
208 TZType = 0;
210 Unknowns.clear();
213 const FieldHandle<TimeZone>::ListT& TimeZone::GetFieldHandles()
215 static FieldHandle<TimeZone>::ListT fhv;
217 if( fhv.size() )
218 return fhv;
220 #undef CONTAINER_OBJECT_NAME
221 #define CONTAINER_OBJECT_NAME fhv
223 #undef RECORD_CLASS_NAME
224 #define RECORD_CLASS_NAME TimeZone
226 FHP(RecType, _("Record Type Code"));
227 FHP(RecordId, _("Unique Record ID"));
229 FHD(Name, _("TimeZone Name"), TZFC_NAME, true);
230 FHD(Index, _("Index"), TZFC_INDEX, false);
231 FHD(UTCOffset, _("TimeZone Offset in Minutes"), TZFC_OFFSET, false);
232 FHD(UseDST, _("Use DST?"), TZFC_DST, false);
233 FHD(DSTOffset, _("DST Offset"), TZFC_DST, false);
234 FHD(StartMonth, _("Start Month"), TZFC_STARTMONTH, false);
235 FHD(EndMonth, _("End Month"), TZFC_ENDMONTH, false);
236 FHD(TZType, _("TimeZone Type"), TZFC_TZTYPE, false);
238 FHP(Unknowns, _("Unknown Fields"));
240 return fhv;
243 std::string TimeZone::GetDescription() const
245 ostringstream oss;
246 oss << Name <<
247 " (" << (UTCOffset > 0 ? "+" : "")
248 << dec << (UTCOffset / 60.0) << ")";
249 return oss.str();
252 void TimeZone::Dump(std::ostream &os) const
254 ios_format_state state(os);
256 static const char *month[] = {
257 N_("Jan"),
258 N_("Feb"),
259 N_("Mar"),
260 N_("Apr"),
261 N_("May"),
262 N_("Jun"),
263 N_("Jul"),
264 N_("Aug"),
265 N_("Sep"),
266 N_("Oct"),
267 N_("Nov"),
268 N_("Dec")
271 os << _("TimeZone entry: ") << "0x" << setbase(16) << RecordId
272 << " (" << (unsigned int)RecType << ")\n";
274 // cycle through the type table
275 for( const FieldLink<TimeZone> *b = TimeZoneFieldLinks;
276 b->type != TZFC_END;
277 b++ )
279 if( b->strMember ) {
280 const std::string &s = this->*(b->strMember);
281 if( s.size() )
282 os << " " << b->name << ": " << s << "\n";
286 int hours, minutes;
287 Split(&hours, &minutes);
289 os << _(" Desc: ") << GetDescription() << "\n";
290 os << _(" Index: ") << "0x" <<setw(2) << Index << "\n";
291 os << _(" Type: ") << "0x" <<setw(2) << (unsigned int)TZType << "\n";
292 os << _(" Offset: ") << setbase(10) << UTCOffset << _(" minutes ")
293 << "(" << dec << (UTCOffset / 60.0) << ")\n";
294 os << _(" Split Offset: hours: ")
295 << dec << hours << _(", minutes: ") << minutes << "\n";
296 os << _(" Sample TZ: ") << GetTz("E") << "\n";
297 os << _(" Use DST: ") << (UseDST ? "true" : "false") << "\n";
298 if (UseDST) {
299 os << _(" DST Offset: ") << setbase(10) << DSTOffset << "\n";
300 if ((StartMonth > 0) && (StartMonth < 11))
301 os << _("Start Month: ") << gettext(month[StartMonth]) << "\n";
302 else
303 os << _("Start Month: unknown (") << setbase(10) << StartMonth << ")\n";
304 if ((EndMonth > 0) && (EndMonth < 11))
305 os << _(" End Month: ") << gettext(month[EndMonth]) << "\n";
306 else
307 os << _(" End Month: unknown (") << setbase(10) << EndMonth << ")\n";
310 os << Unknowns;
311 os << "\n\n";
315 void TimeZone::Split(int *hours, int *minutes) const
317 *hours = UTCOffset / 60;
318 *minutes = UTCOffset % 60;
319 if( *minutes < 0 )
320 *minutes = -*minutes;
323 void TimeZone::SplitAbsolute(bool *west,
324 unsigned int *hours,
325 unsigned int *minutes) const
327 int tmphours, tmpminutes;
328 Split(&tmphours, &tmpminutes);
330 if( tmphours < 0 ) {
331 *west = true;
332 tmphours = -tmphours;
334 else {
335 *west = false;
338 *hours = tmphours;
339 *minutes = tmpminutes;
342 std::string TimeZone::GetTz(const std::string &prefix) const
344 int hours, minutes;
345 Split(&hours, &minutes);
347 // TZ variable uses positive for west, and negative for east,
348 // so swap the hour value. Minutes is always positive for
349 // our purposes, so leave it alone.
350 hours = -hours;
352 // standard time first
353 ostringstream oss;
354 oss << prefix << "ST"
355 << hours << ":"
356 << setfill('0') << setw(2) << minutes
357 << ":00";
359 // daylight saving time next, if available
360 if( UseDST ) {
361 TimeZone dst(UTCOffset + DSTOffset);
363 dst.Split(&hours, &minutes);
365 // swap again for TZ semantics...
366 hours = -hours;
368 oss << prefix << "DT"
369 << hours << ":"
370 << setfill('0') << setw(2) << minutes
371 << ":00";
373 // assume second Sunday of start month to first Sunday of
374 // end month, since we don't have enough data to be more
375 // precise
376 oss << ",M" << (StartMonth + 1) << ".2.0";
377 oss << ",M" << (EndMonth + 1) << ".1.0";
380 return oss.str();
384 ///////////////////////////////////////////////////////////////////////////////
385 // TimeZones Class
387 TimeZones::TimeZones()
389 const StaticTimeZone *zones = GetStaticTimeZoneTable();
390 for( ; zones->Name; zones++ ) {
391 TimeZone tz(zones->HourOffset, zones->MinOffset);
392 tz.Index = zones->Code;
393 tz.Name = gettext( zones->Name );
394 m_list.push_back(tz);
397 sort(begin(), end(), &TimeZone::SortByZone);
400 struct TimeZoneStore
402 TimeZones::ListType &m_list;
404 TimeZoneStore(TimeZones::ListType &list) : m_list(list)
408 void operator()(const TimeZone &rec)
410 m_list.push_back(rec);
414 TimeZones::TimeZones(Barry::Mode::Desktop &desktop)
416 unsigned int dbId = desktop.GetDBID( TimeZone::GetDBName() );
417 TimeZoneStore store(m_list);
418 RecordParser<TimeZone, TimeZoneStore> parser(store);
420 desktop.LoadDatabase(dbId, parser);
422 sort(begin(), end(), &TimeZone::SortByZone);
425 bool TimeZones::IsLoadable(Barry::Mode::Desktop &desktop)
427 unsigned int num;
428 return desktop.GetDBDB().GetDBNumber(TimeZone::GetDBName(), num);
431 TimeZones::iterator TimeZones::Find(int index)
433 for( iterator i = begin(), e = end(); i != e; ++i )
434 if( i->Index == index )
435 return i;
436 return end();
439 TimeZones::const_iterator TimeZones::Find(int index) const
441 return const_cast<TimeZones*>(this)->Find(index);
444 TimeZones::iterator TimeZones::FindByOffset(int utc_offset)
446 for( iterator i = begin(), e = end(); i != e; ++i )
447 if( i->UTCOffset == utc_offset )
448 return i;
449 return end();
452 TimeZones::const_iterator TimeZones::FindByOffset(int utc_offset) const
454 return const_cast<TimeZones*>(this)->FindByOffset(utc_offset);
457 void TimeZones::Dump(std::ostream &os) const
459 const_iterator b = begin(), e = end();
460 for( ; b != e; ++b ) {
461 os << *b << endl;
465 } // namespace Barry