Home | History | Annotate | Download | only in tzcode
      1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4 *******************************************************************************
      5 *
      6 *   Copyright (C) 2007-2016, International Business Machines
      7 *   Corporation and others.  All Rights Reserved.
      8 *
      9 *******************************************************************************
     10 *   file name:  icuzdump.cpp
     11 *   encoding:   US-ASCII
     12 *   tab size:   8 (not used)
     13 *   indentation:4
     14 *
     15 *   created on: 2007-04-02
     16 *   created by: Yoshito Umaoka
     17 *
     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.
     21 */
     22 
     23 #include <cstdlib>
     24 #include <cstring>
     25 #include <fstream>
     26 #include <sstream>
     27 #include <iostream>
     28 
     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"
     38 
     39 #include "cmemory.h"
     40 #include "uoptions.h"
     41 
     42 using namespace std;
     43 
     44 class DumpFormatter {
     45 public:
     46     DumpFormatter() {
     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);
     52     }
     53     ~DumpFormatter() {
     54     }
     55 
     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);
     60         if (offset < 0) {
     61             appendTo += "-";
     62             offset = -offset;
     63         } else {
     64             appendTo += "+";
     65         }
     66 
     67         int32_t hour, min, sec;
     68 
     69         offset /= 1000;
     70         sec = offset % 60;
     71         offset = (offset - sec) / 60;
     72         min = offset % 60;
     73         hour = offset / 60;
     74 
     75         decf->format(hour, appendTo);
     76         decf->format(min, appendTo);
     77         decf->format(sec, appendTo);
     78         appendTo += "[DST=";
     79         if (isDst) {
     80             appendTo += "1";
     81         } else {
     82             appendTo += "0";
     83         }
     84         appendTo += "]";
     85         return appendTo;
     86     }
     87 private:
     88     SimpleTimeZone*     stz;
     89     SimpleDateFormat*   sdf;
     90     DecimalFormat*      decf;
     91 };
     92 
     93 class ICUZDump {
     94 public:
     95     ICUZDump() {
     96         formatter = new DumpFormatter();
     97         loyear = 1902;
     98         hiyear = 2050;
     99         tick = 1000;
    100         linesep = NULL;
    101     }
    102 
    103     ~ICUZDump() {
    104     }
    105 
    106     void setLowYear(int32_t lo) {
    107         loyear = lo;
    108     }
    109 
    110     void setHighYear(int32_t hi) {
    111         hiyear = hi;
    112     }
    113 
    114     void setTick(int32_t t) {
    115         tick = t;
    116     }
    117 
    118     void setTimeZone(TimeZone* tz) {
    119         timezone = tz;
    120     }
    121 
    122     void setDumpFormatter(DumpFormatter* fmt) {
    123         formatter = fmt;
    124     }
    125 
    126     void setLineSeparator(const char* sep) {
    127         linesep = sep;
    128     }
    129 
    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;
    135         UnicodeString str;
    136 
    137         getCutOverTimes(cutlo, cuthi);
    138         t = cutlo;
    139         timezone->getOffset(t, FALSE, rawOffset, dstOffset, status);
    140         while (t < cuthi) {
    141             int32_t newRawOffset, newDstOffset;
    142             UDate newt = t + SEARCH_INCREMENT;
    143 
    144             timezone->getOffset(newt, FALSE, newRawOffset, newDstOffset, status);
    145 
    146             UBool bSameOffset = (rawOffset + dstOffset) == (newRawOffset + newDstOffset);
    147             UBool bSameDst = ((dstOffset != 0) && (newDstOffset != 0)) || ((dstOffset == 0) && (newDstOffset == 0));
    148 
    149             if (!bSameOffset || !bSameDst) {
    150                 // find the boundary
    151                 UDate lot = t;
    152                 UDate hit = newt;
    153                 while (true) {
    154                     int32_t diff = (int32_t)(hit - lot);
    155                     if (diff <= tick) {
    156                         break;
    157                     }
    158                     UDate medt = lot + ((diff / 2) / tick) * tick;
    159                     int32_t medRawOffset, medDstOffset;
    160                     timezone->getOffset(medt, FALSE, medRawOffset, medDstOffset, status);
    161 
    162                     bSameOffset = (rawOffset + dstOffset) == (medRawOffset + medDstOffset);
    163                     bSameDst = ((dstOffset != 0) && (medDstOffset != 0)) || ((dstOffset == 0) && (medDstOffset == 0));
    164 
    165                     if (!bSameOffset || !bSameDst) {
    166                         hit = medt;
    167                     } else {
    168                         lot = medt;
    169                     }
    170                 }
    171                 // write out the boundary
    172                 str.remove();
    173                 formatter->format(lot, rawOffset + dstOffset, (dstOffset == 0 ? FALSE : TRUE), str);
    174                 out << str << " > ";
    175                 str.remove();
    176                 formatter->format(hit, newRawOffset + newDstOffset, (newDstOffset == 0 ? FALSE : TRUE), str);
    177                 out << str;
    178                 if (linesep != NULL) {
    179                     out << linesep;
    180                 } else {
    181                     out << endl;
    182                 }
    183 
    184                 rawOffset = newRawOffset;
    185                 dstOffset = newDstOffset;
    186             }
    187             t = newt;
    188         }
    189     }
    190 
    191 private:
    192     void getCutOverTimes(UDate& lo, UDate& hi) {
    193         UErrorCode status = U_ZERO_ERROR;
    194         GregorianCalendar* gcal = new GregorianCalendar(timezone, Locale::getEnglish(), status);
    195         gcal->clear();
    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);
    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, UPRV_LENGTHOF(options), 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.str() << 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