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"
47 UErrorCode status
= U_ZERO_ERROR
;
48 stz
= new SimpleTimeZone(0, "");
49 sdf
= new SimpleDateFormat((UnicodeString
)"yyyy-MM-dd EEE HH:mm:ss", Locale::getEnglish(), status
);
50 DecimalFormatSymbols
*symbols
= new DecimalFormatSymbols(Locale::getEnglish(), status
);
51 decf
= new DecimalFormat("00", symbols
, status
);
56 UnicodeString
& format(UDate time
, int32_t offset
, UBool isDst
, UnicodeString
& appendTo
) {
57 stz
->setRawOffset(offset
);
58 sdf
->setTimeZone(*stz
);
59 UnicodeString str
= sdf
->format(time
, appendTo
);
67 int32_t hour
, min
, sec
;
71 offset
= (offset
- sec
) / 60;
75 decf
->format(hour
, appendTo
);
76 decf
->format(min
, appendTo
);
77 decf
->format(sec
, appendTo
);
89 SimpleDateFormat
* sdf
;
96 formatter
= new DumpFormatter();
106 void setLowYear(int32_t lo
) {
110 void setHighYear(int32_t hi
) {
114 void setTick(int32_t t
) {
118 void setTimeZone(TimeZone
* tz
) {
122 void setDumpFormatter(DumpFormatter
* fmt
) {
126 void setLineSeparator(const char* sep
) {
130 void dump(ostream
& out
) {
131 UErrorCode status
= U_ZERO_ERROR
;
132 UDate SEARCH_INCREMENT
= 12 * 60 * 60 * 1000; // half day
133 UDate t
, cutlo
, cuthi
;
134 int32_t rawOffset
, dstOffset
;
137 getCutOverTimes(cutlo
, cuthi
);
139 timezone
->getOffset(t
, FALSE
, rawOffset
, dstOffset
, status
);
141 int32_t newRawOffset
, newDstOffset
;
142 UDate newt
= t
+ SEARCH_INCREMENT
;
144 timezone
->getOffset(newt
, FALSE
, newRawOffset
, newDstOffset
, status
);
146 UBool bSameOffset
= (rawOffset
+ dstOffset
) == (newRawOffset
+ newDstOffset
);
147 UBool bSameDst
= ((dstOffset
!= 0) && (newDstOffset
!= 0)) || ((dstOffset
== 0) && (newDstOffset
== 0));
149 if (!bSameOffset
|| !bSameDst
) {
154 int32_t diff
= (int32_t)(hit
- lot
);
158 UDate medt
= lot
+ ((diff
/ 2) / tick
) * tick
;
159 int32_t medRawOffset
, medDstOffset
;
160 timezone
->getOffset(medt
, FALSE
, medRawOffset
, medDstOffset
, status
);
162 bSameOffset
= (rawOffset
+ dstOffset
) == (medRawOffset
+ medDstOffset
);
163 bSameDst
= ((dstOffset
!= 0) && (medDstOffset
!= 0)) || ((dstOffset
== 0) && (medDstOffset
== 0));
165 if (!bSameOffset
|| !bSameDst
) {
171 // write out the boundary
173 formatter
->format(lot
, rawOffset
+ dstOffset
, (dstOffset
== 0 ? FALSE
: TRUE
), str
);
176 formatter
->format(hit
, newRawOffset
+ newDstOffset
, (newDstOffset
== 0 ? FALSE
: TRUE
), str
);
178 if (linesep
!= NULL
) {
184 rawOffset
= newRawOffset
;
185 dstOffset
= newDstOffset
;
192 void getCutOverTimes(UDate
& lo
, UDate
& hi
) {
193 UErrorCode status
= U_ZERO_ERROR
;
194 GregorianCalendar
* gcal
= new GregorianCalendar(timezone
, Locale::getEnglish(), status
);
196 gcal
->set(loyear
, 0, 1, 0, 0, 0);
197 lo
= gcal
->getTime(status
);
198 gcal
->set(hiyear
, 0, 1, 0, 0, 0);
199 hi
= gcal
->getTime(status
);
207 DumpFormatter
* formatter
;
213 ZoneIterator(UBool bAll
= FALSE
) {
215 zenum
= TimeZone::createEnumeration();
225 ZoneIterator(const char** ids
, int32_t num
) {
241 UErrorCode status
= U_ZERO_ERROR
;
242 const UnicodeString
* zid
= zenum
->snext(status
);
244 tz
= TimeZone::createTimeZone(*zid
);
250 tz
= TimeZone::createTimeZone((const UnicodeString
&)zids
[idx
]);
253 tz
= TimeZone::createDefault();
263 StringEnumeration
* zenum
;
270 kOptHelpQuestionMark
,
277 static UOption options
[]={
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
)
287 main(int argc
, char *argv
[]) {
291 const char *dir
= NULL
;
292 const char *linesep
= NULL
;
294 U_MAIN_INIT_ARGS(argc
, argv
);
295 argc
= u_parseArgs(argc
, argv
, UPRV_LENGTHOF(options
), options
);
298 cerr
<< "Illegal command line argument(s)" << endl
<< endl
;
301 if (argc
< 0 || options
[kOptHelpH
].doesOccur
|| options
[kOptHelpQuestionMark
].doesOccur
) {
303 << "Usage: icuzdump [-options] [zoneid1 zoneid2 ...]" << endl
305 << "\tDump all offset transitions for the specified zones." << 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
;
320 bAll
= options
[kOptAllZones
].doesOccur
;
322 if (options
[kOptDestDir
].doesOccur
) {
323 dir
= options
[kOptDestDir
].value
;
326 if (options
[kOptLineSep
].doesOccur
) {
327 if (strcmp(options
[kOptLineSep
].value
, "CR") == 0) {
329 } else if (strcmp(options
[kOptLineSep
].value
, "CRLF") == 0) {
331 } else if (strcmp(options
[kOptLineSep
].value
, "LF") == 0) {
336 if (options
[kOptCutover
].doesOccur
) {
337 char* comma
= (char*)strchr(options
[kOptCutover
].value
, ',');
339 high
= atoi(options
[kOptCutover
].value
);
342 low
= atoi(options
[kOptCutover
].value
);
343 high
= atoi(comma
+ 1);
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
);
357 zit
= new ZoneIterator(TRUE
);
360 zit
= new ZoneIterator();
362 zit
= new ZoneIterator((const char**)&argv
[1], argc
- 1);
370 ios::openmode mode
= ios::out
;
371 if (linesep
!= NULL
) {
375 TimeZone
* tz
= zit
->next();
379 dumper
.setTimeZone(tz
);
384 path
<< dir
<< U_FILE_SEP_CHAR
;
385 id
= id
.findAndReplace("/", "-");
388 ofstream
* fout
= new ofstream(path
.str().c_str(), mode
);
390 cerr
<< "Cannot open file " << path
.str() << endl
;
406 TimeZone
* tz
= zit
->next();
410 dumper
.setTimeZone(tz
);
417 cout
<< "ZONE: " << id
<< endl
;