dc21d42001-05-05Mirar (Pontus Hagland) //! module Calendar //! submodule Timezone //!
78fd532000-07-12Mirar (Pontus Hagland) //! This module contains all the predefined timezones. //! Index it with whatever timezone you want to use. //!
dc21d42001-05-05Mirar (Pontus Hagland) //! Example: //! <tt>Calendar.Calendar my_cal=
78fd532000-07-12Mirar (Pontus Hagland) //! Calendar.ISO->set_timezone(Calendar.Timezone["Europe/Stockholm"]);
dc21d42001-05-05Mirar (Pontus Hagland) //! </tt>
78fd532000-07-12Mirar (Pontus Hagland) //! //! A simpler way of selecting timezones might be //! to just give the string to
dc21d42001-05-05Mirar (Pontus Hagland) //! <ref to=Ruleset.set_timezone>set_timezone</ref>; //! <ref to=Ruleset.set_timezone>it</ref> indexes by itself:
78fd532000-07-12Mirar (Pontus Hagland) //!
dc21d42001-05-05Mirar (Pontus Hagland) //! <tt>Calendar.Calendar my_cal=
78fd532000-07-12Mirar (Pontus Hagland) //! Calendar.ISO->set_timezone("Europe/Stockholm");
dc21d42001-05-05Mirar (Pontus Hagland) //! </tt>
78fd532000-07-12Mirar (Pontus Hagland) //!
dc21d42001-05-05Mirar (Pontus Hagland) //! note //! Do not confuse this module with <ref>Ruleset.Timezone</ref>,
78fd532000-07-12Mirar (Pontus Hagland) //! which is the base class of a timezone object. //!
dc21d42001-05-05Mirar (Pontus Hagland) //! <tt>"CET"</tt> and some other standard abbreviations work too, //! but not all of them (due to more then one country using them).
78fd532000-07-12Mirar (Pontus Hagland) //!
dc21d42001-05-05Mirar (Pontus Hagland) //! Do not call <ref to=Calendar.Time.set_timezone>set_timezone</ref> //! too often, but remember the result if possible. It might take
78fd532000-07-12Mirar (Pontus Hagland) //! some time to initialize a timezone object. //! //! 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>
dc21d42001-05-05Mirar (Pontus Hagland) //! and are not made up from scratch. Timezone bugs may be reported
78fd532000-07-12Mirar (Pontus Hagland) //! to the timezone mailing list,
dc21d42001-05-05Mirar (Pontus Hagland) //! <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
78fd532000-07-12Mirar (Pontus Hagland) //!
e0b3932003-03-10Martin Nilsson //! see also: TZnames
dc21d42001-05-05Mirar (Pontus Hagland) 
e0b3932003-03-10Martin Nilsson //! constant Rule.Timezone locale
dc21d42001-05-05Mirar (Pontus Hagland) //! This contains the local timezone, found from //! various parts of the system, if possible.
e0b3932003-03-10Martin Nilsson //! constant Rule.Timezone localtime
dc21d42001-05-05Mirar (Pontus Hagland) //! 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.
78fd532000-07-12Mirar (Pontus Hagland) 
a580e12000-09-27Fredrik Hübinette (Hubbe) #pike __REAL_VERSION__
a20af62000-09-26Fredrik Hübinette (Hubbe) 
78fd532000-07-12Mirar (Pontus Hagland) // ----------------------------------------------------------------
c0dbee2000-08-01Mirar (Pontus Hagland) // static
78fd532000-07-12Mirar (Pontus Hagland) 
e0b3932003-03-10Martin Nilsson .Rule.Timezone UTC=.Rule.Timezone(0,"UTC");
78fd532000-07-12Mirar (Pontus Hagland)  // ---------------------------------------------------------------- // from the system
e0b3932003-03-10Martin Nilsson .Rule.Timezone locale=0;
78fd532000-07-12Mirar (Pontus Hagland) 
e0b3932003-03-10Martin Nilsson static function(:.Rule.Timezone) _locale()
78fd532000-07-12Mirar (Pontus Hagland) {
e0b3932003-03-10Martin Nilsson  .Rule.Timezone tz;
78fd532000-07-12Mirar (Pontus Hagland)  // 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")) ) { sscanf(s,"%*sZONE=\"%s\"",s); tz=`[](s); // werror("=>%O\n",tz); if (tz) return tz; } #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
094bc02000-10-01Mirar (Pontus Hagland)  // run an expert system try on the localtime() rules, // default to localtime() return expert(localtime());
78fd532000-07-12Mirar (Pontus Hagland) };
094bc02000-10-01Mirar (Pontus Hagland) // ---------------------------------------------------------------- // expert system to pick out the correct timezone
e0b3932003-03-10Martin Nilsson static .Rule.Timezone timezone_expert_rec(.Rule.Timezone try, mapping|array|string tree, object cal)
094bc02000-10-01Mirar (Pontus Hagland) { 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); } else uo=cal->Second(t)->set_timezone(try)->utc_offset(); if (!(tree=tree[uo])) return try; if (mappingp(tree)) return timezone_expert_rec(try,tree,cal); if (arrayp(tree)) return timezone_select(try,tree,cal); // stringp return `[](tree); }
e0b3932003-03-10Martin Nilsson static .Rule.Timezone timezone_select(.Rule.Timezone try,
094bc02000-10-01Mirar (Pontus Hagland)  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) { if (arrayp(tree)) return tree; else if (stringp(tree)) return ({tree}); else return `+(@map(values(tree-({"test"})),timezone_collect)); }
e0b3932003-03-10Martin Nilsson static object expert_cal, expert_tzn; .Rule.Timezone expert(.Rule.Timezone try)
094bc02000-10-01Mirar (Pontus Hagland) {
e0b3932003-03-10Martin Nilsson  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);
094bc02000-10-01Mirar (Pontus Hagland) } // ----------------------------------------------------------------
78fd532000-07-12Mirar (Pontus Hagland) class localtime { constant is_timezone=1; constant is_dst_timezone=1; #if constant(tzname) static 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); } // 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); 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; } int raw_utc_offset(); // N/A but needed for interface } // ---------------------------------------------------------------- // magic timezones
4999cc2001-01-12Mirar (Pontus Hagland) class Timezone_Encapsule {
e0b3932003-03-10Martin Nilsson  .Rule.Timezone what;
4999cc2001-01-12Mirar (Pontus Hagland)  constant is_timezone=1; constant is_dst_timezone=1; // ask me static string extra_name; static int extra_offset; string name;
e0b3932003-03-10Martin Nilsson  static void create(.Rule.Timezone enc,string name,int off)
4999cc2001-01-12Mirar (Pontus Hagland)  { 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) { return (t=='O')?sprintf("%O%s",what,extra_name):0; } int raw_utc_offset() { return what->raw_utc_offset()+extra_offset; } }
e0b3932003-03-10Martin Nilsson static private .Rule.Timezone _make_new_timezone_i(string tz,int plusminus)
78fd532000-07-12Mirar (Pontus Hagland) {
e0b3932003-03-10Martin Nilsson  .Rule.Timezone z=`[](tz);
f2d7a62002-02-14Martin Nilsson  if (!z) return UNDEFINED;
4999cc2001-01-12Mirar (Pontus Hagland)  return make_new_timezone(z,plusminus); } // internal, don't use this outside calendar module
e0b3932003-03-10Martin Nilsson .Rule.Timezone make_new_timezone(.Rule.Timezone z,int plusminus)
4999cc2001-01-12Mirar (Pontus Hagland) { if (plusminus>14*3600 || plusminus<-14*3600)
78fd532000-07-12Mirar (Pontus Hagland)  error("difference out of range -14..14 h\n");
4999cc2001-01-12Mirar (Pontus Hagland)  if (plusminus==0)
78fd532000-07-12Mirar (Pontus Hagland)  return z;
4999cc2001-01-12Mirar (Pontus Hagland)  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);
963f012001-06-22Mirar (Pontus Hagland)  return Timezone_Encapsule(z,s,-plusminus);
78fd532000-07-12Mirar (Pontus Hagland) } static 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" ]);
4999cc2001-01-12Mirar (Pontus Hagland) static 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) 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 }
c0dbee2000-08-01Mirar (Pontus Hagland) 
e0b3932003-03-10Martin Nilsson static private .Rule.Timezone _magic_timezone(string tz)
78fd532000-07-12Mirar (Pontus Hagland) {
4999cc2001-01-12Mirar (Pontus Hagland)  string z,w;
c0dbee2000-08-01Mirar (Pontus Hagland)  if (!runtime_timezone_compiler) runtime_timezone_compiler=Runtime_timezone_compiler();
8530c52000-08-02Mirar (Pontus Hagland) // int t=time(1); // float t1=time(t); // runtime_timezone_compiler->find_rule("EU");
c0dbee2000-08-01Mirar (Pontus Hagland)  object p=runtime_timezone_compiler->find_zone(tz);
8530c52000-08-02Mirar (Pontus Hagland) // float t2=time(t); // werror("%O\n",t2-t1);
c0dbee2000-08-01Mirar (Pontus Hagland)  if (p) return p;
4999cc2001-01-12Mirar (Pontus Hagland)  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));
78fd532000-07-12Mirar (Pontus Hagland)  if ((z=_military_tz[tz])) return `[](z);
b15ef32000-08-12Mirar (Pontus Hagland) 
4999cc2001-01-12Mirar (Pontus Hagland)  if (sscanf(tz,"%[-+]%[-+0-9]",string a,string b)==2) if ((<"-","+">)[a])
b15ef32000-08-12Mirar (Pontus Hagland)  {
ead9722003-01-20Martin Nilsson  switch (sizeof(b))
4999cc2001-01-12Mirar (Pontus Hagland)  { 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..]); }
b15ef32000-08-12Mirar (Pontus Hagland)  }
4999cc2001-01-12Mirar (Pontus Hagland)  else if (a=="+-") return _magic_timezone("-0"+b);
b15ef32000-08-12Mirar (Pontus Hagland) 
78fd532000-07-12Mirar (Pontus Hagland)  return ::`[](replace(tz,"-/+"/1,"__p"/1)); }
e0b3932003-03-10Martin Nilsson .Rule.Timezone `[](string tz)
78fd532000-07-12Mirar (Pontus Hagland) {
e0b3932003-03-10Martin Nilsson  mixed p=::`[](tz); if (!p && tz=="locale") return locale=_locale();
4999cc2001-01-12Mirar (Pontus Hagland) 
e0b3932003-03-10Martin Nilsson  if ((<"make_new_timezone","decode_timeskew">)[tz]) return p;
4999cc2001-01-12Mirar (Pontus Hagland) 
e0b3932003-03-10Martin Nilsson  if (!p) p=_magic_timezone(tz); if (programp(p) || functionp(p)) return p(); return p;
78fd532000-07-12Mirar (Pontus Hagland) }
c0dbee2000-08-01Mirar (Pontus Hagland)  // ================================================================ // this is to runtime-compile timezones // it's not very nice; based on the one-time-compilation // utility I wrote first - but that method was too slow :/ // ================================================================ class Runtime_timezone_compiler { object cal=master()->resolv("Calendar")["ISO_UTC"]; function Year=cal->Year; object nleapy=Year(1999); int is_leap_year(int y) { return (!(((y)%4) || (!((y)%100) && ((y)%400)))); } #define FIXED(D) (yjd+((D)-1)) #define FIX_L(D) (yjd+leap+((D)-1)) #define LDAY(D,W) (yjd+((D)-1)-( (yjd+((D)+(8-W)-1)) % 7)) #define LDAYL(D,W) (yjd+((D)-1)+leap-( (yjd+leap+((D)+(8-W)-1)) % 7)) #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; 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; res=""; if (sscanf(t,"-%d:%d%s",h,m,res)&&res=="") return h*3600+m*60; complain("failed to parse offset %O\n",t); } array parse_tod(string t) { int h,m,s; string res; if (t=="0") return 0; if (sscanf(t,"%d:%d:%d%s",h,m,s,res)==4) return ({h*3600+m*60+s,res}); res=""; if (sscanf(t,"%d:%d%s",h,m,res)==3) return ({h*3600+m*60,res}); if (sscanf(t,"%d%s",h,res)==2) return ({h*3600,res}); complain("failed to parse time of day %O\n",t); } class Shift { string dayrule; int time; string timetype; int offset; string s; string comment; 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]); 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) { return (t=='O')? sprintf("Shift(%s,%d%s,%+d,%O)", dayrule,time,timetype,offset,s): 0; } int `==(Shift other) { return ( dayrule==other->dayrule && time==other->time && timetype==other->timetype && offset==other->offset && s==other->s ); } function(Shift:int) __equal=`==; 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; if (mon=="") return "0"; if (rule==(string)(d=(int)rule)) { if (mon=="Feb") return "FIXED("+(31+d)+")"; if (mon=="Jan") return "FIXED("+(d)+")"; return "FIX_L("+nleapy->month(mon)->day(d)->year_day()+")"; } else if (sscanf(rule,"last%s",ds)) { int wd=wday[ds]; if (!wd) complain("unknown weekday %O (last%s)\n",ds,ds); if (mon=="Jan") return "LDAY ("+31+","+wd+")"; return "LDAYL("+nleapy->month(mon)->day(-1)->year_day()+ ","+wd+")"; } else if (sscanf(rule,"%s>=%d",ds,d)) { int wd=wday[ds]; if (!wd) complain("unknown weekday %O (last%s)\n",ds,ds); if (d>24 && mon=="Feb") complain("can't handle Feb %d in a >= rule\n",d); if (mon=="Jan") return "LDAY ("+(nleapy->month(mon)->day(d)->year_day()+6)+ ","+wd+")"; return "LDAYL("+(nleapy->month(mon)->day(d)->year_day()+6)+ ","+wd+")"; } else if (sscanf(rule,"%s<=%d",ds,d)) { int wd=wday[ds]; if (!wd) complain("unknown weekday %O (last%s)\n",ds,ds); if (d>24 && mon=="Feb") complain("can't handle Feb %d in a <= rule\n",d); if (mon=="Jan" || mon=="Feb") return "LDAY ("+(nleapy->month(mon)->day(d)->year_day())+ ","+wd+")"; 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 (!arrayp(s)) s=({s}); return s+({this_object()}); } 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(); // leap=y1->leap_year(); // int d1=LDAYL(d1,wd); // yjd=y2->julian_day(); // 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->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 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; } string dump(int lastoffset,multiset ys) { string t; array z=(array)ys; int l=is_leap_year(z[0]);
55a4d22000-08-01Mirar (Pontus Hagland)  if (dayrule=="0" && time==0 && offset==0 && s=="") return sprintf("%-40s, // %s", " ZEROSHIFT",comment);
c0dbee2000-08-01Mirar (Pontus Hagland)  foreach (z[1..],int y) if (is_leap_year(y)!=l) { l=2; break; } switch (timetype) { case "s": t=sprintf("UO%+d",time); break; case "u": t=""+time; break; case "w": t=sprintf("UO%+d",(time-lastoffset)); break; default: error("illegal state\n"); } string r=dayrule; if (l!=2) { 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 { string id; 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]); // --- #define INF_YEAR 2050 #define NUL_YEAR 1850 int y1=(int)a[0] || NUL_YEAR; int y2; if (a[1]=="max") y2=INF_YEAR; else if (a[1]=="only") y2=y1; else if (!(y2=(int)a[1])) complain("unknown year %O\n",a[1]); else if (y2>=INF_YEAR) complain("big year\n"); Shift sh=Shift(a[3..]); switch (a[2]) { case "-": for (;y1<=y2;y1++) rules[y1]+=sh; break; case "odd": if (!(y1&1)) y1++; for (;y1<=y2;y1+=2) rules[y1]+=sh; break; case "even": if ((y1&1)) y1++; for (;y1<=y2;y1+=2) rules[y1]+=sh; break; default: complain("unknown year type %O\n",a[2]); } } string dump() { mapping r2=([]); Shift last=Shift(({"0","?",0,"u",0,"?"})); Shift first=last;
8530c52000-08-02Mirar (Pontus Hagland)  array res=({});
c0dbee2000-08-01Mirar (Pontus Hagland)  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+=
8530c52000-08-02Mirar (Pontus Hagland)  ({TZrules_init+ " 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"});
c0dbee2000-08-01Mirar (Pontus Hagland) 
8530c52000-08-02Mirar (Pontus Hagland)  array sr=({});
c0dbee2000-08-01Mirar (Pontus Hagland) 
be52cb2001-01-09Mirar (Pontus Hagland)  int mn=min(@indices(rules)-({NUL_YEAR}));
0332d82000-10-26Mirar (Pontus Hagland)  int firstyear=99999,lastyear=0;
c0dbee2000-08-01Mirar (Pontus Hagland)  for (y=INF_YEAR;sizeof(r2);y--) if (r2[y]) { array z=r2[y]; multiset my=(<y>); foreach (indices(r2),int y2) if (join_periods(z,r2[y2],y,y2)) my[y2]=1; foreach ((array)my,int y2) m_delete(r2,y2);
8530c52000-08-02Mirar (Pontus Hagland)  array tr=({});
c0dbee2000-08-01Mirar (Pontus Hagland)  int y0=min(@(array)my); int y2=max(@(array)my);
0332d82000-10-26Mirar (Pontus Hagland)  if (y0!=NUL_YEAR && y0<firstyear) firstyear=y0; if (y2!=INF_YEAR && y2>lastyear) lastyear=y2;
c0dbee2000-08-01Mirar (Pontus Hagland)  for (; y0<=y2; y0++) if (my[y0]) { int y1=y0; while (my[++y1]); y1--;
0332d82000-10-26Mirar (Pontus Hagland)  // figure first and last interesting year
c0dbee2000-08-01Mirar (Pontus Hagland)  if (y0==NUL_YEAR) {
0332d82000-10-26Mirar (Pontus Hagland)  if (y1!=NUL_YEAR && y1<firstyear) firstyear=y1;
c0dbee2000-08-01Mirar (Pontus Hagland)  if (my[INF_YEAR])
8530c52000-08-02Mirar (Pontus Hagland)  tr+=({" default: // .."+max(y1,mn-1), " and ½½½..\n"});
c0dbee2000-08-01Mirar (Pontus Hagland)  else
8530c52000-08-02Mirar (Pontus Hagland)  tr+=({" default: // .."+max(y1,mn-1), ":\n"});
c0dbee2000-08-01Mirar (Pontus Hagland)  } else if (y0==y1)
8530c52000-08-02Mirar (Pontus Hagland)  tr+=({" case "+y0+":\n"});
0332d82000-10-26Mirar (Pontus Hagland)  else if (y1==INF_YEAR)
c0dbee2000-08-01Mirar (Pontus Hagland)  {
0332d82000-10-26Mirar (Pontus Hagland)  if (y0>lastyear) lastyear=y0;
8530c52000-08-02Mirar (Pontus Hagland)  if (!my[NUL_YEAR]) tr+=({" case "+y0,"..:\n"}); else tr[0]=replace(tr[0],"½½½",(string)y0);
c0dbee2000-08-01Mirar (Pontus Hagland)  } else
8530c52000-08-02Mirar (Pontus Hagland)  tr+=({" case "+y0,".."+y1+":\n"});
c0dbee2000-08-01Mirar (Pontus Hagland)  y0=y1; } int lastoffset=0;
8530c52000-08-02Mirar (Pontus Hagland)  tr+=({" "*12,"return ({"});
c0dbee2000-08-01Mirar (Pontus Hagland)  foreach (z,Shift s) {
8530c52000-08-02Mirar (Pontus Hagland)  tr+=({s->dump(lastoffset,my),"\n"," "*21});
c0dbee2000-08-01Mirar (Pontus Hagland)  lastoffset=s->offset; }
8530c52000-08-02Mirar (Pontus Hagland)  tr[-3]=replace(tr[-3],", ","});"); sr=tr[..sizeof(tr)-2]+sr;
c0dbee2000-08-01Mirar (Pontus Hagland)  }
8530c52000-08-02Mirar (Pontus Hagland)  res+=sr+ ({" }\n" " }\n" "\n"
0332d82000-10-26Mirar (Pontus Hagland)  "constant firstyear="+firstyear+";\n" "constant lastyear="+lastyear+";\n",
8530c52000-08-02Mirar (Pontus Hagland)  "array(string) rule_s=\n"});
55a4d22000-08-01Mirar (Pontus Hagland)  multiset tzname=(<>); foreach (values(rules),array(Shift)|object(Shift) s) if (arrayp(s)) foreach (s,Shift s) tzname[s->s]=1; else tzname[s->s]=1;
8530c52000-08-02Mirar (Pontus Hagland)  res+=({"({",map((array)tzname, lambda(string s) { return sprintf("%O",s); })*",", "});\n", sprintf("string name=%O;\n",id)});
55a4d22000-08-01Mirar (Pontus Hagland) 
8530c52000-08-02Mirar (Pontus Hagland)  return res*"";
c0dbee2000-08-01Mirar (Pontus Hagland)  } int join_periods(array s,array t,int y0,int y1) { if (equal(s,t)) return 1; if (sizeof(s)!=sizeof(t)) return 0; if (s[0]!=t[0]) return 0; // try promote array q=s[..0]; int i; for (i=1; i<sizeof(s); i++) { Shift u=s[i]->try_promote(t[i],y0,y1); if (!u) return 0; q+=({u}); } for (i=1; i<sizeof(s); i++) s[i]=q[i]; // destructive return 1; } array(array(Shift)|Shift) mkperiods(array|Shift s,Shift last,Shift first) { if (!s) s=({}); if (!arrayp(s)) s=({s}); sort(map(s,lambda(Shift s) { return array_sscanf(s->dayrule,"%*[^(](%d")[0]; }),s); 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}); } } class Zone { string id; array rules=({}); void create(string _id) { id=_id; } void add(string line) { array a= array_sscanf(line, replace("%s %s %s %s", " ","%*[ \t]")); if (sizeof(a)<4) complain("parse error\n");
e42b872000-09-05Per Hedbor  a=({parse_offset(a[0]), // offset
c0dbee2000-08-01Mirar (Pontus Hagland)  a[1], // rule or added offset a[2], // string a[3], 0, 0, "tz", 0}); // until a[5]=rule_shift(a); a[4]=clone_rule(a); rules+=({a}); } string clone_rule(array a) { int h,m,s,roff=-17; if (a[1]=="-") roff=0; 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:%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(
e0b3932003-03-10Martin Nilsson  "Rules.Timezone(%d,%O)",
c0dbee2000-08-01Mirar (Pontus Hagland)  -(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); // werror("%O\n",in); int y,d=1,t=0; string mn="Jan",ty="w"; if (sscanf(in,"%d%*[ \t]%s%*[ \t]%d%*[ \t]%[^# \t]", y,mn,d,string tod)==7 && tod!="") [t,ty]=parse_tod(tod); else if (!(sscanf(in,"%d%*[ \t]%[A-Za-z]%*[ \t]%d",y,mn,d)==5 || (sscanf(in,"%d%*[ \t]%[A-Za-z]",y,mn)==3 && mn!="") || (mn="Jan",sscanf(in,"%d",y)))) { // werror("%O\n",array_sscanf(in,"%d%*[ \t]%[A-Za-z]")); complain("failed to understand UNTIL %O\n",in); y=2000; } int utc0=Year(y)->month(mn)->day(d)->unix_time(); switch (ty) { case "u": // utc time // a[3]=sprintf("[%d+%d=%d] %s\n",utc0,t,utc0+t,a[3]); return (string)(utc0+t); break; case "s": // local standard time // a[3]=sprintf("[%d+%d-%d=%d] %s\n",utc0,t,a[0],utc0+t-a[0],a[3]); return (string)(utc0+t-a[0]); break; case "w": case "": // with rule; check rule int h,m,s,roff=-17; if (a[1]=="-") roff=0; 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:%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 { program rulesp=find_rule(a[1]); if (!rulesp) error("ERROR: Missing rule %O (used in Zone %O)\n",a[1],id); object rules=rulesp(-a[0],"-"); roff=rules->tz_jd(Year(y)->month(mn)->day(d)->julian_day())[0]; // werror("Using %O:%O\n",rules,roff); return (string)(utc0+t-roff); } return (string)(utc0+t-a[0]-roff); default: complain("unknown time of day modifier %O\n",ty); } } string dump() {
8530c52000-08-02Mirar (Pontus Hagland)  array(string) res=({});
c0dbee2000-08-01Mirar (Pontus Hagland)  if (!sizeof(rules))
8530c52000-08-02Mirar (Pontus Hagland)  error("no rules for %O\n",id);
c0dbee2000-08-01Mirar (Pontus Hagland)  if (sizeof(rules)==1) // simple zone {
8530c52000-08-02Mirar (Pontus Hagland)  res+=({"object thezone=",rules[0][4],";\n"}); return res*"";
c0dbee2000-08-01Mirar (Pontus Hagland)  } 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++;
8530c52000-08-02Mirar (Pontus Hagland)  res+=({ "inherit TZHistory;\n"
e0b3932003-03-10Martin Nilsson  "Rules.Timezone ",
8530c52000-08-02Mirar (Pontus Hagland)  sort(values(rname))*",",";\n"
e0b3932003-03-10Martin Nilsson  "Rules.Timezone whatrule(int ux)\n"
8530c52000-08-02Mirar (Pontus Hagland)  "{\n" });
c0dbee2000-08-01Mirar (Pontus Hagland)  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; } array last=rules[-1]; foreach (reverse(rules)[1..],array a) {
8530c52000-08-02Mirar (Pontus Hagland)  res+=({sprintf(" if (ux>=%s) // %s %s\n" " return %s || (%s=%s);\n", a[5],a[3],last[7],last[6],last[6],last[4])});
c0dbee2000-08-01Mirar (Pontus Hagland)  last=a; } if (last[7]!="")
8530c52000-08-02Mirar (Pontus Hagland)  res+=({sprintf(" // %s\n",last[7])}); res+=({sprintf(" return %s || (%s=%s);\n", last[6],last[6],last[4])+ "}\n"});
55a4d22000-08-01Mirar (Pontus Hagland)  multiset tzname=(<>); foreach (rules,array a) if (search(a[2],"%s")==-1) 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; }
8530c52000-08-02Mirar (Pontus Hagland)  res+=({"array(string) zone_s=({"+ map((array)tzname, lambda(string s) { return sprintf("%O",s); })*",", "});\n", "array(int) shifts=({"});
55a4d22000-08-01Mirar (Pontus Hagland)  foreach (rules[..sizeof(rules)-2],array a)
8530c52000-08-02Mirar (Pontus Hagland)  res+=({a[5]+","});
094bc02000-10-01Mirar (Pontus Hagland)  res+=({"});\n", sprintf( "string _sprintf(int t) { return (t=='O')?" "%O:0; }\n"
e0b3932003-03-10Martin Nilsson  "string zoneid=%O;\n","Rule.Timezone("+id+")",id)});
55a4d22000-08-01Mirar (Pontus Hagland) 
8530c52000-08-02Mirar (Pontus Hagland)  return res*"";
c0dbee2000-08-01Mirar (Pontus Hagland)  } } string base_path=combine_path(__FILE__,"../tzdata/"); array files= ({ "africa", "antarctica", "asia", "australasia", "backward", "etcetera", "europe", "northamerica",
8530c52000-08-02Mirar (Pontus Hagland)  "southamerica",
c0dbee2000-08-01Mirar (Pontus Hagland)  "pacificnew", "systemv", }); mapping zone_cache=([]); mapping rule_cache=([]); string all_rules=0; string get_all_rules() {
03a3a92002-01-13Martin Nilsson  return map(files, lambda(string fn) { return (master()->master_read_file(base_path+fn) || (error("Failed to open file %O\n",base_path+fn), "")) - "\r"; })*"\n";
c0dbee2000-08-01Mirar (Pontus Hagland)  }
b214192002-11-26Martin Nilsson  class Dummymodule (function(string:mixed) f)
c0dbee2000-08-01Mirar (Pontus Hagland)  { mixed `[](string s) { return f(s); } }
130dd32003-03-09Martin Nilsson  object compile_handler = class { mapping(string:mixed) get_default_module() { return constants; } mapping constants = all_constants() + (["TZrules":Dummymodule(find_rule), "TZRules":TZRules, "TZHistory":TZHistory,
e0b3932003-03-10Martin Nilsson  "Rules":.Rule,
130dd32003-03-09Martin Nilsson  "ZEROSHIFT":({0,0,0,""}) ]); }();
55a4d22000-08-01Mirar (Pontus Hagland) 
8530c52000-08-02Mirar (Pontus Hagland) // #define RTTZC_DEBUG // #define RTTZC_TIMING
c0dbee2000-08-01Mirar (Pontus Hagland)  object find_zone(string s) { #ifdef RTTZC_DEBUG werror("Searching for zone %O\n",s); #endif if (zone_cache[s]) return zone_cache[s];
f2d7a62002-02-14Martin Nilsson  if (s=="") return UNDEFINED;
c0dbee2000-08-01Mirar (Pontus Hagland)  if (!all_rules) all_rules=get_all_rules();
8530c52000-08-02Mirar (Pontus Hagland) #ifdef RTTZC_TIMING
55a4d22000-08-01Mirar (Pontus Hagland)  int t=time(1); float t1=time(t); #endif
c0dbee2000-08-01Mirar (Pontus Hagland)  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)
f2d7a62002-02-14Martin Nilsson  return UNDEFINED;
c0dbee2000-08-01Mirar (Pontus Hagland)  int i=max(n-100,0)-1,j; do i=search(all_rules,"\nZone",(j=i)+1); while (i<n && i!=-1);
55a4d22000-08-01Mirar (Pontus Hagland)  if (j<n &&
c0dbee2000-08-01Mirar (Pontus Hagland)  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)
48e49a2000-09-27Mirar (Pontus Hagland)  {
ead9722003-01-20Martin Nilsson  if (sscanf(line,"%*[ \t]%[-0-9]%s",a,b)==3 && sizeof(a))
c0dbee2000-08-01Mirar (Pontus Hagland)  z->add(a+b);
48e49a2000-09-27Mirar (Pontus Hagland)  else if (sscanf(line,"%*[ ]#%*s")<2)
c0dbee2000-08-01Mirar (Pontus Hagland)  break; // end of zone
48e49a2000-09-27Mirar (Pontus Hagland)  }
c0dbee2000-08-01Mirar (Pontus Hagland)  break; } i=max(n-100,0)-1; do i=search(all_rules,"\nLink",(j=i)+1); while (i<n && i!=-1);
55a4d22000-08-01Mirar (Pontus Hagland)  if (j<n &&
c0dbee2000-08-01Mirar (Pontus Hagland)  sscanf(all_rules[j..j+100],"\nLink%*[ \t]%[^ \t]%*[ \t]%[^ \t\n]", string a,string b)==4 && b==s) return find_zone(a); n++; }
8530c52000-08-02Mirar (Pontus Hagland) #ifdef RTTZC_TIMING
55a4d22000-08-01Mirar (Pontus Hagland)  float t2=time(t); werror("find %O: %O\n",s,t2-t1); #endif
8530c52000-08-02Mirar (Pontus Hagland)  string c=z->dump(); #ifdef RTTZC_TIMING float td=time(t); werror("dump %O: %O\n",s,td-t2); float td=time(t); #endif
55a4d22000-08-01Mirar (Pontus Hagland) #ifdef RTTZC_DEBUG
c0dbee2000-08-01Mirar (Pontus Hagland)  werror("%s\n",c); #endif
130dd32003-03-09Martin Nilsson 
094bc02000-10-01Mirar (Pontus Hagland)  program p;
130dd32003-03-09Martin Nilsson  mixed err=catch { p=compile_string(c, 0, compile_handler); }; if (err)
094bc02000-10-01Mirar (Pontus Hagland)  { int i=0; foreach (c/"\n",string line) write("%2d: %s\n",++i,line);
e0b3932003-03-10Martin Nilsson  error(err);
094bc02000-10-01Mirar (Pontus Hagland)  }
c0dbee2000-08-01Mirar (Pontus Hagland)  object zo=p(); if (zo->thezone) zo=zo->thezone;
8530c52000-08-02Mirar (Pontus Hagland) #ifdef RTTZC_TIMING
55a4d22000-08-01Mirar (Pontus Hagland)  float t3=time(t);
8530c52000-08-02Mirar (Pontus Hagland)  werror("compile %O: %O\n",s,t3-td);
55a4d22000-08-01Mirar (Pontus Hagland) #endif
c0dbee2000-08-01Mirar (Pontus Hagland)  return zone_cache[s]=zo; }
8530c52000-08-02Mirar (Pontus Hagland) // #define RTTZC_TIMING
c0dbee2000-08-01Mirar (Pontus Hagland)  program find_rule(string s) { s=UNFIXID(s); if (rule_cache[s]) return rule_cache[s]; #ifdef RTTZC_DEBUG werror("Searching for rule %O\n",s); #endif if (!all_rules) all_rules=get_all_rules();
8530c52000-08-02Mirar (Pontus Hagland) #ifdef RTTZC_TIMING
55a4d22000-08-01Mirar (Pontus Hagland)  int t=time(1); float t1=time(t); #endif
c0dbee2000-08-01Mirar (Pontus Hagland)  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)
f2d7a62002-02-14Martin Nilsson  return UNDEFINED;
c0dbee2000-08-01Mirar (Pontus Hagland) 
55a4d22000-08-01Mirar (Pontus Hagland)  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)
c0dbee2000-08-01Mirar (Pontus Hagland)  { r->add(b);
8530c52000-08-02Mirar (Pontus Hagland) #ifdef RTTZC_TIMING
55a4d22000-08-01Mirar (Pontus Hagland)  float tf=time(t); werror("find %O at: %O\n",s,tf-t1);
8530c52000-08-02Mirar (Pontus Hagland)  float tq=time(t);
55a4d22000-08-01Mirar (Pontus Hagland) #endif foreach (q/"\n",string line)
c0dbee2000-08-01Mirar (Pontus Hagland)  if (sscanf(line,"Rule%*[ \t]%[^ \t]%*[ \t]%s",a,b)==4 && a==s)
8530c52000-08-02Mirar (Pontus Hagland)  r->add(b);
0332d82000-10-26Mirar (Pontus Hagland)  else if (sscanf(line,"%*[ ]#%*s")<2)
c0dbee2000-08-01Mirar (Pontus Hagland)  break; // end of zone
8530c52000-08-02Mirar (Pontus Hagland) #ifdef RTTZC_TIMING
55a4d22000-08-01Mirar (Pontus Hagland)  float tf=time(t);
8530c52000-08-02Mirar (Pontus Hagland)  werror("load %O: %O\n",s,tf-tq);
55a4d22000-08-01Mirar (Pontus Hagland) #endif
c0dbee2000-08-01Mirar (Pontus Hagland)  break; } n++; } string c=r->dump(); #ifdef RTTZC_DEBUG werror("%s\n",c); #endif
8530c52000-08-02Mirar (Pontus Hagland) #ifdef RTTZC_TIMING
55a4d22000-08-01Mirar (Pontus Hagland)  float t2=time(t); werror("find %O: %O\n",s,t2-t1);
8530c52000-08-02Mirar (Pontus Hagland)  float t2=time(t);
55a4d22000-08-01Mirar (Pontus Hagland) #endif
c0dbee2000-08-01Mirar (Pontus Hagland) 
130dd32003-03-09Martin Nilsson  program p=compile_string(c, 0, compile_handler);
55a4d22000-08-01Mirar (Pontus Hagland) 
8530c52000-08-02Mirar (Pontus Hagland) #ifdef RTTZC_TIMING
55a4d22000-08-01Mirar (Pontus Hagland)  float t3=time(t); werror("compile %O: %O\n",s,t3-t2); #endif
c0dbee2000-08-01Mirar (Pontus Hagland)  return rule_cache[s]=p; } 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; string name; static function(string:string) tzformat; static array names; // ---------------------------------------------------------------- // all rules are based on the gregorian calendar, so // this is the needed gregorian rule: // ---------------------------------------------------------------- static 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) { offset_to_utc=offset; name=_name; if (search(name,"/")!=-1) { names=name/"/"; tzformat=lambda(string s) { if (s=="") return names[0]; else return names[1]; }; } else tzformat=lambda(string s) { return sprintf(name,s); }; } // the Rule: // which julian day does dst start and end this year? static 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]; if (jd<b[0]) break; if (jd==b[0] && -offset_to_utc+b[1]>=0) break; i++; } return ({offset_to_utc-a[i][2],tzformat(a[i][3])}); } // is this unixtime (utc) dst? array tz_ux(int ux) { int jd=2440588+ux/86400; array(array(string|int)) a=jd_year_periods(jd); int i=0,n=sizeof(a)-1; while (i<n) { 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])}); }
e0b3932003-03-10Martin Nilsson  string _sprintf(int t) { return (t=='O')?"Rule.Timezone("+name+")":0; }
c0dbee2000-08-01Mirar (Pontus Hagland)  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
e0b3932003-03-10Martin Nilsson  .Rule.Timezone whatrule(int ux);
c0dbee2000-08-01Mirar (Pontus Hagland)  string name=sprintf("%O",object_program(this_object())); 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); } array(int) tz_jd(int jd) { // werror("tz_jd %O\n",jd); // object z=whatrule((jd-2440588)*86400-86400/2); // werror("%O %O\n",z->offset_to_utc,z->name); // return z->tz_jd(jd); return whatrule((jd-2440588)*86400-86400/2)->tz_jd(jd); } int raw_utc_offset(); } // ---------------------------------------------------------------------- string TZrules_init= #" // useful macros #define FIXED(D) (yjd+((D)-1)) #define FIX_L(D) (yjd+leap+((D)-1)) #define LDAY(D,W) (yjd+((D)-1)-( (yjd+((D)+(8-W)-1)) % 7)) #define LDAYL(D,W) (yjd+((D)-1)+leap-( (yjd+leap+((D)+(8-W)-1)) % 7)) #define UO offset_to_utc "; // ---------------------------------------------------------------------- }