pike.git / lib / modules / Calendar.pmod / Timezone.pmod

version» Context lines:

pike.git/lib/modules/Calendar.pmod/Timezone.pmod:31:   //!   //! There are about 504 timezones with 127 different daylight   //! saving rules. Most of them historic.   //!   //! The timezone information comes from   //! <a href=ftp://elsie.nci.nih.gov/pub/>ftp://elsie.nci.nih.gov/pub/</a>   //! and are not made up from scratch. Timezone bugs may be reported   //! to the timezone mailing list,   //! <a href=mailto:tz@elsie.nci.nih.gov>tz@elsie.nci.nih.gov</a>,   //! preferable with a <tt>cc</tt> to - //! <a href=mailto:mirar@mirar.org>mirar@mirar.org</a>. /Mirar + //! <a href=mailto:mirar+pike@mirar.org>mirar+pike@mirar.org</a>. /Mirar   //! - //! see also: TZnames, Ruleset.Timezone + //! see also: TZnames    - //! constant Ruleset.Timezone locale + //! constant Rule.Timezone locale   //! This contains the local timezone, found from   //! various parts of the system, if possible.    - //! constant Ruleset.Timezone localtime + //! constant Rule.Timezone localtime   //! This is a special timezone, that uses <ref>localtime</ref>()   //! and <ref>tzname</ref>   //! to find out what current offset and timezone string to use.   //!   //! <ref>locale</ref> uses this if there is no other   //! way of finding a better timezone to use.   //!   //! This timezone is limited by <ref>localtime</ref> and   //! libc to the range of <tt>time_t</tt>,   //! which is a MAXINT on most systems - 13 Dec 1901 20:45:52   //! to 19 Jan 2038 3:14:07, UTC.      #pike __REAL_VERSION__    - import "."; -  +    // ----------------------------------------------------------------   // static    - Ruleset.Timezone UTC=Ruleset.Timezone(0,"UTC"); + Calendar.Rule.Timezone UTC=Calendar.Rule.Timezone(0,"UTC");      // ----------------------------------------------------------------   // from the system    - Ruleset.Timezone locale=0; + Calendar.Rule.Timezone locale=0;    - static function(:Ruleset.Timezone) _locale() + protected function(:Calendar.Rule.Timezone) _locale()   { -  Ruleset.Timezone tz; +  Calendar.Rule.Timezone tz;      // try to get the real local time settings      #if 1    string s;       if ( (s=getenv("TZ")) )    {    tz=`[](s);    if (tz) return tz;    }    - // Linux RedHat -  if ( (s=Stdio.read_bytes("/etc/sysconfig/clock")) ) +  // Mapping from file name to variable name. +  foreach(([ "/etc/localtime":0, // Linux & BSDs +  "/etc/sysconfig/clock":"ZONE", // Linux RedHat +  "/etc/TIMEZONE":"TZ", // Solaris +  "/etc/conf.d/clock":"TIMEZONE", // Linux Gentoo +  ]); string fname; string var_name) { +  catch { +  if (Stdio.is_file(fname) && (s = Stdio.read_bytes(fname))) { +  if (!var_name) { +  if (tz = tz_from_tzfile(s)) return tz; +  } else { +  foreach(s/"\n", string line) { +  line = (line/"#")[0]; // Strip comments. +  if (sscanf(s, "%*s" + var_name + "=%s", s) == 2) { +  sscanf(s, "\"%s\"", s); // Strip quotes (if any). +  if (tz = `[](s))    { -  sscanf(s,"%*sZONE=\"%s\"",s); -  tz=`[](s); - // werror("=>%O\n",tz); -  if (tz) return tz; +  // werror("=>%O\n",tz); +  return tz;    } -  +  } +  } +  } +  } +  }; +  }    -  + #ifdef __NT__ +  // FIXME: Consider getting timezone info from the registry. +  // HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet +  // \\Control\\TimeZoneInformation: +  // ActiveTimeBias REG_DWORD 0xffffffc4 +  // Bias REG_DWORD 0xffffffc4 +  // DaylightBias REG_DWORD 0xffffffc4 +  // DaylightName REG_SZ "W. Europe Daylight Time" +  // DaylightStart REG_BINARY 00 00 03 00 05 00 02 00 +  // 00 00 00 00 00 00 00 00 +  // StandardBias REG_DWORD 0x00000000 +  // StandardName REG_SZ "W. Europe Standard Time" +  // StandardStart REG_BINARY 00 00 0a 00 05 00 03 00 +  // 00 00 00 00 00 00 00 00 + #endif /* __NT__ */ +    #if constant(tzname)    mapping l=predef::localtime(time());    array(string) tzn=tzname();       tz=::`[](tzn[0]);    if (tz && l->timezone==tz->raw_utc_offset()) return tz;   #endif   #endif       // run an expert system try on the localtime() rules,    // default to localtime()    return expert(localtime());   };    -  + Calendar.Rule.Timezone tz_from_tzfile(string tzfile) + { +  array header = array_sscanf(tzfile, "%4s%16s%4c%4c%4c%4c%4c%4c"); +  if( sizeof(header)<8 ) return 0; +  array zoneabbr = tzfile[44+header[5]*4+header[5]+header[6]*6..44+header[5]*4+header[5]+header[6]*6+header[7]-1]/"\0"; +  if(!expert_tzn) +  expert_tzn=master()->resolv("Calendar")["TZnames"]; +  +  array validzones = `&(@values(expert_tzn->abbr2zones & mkmultiset(zoneabbr))); +  if(sizeof(validzones) == 1) +  return `[](validzones[0]); + } +  +    // ----------------------------------------------------------------   // expert system to pick out the correct timezone    - static Ruleset.Timezone timezone_expert_rec(Ruleset.Timezone try, + protected Calendar.Rule.Timezone timezone_expert_rec(Calendar.Rule.Timezone try,    mapping|array|string tree,    object cal)   {    int t=tree->test,uo;    if (t<0)    {    if (catch { uo=cal->Second(t)->set_timezone(try)    ->utc_offset(); })    return timezone_select(try,timezone_collect(tree),cal);    }
pike.git/lib/modules/Calendar.pmod/Timezone.pmod:134:    if (mappingp(tree))    return timezone_expert_rec(try,tree,cal);       if (arrayp(tree))    return timezone_select(try,tree,cal);      // stringp    return `[](tree);   }    - static Ruleset.Timezone timezone_select(Ruleset.Timezone try, + protected Calendar.Rule.Timezone timezone_select(Calendar.Rule.Timezone try,    array tree,    object cal)   {   #if constant(tzname)    array res=({});    multiset names=mkmultiset(tzname());    function f=cal->Second(970416317)->set_timezone;    foreach (tree,string tzn)    if (names[f(tzn)->tzname()]) res+=({tzn});    if (!sizeof(res)) return try;    tree=res;   #endif   // pick one    return `[](tree[0]);   }    - static array timezone_collect(string|mapping|array tree) + protected array timezone_collect(string|mapping|array tree)   {    if (arrayp(tree)) return tree;    else if (stringp(tree)) return ({tree});    else return `+(@map(values(tree-({"test"})),timezone_collect));   }    - Ruleset.Timezone expert(Ruleset.Timezone try) + protected object expert_cal, expert_tzn; +  + Calendar.Rule.Timezone expert(Calendar.Rule.Timezone try)   { -  object cal=master()->resolv("Calendar")["ISO_UTC"]; -  return timezone_expert_rec(try,TZnames.timezone_expert_tree,cal); +  if(!expert_cal) +  expert_cal=master()->resolv("Calendar")["ISO_UTC"]; +  if(!expert_tzn) +  expert_tzn=master()->resolv("Calendar")["TZnames"]; +  return timezone_expert_rec(try, expert_tzn->timezone_expert_tree, +  expert_cal);   }      // ----------------------------------------------------------------      class localtime   {    constant is_timezone=1;    constant is_dst_timezone=1;      #if constant(tzname) -  static array(string) names=tzname(); +  protected array(string) names=tzname();   #endif       string name="local";      // is (midnight) this julian day dst?    array tz_jd(int jd)    {    return tz_ux((jd-2440588)*86400);    }    -  +  // Workaround for predef::localtime() on WIN32 and others +  // throwing errors for times before 1970. This interferes +  // with the timezone expert system. +  protected mapping(string:int) paranoia_localtime(int ux) +  { +  if (ux<-0x80000000 || ux>0x7fffffff) +  error("Time is out of range for Timezone.localtime()\n"); +  mixed err = catch { return predef::localtime(ux); }; +  if ((ux < 0) && (ux > -86400*2)) { +  // Try post-adjustment... +  // The code below actually handles up to 30 days, but... +  mapping(string:int) res = predef::localtime(0); +  if ((res->sec += ux) < 0) { +  int delta = res->sec/60; +  res->sec -= delta*60; +  if ((res->min += delta) < 0) { +  delta = res->min/60; +  res->min -= delta*60; +  if ((res->hour += delta) < 0) { +  delta = res->hour/24; +  res->hour -= delta*24; +  if ((res->mday += delta) < 1) { +  // FIXME: Assertions? +  res->yday = 364 + res->mday; +  res->wday = (3 + res->mday) % 7; +  res->mday += 31; +  res->mon = 11; +  res->year -= 1; +  } +  } +  } +  } +  return res; +  } +  throw(err); +  } +    // is this unixtime (utc) dst?    array tz_ux(int ux)    {    if (ux<-0x80000000 || ux>0x7fffffff)    error("Time is out of range for Timezone.localtime()\n");       int z0=ux%86400; -  mapping ll=predef::localtime(ux); +  mapping ll = paranoia_localtime(ux);    int zl=ll->hour*3600+ll->min*60+ll->sec;    int tz=z0-zl;    if (tz>86400/2) tz-=86400;    else if (tz<-86400/2) tz+=86400;   #if constant(tzname)    return ({tz,names[ll->isdst]});   #else    return ({tz,"local"});   #endif    }    -  string _sprintf(int t) { return (t=='O')?"Timezone.localtime()":0; } +  protected string _sprintf(int t) +  { +  return t=='O' && "Timezone.localtime()"; +  }       int raw_utc_offset(); // N/A but needed for interface   }      // ----------------------------------------------------------------   // magic timezones      class Timezone_Encapsule   { -  Ruleset.Timezone what; +  Calendar.Rule.Timezone what;       constant is_timezone=1;    constant is_dst_timezone=1; // ask me    -  static string extra_name; -  static int extra_offset; +  protected string extra_name; +  protected int extra_offset;    string name;    -  static void create(Ruleset.Timezone enc,string name,int off) +  protected void create(Calendar.Rule.Timezone enc,string name,int off)    {    what=enc;    extra_name=name;    extra_offset=off;    name=enc->name+extra_name;    }       array(int) tz_ux(int unixtime)    {    array z=what->tz_ux(unixtime);    return ({z[0]+extra_offset,z[1]+extra_name});    }       array(int) tz_jd(int julian_day)    {    array z=what->tz_jd(julian_day);    return ({z[0]+extra_offset,z[1]+extra_name});    }    -  string _sprintf(int t) +  protected string _sprintf(int t)    { -  return (t=='O')?sprintf("%O%s",what,extra_name):0; +  return t=='O' && sprintf("%O%s",what,extra_name || "");    }       int raw_utc_offset() { return what->raw_utc_offset()+extra_offset; }   }    - static private Ruleset.Timezone _make_new_timezone_i(string tz,int plusminus) + protected private Calendar.Rule.Timezone _make_new_timezone_i(string tz, +  int plusminus)   { -  object(Ruleset.Timezone) z=`[](tz); -  if (!z) return ([])[0]; +  Calendar.Rule.Timezone z=`[](tz); +  if (!z) return UNDEFINED;    return make_new_timezone(z,plusminus);   }      // internal, don't use this outside calendar module - Ruleset.Timezone make_new_timezone(Ruleset.Timezone z,int plusminus) + Calendar.Rule.Timezone make_new_timezone(Calendar.Rule.Timezone z,int plusminus)   {    if (plusminus>14*3600 || plusminus<-14*3600)    error("difference out of range -14..14 h\n");    if (plusminus==0)    return z;    string s;    if (plusminus%60)    s=sprintf("%+d:%02d:%02d",plusminus/3600,plusminus/60%60,plusminus%60);    else if (plusminus/60%60)    s=sprintf("%+d:%02d",plusminus/3600,plusminus/60%60);    else    s=sprintf("%+d",plusminus/3600);       return Timezone_Encapsule(z,s,-plusminus);   }    - static private constant _military_tz= + protected private constant _military_tz=   ([ "Y":"UTC-12", "X":"UTC-11", "W":"UTC-10", "V":"UTC-9", "U":"UTC-8",    "T":"UTC-7", "S":"UTC-6", "R":"UTC-5", "Q":"UTC-4", "P":"UTC-3",    "O":"UTC-2", "N":"UTC-1", "Z":"UTC", "A":"UTC+1", "B":"UTC+2",    "C":"UTC+3", "D":"UTC+4", "E":"UTC+5", "F":"UTC+6", "G":"UTC+7",    "H":"UTC+8", "I":"UTC+9", "K":"UTC+10", "L":"UTC+11", "M":"UTC+12",    "J":"locale" ]);    - static object runtime_timezone_compiler=0; + protected object runtime_timezone_compiler=0;      // internal, don't use this outside calendar module   int decode_timeskew(string w)   {    float f;    int a,b,c;    string s="";    int neg=1;       if (sscanf(w,"-%s",w)) neg=-1;       if (sscanf(w,"%d:%d:%d",a,b,c)==3)    return neg*(a*3600+b*60+c); -  else if (sscanf(w,"%d:%d",a,b,c)==2) +  else if (sscanf(w,"%d:%d",a,b)==2)    return neg*(a*3600+b*60);    sscanf(w,"%d%s",a,s);    if (s!="") { sscanf(w,"%f",f); if (f!=(float)a) return neg*(int)(f*3600); }    return neg*a*3600; // ignore litter   }    - static private Ruleset.Timezone _magic_timezone(string tz) + protected private Calendar.Rule.Timezone _magic_timezone(string tz)   {    string z,w;       if (!runtime_timezone_compiler)    runtime_timezone_compiler=Runtime_timezone_compiler();      // int t=time(1);   // float t1=time(t);   // runtime_timezone_compiler->find_rule("EU");    object p=runtime_timezone_compiler->find_zone(tz);
pike.git/lib/modules/Calendar.pmod/Timezone.pmod:327:       if (sscanf(tz,"%s+%s",z,w)==2 && z!="")    return _make_new_timezone_i(z,decode_timeskew(w));    if (sscanf(tz,"%s-%s",z,w)==2 && z!="" && z!="+")    return _make_new_timezone_i(z,-decode_timeskew(w));    if ((z=_military_tz[tz])) return `[](z);       if (sscanf(tz,"%[-+]%[-+0-9]",string a,string b)==2)    if ((<"-","+">)[a])    { -  switch (strlen(b)) +  switch (sizeof(b))    {    case 2: return _magic_timezone("UTC"+a+b[..1]);    case 4: return _magic_timezone("UTC"+a+b[..1]+":"+b[2..]);    case 6:    return _magic_timezone("UTC"+a+b[..1]+":"+b[2..3]+":"+b[4..]);    }    }    else if (a=="+-") return _magic_timezone("-0"+b);       return ::`[](replace(tz,"-/+"/1,"__p"/1));   }    - Ruleset.Timezone `[](string tz) + Calendar.Rule.Timezone `[](string tz)   {    mixed p=::`[](tz);    if (!p && tz=="locale") return locale=_locale();       if ((<"make_new_timezone","decode_timeskew">)[tz]) return p;       if (!p) p=_magic_timezone(tz);    if (programp(p) || functionp(p)) return p();    return p;   }
pike.git/lib/modules/Calendar.pmod/Timezone.pmod:382:   #define complain error      #define FIXID(s) replace(s,"-+/"/1,"_minus_,_plus_,_slash_"/",")   #define UNFIXID(s) replace(s,"_minus_,_plus_,_slash_"/",","-+/"/1)       int parse_offset(string t)    {    int h,m,s;    string res;    -  if (t=="0") return 0; +  if (t=="0" || t=="-") return 0;       res="";    if (sscanf(t,"-%d:%d:%d%s",h,m,s,res)&&res=="")    return -(h*3600+m*60+s);    res="";    if (sscanf(t,"-%d:%d%s",h,m,res)&&res=="")    return -(h*3600+m*60);    res="";    if (sscanf(t,"%d:%d:%d%s",h,m,s,res)&&res=="")    return h*3600+m*60+s;
pike.git/lib/modules/Calendar.pmod/Timezone.pmod:427:       class Shift    {    string dayrule;    int time;    string timetype;    int offset;    string s;    string comment;    -  void create(array a) +  protected void create(array a)    {    switch (sizeof(a))    {    case 5:    dayrule=think_day(a[0],a[1]);    comment=a[0]+" "+a[1]; -  [time,timetype]=parse_tod(a[2]); +  // NB: The Morocco rule for 2011-07-31 has 0 as AT, +  // while all others have 0:00. +  [time,timetype] = parse_tod(a[2]) || ({ 0, "" });    switch (timetype)    {    case "": timetype="w"; break;    case "s": case "u": case "w": break;    default: complain("unknown time of day type %O\n",timetype);    }    offset=parse_offset(a[3]);    s=(a[4]=="-")?"":a[4];    break;    case 6:    [dayrule,comment,time,timetype,offset,s]=a;    break;    default:    error("illegal size of a\n");    }    }    -  string _sprintf(int t) +  protected string _sprintf(int t)    { -  return (t=='O')? +  return t=='O' &&    sprintf("Shift(%s,%d%s,%+d,%O)", -  dayrule,time,timetype,offset,s): -  0; +  dayrule || "<unset>", time, +  timetype || "<unset>", offset, s);    }       int `==(Shift other)    {    return ( dayrule==other->dayrule &&    time==other->time &&    timetype==other->timetype &&    offset==other->offset &&    s==other->s );    } -  function(Shift:int) __equal=`==; +  int __equal (Shift other) {return `== (other);}       constant wday=(["Mon":1,"Tue":2,"Wed":3,"Thu":4,"Fri":5,"Sat":6,"Sun":7]);    constant vmonth=(<"Jan","Feb","Mar","Apr","May","Jun",    "Jul","Aug","Sep","Nov","Dec">);       string think_day(string mon,string rule)    {    int d;    string ds;   
pike.git/lib/modules/Calendar.pmod/Timezone.pmod:533:       return "LDAYL("+(nleapy->month(mon)->day(d)->year_day())+    ","+wd+")";    }    else    complain("unknown rule method %O\n",rule);    }       Shift|array ``+(array|Shift s)    { -  if (!s) return this_object(); +  if (!s) return this;    if (!arrayp(s)) s=({s}); -  return s+({this_object()}); +  return s+({this});    }       int ldayl_is_fix_l(int d1,int wd,int d2,int yn1,int yn2)    {    return 0;    // object y1=Year(yn1);    // object y2=Year(yn2);    // int yjd,leap;       // yjd=y1->julian_day();
pike.git/lib/modules/Calendar.pmod/Timezone.pmod:560:    // leap=y2->leap_year();    // int d2=FIX_L(d2);       // return d1==d2;    }       Shift try_promote(Shift t,int y0,int y1)    {    // this is year y0    // t is year y1 -  if (t==this_object()) return t; // same! +  if (t==this) return t; // same!    if (t->time!=time ||    t->timetype!=timetype ||    t->offset!=offset ||    t->s!=s) return 0; // no chance       int a,b,c;    if (sscanf(dayrule,"LDAYL(%d,%d)",a,b)==2 &&    sscanf(t->dayrule,"FIX_L(%d)",c)==1)    if (ldayl_is_fix_l(a,b,c,y0,y1)) -  return this_object(); // ldayl +  return this; // ldayl    else    return 0; // no    if (sscanf(t->dayrule,"LDAYL(%d,%d)",a,b)==2 &&    sscanf(dayrule,"FIX_L(%d)",c)==1)    if (ldayl_is_fix_l(a,b,c,y1,y0))    return t; // ldayl    else    return 0; // no       return 0;
pike.git/lib/modules/Calendar.pmod/Timezone.pmod:613:    int d,w;    if (sscanf(r,"FIX_L(%d)",d)) r=sprintf("FIXED(%d)",d+l);    else if (sscanf(r,"LDAYL(%d,%d)",d,w)==2)    r=sprintf("LDAY (%d,%d)",d+l,w);    }    return sprintf("({%-12s,%-10s,%-5d,%-6O}), %s",    r,t,offset,s,comment!=""?"// "+comment:"");    }    }    -  class Rule +  object compile_handler = class { +  mapping(string:mixed) get_default_module() { +  return constants; +  } +  +  mapping constants = all_constants() + +  (["TZrules":Dummymodule(find_rule), +  "TZRules":TZRules, +  "TZHistory":TZHistory, +  "Rule":Calendar.Rule, +  "ZEROSHIFT":({0,0,0,""}) +  ]); +  +  }(); +  +  class Rule(string id)    { -  string id; +  array(string) lines = ({});    -  +  void add_line(string line) +  { +  lines += ({ line }); +  } +  }; +  +  class RuleCompiler(string id, array(string) lines) +  {    mapping rules=([]);       int amt=0;    -  void create(string _id) { id=_id; } -  +     void add(string line)    {    array a= array_sscanf(line, replace("%s %s %s %s %s %s %s %[^\t ]",    " ","%*[ \t]"));       if (sizeof(a)<8) complain("illegal rule line format\n");       if (!(int)a[0] && a[0]!="min")    complain("unknown year %O\n",a[0]);   
pike.git/lib/modules/Calendar.pmod/Timezone.pmod:680:    array res=({});       if (!r2[NUL_YEAR]) r2[NUL_YEAR]=({last});       int y=min(@indices(rules));    for (;y<=INF_YEAR; y++)    [r2[y],last]=mkperiods(rules[y],last,first);       res+=    ({TZrules_init+ -  "import __Calendar_mkzone;\n" +     " inherit TZRules;\n"    " array(array(string|int)) jd_year_periods(int jd)\n"    " {\n"    " [int y,int yjd,int leap]=gregorian_yjd(jd);\n"    " switch (y)\n"    " {\n"});       array sr=({});       int mn=min(@indices(rules)-({NUL_YEAR}));
pike.git/lib/modules/Calendar.pmod/Timezone.pmod:755:    }       int lastoffset=0;    tr+=({" "*12,"return ({"});    foreach (z,Shift s)    {    tr+=({s->dump(lastoffset,my),"\n"," "*21});    lastoffset=s->offset;    }    tr[-3]=replace(tr[-3],", ","});"); -  sr=tr[..sizeof(tr)-2]+sr; +  sr=tr[..<1]+sr;    }    res+=sr+    ({" }\n"    " }\n"    "\n"    "constant firstyear="+firstyear+";\n"    "constant lastyear="+lastyear+";\n",    "array(string) rule_s=\n"});       multiset tzname=(<>);
pike.git/lib/modules/Calendar.pmod/Timezone.pmod:818:    if (first->s=="?")    foreach (s,Shift d) if (!d->offset) first->s=d->s;       s=({last,@s});       last=Shift( ({"0","",0,"u",    s[-1]->offset,s[-1]->s}) );       return ({s, last});    } +  +  +  program compile() +  { + #ifdef RTTZC_TIMING +  float t1=time(t); + #endif + #ifdef RTTZC_DEBUG +  werror("Compiling rule %s\n", id); + #endif +  +  foreach(lines, string line) add(line); +  +  string c = dump(); +  + #ifdef RTTZC_TIMING +  float td=time(t); +  werror("dump %O: %O\n",rule_name,td-t1); + #endif +  + #ifdef RTTZC_DEBUG +  werror("%s\n",c); + #endif +  +  program p; +  mixed err=catch { p=compile_string(c, 0, compile_handler); }; +  if (err) +  { +  int i=0; +  foreach (c/"\n",string line) write("%2d: %s\n",++i,line); +  error(err);    } -  + #ifdef RTTZC_TIMING +  float t3=time(t); +  werror("compile %O: %O\n",rule_name,t3-td); + #endif +  return rule_cache[id] = p; +  }    -  +  } +     class Zone    {    string id;    -  array rules=({}); +  array(string) lines = ({});    -  void create(string _id) { id=_id; } +  array(string) aliases = ({});    -  +  protected void create(string _id) { +  id=_id; +  aliases = ({ id }); +  } +  +  void add_alias(string zone_alias) +  { +  aliases += ({ zone_alias }); +  } +  +  void add_line(string line) +  { +  lines += ({ line }); +  } +  }; +  +  class ZoneCompiler(string id, array(string) lines, array(string) aliases) +  { +  array rules=({}); +     void add(string line)    {    array a= array_sscanf(line, replace("%s %s %s %s",    " ","%*[ \t]"));    if (sizeof(a)<4)    complain("parse error\n");       a=({parse_offset(a[0]), // offset    a[1], // rule or added offset    a[2], // string
pike.git/lib/modules/Calendar.pmod/Timezone.pmod:861:    else if (sscanf(a[1],"%d:%d:%d",h,m,s)==3) roff=h*3600+m*60+s;    else if (sscanf(a[1],"-%d:%d",h,m)==2) roff=h*3600+m*60;    else if (sscanf(a[1],"%d:%d",h,m)==2) roff=h*3600+m*60;       if (roff==-17) // based on DST rule    return sprintf(    "TZrules.%s(%d,%O)",    FIXID(a[1]),-a[0],a[2]);    else // simple timezone    return sprintf( -  "Ruleset.Timezone(%d,%O)", +  "Rule.Timezone(%d,%O)",    -(roff+a[0]),a[2]);    }       string rule_shift(array a)    {    if (a[3]=="" || a[3][0]=='#') return "forever";       string in=a[3];    sscanf(in,"until %s",in);    sscanf(in,"%s#",in);
pike.git/lib/modules/Calendar.pmod/Timezone.pmod:935:    }    }       string dump()    {    array(string) res=({});       if (!sizeof(rules))    error("no rules for %O\n",id);    -  res+=({"import __Calendar_mkzone;\n"}); -  +     if (sizeof(rules)==1) // simple zone    {    res+=({"object thezone=",rules[0][4],";\n"});    return res*"";    }       mapping rname=([]);    int n=1;       foreach (rules,array a)    if (rname[a[4]]) a[6]=rname[a[4]];    else a[6]=rname[a[4]]="tz"+n++;       res+=({ "inherit TZHistory;\n" -  "Ruleset.Timezone ", +  "Rule.Timezone ",    sort(values(rname))*",",";\n" -  "Ruleset.Timezone whatrule(int ux)\n" +  "Rule.Timezone whatrule(int ux)\n"    "{\n" });       foreach (rules,array a)    {    if (!a[5]) a[5]=rule_shift(a);       string s="";    sscanf(a[3],"%s#%*[ \t]%s",a[3],s);    a[3]="from "+reverse(array_sscanf(reverse(a[3]),"%*[ \t]%s")[0]);    a[7]=s;
pike.git/lib/modules/Calendar.pmod/Timezone.pmod:982:    last=a;    }    if (last[7]!="")    res+=({sprintf(" // %s\n",last[7])});    res+=({sprintf(" return %s || (%s=%s);\n",    last[6],last[6],last[4])+    "}\n"});       multiset tzname=(<>);    foreach (rules,array a) -  if (search(a[2],"%s")==-1) +  if (!has_value(a[2], "%s"))    tzname[a[2]]=1;    else    {    program r=find_rule(a[1]);    foreach (r(0,a[2])->rule_s,string s)    tzname[sprintf(a[2],s)]=1;    }       res+=({"array(string) zone_s=({"+    map((array)tzname,    lambda(string s) { return sprintf("%O",s); })*",",    "});\n",    "array(int) shifts=({"}); -  foreach (rules[..sizeof(rules)-2],array a) +  foreach (rules[..<1],array a)    res+=({a[5]+","});    res+=({"});\n",    sprintf( -  "string _sprintf(int t) { return (t=='O')?" -  "%O:0; }\n" -  "string zoneid=%O;\n","Timezone("+id+")",id)}); +  "protected string _sprintf(int t) { return t=='O' &&" +  "%O; }\n" +  "string zoneid=%O;\n","Rule.Timezone("+id+")",id)});       return res*"";    } -  +  +  object compile() +  { +  string zone_name = id; +  // werror("Compiling zone %O...\n", zone_name); +  + #ifdef RTTZC_TIMING +  float t1=time(t); + #endif + #ifdef RTTZC_DEBUG +  werror("Compiling zone %s\n", zone_name); + #endif +  +  rules = ({}); +  foreach(lines, string line) add(line); +  +  string c=dump(); +  + #ifdef RTTZC_TIMING +  float td=time(t); +  werror("dump %O: %O\n",zone_name,td-t1); +  float td=time(t); + #endif +  + #ifdef RTTZC_DEBUG +  werror("%s\n",c); + #endif +  +  program p; +  mixed err=catch { p=compile_string(c, 0, compile_handler); }; +  if (err) +  { +  int i=0; +  foreach (c/"\n",string line) write("%2d: %s\n",++i,line); +  throw(err);    } -  +  object zo=p(); +  if (zo->thezone) zo=zo->thezone;    -  + #ifdef RTTZC_TIMING +  float t3=time(t); +  werror("compile %O: %O\n",zone_name,t3-td); + #endif +  return zo; +  } +  +  } +     string base_path=combine_path(__FILE__,"../tzdata/");    array files=    ({    "africa",    "antarctica",    "asia",    "australasia",    "backward",    "etcetera",    "europe",    "northamerica",    "southamerica",    "pacificnew",    "systemv",    });    -  mapping zone_cache=([]); -  mapping rule_cache=([]); -  string all_rules=0; +  mapping zone_cache; +  mapping rule_cache; +  string all_rules; +  protected mapping(string:Zone) zones = ([]); +  protected mapping(string:Rule) rules = ([]);       string get_all_rules()    {    return    map(files,    lambda(string fn)    { -  return master()->master_read_file(base_path+fn) || -  (error("Failed to open file %O\n",base_path+fn), ""); +  return (master()->master_read_file(base_path+fn) || +  (error("Failed to open file %O\n",base_path+fn), "")) - "\r";    })*"\n";    }    -  class Dummymodule +  class Dummymodule (function(string:mixed) f)    { -  function(string:mixed) f; +     mixed `[](string s) { return f(s); } -  void create(function(string:mixed) _f) { f=_f; } +     }    -  mapping mkzonemod= -  (["TZrules":Dummymodule(find_rule), -  "TZRules":TZRules, -  "TZHistory":TZHistory, -  "Ruleset":Ruleset, -  "ZEROSHIFT":({0,0,0,""})]); -  +    // #define RTTZC_DEBUG   // #define RTTZC_TIMING    -  object find_zone(string s) +  void parse_all_rules()    { - #ifdef RTTZC_DEBUG -  werror("Searching for zone %O\n",s); - #endif -  if (zone_cache[s]) return zone_cache[s]; -  if (s=="") return ([])[0]; -  +     if (!all_rules) all_rules=get_all_rules(); -  +    #ifdef RTTZC_TIMING -  int t=time(1); +     float t1=time(t);   #endif    -  Zone z=Zone(s); -  int n=0; -  for (;;) -  { -  n=search(all_rules,s,n); - #ifdef RTTZC_DEBUG -  werror("hit at: %O\n",n); - #endif -  if (n==-1) -  return ([])[0]; -  int i=max(n-100,0)-1,j; -  do i=search(all_rules,"\nZone",(j=i)+1); while (i<n && i!=-1); +  mapping(string:Zone) new_zones = ([]); +  mapping(string:Rule) new_rules = ([]);    -  if (j<n && -  sscanf(all_rules[j..j+8000],"\nZone%*[ \t]%[^ \t]%*[ \t]%s\n%s", -  string a,string b,string q)==5 && -  a==s) -  { -  z->add(b); -  foreach (q/"\n",string line) -  { -  if (sscanf(line,"%*[ \t]%[-0-9]%s",a,b)==3 && strlen(a)) -  z->add(a+b); -  else if (sscanf(line,"%*[ ]#%*s")<2) -  break; // end of zone +  mapping(string:string) zone_aliases = ([]); +  +  Zone current_zone; +  foreach(all_rules/"\n", string line) { +  line = (line/"#")[0]; +  if ((line == "") || (line == " ")) continue; +  +  if (has_prefix(line, "Rule")) { +  current_zone = 0; +  +  string rule_name, interval; +  if (sscanf(line, "Rule%*[ \t]%[^ \t]%*[ \t]%s", +  rule_name, interval) == 4) { +  Rule r = new_rules[rule_name]; +  if (!r) { +  r = new_rules[rule_name] = Rule(rule_name);    } -  break; +  r->add_line(interval); +  } else { +  werror("Failed to parse directive %O.\n", line);    } -  i=max(n-100,0)-1; -  do i=search(all_rules,"\nLink",(j=i)+1); while (i<n && i!=-1); -  if (j<n && -  sscanf(all_rules[j..j+100],"\nLink%*[ \t]%[^ \t]%*[ \t]%[^ \t\n]", -  string a,string b)==4 && -  b==s) -  return find_zone(a); -  n++; +  } else if (has_prefix(line, "Zone")) { +  string zone_name, zone_info; +  if (sscanf(line, "Zone%*[ \t]%[^ \t]%*[ \t]%s", +  zone_name, zone_info) == 4) { +  // werror("Creating zone %O.\n", zone_name); +  Zone z = current_zone = new_zones[zone_name] = Zone(zone_name); +  z->add_line(zone_info); +  } else { +  werror("Failed to parse directive %O.\n", line);    } -  +  } else if (has_prefix(line, "Link")) { +  string zone_name, zone_alias; +  if (sscanf(line, "Link%*[ \t]%[^ \t]%*[ \t]%[^ \t]", +  zone_name, zone_alias) == 4) { +  Zone z = zones[zone_name]; +  if (z) { +  z->add_alias(zone_alias); +  new_zones[zone_alias] = z; +  } else { +  // werror("Deferred alias: %O ==> %O.\n", zone_alias, zone_name); +  zone_aliases[zone_alias] = zone_name; +  } +  } else { +  werror("Failed to parse directive %O.\n", line); +  } +  } else if (current_zone) { +  string prefix, suffix; +  if ((sscanf(line, "%*[ \t]%[-0-9]%s", prefix, suffix) == 3) && +  sizeof(prefix)) { +  current_zone->add_line(prefix + suffix); +  } +  } else { +  // werror("Skipping line %O.\n", line); +  } +  }   #ifdef RTTZC_TIMING -  float t2=time(t); -  werror("find %O: %O\n",s,t2-t1); - #endif -  -  string c=z->dump(); -  - #ifdef RTTZC_TIMING +     float td=time(t); -  werror("dump %O: %O\n",s,td-t2); -  float td=time(t); +  werror("parsing: %O\n",td-t1);   #endif    - #ifdef RTTZC_DEBUG -  werror("%s\n",c); - #endif -  -  add_constant("__Calendar_mkzone",mkzonemod); -  -  program p; -  mixed err=catch { p=compile_string(c); }; -  if (err) -  { -  int i=0; -  foreach (c/"\n",string line) write("%2d: %s\n",++i,line); -  throw(err); +  // Fixup the zone aliases. +  foreach(zone_aliases; string zone_alias; string zone_name) { +  Zone z; +  if ((z = new_zones[zone_name])) { +  z->add_alias(zone_alias); +  new_zones[zone_alias] = z; +  } else { +  werror("Zone %O is a link to a nonexistant zone %O.\n", +  zone_alias, zone_name);    } -  object zo=p(); -  if (zo->thezone) zo=zo->thezone; +  }    - #ifdef RTTZC_TIMING -  float t3=time(t); -  werror("compile %O: %O\n",s,t3-td); - #endif +  zones = new_zones; +  rules = new_rules;    -  return zone_cache[s]=zo; +  rule_cache = ([]); +  zone_cache = ([]);    }    - // #define RTTZC_TIMING -  -  program find_rule(string s) +  object find_zone(string s)    { -  s=UNFIXID(s); -  if (rule_cache[s]) return rule_cache[s]; +    #ifdef RTTZC_DEBUG -  werror("Searching for rule %O\n",s); +  werror("Searching for zone %O\n",s);   #endif -  -  if (!all_rules) all_rules=get_all_rules(); -  - #ifdef RTTZC_TIMING -  int t=time(1); -  float t1=time(t); - #endif -  -  Rule r=Rule(s); -  int n=0; -  for (;;) -  { -  n=search(all_rules,s,n); - #ifdef RTTZC_DEBUG -  werror("hit at: %O\n",n); - #endif -  if (n==-1) -  return ([])[0]; -  -  int i=max(n-100,0)-1,j; -  do i=search(all_rules,"\nRule",(j=i)+1); while (i<n && i!=-1); -  -  if (j<n && -  sscanf(all_rules[j..j+8000],"\nRule%*[ \t]%[^ \t]%*[ \t]%s\n%s", -  string a,string b,string q)==5 && a==s) -  { -  r->add(b); - #ifdef RTTZC_TIMING -  float tf=time(t); -  werror("find %O at: %O\n",s,tf-t1); -  float tq=time(t); - #endif -  foreach (q/"\n",string line) -  if (sscanf(line,"Rule%*[ \t]%[^ \t]%*[ \t]%s",a,b)==4 && -  a==s) -  r->add(b); -  else if (sscanf(line,"%*[ ]#%*s")<2) -  break; // end of zone - #ifdef RTTZC_TIMING -  float tf=time(t); -  werror("load %O: %O\n",s,tf-tq); - #endif -  break; +  if (!zone_cache) parse_all_rules(); +  object ret = zone_cache[s]; +  if (!zero_type(ret)) return ret || UNDEFINED; +  Zone z; +  if (!(z = zones[s])) { +  // Check if it's a known alias. +  // Note: TZnames.abbr2zones is sorted with +  // the most recent user first. +  array(string) a = .TZnames.abbr2zones[s]; +  if (a && sizeof(a)) +  return zone_cache[s] = find_zone(a[0]); +  return UNDEFINED;    } -  n++; +  ret = ZoneCompiler(z->id, z->lines, z->aliases)->compile(); +  foreach(z->aliases, string zone_alias) { +  zone_cache[zone_alias] = ret; +  m_delete(zones, zone_alias);    } -  string c=r->dump(); +  m_delete(zones, s); +  return ret; +  }    - #ifdef RTTZC_DEBUG -  werror("%s\n",c); - #endif - #ifdef RTTZC_TIMING -  float t2=time(t); -  werror("find %O: %O\n",s,t2-t1); -  float t2=time(t); - #endif -  -  add_constant("__Calendar_mkzone",mkzonemod); -  -  program p=compile_string(c); -  - #ifdef RTTZC_TIMING -  float t3=time(t); -  werror("compile %O: %O\n",s,t3-t2); - #endif -  -  return rule_cache[s]=p; +  program find_rule(string s) +  { +  if (!rule_cache) parse_all_rules(); +  s = UNFIXID(s); +  program ret = rule_cache[s]; +  if (ret) return ret; +  Rule r; +  if (!(r = rules[s])) return UNDEFINED; +  ret = rule_cache[s] = RuleCompiler(s, r->lines)->compile(); +  m_delete(rules, s); +  return ret;    }    -  +     int main(int ac,array(string) am)    {    map(am[1..],find_zone);    return 0;    }      // ----------------------------------------------------------------   // Base class for daylight savings and war time rules   // ----------------------------------------------------------------      // ----------------------------------------------------------------   // Base "Timezone with rules" class   // ----------------------------------------------------------------       class TZRules    {    constant is_timezone=1;    constant is_dst_timezone=1; -  static int offset_to_utc; +  protected int offset_to_utc;    string name;    -  static function(string:string) tzformat; -  static array names; +  protected array names;         // ----------------------------------------------------------------   // all rules are based on the gregorian calendar, so   // this is the needed gregorian rule:   // ----------------------------------------------------------------    -  static array gregorian_yjd(int jd) +  protected array gregorian_yjd(int jd)    {    int d=jd-1721426;       int century=(4*d+3)/146097;    int century_jd=(century*146097)/4;    int century_day=d-century_jd;    int century_year=(100*century_day+75)/36525;       int y=century*100+century_year+1;       return    ({    y,    1721426+century_year*365+century_year/4+century_jd,    (!(((y)%4) || (!((y)%100) && ((y)%400))))    });    }    -  static void create(int offset,string _name) +  protected string tzformat (string s)    { -  offset_to_utc=offset; -  name=_name; -  if (search(name,"/")!=-1) +  if (names)    { -  names=name/"/"; -  tzformat=lambda(string s) -  { +     if (s=="") return names[0]; else return names[1]; -  }; +     }    else -  tzformat=lambda(string s) { return sprintf(name,s); }; +  return sprintf(name,s);    }    -  +  protected void create(int offset,string name) +  { +  offset_to_utc=offset; +  this_program::name=name; +  if (has_value(name, "/")) +  { +  names=name/"/"; +  } +  } +    // the Rule:   // which julian day does dst start and end this year? -  static array(array(string|int)) jd_year_periods(int jd); +  protected array(array(string|int)) jd_year_periods(int jd);      // is (midnight) this julian day dst?    array tz_jd(int jd)    {    array(array(string|int)) a=jd_year_periods(jd);       int i=0,n=sizeof(a)-1;    while (i<n)    {    array b=a[i+1];
pike.git/lib/modules/Calendar.pmod/Timezone.pmod:1330:    array b=a[i+1];    if (jd<b[0]-1) break;    if (jd<b[0]+1 &&    ux<(b[0]-2440588)*86400+b[1]) break;    i++;    }       return ({offset_to_utc-a[i][2],tzformat(a[i][3])});    }    -  string _sprintf(int t) { return (t=='O')?"Timezone("+name+")":0; } +  protected string _sprintf(int t) +  { +  return t=='O' && "Rule.Timezone("+name+")"; +  }       int raw_utc_offset() { return offset_to_utc; }    }       class TZHistory    {    constant is_timezone=1;    constant is_dst_timezone=1;      // figure out what timezone to use -  Ruleset.Timezone whatrule(int ux); +  Calendar.Rule.Timezone whatrule(int ux);    -  string name=sprintf("%O",object_program(this_object())); +  string name=sprintf("%O",this_program);       array(int) tz_ux(int ux)    {    // werror("tz_ux %O\n",ux);    // object z=whatrule(ux);    // werror("%O %O\n",z->offset_to_utc,z->name);    // return z->tz_ux(ux);    return whatrule(ux)->tz_ux(ux);    }