[Python-checkins] python/nondist/sandbox/datetime datetime.c,1.26,1.27 obj_date.c,1.9,1.10
tim_one@users.sourceforge.net
tim_one@users.sourceforge.net
Tue, 26 Nov 2002 14:51:08 -0800
Update of /cvsroot/python/python/nondist/sandbox/datetime
In directory sc8-pr-cvs1:/tmp/cvs-serv4249
Modified Files:
datetime.c obj_date.c
Log Message:
Some of the unimplemented routines for datetime and datetimetz require
heftier normalization helpers than have been written so far. So recoded a
larger part of datetime.py's "struct tmxxx" gimmick in C, and switched
date normalization to use it. Will switch *all* normalization to use it.
This is slower; I don't care.
Index: datetime.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.c,v
retrieving revision 1.26
retrieving revision 1.27
diff -C2 -d -r1.26 -r1.27
*** datetime.c 26 Nov 2002 05:32:06 -0000 1.26
--- datetime.c 26 Nov 2002 22:51:03 -0000 1.27
***************
*** 364,367 ****
--- 364,505 ----
}
+ /* An internal struct used for miserable normalization tasks. */
+ typedef struct s_tmxxx {
+ long year; /* may be negative on output */
+ long month; /* in 1..12 on output */
+ long day; /* in 1 .. days_in_month(year, month) on output */
+ long hour; /* in 0..59 on output */
+ long minute; /* in 0..59 on output */
+ long second; /* in 0 .. 59 on output */
+ long microsecond; /* in 0..999999 on output */
+ } tmxxx;
+
+ #define TMXXX_CLEAR(PTR_TO_TMXXX) \
+ memset(PTR_TO_TMXXX, 0, sizeof(struct s_tmxxx))
+
+ /* One step of mixed-radix conversion. *lower is the lower unit and *higher
+ * the higher unit, such that 1 higher unit is equal to bound lower units.
+ * Normalize *lower to lie in range(bound), adding carries (if needed) to
+ * higher.
+ * If a carry is needed, adding into *higher may overflow. In that case,
+ * retuns a non-zero value. If everything is OK, returns 0.
+ */
+ static int
+ norm1(long *higher, long *lower, long bound)
+ {
+ assert(bound > 0);
+ if (*lower < 0 || *lower >= bound) {
+ long carry;
+ long new_higher;
+
+ carry = divmod(*lower, bound, lower);
+ assert(0 <= *lower && *lower < bound);
+ new_higher = *higher + carry;
+ if (SIGNED_ADD_OVERFLOWED(new_higher, *higher, carry))
+ return 1;
+ *higher = new_higher;
+ }
+ return 0;
+ }
+
+ static int
+ tmxxx_normalize(tmxxx *p)
+ {
+ int dim; /* days in month */
+ char *msg;
+
+ if (norm1(&p->second, &p->microsecond, 1000000)) {
+ msg = "second component too large";
+ goto Overflow;
+ }
+ assert(0 <= p->microsecond && p->microsecond < 1000000);
+
+ if (norm1(&p->minute, &p->second, 60)) {
+ msg = "minute component too large";
+ goto Overflow;
+ }
+ assert(0 <= p->second && p->second < 60);
+
+ if (norm1(&p->hour, &p->minute, 60)) {
+ msg = "hour component too large";
+ goto Overflow;
+ }
+ assert(0 <= p->minute && p->minute < 60);
+
+ if (norm1(&p->day, &p->hour, 60)) {
+ msg = "hour component too large";
+ goto Overflow;
+ }
+ assert(0 <= p->hour && p->hour < 24);
+
+ /* That was easy. Now it gets muddy: the proper range for day
+ * can't be determined without knowing the correct month and year,
+ * but if day is, e.g., plus or minus a million, the current month
+ * and year values make no sense (and may also be out of bounds
+ * themselves).
+ * Saying 12 months == 1 year should be non-controversial.
+ */
+ if (p->month < 1 || p->month > 12) {
+ --p->month;
+ if (norm1(&p->year, &p->month, 12)) {
+ msg = "year component too large";
+ goto Overflow;
+ }
+ ++p->month;
+ assert (1 <= p->month && p->month <= 12);
+ }
+
+ /* The other routines don't deal correctly with years < 0, so cut
+ * that off now.
+ */
+ if (p->year < 0) {
+ msg = "year component is negative";
+ goto Overflow;
+ }
+ /* Now only day can be out of bounds (year may also be out of bounds
+ * for a datetime object, but we don't care about that here).
+ * If day is out of bounds, what to do is arguable, but at least the
+ * method here is principled and explainable.
+ */
+ dim = days_in_month(p->year, p->month);
+ if (p->day < 1 || p->day > dim) {
+ /* Move day-1 days from the first of the month. First try to
+ * get off cheap if we're only one day out of range
+ * (adjustments for timezone alone can't be worse than that).
+ */
+ if (p->day == 0) {
+ --p->month;
+ if (p->month > 0)
+ p->day = days_in_month(p->year, p->month);
+ else {
+ --p->year;
+ p->month = 12;
+ p->day = 31;
+ }
+ }
+ else if (p->day == dim + 1) {
+ /* move forward a day */
+ ++p->month;
+ p->day = 1;
+ if (p->month > 12) {
+ p->month = 1;
+ ++p->year;
+ }
+ }
+ else {
+ long ordinal = ymd_to_ord(p->year, p->month, 1) +
+ p->day - 1;
+ ord_to_ymd(ordinal, &p->year, &p->month, &p->day);
+ }
+ }
+ assert(p->month > 0);
+ assert(p->day > 0);
+ return 1;
+
+ Overflow:
+ PyErr_SetString(PyExc_OverflowError, msg);
+ return 0;
+ }
+
static PyObject *
new_delta(long days, long seconds, long microseconds)
Index: obj_date.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/obj_date.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -C2 -d -r1.9 -r1.10
*** obj_date.c 26 Nov 2002 06:27:24 -0000 1.9
--- obj_date.c 26 Nov 2002 22:51:04 -0000 1.10
***************
*** 12,74 ****
normalize_date(long *year, long *month, long *day)
{
! long carry, dim;
! /* This is muddy: the proper range for day can't be determined
! * without knowing the correct month and year, but if day is,
! * e.g., plus or minus a million, the current month and year
! * values make no sense (and may also be out of bounds themselves).
! * Saying 12 months == 1 year should be non-controversial.
! */
! if (*month < 1 || *month > 12) {
! long m = *month;
! carry = divmod(m-1, 12, &m);
! *month = m+1;
! *year += carry;
! assert(*month >= 1);
! assert(*month <= 12);
! }
! /*
! * If day is out of bounds, what to do is arguable, but at
! * least the method here is principled and explainable.
! */
! dim = days_in_month(*year, *month);
! if (*day < 1 || *day > dim) {
! /* Move day-1 days from the first of the month. First try to
! * get off cheap if we're only one day out of range
! * (adjustments for timezone alone can't be worse than that).
! */
! if (*day == 0) {
! *month -= 1;
! if (*month > 0)
! *day = days_in_month(*year, *month);
! else {
! *year -= 1;
! *month = 12;
! *day = 31;
! }
! }
! else if (*day == dim + 1) {
! /* move forward a day */
! *month += 1;
! *day = 1;
! if (*month > 12) {
! *month = 1;
! *year += 1;
! }
! }
! else {
! long ordinal = ymd_to_ord(*year, *month, 1) +
! *day - 1;
! ord_to_ymd(ordinal, year, month, day);
}
}
! assert(*month > 0);
! assert(*day > 0);
! if (*year < MINYEAR || *year > MAXYEAR) {
! PyErr_SetString(PyExc_OverflowError,
! "date value out of range");
! return 0;
! }
! return 1;
}
--- 12,36 ----
normalize_date(long *year, long *month, long *day)
{
! tmxxx t;
! int result;
! TMXXX_CLEAR(&t);
! t.year = *year;
! t.month = *month;
! t.day = *day;
! result = tmxxx_normalize(&t);
! if (result > 0) {
! /* looks good */
! *year = t.year;
! *month = t.month;
! *day = t.day;
!
! if (*year < MINYEAR || *year > MAXYEAR) {
! PyErr_SetString(PyExc_OverflowError,
! "date value out of range");
! result = 0;
}
}
! return result;
}