Home | History | Annotate | Download | only in tzcode
      1 /*
      2 *******************************************************************************
      3 *
      4 *   Copyright (C) 2007, 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     void dumpZone(ostream& out, const char* linesep, UnicodeString tzid, int32_t low, int32_t high) {
    200     }
    201 
    202     TimeZone*   timezone;
    203     int32_t     loyear;
    204     int32_t     hiyear;
    205     int32_t     tick;
    206 
    207     DumpFormatter*  formatter;
    208     const char*  linesep;
    209 };
    210 
    211 class ZoneIterator {
    212 public:
    213     ZoneIterator(UBool bAll = FALSE) {
    214         if (bAll) {
    215             zenum = TimeZone::createEnumeration();
    216         }
    217         else {
    218             zenum = NULL;
    219             zids = NULL;
    220             idx = 0;
    221             numids = 1;
    222         }
    223     }
    224 
    225     ZoneIterator(const char** ids, int32_t num) {
    226         zenum = NULL;
    227         zids = ids;
    228         idx = 0;
    229         numids = num;
    230     }
    231 
    232     ~ZoneIterator() {
    233         if (zenum != NULL) {
    234             delete zenum;
    235         }
    236     }
    237 
    238     TimeZone* next() {
    239         TimeZone* tz = NULL;
    240         if (zenum != NULL) {
    241             UErrorCode status = U_ZERO_ERROR;
    242             const UnicodeString* zid = zenum->snext(status);
    243             if (zid != NULL) {
    244                 tz = TimeZone::createTimeZone(*zid);
    245             }
    246         }
    247         else {
    248             if (idx < numids) {
    249                 if (zids != NULL) {
    250                     tz = TimeZone::createTimeZone((const UnicodeString&)zids[idx]);
    251                 }
    252                 else {
    253                     tz = TimeZone::createDefault();
    254                 }
    255                 idx++;
    256             }
    257         }
    258         return tz;
    259     }
    260 
    261 private:
    262     const char** zids;
    263     StringEnumeration* zenum;
    264     int32_t idx;
    265     int32_t numids;
    266 };
    267 
    268 enum {
    269   kOptHelpH = 0,
    270   kOptHelpQuestionMark,
    271   kOptAllZones,
    272   kOptCutover,
    273   kOptDestDir,
    274   kOptLineSep
    275 };
    276 
    277 static UOption options[]={
    278     UOPTION_HELP_H,
    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)
    284 };
    285 
    286 extern int
    287 main(int argc, char *argv[]) {
    288     int32_t low = 1902;
    289     int32_t high = 2038;
    290     UBool bAll = FALSE;
    291     const char *dir = NULL;
    292     const char *linesep = NULL;
    293 
    294     U_MAIN_INIT_ARGS(argc, argv);
    295     argc = u_parseArgs(argc, argv, sizeof(options)/sizeof(options[0]), options);
    296 
    297     if (argc < 0) {
    298         cerr << "Illegal command line argument(s)" << endl << endl;
    299     }
    300 
    301     if (argc < 0 || options[kOptHelpH].doesOccur || options[kOptHelpQuestionMark].doesOccur) {
    302         cerr
    303             << "Usage: icuzdump [-options] [zoneid1 zoneid2 ...]" << endl
    304             << endl
    305             << "\tDump all offset transitions for the specified zones." << endl
    306             << 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;
    318     }
    319 
    320     bAll = options[kOptAllZones].doesOccur;
    321 
    322     if (options[kOptDestDir].doesOccur) {
    323         dir = options[kOptDestDir].value;
    324     }
    325 
    326     if (options[kOptLineSep].doesOccur) {
    327         if (strcmp(options[kOptLineSep].value, "CR") == 0) {
    328             linesep = "\r";
    329         } else if (strcmp(options[kOptLineSep].value, "CRLF") == 0) {
    330             linesep = "\r\n";
    331         } else if (strcmp(options[kOptLineSep].value, "LF") == 0) {
    332             linesep = "\n";
    333         }
    334     }
    335 
    336     if (options[kOptCutover].doesOccur) {
    337         char* comma = (char*)strchr(options[kOptCutover].value, ',');
    338         if (comma == NULL) {
    339             high = atoi(options[kOptCutover].value);
    340         } else {
    341             *comma = 0;
    342             low = atoi(options[kOptCutover].value);
    343             high = atoi(comma + 1);
    344         }
    345     }
    346 
    347     ICUZDump dumper;
    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);
    353     }
    354 
    355     ZoneIterator* zit;
    356     if (bAll) {
    357         zit = new ZoneIterator(TRUE);
    358     } else {
    359         if (argc <= 1) {
    360             zit = new ZoneIterator();
    361         } else {
    362             zit = new ZoneIterator((const char**)&argv[1], argc - 1);
    363         }
    364     }
    365 
    366     UnicodeString id;
    367     if (dir != NULL) {
    368         // file output
    369         ostringstream path;
    370         ios::openmode mode = ios::out;
    371         if (linesep != NULL) {
    372             mode |= ios::binary;
    373         }
    374         for (;;) {
    375             TimeZone* tz = zit->next();
    376             if (tz == NULL) {
    377                 break;
    378             }
    379             dumper.setTimeZone(tz);
    380             tz->getID(id);
    381 
    382             // target file path
    383             path.str("");
    384             path << dir << U_FILE_SEP_CHAR;
    385             id = id.findAndReplace("/", "-");
    386             path << id;
    387 
    388             ofstream* fout = new ofstream(path.str().c_str(), mode);
    389             if (fout->fail()) {
    390                 cerr << "Cannot open file " << path << endl;
    391                 delete fout;
    392                 delete tz;
    393                 break;
    394             }
    395 
    396             dumper.dump(*fout);
    397             fout->close();
    398             delete fout;
    399             delete tz;
    400         }
    401 
    402     } else {
    403         // stdout
    404         UBool bFirst = TRUE;
    405         for (;;) {
    406             TimeZone* tz = zit->next();
    407             if (tz == NULL) {
    408                 break;
    409             }
    410             dumper.setTimeZone(tz);
    411             tz->getID(id);
    412             if (bFirst) {
    413                 bFirst = FALSE;
    414             } else {
    415                 cout << endl;
    416             }
    417             cout << "ZONE: " << id << endl;
    418             dumper.dump(cout);
    419             delete tz;
    420         }
    421     }
    422     delete zit;
    423 }
    424