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