Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add the initial implementation.

  • Loading branch information...
commit a646c219dde82c7e6fe42d1c9d171367cd903bf5 1 parent 5d8bf51
@nvie authored
Showing with 162 additions and 0 deletions.
  1. +7 −0 run_tests
  2. +105 −0 tests/test_times.py
  3. +50 −0 times/__init__.py
View
7 run_tests
@@ -0,0 +1,7 @@
+#!/bin/sh
+safe_rg=cat
+if which -s rg; then
+ safe_rg=rg
+fi
+
+/usr/bin/env python -m unittest discover -v -s tests $@ 2>&1 | egrep -v '^test_' | $safe_rg
View
105 tests/test_times.py
@@ -0,0 +1,105 @@
+from unittest import TestCase
+import times, pytz
+from datetime import datetime
+
+
+class TestTimes(TestCase):
+ def setUp(self):
+ est = pytz.timezone('EST')
+ ams = pytz.timezone('Europe/Amsterdam')
+
+ self.sometime_in_newyork = est.localize(datetime(2012, 2, 1, 6, 56, 31))
+ self.sometime_in_amsterdam = ams.localize(datetime(2012, 2, 1, 12, 56, 31))
+ self.sometime_univ = datetime(2012, 2, 1, 11, 56, 31)
+
+
+ def test_now_has_no_tzinfo(self):
+ """times.now() has no attached timezone info"""
+ now = times.now()
+ self.assertIsNone(now.tzinfo)
+
+
+ def test_local_time_with_tzinfo_to_universal(self):
+ """Convert local dates with timezone info to universal date"""
+ ny_time = self.sometime_in_newyork
+ ams_time = self.sometime_in_amsterdam
+
+ self.assertEquals(
+ times.to_universal(ny_time),
+ self.sometime_univ)
+ self.assertEquals(
+ times.to_universal(ams_time),
+ self.sometime_univ)
+
+ self.assertEquals(ny_time.hour, 6)
+ self.assertEquals(
+ times.to_universal(ny_time).hour, 11)
+
+ self.assertEquals(ams_time.hour, 12)
+ self.assertEquals(
+ times.to_universal(ams_time).hour, 11)
+
+
+ def test_local_time_without_tzinfo_to_universal(self):
+ """Convert local dates without timezone info to universal date"""
+
+ # Same as above, but with tzinfo stripped off (as if a NY and Amsterdam
+ # user used datetime.now())
+ ny_time = self.sometime_in_newyork.replace(tzinfo=None)
+ ams_time = self.sometime_in_amsterdam.replace(tzinfo=None)
+
+ # When time has no tzinfo attached, it should be specified explicitly
+ est = pytz.timezone('EST')
+ self.assertEquals(
+ times.to_universal(ny_time, est),
+ self.sometime_univ)
+
+ # ...or simply with a string
+ self.assertEquals(
+ times.to_universal(ams_time, 'Europe/Amsterdam'),
+ self.sometime_univ)
+
+
+ def test_to_universal_rejects_no_tzinfo(self):
+ """Converting to universal times requires source timezone"""
+ now = datetime.now()
+ with self.assertRaises(ValueError):
+ times.to_universal(now)
+
+
+ def test_format_without_tzinfo(self):
+ """Format times without timezone info"""
+ dt = self.sometime_univ
+ auckland = pytz.timezone('Pacific/Auckland')
+ est = pytz.timezone('EST')
+ ams = pytz.timezone('Europe/Amsterdam')
+ self.assertEquals(times.format(dt, auckland), '2012-02-02 00:56:31+1300')
+ self.assertEquals(times.format(dt, ams), '2012-02-01 12:56:31+0100')
+ self.assertEquals(times.format(dt, est), '2012-02-01 06:56:31-0500')
+
+
+ def test_format_refuses_local_times(self):
+ """Format refuses local time input"""
+ auckland = pytz.timezone('Pacific/Auckland')
+ with self.assertRaises(ValueError):
+ times.format(self.sometime_in_amsterdam, auckland)
+
+
+ def test_convert_universal_to_local(self):
+ """Convert universal time to local time"""
+ univ = self.sometime_univ
+ self.assertEquals(
+ times.to_local(univ, pytz.timezone('Europe/Amsterdam')),
+ self.sometime_in_amsterdam)
+ self.assertEquals(
+ times.to_local(univ, pytz.timezone('EST')),
+ self.sometime_in_newyork)
+
+
+ def test_convert_refuses_local_to_local(self):
+ """Refuses to convert between timezones directly"""
+ loc = self.sometime_in_amsterdam
+ with self.assertRaises(ValueError):
+ times.to_local(loc, pytz.timezone('Europe/Amsterdam'))
+
+
View
50 times/__init__.py
@@ -0,0 +1,50 @@
+import datetime
+import pytz
+
+
+fmt = '%Y-%m-%d %H:%M:%S%z'
+"""Default formatting used by times."""
+
+now = datetime.datetime.utcnow
+"""Returns a safe-to-store datetime without tzinfo representing the current moment."""
+
+
+def to_universal(local_dt, timezone=None):
+ """Converts the given local datetime to a universal datetime."""
+ if timezone is not None:
+ if local_dt.tzinfo is not None:
+ raise ValueError('Cannot use timezone-aware datetime with explicit timezone argument.')
+ if isinstance(timezone, basestring):
+ timezone = pytz.timezone(timezone)
+ dt_with_tzinfo = timezone.localize(local_dt)
+ else:
+ if local_dt.tzinfo is None:
+ raise ValueError('Explicit timezone required to convert naive datetimes.')
+ dt_with_tzinfo = local_dt
+ univ_dt = dt_with_tzinfo.astimezone(pytz.utc)
+ return univ_dt.replace(tzinfo=None)
+
+from_local = to_universal
+"""Converts the given local datetime to a universal datetime."""
+
+
+def to_local(dt, timezone):
+ """Converts the given universal datetime to a local representation in the
+ given timezone.
+ """
+ if dt.tzinfo is not None:
+ raise ValueError('Argument `dt` should be a universal time.')
+ return pytz.utc.localize(dt).astimezone(timezone)
+
+from_universal = to_local
+"""Converts the given universal datetime to a local representation in the given
+timezone.
+"""
+
+
+def format(dt, timezone):
+ """Formats the given universal time for display in the given time zone."""
+ if timezone is None:
+ raise ValueError('Please give an explicit timezone.')
+ return to_local(dt, timezone).strftime(fmt)
+
Please sign in to comment.
Something went wrong with that request. Please try again.