utils: Timezone fixes and tests
authorMichael Hanselmann <hansmi@google.com>
Fri, 17 Dec 2010 17:38:40 +0000 (18:38 +0100)
committerMichael Hanselmann <hansmi@google.com>
Tue, 21 Dec 2010 13:18:40 +0000 (14:18 +0100)
- Update docstrings to explicitely mention Epoch
- Fix timezone bug in FormatTimestampWithTZ, where it would
  use GMT/UTC when it should use the local timezone
- Add unittests for time formatting functions

Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Iustin Pop <iustin@google.com>

lib/utils.py
test/ganeti.utils_unittest.py

index bef8057..7c7808f 100644 (file)
@@ -1680,8 +1680,8 @@ def RemoveHostFromEtcHosts(hostname):
 def TimestampForFilename():
   """Returns the current time formatted for filenames.
 
-  The format doesn't contain colons as some shells and applications them as
-  separators.
+  The format doesn't contain colons as some shells and applications treat them
+  as separators. Uses the local timezone.
 
   """
   return time.strftime("%Y-%m-%d_%H_%M_%S")
@@ -2838,8 +2838,11 @@ def TailFile(fname, lines=20):
 def FormatTimestampWithTZ(secs):
   """Formats a Unix timestamp with the local timezone.
 
+  @type secs: number
+  @param secs: Seconds since the Epoch (1970-01-01 00:00:00 UTC)
+
   """
-  return time.strftime("%F %T %Z", time.gmtime(secs))
+  return time.strftime("%F %T %Z", time.localtime(secs))
 
 
 def _ParseAsn1Generalizedtime(value):
@@ -3366,7 +3369,8 @@ def FormatTime(val):
   """Formats a time value.
 
   @type val: float or None
-  @param val: the timestamp as returned by time.time()
+  @param val: Timestamp as returned by time.time() (seconds since Epoch,
+    1970-01-01 00:00:00 UTC)
   @return: a string value or N/A if we don't have a valid timestamp
 
   """
index eaa1839..1f80e6b 100755 (executable)
@@ -1682,6 +1682,23 @@ class TestSafeEncode(unittest.TestCase):
 class TestFormatTime(unittest.TestCase):
   """Testing case for FormatTime"""
 
+  @staticmethod
+  def _TestInProcess(tz, timestamp, expected):
+    os.environ["TZ"] = tz
+    time.tzset()
+    return utils.FormatTime(timestamp) == expected
+
+  def _Test(self, *args):
+    # Need to use separate process as we want to change TZ
+    self.assert_(utils.RunInSeparateProcess(self._TestInProcess, *args))
+
+  def test(self):
+    self._Test("UTC", 0, "1970-01-01 00:00:00")
+    self._Test("America/Sao_Paulo", 1292606926, "2010-12-17 15:28:46")
+    self._Test("Europe/London", 1292606926, "2010-12-17 17:28:46")
+    self._Test("Europe/Zurich", 1292606926, "2010-12-17 18:28:46")
+    self._Test("Australia/Sydney", 1292606926, "2010-12-18 04:28:46")
+
   def testNone(self):
     self.failUnlessEqual(FormatTime(None), "N/A")
 
@@ -1695,6 +1712,25 @@ class TestFormatTime(unittest.TestCase):
     FormatTime(int(time.time()))
 
 
+class TestFormatTimestampWithTZ(unittest.TestCase):
+  @staticmethod
+  def _TestInProcess(tz, timestamp, expected):
+    os.environ["TZ"] = tz
+    time.tzset()
+    return utils.FormatTimestampWithTZ(timestamp) == expected
+
+  def _Test(self, *args):
+    # Need to use separate process as we want to change TZ
+    self.assert_(utils.RunInSeparateProcess(self._TestInProcess, *args))
+
+  def test(self):
+    self._Test("UTC", 0, "1970-01-01 00:00:00 UTC")
+    self._Test("America/Sao_Paulo", 1292606926, "2010-12-17 15:28:46 BRST")
+    self._Test("Europe/London", 1292606926, "2010-12-17 17:28:46 GMT")
+    self._Test("Europe/Zurich", 1292606926, "2010-12-17 18:28:46 CET")
+    self._Test("Australia/Sydney", 1292606926, "2010-12-18 04:28:46 EST")
+
+
 class RunInSeparateProcess(unittest.TestCase):
   def test(self):
     for exp in [True, False]: