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