78fd53 | 2000-07-12 | Mirar (Pontus Hagland) | |
import ".";
inherit Time:Time;
#define this this_object()
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);
static int month_from_yday(int y,int yday);
static array(int) week_from_week(int y,int w);
static array(int) week_from_julian_day(int jd);
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
{
inherit TimeRange;
int y;
int yjd;
int n;
int jd;
int yd;
int m;
int md;
int wy;
int w;
int wd;
int mnd=CALUNKNOWN;
int utco=CALUNKNOWN;
string tzn=0;
Ruleset rules;
constant is_ymd=1;
void create_now()
{
rules=default_rules;
create_unixtime_default(time());
}
void create_unixtime_default(int unixtime)
{
create_julian_day( 2440588+unixtime/86400 );
[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;
}
void make_month()
{
int myd;
[m,md,mnd,myd]=month_from_yday(y,yd);
}
void make_week()
{
int wnd,wjd;
[wy,w,wd,wnd,wjd]=week_from_julian_day(jd);
}
int __hash() { return jd; }
int julian_day()
{
return jd;
}
int unix_time()
{
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; }
mapping datetime(void|int skip_stuff)
{
if (m==CALUNKNOWN) make_month();
if (w==CALUNKNOWN) make_week();
if (skip_stuff)
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(),
"hour": 0,
"minute": 0,
"second": 0,
"fraction": 0.0
]);
}
}
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,
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());
}
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();
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();
}
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);
}
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)
{
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);
}
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);
}
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());
}
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());
}
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());
}
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); });
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)))
{
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()});
}
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);
YMD autopromote() { return this; }
}
function(mixed...:cYear) Year=cYear;
class cYear
{
inherit YMD;
constant is_year=1;
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;
}
}
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();
}
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_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;
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();
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);
}
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);
}
}
function(mixed...:cMonth) Month=cMonth;
class cMonth
{
inherit YMD;
constant is_month=1;
int nd;
int nw;
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;
}
}
string _sprintf(int t)
{
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();
}
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;
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);
if (what->is_day)
return place_day(what->month_day(),what->n,force);
error("place: Incompatible type %O\n",what);
}
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);
}
static int months_to_month(int y,int m);
}
function(mixed...:cWeek) Week=cWeek;
class cWeek
{
inherit YMD;
constant is_week=1;
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;
}
}
string _sprintf(int t)
{
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();
}
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);
if (what->is_day)
return place_day(what->week_day(),what->n,force);
error("place: Incompatible type %O\n",what);
}
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;
return Day("ymd_jd",rules,jd,number_of_days())
->number_of_months();
}
int number_of_weeks()
{
return n;
}
int number_of_days();
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);
}
static int weeks_to_week(int y,int m);
}
function(mixed...:cDay) Day=cDay;
class cDay
{
inherit YMD;
constant is_day=1;
int nw;
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;
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()
{
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;
}
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);
}
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);
}
string iso_name() { return format_ymd(); }
string iso_short_name() { return format_ymd_short(); }
}
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 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
}
TimeRange parse(string fmt,string arg)
{
string nfmt;
nfmt=replace(fmt," %","%*[ \t]%");
#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;
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;
}
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",
|