Home | History | Annotate | Download | only in tzcode
      1 /*
      2 *******************************************************************************
      3 *
      4 *   Copyright (C) 2007-2014, 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:
     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     }
     50     ~DumpFormatter() {
     51     }
     52 
     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:
     92     ICUZDump() {
     93         formatter = new DumpFormatter();
     94         loyear = 1902;
     95         hiyear = 2050;
     96         tick = 1000;
     97         linesep = NULL;
     98     }
     99 
    100     ~ICUZDump() {
    101     }
    102 
    103     void setLowYear(int32_t lo) {
    104         loyear = lo;
    105     }
    106 
    107     void setHighYear(int32_t hi) {
    108         hiyear = hi;
    109     }
    110 
    111     void setTick(int32_t t) {
    112         tick = t;
    113     }
    114 
    115     void setTimeZone(TimeZone* tz) {
    116         timezone = tz;
    117     }
    118 
    119     void setDumpFormatter(DumpFormatter* fmt) {
    120         formatter = fmt;
    121     }
    122 
    123     void setLineSeparator(const char* sep) {
    124         linesep = sep;
    125     }
    126 
    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:
    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 
    199     TimeZone*   timezone;
    200     int32_t     loyear;
    201     int32_t     hiyear;
    202     int32_t     tick;
    203 
    204     DumpFormatter*  formatter;
    205     const char*  linesep;
    206 };
    207 
    208 class ZoneIterator {
    209 public:
    210     ZoneIterator(UBool bAll = FALSE) {
    211         if (bAll) {
    212             zenum = TimeZone::createEnumeration();
    213         }
    214         else {
    215             zenum = NULL;
    216             zids = NULL;
    217             idx = 0;
    218             numids = 1;
    219         }
    220     }
    221 
    222     ZoneIterator(const char** ids, int32_t num) {
    223         zenum = NULL;
    224         zids = ids;
    225         idx = 0;
    226         numids = num;
    227     }
    228 
    229     ~ZoneIterator() {
    230         if (zenum != NULL) {
    231             delete zenum;
    232         }
    233     }
    234 
    235     TimeZone* next() {
    236         TimeZone* tz = NULL;
    237         if (zenum != NULL) {
    238             UErrorCode status = U_ZERO_ERROR;
    239             const UnicodeString* zid = zenum->snext(status);
    240             if (zid != NULL) {
    241                 tz = TimeZone::createTimeZone(*zid);
    242             }
    243         }
    244         else {
    245             if (idx < numids) {
    246                 if (zids != NULL) {
    247                     tz = TimeZone::createTimeZone((const UnicodeString&)zids[idx]);
    248                 }
    249                 else {
    250                     tz = TimeZone::createDefault();
    251                 }
    252                 idx++;
    253             }
    254         }
    255         return tz;
    256     }
    257 
    258 private:
    259     const char** zids;
    260     StringEnumeration* zenum;
    261     int32_t idx;
    262     int32_t numids;
    263 };
    264 
    265 enum {
    266   kOptHelpH = 0,
    267   kOptHelpQuestionMark,
    268   kOptAllZones,
    269   kOptCutover,
    270   kOptDestDir,
    271   kOptLineSep
    272 };
    273 
    274 static UOption options[]={
    275     UOPTION_HELP_H,
    276     UOPTION_HELP_QUESTION_MARK,
    277     UOPTION_DEF("allzones", 'a', UOPT_NO_ARG),
    278     UOPTION_DEF("cutover", 'c', UOPT_REQUIRES_ARG),
    279     UOPTION_DEF("destdir", 'd', UOPT_REQUIRES_ARG),
    280     UOPTION_DEF("linesep", 'l', UOPT_REQUIRES_ARG)
    281 };
    282 
    283 extern int
    284 main(int argc, char *argv[]) {
    285     int32_t low = 1902;
    286     int32_t high = 2038;
    287     UBool bAll = FALSE;
    288     const char *dir = NULL;
    289     const char *linesep = NULL;
    290 
    291     U_MAIN_INIT_ARGS(argc, argv);
    292     argc = u_parseArgs(argc, argv, sizeof(options)/sizeof(options[0]), options);
    293 
    294     if (argc < 0) {
    295         cerr << "Illegal command line argument(s)" << endl << endl;
    296     }
    297 
    298     if (argc < 0 || options[kOptHelpH].doesOccur || options[kOptHelpQuestionMark].doesOccur) {
    299         cerr
    300             << "Usage: icuzdump [-options] [zoneid1 zoneid2 ...]" << endl
    301             << endl
    302             << "\tDump all offset transitions for the specified zones." << endl
    303             << endl
    304             << "Options:" << endl
    305             << "\t-a       : Dump all available zones." << endl
    306             << "\t-d <dir> : When specified, write transitions in a file under" << endl
    307             << "\t           the directory for each zone." << endl
    308             << "\t-l <sep> : New line code type used in file outputs. CR or LF (default)"
    309             << "\t           or CRLF." << endl
    310             << "\t-c [<low_year>,]<high_year>" << endl
    311             << "\t         : When specified, dump transitions starting <low_year>" << endl
    312             << "\t           (inclusive) up to <high_year> (exclusive).  The default" << endl
    313             << "\t           values are 1902(low) and 2038(high)." << endl;
    314         return argc < 0 ? U_ILLEGAL_ARGUMENT_ERROR : U_ZERO_ERROR;
    315     }
    316 
    317     bAll = options[kOptAllZones].doesOccur;
    318 
    319     if (options[kOptDestDir].doesOccur) {
    320         dir = options[kOptDestDir].value;
    321     }
    322 
    323     if (options[kOptLineSep].doesOccur) {
    324         if (strcmp(options[kOptLineSep].value, "CR") == 0) {
    325             linesep = "\r";
    326         } else if (strcmp(options[kOptLineSep].value, "CRLF") == 0) {
    327             linesep = "\r\n";
    328         } else if (strcmp(options[kOptLineSep].value, "LF") == 0) {
    329             linesep = "\n";
    330         }
    331     }
    332 
    333     if (options[kOptCutover].doesOccur) {
    334         char* comma = (char*)strchr(options[kOptCutover].value, ',');
    335         if (comma == NULL) {
    336             high = atoi(options[kOptCutover].value);
    337         } else {
    338             *comma = 0;
    339             low = atoi(options[kOptCutover].value);
    340             high = atoi(comma + 1);
    341         }
    342     }
    343 
    344     ICUZDump dumper;
    345     dumper.setLowYear(low);
    346     dumper.setHighYear(high);
    347     if (dir != NULL && linesep != NULL) {
    348         // use the specified line separator only for file output
    349         dumper.setLineSeparator((const char*)linesep);
    350     }
    351 
    352     ZoneIterator* zit;
    353     if (bAll) {
    354         zit = new ZoneIterator(TRUE);
    355     } else {
    356         if (argc <= 1) {
    357             zit = new ZoneIterator();
    358         } else {
    359             zit = new ZoneIterator((const char**)&argv[1], argc - 1);
    360         }
    361     }
    362 
    363     UnicodeString id;
    364     if (dir != NULL) {
    365         // file output
    366         ostringstream path;
    367         ios::openmode mode = ios::out;
    368         if (linesep != NULL) {
    369             mode |= ios::binary;
    370         }
    371         for (;;) {
    372             TimeZone* tz = zit->next();
    373             if (tz == NULL) {
    374                 break;
    375             }
    376             dumper.setTimeZone(tz);
    377             tz->getID(id);
    378 
    379             // target file path
    380             path.str("");
    381             path << dir << U_FILE_SEP_CHAR;
    382             id = id.findAndReplace("/", "-");
    383             path << id;
    384 
    385             ofstream* fout = new ofstream(path.str().c_str(), mode);
    386             if (fout->fail()) {
    387                 cerr << "Cannot open file " << path.str() << endl;
    388                 delete fout;
    389                 delete tz;
    390                 break;
    391             }
    392 
    393             dumper.dump(*fout);
    394             fout->close();
    395             delete fout;
    396             delete tz;
    397         }
    398 
    399     } else {
    400         // stdout
    401         UBool bFirst = TRUE;
    402         for (;;) {
    403             TimeZone* tz = zit->next();
    404             if (tz == NULL) {
    405                 break;
    406             }
    407             dumper.setTimeZone(tz);
    408             tz->getID(id);
    409             if (bFirst) {
    410                 bFirst = FALSE;
    411             } else {
    412                 cout << endl;
    413             }
    414             cout << "ZONE: " << id << endl;
    415             dumper.dump(cout);
    416             delete tz;
    417         }
    418     }
    419     delete zit;
    420 }
    421