Branch: Tag:

2008-05-23

2008-05-23 00:31:41 by Martin Stjernholm <mast@lysator.liu.se>

Cleaned up year-day (yd) handling so that it never goes below 1.
Instead be careful to use either year (y) or week-year (wy) depending
on context.

Fixed inconsistent range handling in year() and years() that made them
almost but not quite zero-based. Now they are one-based just like
day()/days(), month()/months() etc.

Cleaned up conversion between weeks and years: E.g. if a week has days
in two years then converting it to years will produce a range of both
years. The attempt to always map a week to a single year is gone since
it's inconsistent with how other overlapping time ranges are handled.
If the user wants to convert a week to the year it "unambiguously"
belongs to, (s)he can do Calendar.ISO.Year(week->year_no()).

Needless to say much of this is incompatible. Compat goo exists for
#pike 7.6.

Also fixed some types.

Rev: lib/modules/Calendar.pmod/YMD.pike:1.32

25:   static array(int) year_month_from_month(int y,int m); // [y,m,ndays,myd]   static array(int) month_from_yday(int y,int yday); // [m,day-of-month,ndays,myd]    - 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 array(int) week_from_week(int y,int w); // [wy,w,wd,ndays,wjd] + static array(int) week_from_julian_day(int jd); // [wy,w,wd,ndays,wjd]      static string f_month_name_from_number;   static string f_month_shortname_from_number;
56:      // --- generic for all YMD:    +  // The correct year to use in context with yjd, yd, m and md is y. +  // The correct year to use in context with w is wy. +  // The correct year to use without context is year_no() (= either y or wy). +     int y; // year    int yjd; // julian day of the first day of the year   
226:       string year_name()    { -  return rules->language[f_year_name_from_number](y); +  return rules->language[f_year_name_from_number](year_no());    }       string week_name()
265:    return rules->language[f_week_day_shortname_from_number](wd);    }    -  int leap_year() { return year_leap_year(y); } +  int leap_year() { return year_leap_year(year_no()); }       int hour_no() { return 0; }    int minute_no() { return 0; }
300:   //! 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. + //! + //! note: + //! If this function is called in a Week object that begins with + //! the first week of a year, it returns the previous year if that + //! is where the week starts. To keep the representation + //! unambiguous, the returned week number is then one more than + //! the number of weeks in that year. + //! + //! E.g. Week(2008,1)->datetime() will return year 2007 and week + //! 53 since the first week of 2008 starts in 2007.       mapping datetime(void|int skip_stuff)    {    if (m==CALUNKNOWN) make_month();    if (w==CALUNKNOWN) make_week(); -  +  +  int week; +  if (y == wy) +  week = w; +  else +  // w is the first week of the year and it begins in the +  // previous year, so we know y == wy - 1 and w == 1. +  week = week_from_week (y, 0)[1] + 1; +     if (skip_stuff) // called from timeofday    return ([ "year": y,    "month": m,    "day": md,    "yearday": yd-1, -  "week": w, +  "week": week,    "week_day": compat_week_day(wd)    ]);    else
319:    "month": m,    "day": md,    "yearday": yd-1, -  "week": w, +  "week": week,    "week_day": compat_week_day(wd),    "timezone": utc_offset(),    "julian": jd,
396:    if (m==CALUNKNOWN) make_month();    if (w==CALUNKNOWN) make_week();    return sprintf("%04d-%02d-%02d (%s) -W%02d-%d (%s)", -  ((yd < 1)?y-1:y),m,md, +  y,m,md,    month_shortname(),    w,wd, // fixme - what weekday?    week_day_shortname());
429:    ("SunMonTueWedThuFriSat"/3)[compat_week_day(wd)],    md,    ("zzzJanFebMarAprMayJunJulAugSepOctNovDec"/3)[m], -  ((yd < 1)?y-1:y)); +  y);    }       string format_ext_time_short()
440:    return    sprintf("%s, %d %s %d 00:00:00 GMT",    week_day_shortname(), -  month_day(),month_shortname(),year_no()); +  month_day(),month_shortname(),y);    }       string format_ymd()    {    if (m==CALUNKNOWN) make_month(); -  return sprintf("%04d-%02d-%02d",((yd < 1)?y-1:y),m,md); +  return sprintf("%04d-%02d-%02d",y,m,md);    }       string format_ymd_short()    {    if (m==CALUNKNOWN) make_month(); -  return sprintf("%04d%02d%02d",((yd < 1)?y-1:y),m,md); +  return sprintf("%04d%02d%02d",y,m,md);    }       string format_ymd_xshort()    {    if (m==CALUNKNOWN) make_month(); -  return sprintf("%02d%02d%02d",((yd < 1)?y-1:y)%100,m,md); +  return sprintf("%02d%02d%02d",y%100,m,md);    }       string format_iso_week()
475:       string format_week()    { -  return sprintf("%04d-%s",y,week_name()); +  if (w==CALUNKNOWN) make_week(); +  return sprintf("%04d-%s",wy,week_name());    }       string format_week_short()    { -  return sprintf("%04d%s",y,week_name()); +  if (w==CALUNKNOWN) make_week(); +  return sprintf("%04d%s",wy,week_name());    }       string format_month()
624:    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 (wd==1) return Week("ymd_yjwm",rules,y,yjd,jd,wy,w,t->n*n,md,m,mnd);    }      // fallback on days
689:    return 1+y-year_from_julian_day(jd+m-1)[0];    }    -  array(cYear) years(int ...range) +  array(cYear) years(void|int from, void|int to)    { -  int from=1,n=number_of_years(),to=n; +  int n=number_of_years();    -  if (sizeof(range)) -  if (sizeof(range)<2) -  error("Illegal numbers of arguments to days()\n"); +  if (zero_type (from)) { +  from = 1; +  to = n; +  }    else -  +  if (zero_type (to)) +  error("Illegal numbers of arguments to years()\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 ({}); +  if (from>n) return ({}); else if (from<1) from=1; +  if (to>n) to=n; else if (to<from) return ({});    }       return map(enumerate(1+to-from,1,y+from-1),
710:       cYear year(void|int m)    { -  if (!m || (!n&&m==-1)) +  if (zero_type (m)) m=1; +  +  if (!n&&m==-1)    return Year("ymd_y",rules,y,yjd,1);    -  if (m<0) m=number_of_years()+m; +  if (m<0) m += 1 + number_of_years();       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); +  error("Not in range (Year 1..%d exist)\n", +  number_of_years());    }      
726:       int number_of_days();    -  array(cDay) days(int ...range) +  array(cDay) days(void|int from, void|int to)    { -  int from=1,n=number_of_days(),to=n; +  int n=number_of_days();    -  if (sizeof(range)) -  if (sizeof(range)<2) -  error("Illegal numbers of arguments to days()\n"); +  if (zero_type (from)) { +  from = 1; +  to = n; +  }    else -  +  if (zero_type (to)) +  error("Illegal number 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 ({});    }
748:       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;
775:    return res;    }    -  cDay day(int ... mp) +  cDay day(void|int m, mixed... ignored)    { -  int m; -  if (sizeof(mp)) m=mp[0]; -  else m=1; +  if (zero_type (m)) m=1;       if (!n)    return Day("ymd_yd",rules,y,yjd,jd,yd,1);
1257:       static void convert_from(TimeRange other)    { -  if (other->y) -  create(other->y); -  else + #if 0 +  // The following is disabled since it leads to inconsistent +  // behavior with other time ranges when they are converted to +  // partly overlapping time ranges. If the user wants to convert +  // a week to the year it "unambiguously" belongs to, (s)he can +  // do Calendar.ISO.Year(week->year_no()). +  if (other->is_week) { +  // Weeks aren't even on years but they still unambiguously +  // belong to years. We therefore convert using the week year +  // instead of the default method that uses the julian day. +  create ("ymd_yn", other->rules, other->wy, +  other->n > 1 ? +  other->week(-1)->wy - other->wy + 1 : +  other->n); +  return; +  } + #endif +     ::convert_from(other);    if (other->number_of_years)    n=other->number_of_years();
1278:    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 +  if (!force && week->wy!=y) return 0; // not this year    return week;    }   
1527:    month_shortname(),    mo->month_shortname(),    year_name()); -  return nice_print()+" .. "+month(-1)->nice_print(); +  return nice_print()+" .. "+mo->nice_print();    }    -  cDay beginning() +  TimeRange beginning()    {    return Month("ymd_yjmw",rules,y,yjd,jd,m,0,wd,w,wy)    ->autopromote();    }    -  cDay end() +  TimeRange end()    {    return Month("ymd_ym",rules,y,m+n,0)    ->autopromote();
1697:   {    inherit YMD;    +  // Note: wy, w and wd are never CALUNKNOWN in this class. +     constant is_week=1;      //!
1723:    {    case "ymd_yw":    rules=args[1]; -  y=args[2]; +  wy=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); +  [wy,w,wd,int nd,jd]=week_from_week(wy,w); +  int wyjd=julian_day_from_year(wy); +  if (wyjd > jd) +  y = wy - 1, yjd = julian_day_from_year (y); +  else +  y = wy, yjd = wyjd;    yd=1+jd-yjd; -  wy=y; +     if (n!=1) nd=CALUNKNOWN;    return;    case "ymd_yjwm":
1739:    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]; +  wy=args[5]; +  w=args[6]; +  n=args[7]; +  md=args[8]; +  m=args[9]; +  mnd=args[10];    wd=1; -  wy=y; -  // Adjust according to julian day (typically to -  // correct the year). -  create_julian_day(jd); +     nd=CALUNKNOWN;    return;    case "ymd_jd":
1763:    if (intp(args[0]) && sizeof(args)==2)    {    create("ymd_yw",default_rules,args[0],args[1],1); -  if (y!=args[0]) +  if (wy!=args[0]) +  // FIXME: Allow weeks 0 and 53/54 if they contain +  // at least one day in this year.    error("Week %d doesn't exist in %d\n",args[1],args[0]);    return;    }
1781:    else    {    int zwd; -  [y,w,zwd,int nd,jd]=week_from_julian_day(_jd); -  yjd=julian_day_from_year(y); +  [wy,w,zwd,int nd,jd]=week_from_julian_day(_jd); +  [y,yjd]=year_from_julian_day(_jd);    yd=1+jd-yjd;       n=1;    wd=1; -  wy=y; +     md=m=CALUNKNOWN; // unknown    }    }
1809:    }    }    +  int year_no() +  { +  return wy>0?wy:-1+wy; +  } +  +  int year_day() +  //! Can be less than 1 for the first week of the year if it begins +  //! in the previous year. +  { +  return y != wy ? 1 + jd - julian_day_from_year (wy) : yd; +  } +     string nice_print()    {    return
1830:    {    if (!n) return day()->nice_print()+" "+minute()->nice_print()+" sharp";    cWeek wo=week(-1); -  if (wo->y==y) +  if (wo->wy==wy)    return sprintf("%s..%s %s",    week_name(),    wo->week_name(),    year_name()); -  return nice_print()+" .. "+week(-1)->nice_print(); +  return nice_print()+" .. "+wo->nice_print();    }    -  cDay beginning() +  TimeRange beginning()    { -  return Week("ymd_yjwm",rules,y,yjd,jd,w,0,md,m,mnd) +  return Week("ymd_yjwm",rules,y,yjd,jd,wy,w,0,md,m,mnd)    ->autopromote();    }    -  cDay end() +  TimeRange end()    { -  return Week("ymd_yw",rules,y,w+n,0) +  return Week("ymd_yw",rules,wy,w+n,0)    ->autopromote();    }   
1859:       if (to->is_week)    { -  int n1=weeks_to_week(to->y,to->w); +  int n1=weeks_to_week(to->wy,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) +  return Week("ymd_yjwm",rules,y,yjd,jd,wy,w,n1,md,m,mnd)    ->autopromote();    }   
1891:    TimeRange _move(int x,YMD step)    {    if (step->is_week) -  return Week("ymd_yw",rules,y,w+x*step->n,n) +  return Week("ymd_yw",rules,wy,w+x*step->n,n)    ->autopromote();    -  if (step->is_year) -  return year()->add(x,step)->place(this,1); +  if (step->is_year) { +  TimeRange stepped = year()->add(x,step); +  if (TimeRange placed = stepped->place(this,0)) +  return placed; +  // If we couldn't place our week in the target year it means +  // we're in week 53 and the target year got only 52 weeks. We +  // return the closest week of the target year, i.e. week 52. +  return Week ("ymd_yw", rules, stepped->y, 52, n); +  }       if (step->number_of_days)    return Day("ymd_jd",rules,
1927:    return month()->place(what,force); // just fallback       if (what->is_week) -  return Week("ymd_yw",rules,y,w,what->number_of_weeks()); +  return Week("ymd_yw",rules,wy,w,what->number_of_weeks());       if (what->is_day)    return place_day(what->week_day(),what->n,force);
1942:       int number_of_years()    { -  if (n<=1) return 1; +  if (n<=1 && y == wy) return 1;    -  [int y2,int w2,int wd2,int nd2,int jd2]=week_from_week(y,w+n); +  [int y2,int w2,int wd2,int nd2,int jd2]=week_from_week(wy,w+n);    return 1+y2-y;    }   
1997:       cWeek set_ruleset(Calendar.Ruleset r)    { -  return Week("ymd_yjwm",r,y,yjd,jd,w,n,md,m,mnd); +  return Week("ymd_yjwm",r,y,yjd,jd,wy,w,n,md,m,mnd);    }      // --- needs to be defined
2053:    w=args[10];    wd=args[11];    mnd=args[12]; -  // Adjust according to julian day (typically to -  // correct the year in case weeks are involved). -  create_julian_day(jd); +     nw=CALUNKNOWN;    return;    case "ymd_yd":
2202:    return nice_print()+" .. "+day(-1)->nice_print();    }    -  cDay beginning() +  TimeRange beginning()    {    return Day("ymd_ydmw",rules,y,yjd,jd,yd,0,m,md,wy,w,wd,mnd);    }    -  cDay end() +  TimeRange end()    {    return Day("ymd_jd",rules,jd+n,0)    ->autopromote();