If provided, *tz* must be an instance of a :class:`tzinfo` subclass, and its
:meth:`utcoffset` and :meth:`dst` methods must not return ``None``. If *self*
- is naive (``self.tzinfo is None``), it is presumed to represent time in the
- system timezone.
+ is naive, it is presumed to represent time in the system timezone.
If called without arguments (or with ``tz=None``) the system local
timezone is assumed for the target timezone. The ``.tzinfo`` attribute of the converted
mytz = self.tzinfo
if mytz is None:
mytz = self._local_timezone()
+ myoffset = mytz.utcoffset(self)
+ else:
+ myoffset = mytz.utcoffset(self)
+ if myoffset is None:
+ mytz = self.replace(tzinfo=None)._local_timezone()
+ myoffset = mytz.utcoffset(self)
if tz is mytz:
return self
# Convert self to UTC, and attach the new time zone object.
- myoffset = mytz.utcoffset(self)
- if myoffset is None:
- raise ValueError("astimezone() requires an aware datetime")
utc = (self - myoffset).replace(tzinfo=tz)
# Convert from UTC to tz's local time.
base = cls(2000, 2, 29)
self.assertRaises(ValueError, base.replace, year=2001)
+ @support.run_with_tz('EDT4')
def test_astimezone(self):
- return # The rest is no longer applicable
- # Pretty boring! The TZ test is more interesting here. astimezone()
- # simply can't be applied to a naive object.
dt = self.theclass.now()
- f = FixedOffset(44, "")
- self.assertRaises(ValueError, dt.astimezone) # naive
+ f = FixedOffset(44, "0044")
+ dt_utc = dt.replace(tzinfo=timezone(timedelta(hours=-4), 'EDT'))
+ self.assertEqual(dt.astimezone(), dt_utc) # naive
self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
- self.assertRaises(ValueError, dt.astimezone, f) # naive
- self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
+ dt_f = dt.replace(tzinfo=f) + timedelta(hours=4, minutes=44)
+ self.assertEqual(dt.astimezone(f), dt_f) # naive
+ self.assertEqual(dt.astimezone(tz=f), dt_f) # naive
class Bogus(tzinfo):
def utcoffset(self, dt): return None
def dst(self, dt): return timedelta(0)
bog = Bogus()
self.assertRaises(ValueError, dt.astimezone, bog) # naive
- self.assertRaises(ValueError,
- dt.replace(tzinfo=bog).astimezone, f)
+ self.assertEqual(dt.replace(tzinfo=bog).astimezone(f), dt_f)
class AlsoBogus(tzinfo):
def utcoffset(self, dt): return timedelta(0)
alsobog = AlsoBogus()
self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
+ class Broken(tzinfo):
+ def utcoffset(self, dt): return 1
+ def dst(self, dt): return 1
+ broken = Broken()
+ dt_broken = dt.replace(tzinfo=broken)
+ with self.assertRaises(TypeError):
+ dt_broken.astimezone()
+
def test_subclass_datetime(self):
class C(self.theclass):
return NULL;
if (!HASTZINFO(self) || self->tzinfo == Py_None) {
+ naive:
self_tzinfo = local_timezone_from_local(self);
if (self_tzinfo == NULL)
return NULL;
Py_DECREF(self_tzinfo);
if (offset == NULL)
return NULL;
+ else if(offset == Py_None) {
+ Py_DECREF(offset);
+ goto naive;
+ }
+ else if (!PyDelta_Check(offset)) {
+ Py_DECREF(offset);
+ PyErr_Format(PyExc_TypeError, "utcoffset() returned %.200s,"
+ " expected timedelta or None", Py_TYPE(offset)->tp_name);
+ return NULL;
+ }
/* result = self - offset */
result = (PyDateTime_DateTime *)add_datetime_timedelta(self,
(PyDateTime_Delta *)offset, -1);