Skip to content

Commit 02d9513

Browse files
committed
datetime: Apply localtz patch to include naive date/time support.
Change in datetime.mpy: 8466 -> 8897, so +431 bytes. Signed-off-by: Damien George <damien@micropython.org>
1 parent bdc4706 commit 02d9513

File tree

4 files changed

+65
-180
lines changed

4 files changed

+65
-180
lines changed

python-stdlib/datetime/datetime.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,10 @@ def fromtimestamp(cls, ts, tz=None):
633633
else:
634634
us = 0
635635
if tz is None:
636-
raise NotImplementedError
636+
dt = cls(*_tmod.localtime(ts)[:6], microsecond=us, tzinfo=tz)
637+
s = (dt - datetime(*_tmod.localtime(ts - 86400)[:6]))._us // 1_000_000 - 86400
638+
if s < 0 and dt == datetime(*_tmod.localtime(ts + s)[:6]):
639+
dt._fd = 1
637640
else:
638641
dt = cls(*_tmod.gmtime(ts)[:6], microsecond=us, tzinfo=tz)
639642
dt = tz.fromutc(dt)
@@ -810,13 +813,45 @@ def astimezone(self, tz=None):
810813
return self
811814
_tz = self._tz
812815
if _tz is None:
813-
raise NotImplementedError
816+
ts = int(self._mktime())
817+
os = datetime(*_tmod.localtime(ts)[:6]) - datetime(*_tmod.gmtime(ts)[:6])
814818
else:
815819
os = _tz.utcoffset(self)
816820
utc = self - os
817821
utc = utc.replace(tzinfo=tz)
818822
return tz.fromutc(utc)
819823

824+
def _mktime(self):
825+
def local(u):
826+
return (datetime(*_tmod.localtime(u)[:6]) - epoch)._us // 1_000_000
827+
828+
epoch = datetime.EPOCH.replace(tzinfo=None)
829+
t, us = divmod((self - epoch)._us, 1_000_000)
830+
ts = None
831+
832+
a = local(t) - t
833+
u1 = t - a
834+
t1 = local(u1)
835+
if t1 == t:
836+
u2 = u1 + (86400 if self.fold else -86400)
837+
b = local(u2) - u2
838+
if a == b:
839+
ts = u1
840+
else:
841+
b = t1 - u1
842+
if ts is None:
843+
u2 = t - b
844+
t2 = local(u2)
845+
if t2 == t:
846+
ts = u2
847+
elif t1 == t:
848+
ts = u1
849+
elif self.fold:
850+
ts = min(u1, u2)
851+
else:
852+
ts = max(u1, u2)
853+
return ts + us / 1_000_000
854+
820855
def utcoffset(self):
821856
return None if self._tz is None else self._tz.utcoffset(self)
822857

@@ -840,7 +875,7 @@ def toordinal(self):
840875

841876
def timestamp(self):
842877
if self._tz is None:
843-
raise NotImplementedError
878+
return self._mktime()
844879
else:
845880
return (self - datetime.EPOCH).total_seconds()
846881

python-stdlib/datetime/localtz.patch

Lines changed: 0 additions & 84 deletions
This file was deleted.

python-stdlib/datetime/manifest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
metadata(version="4.0.0")
1+
metadata(version="4.1.0")
22

33
# Originally written by Lorenzo Cappelletti.
44

python-stdlib/datetime/test_datetime.py

Lines changed: 26 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,6 @@
6868
import unittest
6969

7070

71-
# See localtz.patch
72-
try:
73-
datetime.fromtimestamp(0)
74-
LOCALTZ = True
75-
except NotImplementedError:
76-
LOCALTZ = False
77-
78-
7971
if hasattr(datetime, "EPOCH"):
8072
EPOCH = datetime.EPOCH
8173
else:
@@ -1619,11 +1611,8 @@ def test___init__24(self):
16191611
def test_fromtimestamp00(self):
16201612
with LocalTz("Europe/Rome"):
16211613
ts = 1012499103.001234
1622-
if LOCALTZ:
1623-
dt = datetime.fromtimestamp(ts)
1624-
self.assertEqual(dt, d1t1)
1625-
else:
1626-
self.assertRaises(NotImplementedError, datetime.fromtimestamp, ts)
1614+
dt = datetime.fromtimestamp(ts)
1615+
self.assertEqual(dt, d1t1)
16271616

16281617
def test_fromtimestamp01(self):
16291618
ts = 1012506303.001234
@@ -1642,48 +1631,35 @@ def test_fromtimestamp04(self):
16421631
dt = datetime(2010, 10, 31, 0, 30, tzinfo=timezone.utc)
16431632
ts = (dt - EPOCH).total_seconds()
16441633
dt = dt.replace(tzinfo=None) + 2 * td1h
1645-
if LOCALTZ:
1646-
ds = datetime.fromtimestamp(ts)
1647-
self.assertEqual(ds, dt)
1648-
self.assertFalse(ds.fold)
1649-
else:
1650-
self.assertRaises(NotImplementedError, datetime.fromtimestamp, ts)
1634+
ds = datetime.fromtimestamp(ts)
1635+
self.assertEqual(ds, dt)
1636+
self.assertFalse(ds.fold)
16511637

16521638
def test_fromtimestamp05(self):
16531639
with LocalTz("Europe/Rome"):
16541640
dt = datetime(2010, 10, 31, 1, 30, tzinfo=timezone.utc)
16551641
ts = (dt - EPOCH).total_seconds()
16561642
dt = dt.replace(tzinfo=None) + 1 * td1h
1657-
if LOCALTZ:
1658-
ds = datetime.fromtimestamp(ts)
1659-
self.assertEqual(ds, dt)
1660-
self.assertTrue(ds.fold)
1661-
else:
1662-
self.assertRaises(NotImplementedError, datetime.fromtimestamp, ts)
1643+
ds = datetime.fromtimestamp(ts)
1644+
self.assertEqual(ds, dt)
1645+
self.assertTrue(ds.fold)
16631646

16641647
def test_fromtimestamp06(self):
16651648
with LocalTz("US/Eastern"):
16661649
dt = datetime(2020, 11, 1, 5, 30, tzinfo=timezone.utc)
16671650
ts = (dt - EPOCH).total_seconds()
16681651
dt = dt.replace(tzinfo=None) - 4 * td1h
1669-
if LOCALTZ:
1670-
ds = datetime.fromtimestamp(ts)
1671-
self.assertEqual(ds, dt)
1672-
else:
1673-
self.assertRaises(NotImplementedError, datetime.fromtimestamp, ts)
1652+
ds = datetime.fromtimestamp(ts)
1653+
self.assertEqual(ds, dt)
16741654

16751655
def test_fromtimestamp07(self):
16761656
with LocalTz("US/Eastern"):
16771657
dt = datetime(2020, 11, 1, 7, 30, tzinfo=timezone.utc)
16781658
ts = (dt - EPOCH).total_seconds()
16791659
dt = dt.replace(tzinfo=None) - 5 * td1h
1680-
if LOCALTZ:
1681-
ds = datetime.fromtimestamp(ts)
1682-
self.assertEqual(ds, dt)
1683-
else:
1684-
self.assertRaises(NotImplementedError, datetime.fromtimestamp, ts)
1660+
ds = datetime.fromtimestamp(ts)
1661+
self.assertEqual(ds, dt)
16851662

1686-
@unittest.skipIf(not LOCALTZ, "naive datetime not supported")
16871663
def test_now00(self):
16881664
tm = datetime(*mod_time.localtime()[:6])
16891665
dt = datetime.now()
@@ -2004,46 +1980,31 @@ def test_astimezone04(self):
20041980
with LocalTz("Europe/Rome"):
20051981
dt1 = dt27tz2
20061982
dt2 = dt1.replace(tzinfo=None)
2007-
if LOCALTZ:
2008-
self.assertEqual(dt1, dt2.astimezone(tz2))
2009-
else:
2010-
self.assertRaises(NotImplementedError, dt2.astimezone, tz2)
1983+
self.assertEqual(dt1, dt2.astimezone(tz2))
20111984

20121985
def test_astimezone05(self):
20131986
with LocalTz("Europe/Rome"):
20141987
dt1 = dt28tz2
20151988
dt2 = dt1.replace(tzinfo=None)
2016-
if LOCALTZ:
2017-
self.assertEqual(dt1, dt2.astimezone(tz2))
2018-
else:
2019-
self.assertRaises(NotImplementedError, dt2.astimezone, tz2)
1989+
self.assertEqual(dt1, dt2.astimezone(tz2))
20201990

20211991
def test_astimezone06(self):
20221992
with LocalTz("Europe/Rome"):
20231993
dt1 = dt30tz2
20241994
dt2 = dt1.replace(tzinfo=None)
2025-
if LOCALTZ:
2026-
self.assertEqual(dt1, dt2.astimezone(tz2))
2027-
else:
2028-
self.assertRaises(NotImplementedError, dt2.astimezone, tz2)
1995+
self.assertEqual(dt1, dt2.astimezone(tz2))
20291996

20301997
def test_astimezone07(self):
20311998
with LocalTz("Europe/Rome"):
20321999
dt1 = dt31tz2
20332000
dt2 = dt1.replace(tzinfo=None)
2034-
if LOCALTZ:
2035-
self.assertEqual(dt1, dt2.astimezone(tz2))
2036-
else:
2037-
self.assertRaises(NotImplementedError, dt2.astimezone, tz2)
2001+
self.assertEqual(dt1, dt2.astimezone(tz2))
20382002

20392003
def test_astimezone08(self):
20402004
with LocalTz("Europe/Rome"):
20412005
dt1 = dt3
20422006
dt2 = dt1.replace(tzinfo=None)
2043-
if LOCALTZ:
2044-
self.assertEqual(dt1, dt2.astimezone(tz2))
2045-
else:
2046-
self.assertRaises(NotImplementedError, dt2.astimezone, tz2)
2007+
self.assertEqual(dt1, dt2.astimezone(tz2))
20472008

20482009
def test_utcoffset00(self):
20492010
self.assertEqual(dt1.utcoffset(), None)
@@ -2123,77 +2084,50 @@ def test_weekday00(self):
21232084

21242085
def test_timestamp00(self):
21252086
with LocalTz("Europe/Rome"):
2126-
if LOCALTZ:
2127-
self.assertEqual(d1t1.timestamp(), 1012499103.001234)
2128-
else:
2129-
self.assertRaises(NotImplementedError, d1t1.timestamp)
2087+
self.assertEqual(d1t1.timestamp(), 1012499103.001234)
21302088

21312089
def test_timestamp01(self):
21322090
self.assertEqual(d1t1z.timestamp(), 1012506303.001234)
21332091

21342092
def test_timestamp02(self):
21352093
with LocalTz("Europe/Rome"):
21362094
dt = datetime(2010, 3, 28, 2, 30) # doens't exist
2137-
if LOCALTZ:
2138-
self.assertEqual(dt.timestamp(), 1269739800.0)
2139-
else:
2140-
self.assertRaises(NotImplementedError, dt.timestamp)
2095+
self.assertEqual(dt.timestamp(), 1269739800.0)
21412096

21422097
def test_timestamp03(self):
21432098
with LocalTz("Europe/Rome"):
21442099
dt = datetime(2010, 8, 10, 2, 30)
2145-
if LOCALTZ:
2146-
self.assertEqual(dt.timestamp(), 1281400200.0)
2147-
else:
2148-
self.assertRaises(NotImplementedError, dt.timestamp)
2100+
self.assertEqual(dt.timestamp(), 1281400200.0)
21492101

21502102
def test_timestamp04(self):
21512103
with LocalTz("Europe/Rome"):
21522104
dt = datetime(2010, 10, 31, 2, 30, fold=0)
2153-
if LOCALTZ:
2154-
self.assertEqual(dt.timestamp(), 1288485000.0)
2155-
else:
2156-
self.assertRaises(NotImplementedError, dt.timestamp)
2105+
self.assertEqual(dt.timestamp(), 1288485000.0)
21572106

21582107
def test_timestamp05(self):
21592108
with LocalTz("Europe/Rome"):
21602109
dt = datetime(2010, 10, 31, 2, 30, fold=1)
2161-
if LOCALTZ:
2162-
self.assertEqual(dt.timestamp(), 1288488600.0)
2163-
else:
2164-
self.assertRaises(NotImplementedError, dt.timestamp)
2110+
self.assertEqual(dt.timestamp(), 1288488600.0)
21652111

21662112
def test_timestamp06(self):
21672113
with LocalTz("US/Eastern"):
21682114
dt = datetime(2020, 3, 8, 2, 30) # doens't exist
2169-
if LOCALTZ:
2170-
self.assertEqual(dt.timestamp(), 1583652600.0)
2171-
else:
2172-
self.assertRaises(NotImplementedError, dt.timestamp)
2115+
self.assertEqual(dt.timestamp(), 1583652600.0)
21732116

21742117
def test_timestamp07(self):
21752118
with LocalTz("US/Eastern"):
21762119
dt = datetime(2020, 8, 10, 2, 30)
2177-
if LOCALTZ:
2178-
self.assertEqual(dt.timestamp(), 1597041000.0)
2179-
else:
2180-
self.assertRaises(NotImplementedError, dt.timestamp)
2120+
self.assertEqual(dt.timestamp(), 1597041000.0)
21812121

21822122
def test_timestamp08(self):
21832123
with LocalTz("US/Eastern"):
21842124
dt = datetime(2020, 11, 1, 2, 30, fold=0)
2185-
if LOCALTZ:
2186-
self.assertEqual(dt.timestamp(), 1604215800.0)
2187-
else:
2188-
self.assertRaises(NotImplementedError, dt.timestamp)
2125+
self.assertEqual(dt.timestamp(), 1604215800.0)
21892126

21902127
def test_timestamp09(self):
21912128
with LocalTz("US/Eastern"):
21922129
dt = datetime(2020, 11, 1, 2, 30, fold=1)
2193-
if LOCALTZ:
2194-
self.assertEqual(dt.timestamp(), 1604215800.0)
2195-
else:
2196-
self.assertRaises(NotImplementedError, dt.timestamp)
2130+
self.assertEqual(dt.timestamp(), 1604215800.0)
21972131

21982132
def test_isoweekday00(self):
21992133
self.assertEqual(dt1.isoweekday(), d1.isoweekday())

0 commit comments

Comments
 (0)