[Jython-checkins] jython: Restores __tojava__ to datetime.{date, datetime, time} classes

jim.baker jython-checkins at python.org
Tue Mar 3 06:24:52 CET 2015


https://hg.python.org/jython/rev/67210f355576
changeset:   7602:67210f355576
user:        Jim Baker <jim.baker at rackspace.com>
date:        Mon Mar 02 22:24:31 2015 -0700
summary:
  Restores __tojava__ to datetime.{date, datetime, time} classes

Also adds timezone support compared to original implementation. Tests
conversions in test_datetime_jy, and this test now runs in regrtest.

Fixes http://bugs.jython.org/issue2271

files:
  Lib/datetime.py              |   88 ++++++++++++++++++
  Lib/test/test_datetime_jy.py |  111 +++++++++++++++++++++-
  2 files changed, 192 insertions(+), 7 deletions(-)


diff --git a/Lib/datetime.py b/Lib/datetime.py
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -20,6 +20,49 @@
 import time as _time
 import math as _math
 import struct as _struct
+import sys as _sys
+
+if _sys.platform.startswith('java'):
+    from java.lang import Object
+    from java.sql import Date, Time, Timestamp
+    from java.util import Calendar, GregorianCalendar, TimeZone
+    from org.python.core import Py
+
+    # Java does have the distinction between naive and aware times and
+    # datetimes provided by Python. To avoid using the local timezone,
+    # we assume it is UTC as this is most conventional (and least
+    # surprising). See for example - or in this library itself! -
+    # https://docs.python.org/2.7/library/datetime.html#datetime.datetime.utcnow
+    # - tzinfo is None
+
+    _utc_timezone = TimeZone.getTimeZone("UTC")
+    _is_jython = True
+
+    def _make_java_utc_calendar():
+        cal = GregorianCalendar(_utc_timezone)
+        cal.clear()
+        return cal
+
+    def _make_java_calendar(d):
+        tzinfo = d.tzinfo
+        if tzinfo == None:
+            cal = GregorianCalendar(_utc_timezone)
+        else:
+            tzname = tzinfo.tzname(d)
+            if tzname != None:
+                # Only applicable if Python code is using the Olson
+                # timezone database, which is what Java uses
+                tz = TimeZone.getTimeZone(tzname)
+                if tz.getID() != tzname and tz.getID() == "GMT":
+                    cal = GregorianCalendar(_utc_timezone)
+                    cal.set(Calendar.DST_OFFSET, int(tzinfo.dst(d).total_seconds() * 1000))
+                    cal.set(Calendar.ZONE_OFFSET, int(tzinfo.utcoffset(d).total_seconds() * 1000))
+                else:
+                    cal = GregorianCalendar(tz)
+        return cal
+else:
+    _is_jython = False
+
 
 def _cmp(x, y):
     return 0 if x == y else 1 if x > y else -1
@@ -1026,6 +1069,18 @@
     def __reduce__(self):
         return (self.__class__, self._getstate())
 
+    if _is_jython:
+        def __tojava__(self, java_class):
+            if java_class not in (Calendar, Date, Object):
+                return Py.NoConversion
+            calendar = _make_java_utc_calendar()
+            calendar.set(self.year, self.month - 1, self.day)
+            if java_class == Calendar:
+                return calendar
+            else:
+                return Date(calendar.getTimeInMillis())
+
+
 _date_class = date  # so functions w/ args named "date" can get at the class
 
 date.min = date(1, 1, 1)
@@ -1431,6 +1486,21 @@
     def __reduce__(self):
         return (time, self._getstate())
 
+    if _is_jython:
+        def __tojava__(self, java_class):
+            if java_class not in (Calendar, Time, Object):
+                return Py.NoConversion
+            calendar = _make_java_calendar(self)
+            if calendar == Py.NoConversion:
+                return Py.NoConversion
+            epoch_ms = (self.hour * 3600 + self.minute * 60 + self.second) * 1000 + self.microsecond // 1000
+            calendar.setTimeInMillis(epoch_ms)
+            if java_class == Calendar:
+                return calendar
+            else:
+                return Time(calendar.getTimeInMillis())
+
+
 _time_class = time  # so functions w/ args named "time" can get at the class
 
 time.min = time(0, 0, 0)
@@ -1928,6 +1998,24 @@
     def __reduce__(self):
         return (self.__class__, self._getstate())
 
+    if _is_jython:
+        def __tojava__(self, java_class):
+            if java_class not in (Calendar, Timestamp, Object):
+                return Py.NoConversion
+            calendar = _make_java_calendar(self)
+            if calendar == Py.NoConversion:
+                return Py.NoConversion
+            calendar.set(self.year, self.month - 1, self.day,
+                         self.hour, self.minute, self.second)
+
+            if java_class == Calendar:
+                calendar.set(Calendar.MILLISECOND, self.microsecond // 1000)
+                return calendar
+            else:
+                timestamp = Timestamp(calendar.getTimeInMillis())
+                timestamp.setNanos(self.microsecond * 1000)
+                return timestamp
+
 
 datetime.min = datetime(1, 1, 1)
 datetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999)
diff --git a/Lib/test/test_datetime_jy.py b/Lib/test/test_datetime_jy.py
--- a/Lib/test/test_datetime_jy.py
+++ b/Lib/test/test_datetime_jy.py
@@ -6,30 +6,127 @@
 from datetime import time
 from datetime import date, datetime
 
-from java.util import Calendar
-from java.sql import Date
+from java.util import Calendar, GregorianCalendar
+from java.sql import Date, Time, Timestamp
 
 
-class TestJavaDatetime(unittest.TestCase):
+class TestCalendar(unittest.TestCase):
 
     def test_datetime(self):
         self.assertTrue(hasattr(datetime, "__tojava__"))
         x = datetime(2007, 1, 3)
         y = x.__tojava__(Calendar)
-        self.assertTrue(isinstance(y, Calendar))
+        self.assertIsInstance(y, GregorianCalendar)
+        self.assertEqual(y.get(Calendar.YEAR), 2007)
+        self.assertEqual(y.get(Calendar.MONTH), 0)
+        self.assertEqual(y.get(Calendar.DAY_OF_MONTH), 3)
+        self.assertEqual(y.get(Calendar.HOUR), 0)
+        self.assertEqual(y.get(Calendar.MINUTE), 0)
+        self.assertEqual(y.get(Calendar.SECOND), 0)
 
     def test_date(self):
         self.assertTrue(hasattr(date, "__tojava__"))
         x = date(2007, 1, 3)
         y = x.__tojava__(Calendar)
-        self.assertTrue(isinstance(y, Calendar))
+        self.assertIsInstance(y, GregorianCalendar)
+        self.assertEqual(y.get(Calendar.YEAR), 2007)
+        self.assertEqual(y.get(Calendar.MONTH), 0)
+        self.assertEqual(y.get(Calendar.DAY_OF_MONTH), 3)
 
     def test_time(self):
         self.assertTrue(hasattr(time, "__tojava__"))
         x = time(1, 3)
         y = x.__tojava__(Calendar)
-        self.assertTrue(isinstance(y, Calendar))
+        self.assertIsInstance(y, GregorianCalendar)
+        # Note obvious implementation details from GregorianCalendar
+        # and its definition in terms of the epoch
+        self.assertEqual(y.get(Calendar.YEAR), 1970)
+        self.assertEqual(y.get(Calendar.MONTH), 0)
+        self.assertEqual(y.get(Calendar.DAY_OF_MONTH), 1)
+        self.assertEqual(y.get(Calendar.HOUR), 1)
+        self.assertEqual(y.get(Calendar.MINUTE), 3)
+        self.assertEqual(y.get(Calendar.SECOND), 0)
+
+
+class TestTimezone(unittest.TestCase):
+
+    def test_olson(self):
+        class GMT1(tzinfo):
+            def utcoffset(self, dt):
+                return timedelta(hours=1)
+            def dst(self, dt):
+                return timedelta(0)
+            def tzname(self,dt):
+                return "Europe/Prague"
+
+        self.assertTrue(hasattr(datetime, "__tojava__"))
+        x = datetime(2007, 1, 3, tzinfo=GMT1())
+        y = x.__tojava__(Calendar)
+        self.assertIsInstance(y, GregorianCalendar)
+        self.assertEqual(y.get(Calendar.YEAR), 2007)
+        self.assertEqual(y.get(Calendar.MONTH), 0)
+        self.assertEqual(y.get(Calendar.DAY_OF_MONTH), 3)
+        self.assertEqual(y.get(Calendar.HOUR), 0)
+        self.assertEqual(y.get(Calendar.MINUTE), 0)
+        self.assertEqual(y.get(Calendar.SECOND), 0)
+        self.assertEqual(y.getTimeZone().getID(), "Europe/Prague")
+
+    def test_offset(self):
+        class Offset(tzinfo):
+            def utcoffset(self, dt):
+                return timedelta(hours=1, minutes=15)
+            def dst(self, dt):
+                return timedelta(seconds=-900)
+            def tzname(self,dt):
+                return "Foo/Bar"
+
+        self.assertTrue(hasattr(datetime, "__tojava__"))
+        x = datetime(2007, 1, 3, tzinfo=Offset())
+        y = x.__tojava__(Calendar)
+        self.assertIsInstance(y, GregorianCalendar)
+        self.assertEqual(y.get(Calendar.YEAR), 2007)
+        self.assertEqual(y.get(Calendar.MONTH), 0)
+        self.assertEqual(y.get(Calendar.DAY_OF_MONTH), 3)
+        self.assertEqual(y.get(Calendar.HOUR), 0)
+        self.assertEqual(y.get(Calendar.MINUTE), 0)
+        self.assertEqual(y.get(Calendar.SECOND), 0)
+        self.assertEqual(y.getTimeZone().getID(), "UTC")
+        self.assertEqual(y.get(Calendar.DST_OFFSET), -900 * 1000)
+        self.assertEqual(y.get(Calendar.ZONE_OFFSET), 4500 * 1000)
+
+
+class TestSQL(unittest.TestCase):
+
+    def test_datetime(self):
+        self.assertTrue(hasattr(datetime, "__tojava__"))
+        x = datetime(2007, 1, 3)
+        y = x.__tojava__(Timestamp)
+        self.assertIsInstance(y, Timestamp)
+        self.assertEqual(y.getTime(), (x - datetime(1970, 1, 1)).total_seconds() * 1000)
+
+    def test_date(self):
+        self.assertTrue(hasattr(date, "__tojava__"))
+        x = date(2007, 1, 3)
+        y = x.__tojava__(Date)
+        self.assertIsInstance(y, Date)
+        self.assertEqual(y.getTime(), (x - date(1970, 1, 1)).total_seconds() * 1000)
+
+    def test_time(self):
+        self.assertTrue(hasattr(time, "__tojava__"))
+        x = time(1, 3)
+        y = x.__tojava__(Time)
+        self.assertIsInstance(y, Time)
+        epoch = y.getTime()/1000.
+        self.assertEqual(epoch // 3600, 1)   # 1 hour
+        self.assertEqual(epoch % 3600, 180)  # 3 minutes
+
+
+def test_main():
+    test_support.run_unittest(
+        TestCalendar,
+        TestSQL,
+        TestTimezone)
 
 
 if __name__ == '__main__':
-    unittest.main()
+    test_main()

-- 
Repository URL: https://hg.python.org/jython


More information about the Jython-checkins mailing list