git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[StGit PATCH 1/3] Parse the date instead of treating it as an opaque string

From
Karl Hasselström <kha@treskal.com>
Date
Feb 1, 2008, 07:50 UTC
Message-ID
<20080201074957.7905.52335.stgit@yoghurt>
In-Reply-To
<20080201074708.7905.98305.stgit@yoghurt>

This is needed for when we want to display it, since the seconds-since-the-epoch format isn't that human-friendly.

Signed-off-by: Karl Hasselström <kha@treskal.com>
---
 stgit/lib/git.py |   76 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 72 insertions(+), 4 deletions(-)
diff --git a/stgit/lib/git.py b/stgit/lib/git.py
index d75f724..50dc4f1 100644
--- a/stgit/lib/git.py
+++ b/stgit/lib/git.py
@@ -1,10 +1,17 @@
 import os, os.path, re
+from datetime import datetime, timedelta, tzinfo
+
 from stgit import exception, run, utils
 from stgit.config import config
 
 class RepositoryException(exception.StgException):
     pass
 
+class DateException(exception.StgException):
+    def __init__(self, string, type):
+        exception.StgException.__init__(
+            self, '"%s" is not a valid %s' % (string, type))
+
 class DetachedHeadException(RepositoryException):
     def __init__(self):
         RepositoryException.__init__(self, 'Not on any branch')
@@ -26,6 +33,65 @@ def make_defaults(defaults):
             return None
     return d
 
+class TimeZone(tzinfo, Repr):
+    def __init__(self, tzstring):
+        m = re.match(r'^([+-])(\d{2}):?(\d{2})$', tzstring)
+        if not m:
+            raise DateException(tzstring, 'time zone')
+        sign = int(m.group(1) + '1')
+        try:
+            self.__offset = timedelta(hours = sign*int(m.group(2)),
+                                      minutes = sign*int(m.group(3)))
+        except OverflowError:
+            raise DateException(tzstring, 'time zone')
+        self.__name = tzstring
+    def utcoffset(self, dt):
+        return self.__offset
+    def tzname(self, dt):
+        return self.__name
+    def dst(self, dt):
+        return timedelta(0)
+    def __str__(self):
+        return self.__name
+
+class Date(Repr):
+    """Immutable."""
+    def __init__(self, datestring):
+        # Try git-formatted date.
+        m = re.match(r'^(\d+)\s+([+-]\d\d:?\d\d)$', datestring)
+        if m:
+            try:
+                self.__time = datetime.fromtimestamp(int(m.group(1)),
+                                                     TimeZone(m.group(2)))
+            except ValueError:
+                raise DateException(datestring, 'date')
+            return
+
+        # Try iso-formatted date.
+        m = re.match(r'^(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})\s+'
+                     + r'([+-]\d\d:?\d\d)$', datestring)
+        if m:
+            try:
+                self.__time = datetime(
+                    *[int(m.group(i + 1)) for i in xrange(6)],
+                    **{'tzinfo': TimeZone(m.group(7))})
+            except ValueError:
+                raise DateException(datestring, 'date')
+            return
+
+        raise DateException(datestring, 'date')
+    def __str__(self):
+        return self.isoformat()
+    def isoformat(self):
+        """Human-friendly ISO 8601 format."""
+        return '%s %s' % (self.__time.replace(tzinfo = None).isoformat(' '),
+                          self.__time.tzinfo)
+    @classmethod
+    def maybe(cls, datestring):
+        if datestring in [None, NoValue]:
+            return datestring
+        return cls(datestring)
+
 class Person(Repr):
     """Immutable."""
     def __init__(self, name = NoValue, email = NoValue,
@@ -34,6 +100,7 @@ class Person(Repr):
         self.__name = d(name, 'name')
         self.__email = d(email, 'email')
         self.__date = d(date, 'date')
+        assert isinstance(self.__date, Date) or self.__date in [None, NoValue]
     name = property(lambda self: self.__name)
     email = property(lambda self: self.__email)
     date = property(lambda self: self.__date)
@@ -51,7 +118,7 @@ class Person(Repr):
         assert m
         name = m.group(1).strip()
         email = m.group(2)
-        date = m.group(3)
+        date = Date(m.group(3))
         return cls(name, email, date)
     @classmethod
     def user(cls):
@@ -65,7 +132,7 @@ class Person(Repr):
             cls.__author = cls(
                 name = os.environ.get('GIT_AUTHOR_NAME', NoValue),
                 email = os.environ.get('GIT_AUTHOR_EMAIL', NoValue),
-                date = os.environ.get('GIT_AUTHOR_DATE', NoValue),
+                date = Date.maybe(os.environ.get('GIT_AUTHOR_DATE', NoValue)),
                 defaults = cls.user())
         return cls.__author
     @classmethod
@@ -74,7 +141,8 @@ class Person(Repr):
             cls.__committer = cls(
                 name = os.environ.get('GIT_COMMITTER_NAME', NoValue),
                 email = os.environ.get('GIT_COMMITTER_EMAIL', NoValue),
-                date = os.environ.get('GIT_COMMITTER_DATE', NoValue),
+                date = Date.maybe(
+                    os.environ.get('GIT_COMMITTER_DATE', NoValue)),
                 defaults = cls.user())
         return cls.__committer
 
@@ -301,7 +369,7 @@ class Repository(RunWithEnv):
                 for attr, v2 in (('name', 'NAME'), ('email', 'EMAIL'),
                                  ('date', 'DATE')):
                     if getattr(p, attr) != None:
-                        env['GIT_%s_%s' % (v1, v2)] = getattr(p, attr)
+                        env['GIT_%s_%s' % (v1, v2)] = str(getattr(p, attr))
         sha1 = self.run(c, env = env).raw_input(commitdata.message
                                                 ).output_one_line()
         return self.get_commit(sha1)
Previous: Karl HasselströmNext: Karl Hasselström
Message 23 of 25 in “kha/safe and kha/experimental updated”
  1. Karl HasselströmJan 29, 2008
  2. 0/5 Various cleanupsKarl Hasselström, Jan 29, 2008
  3. 1/5 Create index and worktree objects just onceKarl Hasselström, Jan 29, 2008
  4. 2/5 Wrap excessively long lineKarl Hasselström, Jan 29, 2008
  5. 3/5 Eliminate temp variable that's used just onceKarl Hasselström, Jan 29, 2008
  6. 4/5 Simplify editor selection logicKarl Hasselström, Jan 29, 2008
  7. Peter OberndorferJan 29, 2008
  8. Karl HasselströmJan 30, 2008
  9. Jay SoffianJan 30, 2008
  10. Karl HasselströmJan 30, 2008
  11. 5/5 Let the caller supply the diff text to diffstat()Karl Hasselström, Jan 29, 2008
  12. 0/2 "stg clean" test+bugfixKarl Hasselström, Jan 29, 2008
  13. 1/2 Add test to ensure that "stg clean" preserves conflicting patchesKarl Hasselström, Jan 29, 2008
  14. 2/2 Don't clean away patches with conflictsKarl Hasselström, Jan 29, 2008
  15. 0/4 Rewrite "stg edit" to use new infrastructureKarl Hasselström, Jan 29, 2008
  16. 1/4 Teach new infrastructure about the default author and committerKarl Hasselström, Jan 29, 2008
  17. 2/4 Teach new infrastructure to apply patchesKarl Hasselström, Jan 29, 2008
  18. 3/4 Teach new infrastructure to diff two treesKarl Hasselström, Jan 29, 2008
  19. David KågedalJan 29, 2008
  20. Karl HasselströmJan 29, 2008
  21. 4/4 Convert "stg edit" to the new infrastructureKarl Hasselström, Jan 29, 2008
  22. 0/3 "stg edit" fixupsKarl Hasselström, Feb 1, 2008
  23. 1/3 Parse the date instead of treating it as an opaque stringKarl Hasselström, Feb 1, 2008
  24. 2/3 Convert "stg edit" to the new infrastructureKarl Hasselström, Feb 1, 2008
  25. 3/3 It's possible to edit unapplied patches nowKarl Hasselström, Feb 1, 2008

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.