tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

icuzdump.cpp (12157B)


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