78fd532000-07-12Mirar (Pontus Hagland) //! //! module Calendar //! submodule YMD //! //! base for all Roman-kind of Calendars, //! ie, one with years, months, weeks and days //! // #pragma strict_types import "."; inherit Time:Time; #define this this_object() // ---------------- // virtual methods to tell how this calendar works // ---------------- static array(int) year_from_julian_day(int jd); static int julian_day_from_year(int year); static int year_remaining_days(int y,int yday); static array(int) year_month_from_month(int y,int m); // [y,m,ndays,myd] static int month_from_yday(int y,int yday); static array(int) week_from_week(int y,int w); // [y,w,wd,ndays,wjd] static array(int) week_from_julian_day(int jd); // [y,w,wd,ndays,wjd] static string f_month_name_from_number; static string f_month_shortname_from_number; static string f_month_number_from_name; static string f_month_day_name_from_number; static string f_week_name_from_number; static string f_week_day_number_from_name; static string f_week_day_shortname_from_number; static string f_week_day_name_from_number; static string f_year_name_from_number; static string f_year_number_from_name; static int(0..1) year_leap_year(int y); static int compat_week_day(int n); //------------------------------------------------------------------------ //! class YMD //! Base (virtual) time period of the Roman-kind of calendar. //! inherits TimeRange //------------------------------------------------------------------------ class YMD { inherit TimeRange; // --- generic for all YMD: int y; // year int yjd; // julian day of the first day of the year int n; // number of this in the period int jd; // julian day of first day int yd; // day of year (1..) int m; // [*] month of year (1..12?), like int md; // [*] day of month (1..) int wy; // [*] week year int w; // [*] week of week year (1..) int wd; // [*] day of week (1..7?) int mnd=CALUNKNOWN; // days in current month int utco=CALUNKNOWN; // [*] distance to UTC string tzn=0; // timezone name // // ^^^ might be uninitialized (CALUNKNOWN) Ruleset rules; constant is_ymd=1; // ---------------------------------------- // basic Y-M-D stuff // ---------------------------------------- void create_now() { rules=default_rules; create_unixtime_default(time()); } void create_unixtime_default(int unixtime) { // 1970-01-01 is julian day 2440588 create_julian_day( 2440588+unixtime/86400 ); // we can't reuse this; it might not be start of day [int mutco,string mtzn]=rules->timezone->tz_ux(unixtime); int uxo=unixtime%86400-mutco; if (uxo<0) create_julian_day( 2440588+unixtime/86400-1 ); else if (uxo>=86400) create_julian_day( 2440588+unixtime/86400+1 ); else if (uxo==0) utco=mutco,tzn=mtzn; // reuse, it *is* start of day } void make_month() // set m and md from y and yd { int myd; [m,md,mnd,myd]=month_from_yday(y,yd); } void make_week() // set w from y and yd { int wnd,wjd; [wy,w,wd,wnd,wjd]=week_from_julian_day(jd); } int __hash() { return jd; } // --- query //! method float fraction_no() //! method int hour_no() //! method int julian_day() //! method int leap_year() //! method int minute_no() //! method int month_day() //! method int month_no() //! method int second_no() //! method int unix_time() //! method int utc_offset() //! method int week_day() //! method int week_no() //! method int year_day() //! method int year_no() //! method string month_name() //! method string month_shortname() //! method string month_day_name() //! method string week_day_name() //! method string week_day_shortname() //! method string week_name() //! method string year_name() //! method string tzname() //! method string tzname_iso() int julian_day() { return jd; } int unix_time() { // 1970-01-01 is julian day 2440588 int ux=(jd-2440588)*86400; if (utco==CALUNKNOWN) [utco,tzn]=rules->timezone->tz_jd(jd); return ux+utco; } int utc_offset() { if (utco==CALUNKNOWN) [utco,tzn]=rules->timezone->tz_jd(jd); return utco; } string tzname() { if (!tzn) [utco,tzn]=rules->timezone->tz_jd(jd); return tzn; } string tzname_iso() { int u=utc_offset(); if (!(u%3600)) return sprintf("UTC%+d",-u/3600); if (!(u%60)) return (u<0) ?sprintf("UTC+%d:%02d",-u/3600,(-u/60)%60) :sprintf("UTC-%d:%02d",u/3600,(u/60)%60); return (u<0) ?sprintf("UTC+%d:%02d:%02d",-u/3600,(-u/60)%60,(-u)%60) :sprintf("UTC-%d:%02d:%02d",u/3600,(u/60)%60,u%60); } int year_no() { return y>0?y:-1+y; } int month_no() { if (m==CALUNKNOWN) make_month(); return m; } int week_no() { if (w==CALUNKNOWN) make_week(); return w; } int month_day() { if (md==CALUNKNOWN) make_month(); return md; } int week_day() { if (wd==CALUNKNOWN) make_week(); return wd; } int year_day() { return yd; } string year_name() { return rules->language[f_year_name_from_number](y); } string week_name() { if (w==CALUNKNOWN) make_week(); return rules->language[f_week_name_from_number](w); } string month_name() { if (m==CALUNKNOWN) make_month(); return rules->language[f_month_name_from_number](m); } string month_shortname() { if (m==CALUNKNOWN) make_month(); return rules->language[f_month_shortname_from_number](m); } string month_day_name() { if (mnd==CALUNKNOWN) make_month(); return rules->language[f_month_day_name_from_number](md,mnd); } string week_day_name() { if (wd==CALUNKNOWN) make_week(); return rules->language[f_week_day_name_from_number](wd); } string week_day_shortname() { if (wd==CALUNKNOWN) make_week(); return rules->language[f_week_day_shortname_from_number](wd); } int leap_year() { return year_leap_year(y); } int hour_no() { return 0; } int minute_no() { return 0; } int second_no() { return 0; } float fraction_no() { return 0.0; } //! function method datetime() //! This gives back a mapping with the relevant //! time information (representing the start of the period); //! <pre> //! ([ "year": int // year number (2000 AD=2000, 1 BC==0) //! "month": int(1..) // month of year //! "day": int(1..) // day of month //! "yearday": int(0..) // day of year //! "week": int(1..) // week of year //! "week_day": int(0..) // day of week //! "timezone": int // offset to utc, including dst //! //! "unix": int // unix time //! "julian": int // julian day //! // for compatibility: //! "hour": 0 // hour of day, including dst //! "minute": 0 // minute of hour //! "second": 0 // second of minute //! "fraction": 0.0 // fraction of second //! ]); //! </pre> //! //! note: //! Day of week is compatible with old versions, //! ie, 0 is sunday, 6 is saturday, so it shouldn't be //! used to calculate the day of the week with the given //! week number. Year day is also backwards compatible, //! ie, one (1) less then from the year_day() function. mapping datetime(void|int skip_stuff) { if (m==CALUNKNOWN) make_month(); if (w==CALUNKNOWN) make_week(); if (skip_stuff) // called from timeofday return ([ "year": y, "month": m, "day": md, "yearday": yd-1, "week": w, "week_day": compat_week_day(wd) ]); else { return ([ "year": y, "month": m, "day": md, "yearday": yd-1, "week": w, "week_day": compat_week_day(wd), "timezone": utc_offset(), "julian": jd, "unix": unix_time(), // for compatibility: "hour": 0, "minute": 0, "second": 0, "fraction": 0.0 ]); } } // --- string format ---- //! method string format_iso_ymd(); //! method string format_ymd(); //! method string format_ymd_short(); //! method string format_ymd_xshort(); //! method string format_iso_week(); //! method string format_iso_week_short(); //! method string format_week(); //! method string format_week_short(); //! method string format_month(); //! method string format_month_short(); //! method string format_iso_time(); //! method string format_time(); //! method string format_time_short(); //! method string format_time_xshort(); //! method string format_mtime(); //! method string format_xtime(); //! method string format_tod(); //! method string format_todz(); //! method string format_xtod(); //! method string format_mod(); //! Format the object into nice strings; //! <pre> //! iso_ymd "2000-06-02 (Jun) -W22-5 (Fri)" [2] //! ext_ymd "Friday, 2 June 2000" [2] //! ymd "2000-06-02" //! ymd_short "20000602" //! ymd_xshort "000602" [1] //! iso_week "2000-W22" //! iso_week_short "2000W22" //! week "2000-w22" [2] //! week_short "2000w22" [2] //! month "2000-06" //! month_short "200006" [1] //! iso_time "2000-06-02 (Jun) -W22-5 (Fri) 00:00:00 UTC+1" [2] //! ext_time "Friday, 2 June 2000, 00:00:00" [2] //! ctime "Fri Jun 2 00:00:00 2000\n" [2] [3] //! http "Fri, 02 Jun 2000 00:00:00 GMT" [4] //! time "2000-06-02 00:00:00" //! time_short "20000602 00:00:00" //! time_xshort "000602 00:00:00" //! mtime "2000-06-02 00:00" //! xtime "2000-06-02 00:00:00.000000" //! tod "00:00:00" //! tod_short "000000" //! todz "00:00:00 CET" //! todz_iso "00:00:00 UTC+1" //! xtod "00:00:00.000000" //! mod "00:00" //! </pre> //! <tt>[1]</tt> note conflict (think 1 February 2003) //! <br><tt>[2]</tt> language dependent //! <br><tt>[3]</tt> as from the libc function ctime() //! <br><tt>[4]</tt> as specified by the HTTP standard; //! not language dependent. string format_iso_ymd() { if (m==CALUNKNOWN) make_month(); if (w==CALUNKNOWN) make_week(); return sprintf("%04d-%02d-%02d (%s) -W%02d-%d (%s)", y,m,md, month_shortname(), w,wd, // fixme - what weekday? week_day_shortname()); } string format_ext_ymd() { if (m==CALUNKNOWN) make_month(); return sprintf("%s, %s %s %s", week_day_name(), month_day_name(),month_name(),year_name()); } string format_ctime() { return sprintf("%s %s %2d 00:00:00 %s\n", week_day_shortname(), month_shortname(), md, year_name()); } string format_http() { if (wd==CALUNKNOWN) make_week(); if (md==CALUNKNOWN) make_month(); return sprintf("%s, %02d %s %04d 00:00:00 GMT", ("SunMonTueWedThuFriSat"/3)[compat_week_day(wd)], md, ("zzzJanFebMarAprMayJunJulAugSepOctNovDec"/3)[m], y); } string format_ymd() { if (m==CALUNKNOWN) make_month(); return sprintf("%04d-%02d-%02d",y,m,md); } string format_ymd_short() { if (m==CALUNKNOWN) make_month(); return sprintf("%04d%02d%02d",y,m,md); } string format_ymd_xshort() { if (m==CALUNKNOWN) make_month(); return sprintf("%02d%02d%02d",y%100,m,md); } string format_iso_week() { if (w==CALUNKNOWN) make_week(); return sprintf("%04d-W%02d",y,w); } string format_iso_week_short() { if (w==CALUNKNOWN) make_week(); return sprintf("%04d%02d",y,w); } string format_week() { return sprintf("%04d-%s",y,week_name()); } string format_week_short() { return sprintf("%04d%s",y,week_name()); } string format_month() { if (m==CALUNKNOWN) make_month(); return sprintf("%04d-%02d",y,m); } string format_month_short() { if (m==CALUNKNOWN) make_month(); return sprintf("%04d%02d",y,m); } string format_iso_time() { return format_iso_ymd()+" 00:00:00"; } string format_ext_time() { return format_ext_ymd()+" 00:00:00"; } string format_time() { return format_ymd()+" 00:00:00"; } string format_time_short() { return format_ymd_short()+" 00:00:00"; } string format_time_xshort() { return format_ymd_xshort()+" 00:00:00"; } string format_mtime() { return format_ymd_short()+" 00:00"; } string format_xtime() { return format_ymd_short()+" 00:00:00.000000"; } string format_tod() { return "00:00:00"; } string format_tod_short() { return "000000"; } string format_todz() { return "00:00:00 "+tzname(); } string format_todz_iso() { return "00:00:00 "+tzname_iso(); } string format_mod() { return "00:00"; } string format_xtod() { return "00:00:00.000000"; } string format_elapsed() { return sprintf("%dd",number_of_days()); } // --- size and move --- static TimeRange _set_size(int n,TimeRange t) { if (t->is_timeofday) return second()->set_size(n,t); if (yd==1 && t->is_year) return Year("ymd_y",rules,y,yjd,t->n*n) ->autopromote(); // months are even on years if (t->is_year || t->is_month) { if (md==CALUNKNOWN) make_month(); if (md==1) return Month("ymd_yjmw",rules,y,yjd,jd,m, t->number_of_months()*n,wd,w) ->autopromote(); } // weeks are not if (t->is_week) { if (wd==CALUNKNOWN) make_week(); if (wd==1) return Week("ymd_yjwm",rules,y,yjd,jd,w,t->n*n,md,m,mnd); } // fallback on days if (t->is_ymd) return Day("ymd_ydmw",rules,y,yjd,jd,yd, n*t->number_of_days(),m,md,w,wd,mnd); error("set_size: incompatible class %O\n", object_program(t)); } static TimeRange _add(int n,TimeRange step) { if (step->is_ymd) return _move(n,step); if (step->is_timeofday) return second()->range(second(-1))->add(n,step); error("add: incompatible class %O\n", object_program(step)); } array(int(-1..1)) _compare(TimeRange with) { if (with->is_timeofday) { // wrap array(int(-1..1)) cmp=with->_compare(this_object()); return ({-cmp[0], -cmp[2], -cmp[1], -cmp[3]}); } else if (with->is_ymd || with->julian_day) { #define CMP(A,B) ( ((A)<(B))?-1:((A)>(B))?1:0 ) int b1=julian_day(); int e1=b1+number_of_days(); int b2=with->julian_day(); int e2=b2+with->number_of_days(); return ({ CMP(b1,b2),CMP(b1,e2),CMP(e1,b2),CMP(e1,e2) }); } return ::_compare(with); } // --- to other YMD // years int number_of_years() { int m=number_of_days(); if (m<=1 || m+yd-1<year()->number_of_days()) return 1; return 1+y-year_from_julian_day(jd+m-1)[0]; } array(cYear) years(int ...range) { int from=1,n=number_of_years(),to=n; if (sizeof(range)) if (sizeof(range)<2) error("Illegal numbers of arguments to days()\n"); else { [from,to]=range; if (from>=n) return ({}); else if (from<0) from=0; if (to>=n) to=n; else if (to<from) return ({}); } return map(enumerate(1+to-from,1,y+from), lambda(int x) { return Year("ymd_yn",rules,x,1); }); } cYear year(void|int m) { if (!m || (!n&&m==-1)) return Year("ymd_y",rules,y,yjd,1); if (m<0) m=number_of_years()+m; array(TimeRange) res=years(m,m); if (sizeof(res)==1) return res[0]; error("not in range (Year 0..%d exist)\n", number_of_years()-1); } // days int number_of_days(); array(cDay) days(int ...range) { int from=1,n=number_of_days(),to=n; if (sizeof(range)) if (sizeof(range)<2) error("Illegal numbers of arguments to days()\n"); else { [from,to]=range; if (from>n) return ({}); else if (from<1) from=1; if (to>n) to=n; else if (to<from) return ({}); } int zy=y; int zyd=yd+from-1; int zjd=jd+from-1; int zyjd=yjd; array(cDay) res=({}); to-=from-1; if (zyd<1) { [zy,zyjd]=year_from_julian_day(zjd); zyd=zjd-zyjd+1; } for (;;) { int rd=year_remaining_days(zy,zyd)+1; if (rd>0) { if (rd>to) rd=to; res+=map(enumerate(rd,1,zyd), lambda(int x) { return Day("ymd_yd",rules,zy,zyjd,zyjd+x-1,x,1); }); if (rd==to) break; zjd+=rd; to-=rd; } [zy,zyjd]=year_from_julian_day(zjd); zyd=zjd-zyjd+1; } return res; } cDay day(int ... mp) { if (!sizeof(mp)) return Day("ymd_yd",rules,y,yjd,jd,yd,1); int m=mp[0]; if (m==-1 && !n) return Day("ymd_yd",rules,y,yjd,jd,yd,1); if (m<0) m+=1+number_of_days(); array(TimeRange) res=days(m,m); if (sizeof(res)==1) return res[0]; error("not in range (Day 1..%d exist)\n", number_of_days()); } // --- months int number_of_months(); array(cMonth) months(int ...range) { int from=1,n=number_of_months(),to=n; if (sizeof(range)) if (sizeof(range)<2) error("Illegal numbers of arguments to months()\n"); else { [from,to]=range; if (from>n) return ({}); else if (from<1) from=1; if (to>n) to=n; else if (to<from) return ({}); } if (md==CALUNKNOWN) make_month(); return map(enumerate(1+to-from,1,from+m-1), lambda(int x) { return Month("ymd_ym",rules,y,x,1); }); } cMonth month(int ... mp) { if (md==CALUNKNOWN) make_month(); if (!sizeof(mp)) return Month("ymd_ym",rules,y,m,1); int num=mp[0]; if (num==-1 && !n) return Month("ymd_ym",rules,y,m,1); if (num<0) num+=1+number_of_months(); array(TimeRange) res=months(num,num); if (sizeof(res)==1) return res[0]; error("not in range; Month 1..%d exist in %O\n", number_of_months(),this_object()); } //---- week int number_of_weeks(); array(cWeek) weeks(int ...range) { int from=1,n=number_of_weeks(),to=n; if (sizeof(range)) if (sizeof(range)<2) error("Illegal numbers of arguments to weeks()\n"); else { [from,to]=range; if (from>n) return ({}); else if (from<1) from=1; if (to>n) to=n; else if (to<from) return ({}); } if (wd==CALUNKNOWN) make_week(); return map(enumerate(1+to-from,1,from+w-1), lambda(int x) { return Week("ymd_yw",rules,wy,x,1); }); } cWeek week(int ... mp) { if (wd==CALUNKNOWN) make_week(); if (!sizeof(mp)) return Week("ymd_yw",rules,wy,w,1); int num=mp[0]; if (num==-1 && !n) return Week("ymd_yw",rules,wy,w,1); if (num<0) num+=1+number_of_weeks(); array(TimeRange) res=weeks(num,num); if (sizeof(res)==1) return res[0]; error("not in range (Week 1..%d exist)\n", number_of_weeks()); } // --- functions to conform to Time.* static TimeRange get_unit(string unit,int m) { if (!n) return day()[unit](); if (m<0) m+=::`[]("number_of_"+unit+"s")(); array(TimeRange) res=::`[](unit+"s")(m,m); if (sizeof(res)==1) return res[0]; error("not in range ("+unit+" 0..%d exist)\n", ::`[]("number_of_"+unit+"s")()-1); } static array(TimeRange) get_timeofday(string unit, int start,int step,program p, int ... range) { int from=0,n=::`[]("number_of_"+unit)(),to=n-1; if (sizeof(range)) if (sizeof(range)<2) error("Illegal numbers of arguments to "+unit+"()\n"); else { [from,to]=range; if (from>=n) return ({}); else if (from<0) from=0; if (to>=n) to=n-1; else if (to<from) return ({}); } from*=step; to*=step; to-=from-step; from+=unix_time(); array z= map(enumerate(to/step,step,from), lambda(int x) { return p("timeofday",rules,x,step); }); // return z; if (sizeof(z)>1 && ((p==cHour && z[0]->utc_offset()%3600 != z[-1]->utc_offset()%3600) || (p==cMinute && z[0]->utc_offset()%60 != z[-1]->utc_offset()%60))) { // we're in a zone shifting, and we shift a non-hour (or non-minute) cSecond sec=Second(); int i,uo=z[0]->utc_offset(); for (i=1; i<sizeof(z); i++) if (z[i]->utc_offset()!=uo) { int uq=(z[i]->utc_offset()-uo); werror("%O %O\n",z[i],z); if (uq<0) { if (uq<=-step) uq=-(-uq%step); z= z[..i-1]+ ({z[i]->set_size(step+uq,sec)})+ map(z[i+1..],"add",uq,sec); } else { if (uq>=step) uq%=step; z= z[..i-1]+ ({z[i]->set_size(uq,sec)})+ map(z[i..sizeof(z)-2],"add",uq,sec); i++; } werror("=> %O %O\n",z[i],z); uo=z[i]->utc_offset(); } } return z; } int number_of_hours() { return (number_of_seconds()+3599)/3600; } cHour hour(void|int n) { return get_unit("hour",n); } array(cHour) hours(int ...range) { return get_timeofday("hours",0,3600,Hour,@range); } int number_of_minutes() { return (number_of_seconds()+59)/60; } cMinute minute(void|int n) { return get_unit("minute",n); } array(cMinute) minutes(int ...range) { return get_timeofday("minutes",0,60,Minute,@range); } int number_of_seconds() { return end()->unix_time()-unix_time(); } cSecond second(void|int n,int ...time) { if (sizeof(time)==2) return hour(n)->minute(time[0])->second(time[1]); return get_unit("second",n); } array(cSecond) seconds(int ...range) { return get_timeofday("seconds",0,1,Second,@range); } float number_of_fractions() { return (float)number_of_seconds(); } cSecond fraction(void|float|int n) { return fractions()[0]; } array(cSecond) fractions(int|float ...range) { float from,to,n=number_of_fractions(); if (sizeof(range)==2) from=(float)range[0],to=(float)range[1]; else if (sizeof(range)==0) from=0.0,to=n; else error("Illegal arguments\n"); if (from<0.0) from=0.0; if (to>n) to=n; return ({Fraction("timeofday_f",rules,unix_time(),0, (int)to,(int)(inano*(to-(int)to))) ->autopromote()}); } // ---------------------------------------- // virtual functions needed // ---------------------------------------- string nice_print(); string _sprintf(int t); void create_julian_day(int|float jd); static TimeRange _move(int n,YMD step); TimeRange place(TimeRange what,void|int force); // not needed YMD autopromote() { return this; } } //------------------------------------------------------------------------ //! class Year //! This is the time period of a year. //! inherits TimeRange //! inherits YMD //------------------------------------------------------------------------ function(mixed...:cYear) Year=cYear; class cYear { inherit YMD; constant is_year=1; // --- //! //! method void create("unix",int unix_time) //! method void create("julian",int|float julian_day) //! method void create(int year) //! method void create(string year) //! It's possible to create the standard week //! by using three different methods; either the normal //! way - from standard unix time or the julian day, //! and also, for more practical use, from the year number. //! void create(mixed ...args) { if (!sizeof(args)) { create_now(); return; } else switch (args[0]) { case "ymd_y": rules=args[1]; y=args[2]; jd=yjd=args[3]; n=args[4]; m=md=w=wd=CALUNKNOWN; yd=1; return; case "ymd_yn": rules=args[1]; y=args[2]; jd=yjd=julian_day_from_year(y); n=args[3]; m=md=w=wd=CALUNKNOWN; yd=1; return; default: if (intp(args[0]) && sizeof(args)==1) { rules=default_rules; y=args[0]; jd=yjd=julian_day_from_year(y); n=1; m=md=w=wd=CALUNKNOWN; yd=1; return; } else if (stringp(args[0])) { y=default_rules->language[f_year_number_from_name](args[0]); rules=default_rules; jd=yjd=julian_day_from_year(y); n=1; m=md=w=wd=CALUNKNOWN; yd=1; return; } break; } rules=default_rules; ::create(@args); } void create_julian_day(int|float _jd) { if (floatp(_jd)) create_unixtime_default((int)((jd-2440588)*86400)); else { [y,yjd]=year_from_julian_day(_jd); jd=yjd; n=1; md=yd=m=1; wd=w=CALUNKNOWN; // unknown } } TimeRange beginning() { return Year("ymd_y",rules,y,yjd,0); } TimeRange end() { return Year("ymd_yn",rules,y+n,0); } // ---------------- string _sprintf(int t) { switch (t) { case 'O': if (n!=1) return sprintf("Year(%s)",nice_print_period()); return sprintf("Year(%s)",nice_print()); default: return 0; } } string nice_print_period() { if (!n) return nice_print()+" sharp"; return sprintf("%s..%s",nice_print(),year(-1)->nice_print()); } string nice_print() { return year_name(); } // --- Year _move TimeRange _move(int m,YMD step) { if (!step->n || !m) return this; if (step->is_year) return Year("ymd_yn",rules,y+m*step->n,n) ->autopromote(); if (step->is_month) return month()->add(m,step)->set_size(this_object()); // if (step->is_week) // return week()->add(m,step)->set_size(this_object()); if (step->is_ymd) return Day("ymd_jd",rules, yjd+m*step->number_of_days(),number_of_days()) ->autopromote(); error("_move: Incompatible type %O\n",step); } static void convert_from(TimeRange other) { ::convert_from(other); if (other->number_of_years) n=other->number_of_years(); else n=0; } TimeRange place(TimeRange what,void|int force) { if (what->is_day) { int yd=what->yd; return Day("ymd_yd",rules,y,yjd,yjd+yd-1,yd,what->n); } if (what->is_week) { cWeek week=Week("ymd_yw",rules,y,what->w,what->n); if (!force && week->y!=y) return 0; // not this year return week; } error("place: Incompatible type %O\n",what); } TimeRange distance(TimeRange to) { if (to->is_timeofday) { return hour()->distance(to); } if (to->is_ymd) { if (to->is_year) { int y1=y; int y2=to->y; if (y2<y1) error("distance: negative distance\n"); return Year("ymd_yn",rules,y,y2-y1) ->autopromote(); } if (to->is_month) return month()->distance(to); return day()->distance(to); } error("distance: Incompatible type %O\n",to); } // --- int number_of_years() { return n; } int number_of_weeks(); //! method Month month() //! method Month month(int n) //! method Month month(string name) //! The Year type overloads the month() method, //! so it is possible to get a specified month //! by string: //! //! <tt>year-&gt;month("April")</tt> //! //! The integer and no argument behavior is inherited //! from <ref to=YMD.month>YMD</ref>(). cMonth month(int|string ... mp) { if (sizeof(mp) && stringp(mp[0])) { int num=((int)mp[0]) || rules->language[f_month_number_from_name](mp[0]); if (!num) error("no such month %O in %O\n",mp[0],this_object()); return ::month(num); } else return ::month(@mp); } //! method Week week() //! method Week week(int n) //! method Week week(string name) //! The Year type overloads the week() method, //! so it is possible to get a specified week //! by name: //! //! <tt>year-&gt;week("17")</tt> //! <tt>year-&gt;week("w17")</tt> //! //! The integer and no argument behavior is inherited //! from <ref to=YMD.week>YMD</ref>(). //! //! This is useful, since the first week of a year //! not always (about half the years, in the ISO calendar) //! is numbered '1'. //! cWeek week(int|string ... mp) { if (sizeof(mp) && stringp(mp[0])) { int num; sscanf(mp[0],"%d",num); sscanf(mp[0],"w%d",num); cWeek w=::week(num); if (w->week_no()==num) return w; return ::week(num-(w->week_no()-num)); } else return ::week(@mp); } cYear set_ruleset(Ruleset r) { return Year("ymd_y",r,y,yjd,n); } } // ---------------------------------------------------------------- // Month // ---------------------------------------------------------------- function(mixed...:cMonth) Month=cMonth; class cMonth { inherit YMD; constant is_month=1; int nd; // number of days int nw; // number of weeks void create(mixed ...args) { if (!sizeof(args)) { rules=default_rules; create_unixtime_default(time()); return; } else switch (args[0]) { case "ymd_ym": rules=args[1]; y=args[2]; m=args[3]; n=args[4]; md=1; w=wd=CALUNKNOWN; [y,m,nd,yd]=year_month_from_month(y,m); yjd=julian_day_from_year(y); jd=yjd+yd-1; if (n!=1) nd=CALUNKNOWN; nw=CALUNKNOWN; return; case "ymd_yjmw": rules=args[1]; y=args[2]; yjd=args[3]; jd=args[4]; yd=1+jd-yjd; m=args[5]; n=args[6]; wd=args[7]; w=args[8]; md=1; nw=nd=CALUNKNOWN; return; case "ymd_jd": rules=args[1]; create_julian_day(args[2]); n=args[3]; return; default: if (intp(args[0]) && sizeof(args)==2) { create("ymd_ym",default_rules,args[0],args[1],1); if (y!=args[0]) error("month %d doesn't exist in %d\n",args[1],args[0]); return; } break; } rules=default_rules; ::create(@args); } void create_julian_day(int|float _jd) { if (floatp(_jd)) create_unixtime_default((int)((jd-2440588)*86400)); else { int zmd; [y,yjd]=year_from_julian_day(jd=_jd); [m,zmd,nd,yd]=month_from_yday(y,1+jd-yjd); jd=yd+yjd-1; n=1; md=1; nw=wd=w=CALUNKNOWN; // unknown } } string _sprintf(int t) { // return sprintf("month y=%d yjd=%d m=%d jd=%d yd=%d n=%d nd=%d", // y,yjd,m,jd,yd,n,number_of_days()); switch (t) { case 'O': if (n!=1) return sprintf("Month(%s)",nice_print_period()); return sprintf("Month(%s)",nice_print()); default: return 0; } } string nice_print() { return sprintf("%s %s", month_name(), year_name()); } string nice_print_period() { if (!n) return day()->nice_print()+" 0:00 sharp"; cMonth mo=month(-1); if (mo->y==y) return sprintf("%s..%s %s", month_shortname(), mo->month_shortname(), year_name()); return nice_print()+" .. "+month(-1)->nice_print(); } cDay beginning() { return Month("ymd_yjmw",rules,y,yjd,jd,m,0,wd,w) ->autopromote(); } cDay end() { return Month("ymd_ym",rules,y,m+n,0) ->autopromote(); } // --- month position and distance TimeRange distance(TimeRange to) { if (to->is_timeofday) return hour()->distance(to); if (to->is_ymd) { if (to->is_month || to->is_year) { int n1=months_to_month(to->y,to->is_year?1:to->m); if (n1<0) error("distance: negative distance (%d months)\n",n1); return Month("ymd_yjmw",rules,y,yjd,jd,m,n1,wd,w) ->autopromote(); } int d1=jd; int d2=to->jd; if (d2<d1) error("distance: negative distance (%d days)\n",d2-d1); return Day("ymd_ydmw",rules,y,yjd,jd,yd,d2-d1,m,1,w,wd,mnd) ->autopromote(); } error("distance: Incompatible type %O\n",to); } static void convert_from(TimeRange other) { ::convert_from(other); if (other->number_of_months) n=other->number_of_months(); else n=0; } TimeRange _move(int x,YMD step) { if (step->is_year) return Month("ymd_ym",rules,y+x*step->n,m,n) ->autopromote(); if (step->is_month) return Month("ymd_ym",rules,y,m+x*step->n,n) ->autopromote(); return Day("ymd_jd",rules,jd+x*step->number_of_days(),number_of_days()) ->autopromote(); } TimeRange place_day(int day,int day_n,void|int force) { if (day>number_of_days()) return 0; // doesn't exist return Day("ymd_jd",rules,jd+day-1,day_n)->autopromote(); } TimeRange place(TimeRange what,void|int force) { if (what->is_year) return year()->place(what,force); // just fallback if (what->is_day) return place_day(what->month_day(),what->n,force); error("place: Incompatible type %O\n",what); } // --- Month to other units int number_of_years() { if (n<=1) return 1; [int y2,int m2,int nd2,int yd2]=year_month_from_month(y,m+n); return 1+y2-y; } int number_of_days() { if (nd!=CALUNKNOWN) return nd; [int y2,int m2,int nd2,int yd2]=year_month_from_month(y,m+n); return nd=julian_day_from_year(y2)+yd2-jd-1; } int number_of_weeks() { if (nw!=CALUNKNOWN) return nw; [int y2,int m2,int nd2,int yd2]=year_month_from_month(y,m+n); return nw= Week("julian_r",jd,rules) ->range(Week("julian_r",julian_day_from_year(y2)+yd2-2,rules)) ->number_of_weeks(); } int number_of_months() { return n; } cMonth set_ruleset(Ruleset r) { return Month("ymd_yjmw",r,y,yjd,jd,m,n,wd,w); } // --- needs to be defined static int months_to_month(int y,int m); } // ---------------------------------------------------------------- //! class Week //! The Calendar week represents a standard time period of //! a week. In the Gregorian calendar, the standard week //! starts on a sunday and ends on a saturday; in the ISO //! calendar, it starts on a monday and ends on a sunday. //! //! The week are might not be aligned to the year, and thus //! the week may cross year borders and the year of //! the week might not be the same as the year of all the //! days in the week. The basic rule is that the week year //! is the year that has the most days in the week, but //! since week number only is specified in the ISO calendar //! - and derivates - the week number of most calendars //! is the week number of most of the days in the ISO //! calendar, which modifies this rule for the Gregorian calendar; //! the week number and year is the same as for the ISO calendar, //! except for the sundays. //! //! When adding, moving and subtracting months //! to a week, it falls back to using days. //! //! When adding, moving or subtracting years, //! if tries to place the moved week in the //! resulting year. // ---------------------------------------------------------------- function(mixed...:cWeek) Week=cWeek; class cWeek { inherit YMD; constant is_week=1; //! //! method void create("unix",int unix_time) //! method void create("julian",int|float julian_day) //! method void create(int year,int week) //! It's possible to create the standard week //! by using three different methods; either the normal //! way - from standard unix time or the julian day, //! and also, for more practical use, from year and week //! number. //! void create(mixed ...args) { if (!sizeof(args)) { rules=default_rules; create_unixtime_default(time()); return; } else switch (args[0]) { case "ymd_yw": rules=args[1]; y=args[2]; w=args[3]; n=args[4]; m=md=CALUNKNOWN; [y,w,wd,int nd,jd]=week_from_week(y,w); yjd=julian_day_from_year(y); yd=1+jd-yjd; wy=y; if (n!=1) nd=CALUNKNOWN; return; case "ymd_yjwm": rules=args[1]; y=args[2]; yjd=args[3]; jd=args[4]; yd=1+jd-yjd; w=args[5]; n=args[6]; md=args[7]; m=args[8]; mnd=args[9]; wd=1; wy=y; nd=CALUNKNOWN; return; case "ymd_jd": rules=args[1]; create_julian_day(args[2]); n=args[3]; return; default: if (intp(args[0]) && sizeof(args)==2) { create("ymd_yw",default_rules,args[0],args[1],1); if (y!=args[0]) error("month %d doesn't exist in %d\n",args[1],args[0]); return; } break; } rules=default_rules; ::create(@args); } void create_julian_day(int|float _jd) { if (floatp(_jd)) create_unixtime_default((int)((jd-2440588)*86400)); else { int zwd; [y,w,zwd,int nd,jd]=week_from_julian_day(_jd); yjd=julian_day_from_year(y); yd=1+jd-yjd; n=1; wd=1; wy=y; md=m=CALUNKNOWN; // unknown } } string _sprintf(int t) { // return sprintf("week y=%d yjd=%d w=%d jd=%d yd=%d n=%d nd=%d", // y,yjd,w,jd,yd,n,number_of_days()); switch (t) { case 'O': if (n!=1) return sprintf("Week(%s)",nice_print_period()); return sprintf("Week(%s)",nice_print()); default: return 0; } } string nice_print() { return sprintf("%s %s", week_name(), year_name()); } string nice_print_period() { if (!n) return day()->nice_print()+" 0:00 sharp"; cWeek wo=week(-1); if (wo->y==y) return sprintf("%s..%s %s", week_name(), wo->week_name(), year_name()); return nice_print()+" .. "+week(-1)->nice_print(); } cDay beginning() { return Week("ymd_yjwm",rules,y,yjd,jd,w,0,md,m,mnd) ->autopromote(); } cDay end() { return Week("ymd_yw",rules,y,w+n,0) ->autopromote(); } // --- week position and distance TimeRange distance(TimeRange to) { if (to->is_timeofday) return hour()->distance(to); if (to->is_week) { int n1=weeks_to_week(to->y,to->w); if (n1<0) error("distance: negative distance (%d weeks)\n",n1); return Week("ymd_yjwm",rules,y,yjd,jd,w,n1,md,m,mnd) ->autopromote(); } if (to->julian_day) { int d1=jd; int d2=to->julian_day(); if (d2<d1) error("distance: negative distance (%d days)\n",d2-d1); return Day("ymd_ydmw",rules,y,yjd,jd,yd,d2-d1,m,md,w,1,mnd) ->autopromote(); } error("distance: Incompatible type %O\n",to); } static void convert_from(TimeRange other) { ::convert_from(other); if (other->number_of_weeks) n=other->number_of_weeks(); else n=0; } TimeRange _move(int x,YMD step) { if (step->is_week) return Week("ymd_yw",rules,y,w+x*step->n,n) ->autopromote(); if (step->is_year) return year()->add(x,step)->place(this_object(),1); if (step->number_of_days) return Day("ymd_jd",rules, jd+x*step->number_of_days(),number_of_days()) ->autopromote(); error("add: Incompatible type %O\n",step); } TimeRange place_day(int day,int day_n,int force) { if (day>number_of_days()) if (!force) return 0; else return Day("ymd_jd",rules,jd+day-1,max(0,day_n-1))->autopromote(); return Day("ymd_jd",rules,jd+day-1,day_n)->autopromote(); } TimeRange place(TimeRange what,void|int force) { if (what->is_year) return year()->place(what,force); // just fallback if (what->is_day) return place_day(what->week_day(),what->n,force); error("place: Incompatible type %O\n",what); } // --- Week to other units int number_of_years() { if (n<=1) return 1; [int y2,int w2,int wd2,int nd2,int jd2]=week_from_week(y,w+n); return 1+y2-y; } int number_of_months() { if (!n) return 1; // cheat return Day("ymd_jd",rules,jd,number_of_days()) ->number_of_months(); } int number_of_weeks() { return n; } int number_of_days(); //! method Day day() //! method Day day(int n) //! method Day day(string name) //! The Week type overloads the day() method, //! so it is possible to get a specified weekday //! by string: //! //! <tt>week-&gt;day("sunday")</tt> //! //! The integer and no argument behavior is inherited //! from <ref to=YMD.day>YMD</ref>(). //! //! note: //! the weekday-from-string routine is language dependent. cDay day(int|string ... mp) { if (sizeof(mp) && stringp(mp[0])) { int num=((int)mp[0]) || rules->language[f_week_day_number_from_name](mp[0]); if (!num) error("no such day %O in %O\n",mp[0],this_object()); return ::day(num); } else return ::day(@mp); } cWeek set_ruleset(Ruleset r) { return Week("ymd_yjwm",r,y,yjd,jd,w,n,md,m,mnd); } // --- needs to be defined static int weeks_to_week(int y,int m); } // ---------------------------------------------------------------- // Day // ---------------------------------------------------------------- function(mixed...:cDay) Day=cDay; class cDay { inherit YMD; constant is_day=1; int nw; //! //! method void create("unix",int unix_time) //! method void create("julian",int|float julian_day) //! method void create(int year,int month,int day) //! method void create(int year,int year_day) //! method void create(int julian_day) //! It's possible to create the day //! by using five different methods; either the normal //! way - from standard unix time or the julian day, //! and also, for more practical use, from year, month and day, //! from year and day of year, and from julian day //! without extra fuzz. void create(mixed ...args) { if (!sizeof(args)) { rules=default_rules; create_unixtime_default(time()); return; } else switch (args[0]) { case "ymd_ydmw": rules=args[1]; y=args[2]; yjd=args[3]; jd=args[4]; yd=args[5]; n=args[6]; m=args[7]; md=args[8]; w=args[9]; wd=args[10]; mnd=args[11]; nw=CALUNKNOWN; return; case "ymd_yd": rules=args[1]; y=args[2]; yjd=args[3]; jd=args[4]; yd=args[5]; n=args[6]; wd=nw=md=m=w=CALUNKNOWN; return; case "ymd_jd": rules=args[1]; create_julian_day(args[2]); n=args[3]; wd=nw=md=m=w=CALUNKNOWN; return; case "unix_r": case "julian_r": case "unix": case "julian": break; default: rules=default_rules; wd=nw=md=m=w=CALUNKNOWN; n=1; switch (sizeof(args)) { case 1: if (intp(args[0])) { create_julian_day(args[0]); return; } break; case 2: if (stringp(args[0])) y=default_rules->language[f_year_number_from_name] (args[0]); else if (intp(args[0])) y=args[0]; else break; if (!intp(args[1])) break; yd=args[1]; yjd=julian_day_from_year(y); jd=yjd+yd-1; return; case 3: if (stringp(args[0])) y=default_rules->language[f_year_number_from_name] (args[0]); else if (intp(args[0])) y=args[0]; else break; if (!intp(args[1]) || !intp(args[2])) break; md=args[2]; [y,m,int nmd,int myd]= year_month_from_month(y,args[1]); if (m!=args[1] || y!=args[0]) error("No such month (%d-%02d)\n",args[0],args[1]); yjd=julian_day_from_year(y); md=args[2]; jd=yjd+myd+md-2; yd=jd-yjd+1; if (md>nmd || md<1) error("No such day of month (%d-%02d-%02d)\n", @args); return; } } rules=default_rules; ::create(@args); } void create_julian_day(int|float _jd) { n=1; nw=md=m=wd=w=CALUNKNOWN; // unknown if (floatp(_jd)) { create_unixtime_default((int)((jd-2440588)*86400)); } else { [y,yjd]=year_from_julian_day(jd=_jd); yd=1+jd-yjd; } } string _sprintf(int t) { switch (t) { case 'O': if (n!=1) return sprintf("Day(%s)",nice_print_period()); return sprintf("Day(%s)",nice_print()); default: return 0; } } string nice_print() { if (m==CALUNKNOWN) make_month(); if (wd==CALUNKNOWN) make_week(); return sprintf("%s %s %s %s", week_day_shortname(), month_day_name(),month_shortname(), year_name()); } string nice_print_period() { // return nice_print()+" n="+n+""; if (!n) return nice_print()+" 0:00 sharp"; return nice_print()+" .. "+day(-1)->nice_print(); } cDay beginning() { return Day("ymd_ydmw",rules,y,yjd,jd,yd,0,m,md,w,wd,mnd); } cDay end() { return Day("ymd_jd",rules,jd+n,0) ->autopromote(); } static void convert_from(TimeRange other) { ::convert_from(other); if (other->number_of_days) n=other->number_of_days(); else n=0; } // --- Day _move static TimeRange _move(int x,YMD step) { if (step->is_year) return year()->add(x,step)->place(this_object(),1); if (step->is_month) return month()->add(x,step)->place(this_object(),1); if (step->is_week) return week()->add(x,step)->place(this_object(),1); if (step->is_day) return Day("ymd_jd",rules,jd+x*step->n,n) ->autopromote(); error("_move: Incompatible type %O\n",step); } TimeRange place(TimeRange what) { if (what->is_timeofday) { int lux= what->ux- Day("unix_r",what->unix_time(),what->ruleset()) ->unix_time(); if (what->is_timeofday_f) return Fraction("timeofday_f",rules, lux+unix_time(),what->ns,what->s_len,what->ns_len) ->autopromote(); return Second("timeofday",rules,unix_time()+lux,what->len) ->autopromote(); } error("place: Incompatible type %O\n",what); } TimeRange distance(TimeRange to) { if (to->is_timeofday) return hour()->distance(to); if (to->is_ymd) { int d1=jd; int d2=to->jd; if (d2<d1) error("distance: negative distance (%d days)\n",d2-d1); return Day("ymd_ydmw",rules,y,yjd,jd,yd,d2-d1,m,md,w,wd,mnd) ->autopromote(); } error("distance: Incompatible type %O\n",to); } // --- Day to other YMD int number_of_days() { return n; } int number_of_years() { if (n<=1) return 1; return 1+year_from_julian_day(jd+n-1)[0]-y; } int number_of_weeks() { if (nw!=CALUNKNOWN) return nw; if (n<=1) return nw=1; return nw= Week("julian_r",jd,rules) ->range(Week("julian_r",jd+n-1,rules)) ->number_of_weeks(); } cDay set_ruleset(Ruleset r) { return Day("ymd_ydmw",r,y,yjd,jd,yd,n,m,md,w,wd,mnd); } // backwards compatible with calendar I string iso_name() { return format_ymd(); } string iso_short_name() { return format_ymd_short(); } } //------------------------------------------------------------------------ //- class YMD_Time //------------------------------------------------------------------------ class YMD_Time { #define MKRBASE \ do \ { \ int n; \ if (!rbase) \ rbase=Day("unix_r",this->ux,this->rules)->range(Day("unix_r",this->ux+this->len,this->rules)); \ } while (0) #define RBASE (this->base || this->make_base()) cDay day(int ...n) { return RBASE->day(@n); } cDay number_of_days() { return RBASE->number_of_days(); } array(cDay) days(int ...r) { return RBASE->days(@r); } cMonth month(int ...n) { return RBASE->month(@n); } cMonth number_of_months() { return RBASE->number_of_months(); } array(cMonth) months(int ...r) { return RBASE->months(@r); } cWeek week(int ...n) { return RBASE->week(@n); } cWeek number_of_weeks() { return RBASE->number_of_weeks(); } array(cWeek) weeks(int ...r) { return RBASE->weeks(@r); } cYear year(int ...n) { return RBASE->year(@n); } cYear number_of_years() { return RBASE->number_of_years(); } array(cYear) years(int ...r) { return RBASE->years(@r); } int year_no() { return RBASE->year_no(); } int month_no() { return RBASE->month_no(); } int week_no() { return RBASE->week_no(); } int month_name() { return RBASE->month_name(); } int month_shortname() { return RBASE->month_shortname(); } int month_day() { return RBASE->month_day(); } int month_day_name() { return RBASE->month_day_name(); } int week_day() { return RBASE->week_day(); } int year_day() { return RBASE->year_day(); } string week_name() { return RBASE->week_name(); } string week_day_name() { return RBASE->week_day_name(); } string week_day_shortname() { return RBASE->week_day_shortname(); } int leap_year() { return RBASE->leap_year(); } string format_iso_ymd() { return RBASE->format_iso_ymd(); } string format_ext_ymd() { return RBASE->format_ext_ymd(); } string format_ymd() { return RBASE->format_ymd(); } string format_ymd_short() { return RBASE->format_ymd_short(); } string format_ymd_xshort() { return RBASE->format_ymd_xshort(); } string format_iso_week() { return RBASE->format_iso_week(); } string format_iso_week_short() { return RBASE->format_iso_week_short(); } string format_week() { return RBASE->format_week(); } string format_week_short() { return RBASE->format_week_short(); } string format_month() { return RBASE->format_month(); } string format_month_short() { return RBASE->format_month_short(); } #undef RBASE } #define OVERLOAD_TIMEOFDAY \ \ static int(0..1) create_backtry(mixed ... args) \ { \ if (sizeof(args)>=5 && \ (intp(args[0])||stringp(args[0])) && \ intp(args[1]) && \ intp(args[2])) \ { \ base=Day(@args[..2]); \ return ::create_backtry(@args[3..]); \ } \ return ::create_backtry(@args); \ } //------------------------------------------------------------------------ //! class Hour //! inherits Time.Hour //! inherits YMD //------------------------------------------------------------------------ class cHour { inherit Time::cHour; inherit YMD_Time; OVERLOAD_TIMEOFDAY; } class cMinute { inherit Time::cMinute; inherit YMD_Time; OVERLOAD_TIMEOFDAY; } class cSecond { inherit Time::cSecond; inherit YMD_Time; OVERLOAD_TIMEOFDAY; } class cFraction { inherit Time::cFraction; inherit YMD_Time; OVERLOAD_TIMEOFDAY; } class cSuperTimeRange { inherit Time::cSuperTimeRange; array(cYear) years(int ...range) { return get_units("years",@range); } cYear year(void|int n) { return get_unit("years",n); } int number_of_years() { return num_units("years"); } array(cMonth) months(int ...range) { return get_units("months",@range); } cMonth month(void|int n) { return get_unit("months",n); } int number_of_months() { return num_units("months"); } array(cWeek) weeks(int ...range) { return get_units("weeks",@range); } cWeek week(void|int n) { return get_unit("weeks",n); } int number_of_weeks() { return num_units("weeks"); } array(cDay) days(int ...range) { return get_units("days",@range); } cDay day(void|int n) { return get_unit("days",n); } int number_of_days() { return num_units("days"); } #define RBASE parts[0] int year_no() { return RBASE->year_no(); } int month_no() { return RBASE->month_no(); } int week_no() { return RBASE->week_no(); } int month_name() { return RBASE->month_name(); } int month_shortname() { return RBASE->month_shortname(); } int month_day() { return RBASE->month_day(); } int month_day_name() { return RBASE->month_day_name(); } int week_day() { return RBASE->week_day(); } int year_day() { return RBASE->year_day(); } string week_name() { return RBASE->week_name(); } string week_day_name() { return RBASE->week_day_name(); } string week_day_shortname() { return RBASE->week_day_shortname(); } int leap_year() { return RBASE->leap_year(); } string format_iso_ymd() { return RBASE->format_iso_ymd(); } string format_ext_ymd() { return RBASE->format_ext_ymd(); } string format_ymd() { return RBASE->format_ymd(); } string format_ymd_short() { return RBASE->format_ymd_short(); } string format_ymd_xshort() { return RBASE->format_ymd_xshort(); } string format_iso_week() { return RBASE->format_iso_week(); } string format_iso_week_short() { return RBASE->format_iso_week_short(); } string format_week() { return RBASE->format_week(); } string format_week_short() { return RBASE->format_week_short(); } string format_month() { return RBASE->format_month(); } string format_month_short() { return RBASE->format_month_short(); } #undef RBASE } //------------------------------------------------------------------------ //! global convinience functions //------------------------------------------------------------------------ //! method TimeRange parse(string fmt,string arg) //! parse a date, create relevant object //! fmt is in the format "abc%xdef..." //! where abc and def is matched, and %x is //! one of those time units: //! <pre> //! %Y absolute year //! %y dwim year (70-99 is 1970-1999, 0-69 is 2000-2069) //! %M month (number, name or short name) (needs %y) //! %W week (needs %y) //! %D date (needs %y, %m) //! %d short date (20000304, 000304) //! %a day (needs %y) //! %e weekday (needs %y, %w) //! %h hour (needs %d, %D or %W) //! %m minute (needs %h) //! %s second (needs %m) //! %f fraction of a second (needs %s) //! %t short time (205314, 2053) //! </pre> //! //! note: //! Returns 0 if format doesn't match data. TimeRange parse(string fmt,string arg) { string nfmt; nfmt=replace(fmt," %","%*[ \t]%"); // whitespace -> whitespace #define ALNU "%[^ -,./:-?[-`{-¿]" #define NUME "%[0-9]" nfmt=replace(nfmt, ({"%Y","%y","%M","%W","%D","%a","%e","%h","%m","%s", "%t","%f","%d","%z"}), ({ALNU,ALNU,ALNU,"%d","%d","%d",ALNU,"%d","%d","%d", NUME,NUME,NUME,"%[-+0-9A-Za-z/]"})); array q=Array.map(replace(fmt,({"%*","%%"}),({"",""}))/"%", lambda(string s){ return s[..0];})-({""}); array res=array_sscanf(arg,nfmt); if (sizeof(res)<sizeof(q)) return 0; // parse error mapping m=mkmapping(q,res); TimeRange low; werror("%O\n",m); Calendar cal=this_object(); if (catch { if (m->z) cal=cal->set_timezone(m->z); werror("%O\n",m->z); string x; if (m->Y) m->Y=default_rules->language[f_year_number_from_name](m->Y); if (!zero_type(m->Y) && m->D && (int)m->M) low=m->day=cal->Day(m->Y,(int)m->M,m->D); if (m->d) { int y,mo,d; if (strlen(m->d)==6) { [y,mo,d]=(array(int))(m->d/2); if (y<70) y+=2000; else y+=1900; } else if (strlen(m->d)==8) [y,mo,d]=(array(int))array_sscanf(m->d,"%4s%2s%2s"); else return 0; low=m->day=cal->Day(y,mo,d); } else { if (!zero_type(m->Y)) m->year=cal->Year(m->Y); else if (m->y) { if (strlen(m->y)<3) { m->y=(int)m->y; if (m->y<70) m->y+=2000; else if (m->y<100) m->y+=1900; } low=m->year=cal->Year(m->y); } else low=m->year=cal->Year(); if (m->M) { m->month=low=m->year->month(m->M); } if (m->W) m->week=low=m->year->week("w"+m->W); if (!zero_type(m->D)) if (stringp(m->D)) return 0; else m->day=low=(m->month||cal->Month())->day(m->D); else if (!zero_type(m->a)) m->day=low=m->year->day(m->a); else if (!zero_type(m->e)) m->day=low=(m->week||cal->Week())->day(m->e); else low=m->day=cal->Day(); } if (m->t) { int h,mi,s; if (strlen(m->t)==6) [h,mi,s]=(array(int))(m->t/2); else if (strlen(m->t)==4) [h,mi]=(array(int))(m->t/2),s=0; else return 0; low=m->second=m->day->second(h,mi,s); } else if (!zero_type(m->h) && !zero_type(m->m) && !zero_type(m->s)) low=m->second=m->day->second(m->h,m->m,m->s); else { if (!zero_type(m->h)) low=m->hour=(m->day||cal->Day())->hour(m->h); if (!zero_type(m->m)) low=m->minute=(m->hour||cal->Hour())->minute(m->m); if (!zero_type(m->s)) low=m->second=(m->minute||cal->Minute())->second(m->s); } return low; }) return 0; } //! function Day dwim_day(string date) //! Tries a number of different formats on the given date (in order): //! <pre> //! <ref>parse</ref> format as in //! "%y-%M-%D (%M) -W%W-%e (%e)" "2000-03-20 (Mar) -W12-1 (Mon)" //! "%D%*[ /]%M%*[ /-,]%y" "20/3/2000" "20 mar 2000" "20/3 -00" //! "%e%*[ ]%D%*[ /]%M%*[ /-,]%y" "Mon 20 Mar 2000" "Mon 20/3 2000" //! "%y-%M-%D" "2000-03-20", "00-03-20" //! "%d" "20000320", "000320" //! "-%y%*[ /]%D%*[ /]%M" "-00 20/3" "-00 20 mar" //! "-%y%*[ /]%M%*[ /]%D" "-00 3/20" "-00 march 20" //! "%y%*[ /]%D%*[ /]%M" "00 20 mar" "2000 20/3" //! "%y%*[ /]%M%*[ /]%D" "2000 march 20" //! "%D%*[ -/]%M" "20/3" "20 mar" "20-03" //! "%M%*[ -/]%D" "3/20" "march 20" //! "%e%*[ -/wv]%W%*[ -/]%y" "mon w12 -00" "1 w12 2000" //! "%e%*[ -/wv]%W" "mon w12" //! "%e" "monday" "1" //! "today" "today" //! "last %e" "last monday" //! "next %e" "next monday" //! </pre> //! //! note: //! Casts exception if it fails to dwim out a day. //! "dwim" means do-what-i-mean. /* tests: Calendar.dwim_day("2000-03-20 (Mar) -W12-1 (Mon)"); Calendar.dwim_day("20/3/2000"); Calendar.dwim_day("20 mar 2000"); Calendar.dwim_day("20/3 -00"); Calendar.dwim_day("Mon 20 Mar 2000" ); Calendar.dwim_day("Mon 20/3 2000"); Calendar.dwim_day("2000-03-20"); Calendar.dwim_day("00-03-20"); Calendar.dwim_day("20000320"); Calendar.dwim_day("000320"); Calendar.dwim_day("-00 20/3" ); Calendar.dwim_day("-00 20 mar"); Calendar.dwim_day("-00 3/20" ); Calendar.dwim_day("-00 march 20"); Calendar.dwim_day("00 20 mar" ); Calendar.dwim_day("2000 20/3"); Calendar.dwim_day("2000 march 20"); Calendar.dwim_day("20/3" ); Calendar.dwim_day("20 mar" ); Calendar.dwim_day("20-03"); Calendar.dwim_day("3/20" ); Calendar.dwim_day("march 20"); Calendar.dwim_day("mon w12 -00" ); Calendar.dwim_day("1 w12 2000"); Calendar.dwim_day("mon w12"); Calendar.dwim_day("monday" ); Calendar.dwim_day("1"); Calendar.dwim_day("today"); Calendar.dwim_day("last monday"); Calendar.dwim_day("next monday"); */ cDay dwim_day(string day) { cDay d; foreach ( ({ "%y-%M-%D (%*s) -W%W-%e (%e)", "%D%*[ /]%M%*[- /,]%y", "%M %D%*[- /,]%y", "%e%*[ ]%D%*[ /]%M%*[-/ ,]%y", "%y-%M-%D", "-%y%*[ /]%D%*[ /]%M", "-%y%*[ /]%M%*[ /]%D", "%y%*[ /]%D%*[ /]%M", "%y%*[ /]%M%*[ /]%D", "%D%*[- /]%M", "%M%*[- /]%D", "%e%*[- /wv]%W%*[ -/]%y", "%e%*[- /wv]%W", "%d"}), string dayformat) if ( (d=parse(dayformat,day)) ) return d; cDay t=Day(); if ( (d=parse("%e",day)) ) { if (d>=t) return d; else return (d->week()+1)->place(d); } if (strlen(day)==4) catch { return parse("%M/%D",day/2*"/"); }; if (day=="today") return t; if (day=="tomorrow") return t+1; if (day=="yesterday") return t-1; if (sscanf(day,"last %s",day)) { cDay d=dwim_day(day); return (d->week()-1)->place(d); } if (sscanf(day,"next %s",day)) { cDay d=dwim_day(day); return (d->week()+1)->place(d); } error("Failed to dwim day from %O\n",day); } TimeofDay dwim_time(string what) { string a,h,m,s; TimeofDay t; foreach ( ({ " %z","%z",""}), string zone ) foreach ( ({ "%t", "%h:%*[ :]%m%*[ :]:%s", "%h:%*[ :]%m" }), string todformat ) foreach ( ({ "%y-%M-%D (%*s) -W%W-%e (%e)", "%D%*[ /]%M%*[- /,]%y", "%M %D%*[- /,]%y", "%e%*[ ]%D%*[ /]%M%*[-/ ,]%y", "%y-%M-%D", "%d", "-%y%*[ /]%D%*[ /]%M", "-%y%*[ /]%M%*[ /]%D", "%y%*[ /]%D%*[ /]%M", "%y%*[ /]%M%*[ /]%D", "%D%*[- /]%M", "%M%*[- /]%D", "%e%*[- /wv]%W%*[ -/]%y", "%e%*[- /wv]%W" }), string dayformat ) { if ( (t=parse(dayformat+" "+todformat+zone,what)) ) return t; if ( (t=parse(todformat+zone+" "+dayformat,what)) ) return t; } error("Failed to dwim time from %O\n",what); } //-- auxillary functions------------------------------------------------ //! //! function datetime(int|void unix_time) //! Replacement for localtime; gives back a mapping: //! <pre> //! ([ "year": int // year number (2000 AD=2000, 1 BC==0) //! "month": int(1..) // month of year //! "day": int(1..) // day of month //! "yearday": int(1..) // day of year //! "week": int(1..) // week of year //! "week_day": int(1..) // day of week (depending on calendar) //! "unix": int // unix time //! "julian": float // julian day //! "hour": int(0..) // hour of day, including dst //! "minute": int(0..59) // minute of hour //! "second": int(0..59) // second of minute //! "fraction": float // fraction of second //! "timezone": int // offset to utc, including dst //! ]); //! </pre> //! This is the same as calling <ref>Second</ref>()-><ref to=Second.datetime>datetime</ref>(). //! //! function datetime_name(int|void unix_time) //! function datetime_short_name(int|void unix_time) //! Compat functions; same as <ref>format_iso</ref> //! and <ref>format_iso_short</ref>. //! //! function string format_iso(void|int unix_time) //! function string format_iso_short(void|int unix_time) //! function string format_iso_tod(void|int unix_time) //! function string format_day_iso(void|int unix_time) //! function string format_day_iso_short(void|int unix_time) //! Format the object into nice strings; //! <pre> //! iso "2000-06-02 (Jun) -W22-5 (Fri) 11:57:18 CEST" //! iso_short "2000-06-02 11:57:18" //! iso_tod "11:57:18" //! </pre> // Sane replacement for localtime(). mapping(string:int) datetime(int|void unix_time) { return Second("unix",unix_time||time())->datetime(); } string datetime_name(int|void unix_time) { return Second("unix",unix_time||time())->format_iso(); } string datetime_short_name(int|void unix_time) { return Second("unix",unix_time||time())->format_iso_short(); } string format_iso(int|void unix_time) { return Second("unix",unix_time||time())->format_iso(); } string format_iso_short(int|void unix_time) { return Second("unix",unix_time||time())->format_iso_short(); } string format_iso_tod(int|void unix_time) { return Second("unix",unix_time||time())->format_iso_tod(); } string format_day_iso(int|void unix_time) { return Day("unix",unix_time||time())->format_iso(); } string format_day_iso_short(int|void unix_time) { return Day("unix",unix_time||time())->format_iso_short(); }