• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 *
4 *   Copyright (C) 2007, International Business Machines
5 *   Corporation and others.  All Rights Reserved.
6 *
7 *******************************************************************************
8 *   file name:  icuzdump.cpp
9 *   encoding:   US-ASCII
10 *   tab size:   8 (not used)
11 *   indentation:4
12 *
13 *   created on: 2007-04-02
14 *   created by: Yoshito Umaoka
15 *
16 *   This tool write out timezone transitions for ICU timezone.  This tool
17 *   is used as a part of tzdata update process to check if ICU timezone
18 *   code works as well as the corresponding Olson stock localtime/zdump.
19 */
20 
21 #include <cstdlib>
22 #include <cstring>
23 #include <fstream>
24 #include <sstream>
25 #include <iostream>
26 
27 #include "unicode/utypes.h"
28 #include "unicode/ustring.h"
29 #include "unicode/timezone.h"
30 #include "unicode/simpletz.h"
31 #include "unicode/smpdtfmt.h"
32 #include "unicode/decimfmt.h"
33 #include "unicode/gregocal.h"
34 #include "unicode/ustream.h"
35 #include "unicode/putil.h"
36 
37 #include "uoptions.h"
38 
39 using namespace std;
40 
41 class DumpFormatter {
42 public:
DumpFormatter()43     DumpFormatter() {
44         UErrorCode status = U_ZERO_ERROR;
45         stz = new SimpleTimeZone(0, "");
46         sdf = new SimpleDateFormat((UnicodeString)"yyyy-MM-dd EEE HH:mm:ss", Locale::getEnglish(), status);
47         DecimalFormatSymbols *symbols = new DecimalFormatSymbols(Locale::getEnglish(), status);
48         decf = new DecimalFormat("00", symbols, status);
49     }
~DumpFormatter()50     ~DumpFormatter() {
51     }
52 
format(UDate time,int32_t offset,UBool isDst,UnicodeString & appendTo)53     UnicodeString& format(UDate time, int32_t offset, UBool isDst, UnicodeString& appendTo) {
54         stz->setRawOffset(offset);
55         sdf->setTimeZone(*stz);
56         UnicodeString str = sdf->format(time, appendTo);
57         if (offset < 0) {
58             appendTo += "-";
59             offset = -offset;
60         } else {
61             appendTo += "+";
62         }
63 
64         int32_t hour, min, sec;
65 
66         offset /= 1000;
67         sec = offset % 60;
68         offset = (offset - sec) / 60;
69         min = offset % 60;
70         hour = offset / 60;
71 
72         decf->format(hour, appendTo);
73         decf->format(min, appendTo);
74         decf->format(sec, appendTo);
75         appendTo += "[DST=";
76         if (isDst) {
77             appendTo += "1";
78         } else {
79             appendTo += "0";
80         }
81         appendTo += "]";
82         return appendTo;
83     }
84 private:
85     SimpleTimeZone*     stz;
86     SimpleDateFormat*   sdf;
87     DecimalFormat*      decf;
88 };
89 
90 class ICUZDump {
91 public:
ICUZDump()92     ICUZDump() {
93         formatter = new DumpFormatter();
94         loyear = 1902;
95         hiyear = 2050;
96         tick = 1000;
97         linesep = NULL;
98     }
99 
~ICUZDump()100     ~ICUZDump() {
101     }
102 
setLowYear(int32_t lo)103     void setLowYear(int32_t lo) {
104         loyear = lo;
105     }
106 
setHighYear(int32_t hi)107     void setHighYear(int32_t hi) {
108         hiyear = hi;
109     }
110 
setTick(int32_t t)111     void setTick(int32_t t) {
112         tick = t;
113     }
114 
setTimeZone(TimeZone * tz)115     void setTimeZone(TimeZone* tz) {
116         timezone = tz;
117     }
118 
setDumpFormatter(DumpFormatter * fmt)119     void setDumpFormatter(DumpFormatter* fmt) {
120         formatter = fmt;
121     }
122 
setLineSeparator(const char * sep)123     void setLineSeparator(const char* sep) {
124         linesep = sep;
125     }
126 
dump(ostream & out)127     void dump(ostream& out) {
128         UErrorCode status = U_ZERO_ERROR;
129         UDate SEARCH_INCREMENT = 12 * 60 * 60 * 1000; // half day
130         UDate t, cutlo, cuthi;
131         int32_t rawOffset, dstOffset;
132         UnicodeString str;
133 
134         getCutOverTimes(cutlo, cuthi);
135         t = cutlo;
136         timezone->getOffset(t, FALSE, rawOffset, dstOffset, status);
137         while (t < cuthi) {
138             int32_t newRawOffset, newDstOffset;
139             UDate newt = t + SEARCH_INCREMENT;
140 
141             timezone->getOffset(newt, FALSE, newRawOffset, newDstOffset, status);
142 
143             UBool bSameOffset = (rawOffset + dstOffset) == (newRawOffset + newDstOffset);
144             UBool bSameDst = ((dstOffset != 0) && (newDstOffset != 0)) || ((dstOffset == 0) && (newDstOffset == 0));
145 
146             if (!bSameOffset || !bSameDst) {
147                 // find the boundary
148                 UDate lot = t;
149                 UDate hit = newt;
150                 while (true) {
151                     int32_t diff = (int32_t)(hit - lot);
152                     if (diff <= tick) {
153                         break;
154                     }
155                     UDate medt = lot + ((diff / 2) / tick) * tick;
156                     int32_t medRawOffset, medDstOffset;
157                     timezone->getOffset(medt, FALSE, medRawOffset, medDstOffset, status);
158 
159                     bSameOffset = (rawOffset + dstOffset) == (medRawOffset + medDstOffset);
160                     bSameDst = ((dstOffset != 0) && (medDstOffset != 0)) || ((dstOffset == 0) && (medDstOffset == 0));
161 
162                     if (!bSameOffset || !bSameDst) {
163                         hit = medt;
164                     } else {
165                         lot = medt;
166                     }
167                 }
168                 // write out the boundary
169                 str.remove();
170                 formatter->format(lot, rawOffset + dstOffset, (dstOffset == 0 ? FALSE : TRUE), str);
171                 out << str << " > ";
172                 str.remove();
173                 formatter->format(hit, newRawOffset + newDstOffset, (newDstOffset == 0 ? FALSE : TRUE), str);
174                 out << str;
175                 if (linesep != NULL) {
176                     out << linesep;
177                 } else {
178                     out << endl;
179                 }
180 
181                 rawOffset = newRawOffset;
182                 dstOffset = newDstOffset;
183             }
184             t = newt;
185         }
186     }
187 
188 private:
getCutOverTimes(UDate & lo,UDate & hi)189     void getCutOverTimes(UDate& lo, UDate& hi) {
190         UErrorCode status = U_ZERO_ERROR;
191         GregorianCalendar* gcal = new GregorianCalendar(timezone, Locale::getEnglish(), status);
192         gcal->clear();
193         gcal->set(loyear, 0, 1, 0, 0, 0);
194         lo = gcal->getTime(status);
195         gcal->set(hiyear, 0, 1, 0, 0, 0);
196         hi = gcal->getTime(status);
197     }
198 
dumpZone(ostream & out,const char * linesep,UnicodeString tzid,int32_t low,int32_t high)199     void dumpZone(ostream& out, const char* linesep, UnicodeString tzid, int32_t low, int32_t high) {
200     }
201 
202     TimeZone*   timezone;
203     int32_t     loyear;
204     int32_t     hiyear;
205     int32_t     tick;
206 
207     DumpFormatter*  formatter;
208     const char*  linesep;
209 };
210 
211 class ZoneIterator {
212 public:
ZoneIterator(UBool bAll=FALSE)213     ZoneIterator(UBool bAll = FALSE) {
214         if (bAll) {
215             zenum = TimeZone::createEnumeration();
216         }
217         else {
218             zenum = NULL;
219             zids = NULL;
220             idx = 0;
221             numids = 1;
222         }
223     }
224 
ZoneIterator(const char ** ids,int32_t num)225     ZoneIterator(const char** ids, int32_t num) {
226         zenum = NULL;
227         zids = ids;
228         idx = 0;
229         numids = num;
230     }
231 
~ZoneIterator()232     ~ZoneIterator() {
233         if (zenum != NULL) {
234             delete zenum;
235         }
236     }
237 
next()238     TimeZone* next() {
239         TimeZone* tz = NULL;
240         if (zenum != NULL) {
241             UErrorCode status = U_ZERO_ERROR;
242             const UnicodeString* zid = zenum->snext(status);
243             if (zid != NULL) {
244                 tz = TimeZone::createTimeZone(*zid);
245             }
246         }
247         else {
248             if (idx < numids) {
249                 if (zids != NULL) {
250                     tz = TimeZone::createTimeZone((const UnicodeString&)zids[idx]);
251                 }
252                 else {
253                     tz = TimeZone::createDefault();
254                 }
255                 idx++;
256             }
257         }
258         return tz;
259     }
260 
261 private:
262     const char** zids;
263     StringEnumeration* zenum;
264     int32_t idx;
265     int32_t numids;
266 };
267 
268 enum {
269   kOptHelpH = 0,
270   kOptHelpQuestionMark,
271   kOptAllZones,
272   kOptCutover,
273   kOptDestDir,
274   kOptLineSep
275 };
276 
277 static UOption options[]={
278     UOPTION_HELP_H,
279     UOPTION_HELP_QUESTION_MARK,
280     UOPTION_DEF("allzones", 'a', UOPT_NO_ARG),
281     UOPTION_DEF("cutover", 'c', UOPT_REQUIRES_ARG),
282     UOPTION_DEF("destdir", 'd', UOPT_REQUIRES_ARG),
283     UOPTION_DEF("linesep", 'l', UOPT_REQUIRES_ARG)
284 };
285 
286 extern int
main(int argc,char * argv[])287 main(int argc, char *argv[]) {
288     int32_t low = 1902;
289     int32_t high = 2038;
290     UBool bAll = FALSE;
291     const char *dir = NULL;
292     const char *linesep = NULL;
293 
294     U_MAIN_INIT_ARGS(argc, argv);
295     argc = u_parseArgs(argc, argv, sizeof(options)/sizeof(options[0]), options);
296 
297     if (argc < 0) {
298         cerr << "Illegal command line argument(s)" << endl << endl;
299     }
300 
301     if (argc < 0 || options[kOptHelpH].doesOccur || options[kOptHelpQuestionMark].doesOccur) {
302         cerr
303             << "Usage: icuzdump [-options] [zoneid1 zoneid2 ...]" << endl
304             << endl
305             << "\tDump all offset transitions for the specified zones." << endl
306             << endl
307             << "Options:" << endl
308             << "\t-a       : Dump all available zones." << endl
309             << "\t-d <dir> : When specified, write transitions in a file under" << endl
310             << "\t           the directory for each zone." << endl
311             << "\t-l <sep> : New line code type used in file outputs. CR or LF (default)"
312             << "\t           or CRLF." << endl
313             << "\t-c [<low_year>,]<high_year>" << endl
314             << "\t         : When specified, dump transitions starting <low_year>" << endl
315             << "\t           (inclusive) up to <high_year> (exclusive).  The default" << endl
316             << "\t           values are 1902(low) and 2038(high)." << endl;
317         return argc < 0 ? U_ILLEGAL_ARGUMENT_ERROR : U_ZERO_ERROR;
318     }
319 
320     bAll = options[kOptAllZones].doesOccur;
321 
322     if (options[kOptDestDir].doesOccur) {
323         dir = options[kOptDestDir].value;
324     }
325 
326     if (options[kOptLineSep].doesOccur) {
327         if (strcmp(options[kOptLineSep].value, "CR") == 0) {
328             linesep = "\r";
329         } else if (strcmp(options[kOptLineSep].value, "CRLF") == 0) {
330             linesep = "\r\n";
331         } else if (strcmp(options[kOptLineSep].value, "LF") == 0) {
332             linesep = "\n";
333         }
334     }
335 
336     if (options[kOptCutover].doesOccur) {
337         char* comma = (char*)strchr(options[kOptCutover].value, ',');
338         if (comma == NULL) {
339             high = atoi(options[kOptCutover].value);
340         } else {
341             *comma = 0;
342             low = atoi(options[kOptCutover].value);
343             high = atoi(comma + 1);
344         }
345     }
346 
347     ICUZDump dumper;
348     dumper.setLowYear(low);
349     dumper.setHighYear(high);
350     if (dir != NULL && linesep != NULL) {
351         // use the specified line separator only for file output
352         dumper.setLineSeparator((const char*)linesep);
353     }
354 
355     ZoneIterator* zit;
356     if (bAll) {
357         zit = new ZoneIterator(TRUE);
358     } else {
359         if (argc <= 1) {
360             zit = new ZoneIterator();
361         } else {
362             zit = new ZoneIterator((const char**)&argv[1], argc - 1);
363         }
364     }
365 
366     UnicodeString id;
367     if (dir != NULL) {
368         // file output
369         ostringstream path;
370         ios::openmode mode = ios::out;
371         if (linesep != NULL) {
372             mode |= ios::binary;
373         }
374         for (;;) {
375             TimeZone* tz = zit->next();
376             if (tz == NULL) {
377                 break;
378             }
379             dumper.setTimeZone(tz);
380             tz->getID(id);
381 
382             // target file path
383             path.str("");
384             path << dir << U_FILE_SEP_CHAR;
385             id = id.findAndReplace("/", "-");
386             path << id;
387 
388             ofstream* fout = new ofstream(path.str().c_str(), mode);
389             if (fout->fail()) {
390                 cerr << "Cannot open file " << path << endl;
391                 delete fout;
392                 delete tz;
393                 break;
394             }
395 
396             dumper.dump(*fout);
397             fout->close();
398             delete fout;
399             delete tz;
400         }
401 
402     } else {
403         // stdout
404         UBool bFirst = TRUE;
405         for (;;) {
406             TimeZone* tz = zit->next();
407             if (tz == NULL) {
408                 break;
409             }
410             dumper.setTimeZone(tz);
411             tz->getID(id);
412             if (bFirst) {
413                 bFirst = FALSE;
414             } else {
415                 cout << endl;
416             }
417             cout << "ZONE: " << id << endl;
418             dumper.dump(cout);
419             delete tz;
420         }
421     }
422     delete zit;
423 }
424