1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
6 * Copyright (C) 2007-2016, International Business Machines
7 * Corporation and others. All Rights Reserved.
9 *******************************************************************************
10 * file name: icuzdump.cpp
12 * tab size: 8 (not used)
15 * created on: 2007-04-02
16 * created by: Yoshito Umaoka
18 * This tool write out timezone transitions for ICU timezone. This tool
19 * is used as a part of tzdata update process to check if ICU timezone
20 * code works as well as the corresponding Olson stock localtime/zdump.
29 #include "unicode/utypes.h"
30 #include "unicode/ustring.h"
31 #include "unicode/timezone.h"
32 #include "unicode/simpletz.h"
33 #include "unicode/smpdtfmt.h"
34 #include "unicode/decimfmt.h"
35 #include "unicode/gregocal.h"
36 #include "unicode/ustream.h"
37 #include "unicode/putil.h"
48 UErrorCode status
= U_ZERO_ERROR
;
49 stz
= new SimpleTimeZone(0, "");
50 sdf
= new SimpleDateFormat((UnicodeString
)"yyyy-MM-dd EEE HH:mm:ss", Locale::getEnglish(), status
);
51 DecimalFormatSymbols
*symbols
= new DecimalFormatSymbols(Locale::getEnglish(), status
);
52 decf
= new DecimalFormat("00", symbols
, status
);
57 UnicodeString
& format(UDate time
, int32_t offset
, UBool isDst
, UnicodeString
& appendTo
) {
58 stz
->setRawOffset(offset
);
59 sdf
->setTimeZone(*stz
);
60 UnicodeString str
= sdf
->format(time
, appendTo
);
68 int32_t hour
, min
, sec
;
72 offset
= (offset
- sec
) / 60;
76 decf
->format(hour
, appendTo
);
77 decf
->format(min
, appendTo
);
78 decf
->format(sec
, appendTo
);
90 SimpleDateFormat
* sdf
;
97 formatter
= new DumpFormatter();
107 void setLowYear(int32_t lo
) {
111 void setHighYear(int32_t hi
) {
115 void setTick(int32_t t
) {
119 void setTimeZone(TimeZone
* tz
) {
123 void setDumpFormatter(DumpFormatter
* fmt
) {
127 void setLineSeparator(const char* sep
) {
131 void dump(ostream
& out
) {
132 UErrorCode status
= U_ZERO_ERROR
;
133 UDate SEARCH_INCREMENT
= 12 * 60 * 60 * 1000; // half day
134 UDate t
, cutlo
, cuthi
;
135 int32_t rawOffset
, dstOffset
;
138 getCutOverTimes(cutlo
, cuthi
);
140 timezone
->getOffset(t
, FALSE
, rawOffset
, dstOffset
, status
);
142 int32_t newRawOffset
, newDstOffset
;
143 UDate newt
= t
+ SEARCH_INCREMENT
;
145 timezone
->getOffset(newt
, FALSE
, newRawOffset
, newDstOffset
, status
);
147 UBool bSameOffset
= (rawOffset
+ dstOffset
) == (newRawOffset
+ newDstOffset
);
148 UBool bSameDst
= ((dstOffset
!= 0) && (newDstOffset
!= 0)) || ((dstOffset
== 0) && (newDstOffset
== 0));
150 if (!bSameOffset
|| !bSameDst
) {
155 int32_t diff
= (int32_t)(hit
- lot
);
159 UDate medt
= lot
+ ((diff
/ 2) / tick
) * tick
;
160 int32_t medRawOffset
, medDstOffset
;
161 timezone
->getOffset(medt
, FALSE
, medRawOffset
, medDstOffset
, status
);
163 bSameOffset
= (rawOffset
+ dstOffset
) == (medRawOffset
+ medDstOffset
);
164 bSameDst
= ((dstOffset
!= 0) && (medDstOffset
!= 0)) || ((dstOffset
== 0) && (medDstOffset
== 0));
166 if (!bSameOffset
|| !bSameDst
) {
172 // write out the boundary
174 formatter
->format(lot
, rawOffset
+ dstOffset
, (dstOffset
== 0 ? FALSE
: TRUE
), str
);
177 formatter
->format(hit
, newRawOffset
+ newDstOffset
, (newDstOffset
== 0 ? FALSE
: TRUE
), str
);
179 if (linesep
!= NULL
) {
185 rawOffset
= newRawOffset
;
186 dstOffset
= newDstOffset
;
193 void getCutOverTimes(UDate
& lo
, UDate
& hi
) {
194 UErrorCode status
= U_ZERO_ERROR
;
195 GregorianCalendar
* gcal
= new GregorianCalendar(timezone
, Locale::getEnglish(), status
);
197 gcal
->set(loyear
, 0, 1, 0, 0, 0);
198 lo
= gcal
->getTime(status
);
199 gcal
->set(hiyear
, 0, 1, 0, 0, 0);
200 hi
= gcal
->getTime(status
);
208 DumpFormatter
* formatter
;
214 ZoneIterator(UBool bAll
= FALSE
) {
216 zenum
= TimeZone::createEnumeration();
226 ZoneIterator(const char** ids
, int32_t num
) {
242 UErrorCode status
= U_ZERO_ERROR
;
243 const UnicodeString
* zid
= zenum
->snext(status
);
245 tz
= TimeZone::createTimeZone(*zid
);
251 tz
= TimeZone::createTimeZone((const UnicodeString
&)zids
[idx
]);
254 tz
= TimeZone::createDefault();
264 StringEnumeration
* zenum
;
271 kOptHelpQuestionMark
,
278 static UOption options
[]={
280 UOPTION_HELP_QUESTION_MARK
,
281 UOPTION_DEF("allzones", 'a', UOPT_NO_ARG
),
282 UOPTION_DEF("cutover", 'c', UOPT_REQUIRES_ARG
),
283 UOPTION_DEF("destdir", 'd', UOPT_REQUIRES_ARG
),
284 UOPTION_DEF("linesep", 'l', UOPT_REQUIRES_ARG
)
288 main(int argc
, char *argv
[]) {
292 const char *dir
= NULL
;
293 const char *linesep
= NULL
;
295 U_MAIN_INIT_ARGS(argc
, argv
);
296 argc
= u_parseArgs(argc
, argv
, UPRV_LENGTHOF(options
), options
);
299 cerr
<< "Illegal command line argument(s)" << endl
<< endl
;
302 if (argc
< 0 || options
[kOptHelpH
].doesOccur
|| options
[kOptHelpQuestionMark
].doesOccur
) {
304 << "Usage: icuzdump [-options] [zoneid1 zoneid2 ...]" << endl
306 << "\tDump all offset transitions for the specified zones." << endl
308 << "Options:" << endl
309 << "\t-a : Dump all available zones." << endl
310 << "\t-d <dir> : When specified, write transitions in a file under" << endl
311 << "\t the directory for each zone." << endl
312 << "\t-l <sep> : New line code type used in file outputs. CR or LF (default)"
313 << "\t or CRLF." << endl
314 << "\t-c [<low_year>,]<high_year>" << endl
315 << "\t : When specified, dump transitions starting <low_year>" << endl
316 << "\t (inclusive) up to <high_year> (exclusive). The default" << endl
317 << "\t values are 1902(low) and 2038(high)." << endl
;
318 return argc
< 0 ? U_ILLEGAL_ARGUMENT_ERROR
: U_ZERO_ERROR
;
321 bAll
= options
[kOptAllZones
].doesOccur
;
323 if (options
[kOptDestDir
].doesOccur
) {
324 dir
= options
[kOptDestDir
].value
;
327 if (options
[kOptLineSep
].doesOccur
) {
328 if (strcmp(options
[kOptLineSep
].value
, "CR") == 0) {
330 } else if (strcmp(options
[kOptLineSep
].value
, "CRLF") == 0) {
332 } else if (strcmp(options
[kOptLineSep
].value
, "LF") == 0) {
337 if (options
[kOptCutover
].doesOccur
) {
338 char* comma
= (char*)strchr(options
[kOptCutover
].value
, ',');
340 high
= atoi(options
[kOptCutover
].value
);
343 low
= atoi(options
[kOptCutover
].value
);
344 high
= atoi(comma
+ 1);
349 dumper
.setLowYear(low
);
350 dumper
.setHighYear(high
);
351 if (dir
!= NULL
&& linesep
!= NULL
) {
352 // use the specified line separator only for file output
353 dumper
.setLineSeparator((const char*)linesep
);
358 zit
= new ZoneIterator(TRUE
);
361 zit
= new ZoneIterator();
363 zit
= new ZoneIterator((const char**)&argv
[1], argc
- 1);
371 ios::openmode mode
= ios::out
;
372 if (linesep
!= NULL
) {
376 TimeZone
* tz
= zit
->next();
380 dumper
.setTimeZone(tz
);
385 path
<< dir
<< U_FILE_SEP_CHAR
;
386 id
= id
.findAndReplace("/", "-");
389 ofstream
* fout
= new ofstream(path
.str().c_str(), mode
);
391 cerr
<< "Cannot open file " << path
.str() << endl
;
407 TimeZone
* tz
= zit
->next();
411 dumper
.setTimeZone(tz
);
418 cout
<< "ZONE: " << id
<< endl
;