Index: AUTHORS
===================================================================
--- AUTHORS	(revision 5779)
+++ AUTHORS	(working copy)
@@ -260,7 +260,7 @@
     tstromberg@google.com
     Makoto Tsuyuki <mtsuyuki@gmail.com>
     tt@gurgle.no
-    Amit Upadhyay
+    Amit Upadhyay <http://www.amitu.com/blog/>
     Geert Vanderkelen
     viestards.lists@gmail.com
     Vlado <vlado@labath.org>
Index: django/contrib/sessions/middleware.py
===================================================================
--- django/contrib/sessions/middleware.py	(revision 5881)
+++ django/contrib/sessions/middleware.py	(working copy)
@@ -51,6 +51,52 @@
     def delete_test_cookie(self):
         del self[TEST_COOKIE_NAME]
 
+    def get_max_age(self):
+        expiry = self.get('_session_expiry')
+        if not expiry:   # Checks both None and 0 cases
+            return settings.SESSION_COOKIE_AGE
+        if not isinstance(expiry, datetime.datetime):
+            return expiry
+        delta = expiry - datetime.datetime.now()
+        return delta.days * 86400 + delta.seconds
+
+    def get_expiry_date(self):
+        expiry = self.get('_session_expiry', settings.SESSION_COOKIE_AGE)
+        if isinstance(expiry, datetime.datetime):
+            return expiry
+        return datetime.datetime.now() + datetime.timedelta(seconds=expiry)
+
+    def set_expiry(self, value):
+        """
+        Sets a custom expiration for the session. ``value`` can be an integer, a
+        Python ``datetime`` or ``timedelta`` object or ``None``.
+
+        If ``value`` is an integer, the session will expire after that many
+        seconds of inactivity. If set to ``0`` then the session will expire on
+        browser close.
+
+        If ``value`` is a ``datetime`` or ``timedelta`` object, the session
+        will expire at that specific future time.
+
+        If ``value`` is ``None``, the session uses the global session expiry
+        policy.
+        """
+        if value is None:
+            # Remove any custom expiration for this session.
+            try:
+                del self['_session_expiry']
+            except KeyError:
+                pass
+            return
+        if isinstance(value, datetime.timedelta):
+            value = datetime.datetime.now() + value
+        self['_session_expiry'] = value
+
+    def get_expire_at_browser_close(self):
+        if self.get('_session_expiry') is None:
+            return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
+        return self.get('_session_expiry') == 0
+
     def _get_session(self):
         # Lazily loads session from storage.
         self.accessed = True
@@ -95,19 +141,20 @@
                     obj = Session.objects.get_new_session_object()
                     session_key = obj.session_key
 
-                if settings.SESSION_EXPIRE_AT_BROWSER_CLOSE:
-                    max_age = None
-                    expires = None
-                else:
-                    max_age = settings.SESSION_COOKIE_AGE
-                    rfcdate = formatdate(time.time() + settings.SESSION_COOKIE_AGE)
+                if request.session.get_expire_at_browser_close():
+                    max_age = None 
+                    expires = None 
+                else: 
+                    max_age = request.session.get_max_age()
+                    rfcdate = formatdate(time.time() + max_age)
                     # Fixed length date must have '-' separation in the format
                     # DD-MMM-YYYY for compliance with Netscape cookie standard
                     expires = (rfcdate[:7] + "-" + rfcdate[8:11]
                                + "-" + rfcdate[12:26] + "GMT")
-                new_session = Session.objects.save(session_key, request.session._session,
-                    datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE))
+                Session.objects.save(session_key, request.session._session,
+                                     request.session.get_expiry_date())
                 response.set_cookie(settings.SESSION_COOKIE_NAME, session_key,
-                    max_age=max_age, expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
+                    max_age=max_age, expires=expires,
+                    domain=settings.SESSION_COOKIE_DOMAIN,
                     secure=settings.SESSION_COOKIE_SECURE or None)
         return response
Index: django/contrib/sessions/tests.py
===================================================================
--- django/contrib/sessions/tests.py	(revision 5881)
+++ django/contrib/sessions/tests.py	(working copy)
@@ -26,6 +26,96 @@
 
 >>> s.pop('some key', 'does not exist')
 'does not exist'
+
+#########################
+# Custom session expiry #
+#########################
+
+>>> from django.conf import settings
+>>> from datetime import datetime, timedelta
+
+>>> td9 = timedelta(seconds=9)
+>>> td10 = timedelta(seconds=10)
+>>> td11 = timedelta(seconds=11)
+
+# A normal session has a max age equal to settings 
+>>> s.get_max_age() == settings.SESSION_COOKIE_AGE
+True
+
+# So does a custom session with an idle expiration time of 0 (but it'll expire
+# at browser close)
+>>> s.set_expiry(0)
+>>> s.get_max_age() == settings.SESSION_COOKIE_AGE
+True
+
+# Custom session idle expiration time
+>>> s.set_expiry(10)
+>>> s.get_expiry_date() == datetime.now() + td10
+True
+>>> s.get_max_age()
+10
+
+# Custom session fixed expiry date (timedelta)
+>>> s.set_expiry(td10)
+>>> s.get_expiry_date() == datetime.now() + td10
+True
+>>> s.get_max_age()
+10
+
+# Custom session fixed expiry date (fixed datetime)
+>>> s.set_expiry(datetime.now() + td10)
+>>> s.get_expiry_date() == datetime.now() + td10
+True
+>>> s.get_max_age()
+10
+
+# Set back to default session age
+>>> s.set_expiry(None)
+>>> s.get_max_age() == settings.SESSION_COOKIE_AGE
+True
+
+# Allow to set back to default session age even if no alternate has been set
+>>> s.set_expiry(None)
+
+
+# We're changing the setting then reverting back to the original setting at the
+# end of these tests.
+>>> original_expire_at_browser_close = settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = False
+
+# Custom session age
+>>> s.set_expiry(10)
+>>> s.get_expire_at_browser_close()
+False
+
+# Custom expire-at-browser-close
+>>> s.set_expiry(0)
+>>> s.get_expire_at_browser_close()
+True
+
+# Default session age
+>>> s.set_expiry(None)
+>>> s.get_expire_at_browser_close()
+False
+
+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = True
+
+# Custom session age
+>>> s.set_expiry(10)
+>>> s.get_expire_at_browser_close()
+False
+
+# Custom expire-at-browser-close
+>>> s.set_expiry(0)
+>>> s.get_expire_at_browser_close()
+True
+
+# Default session age
+>>> s.set_expiry(None)
+>>> s.get_expire_at_browser_close()
+True
+
+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = original_expire_at_browser_close
 """
 
 from django.contrib.sessions.middleware import SessionWrapper
Index: docs/sessions.txt
===================================================================
--- docs/sessions.txt	(revision 5779)
+++ docs/sessions.txt	(working copy)
@@ -39,42 +39,81 @@
 It implements the following standard dictionary methods:
 
     * ``__getitem__(key)``
+
       Example: ``fav_color = request.session['fav_color']``
 
     * ``__setitem__(key, value)``
+
       Example: ``request.session['fav_color'] = 'blue'``
 
     * ``__delitem__(key)``
+
       Example: ``del request.session['fav_color']``. This raises ``KeyError``
       if the given ``key`` isn't already in the session.
 
     * ``__contains__(key)``
+
       Example: ``'fav_color' in request.session``
 
     * ``get(key, default=None)``
+
       Example: ``fav_color = request.session.get('fav_color', 'red')``
 
     * ``keys()``
 
     * ``items()``
 
-It also has these three methods:
+It also has these methods:
 
     * ``set_test_cookie()``
+
       Sets a test cookie to determine whether the user's browser supports
       cookies. Due to the way cookies work, you won't be able to test this
       until the user's next page request. See "Setting test cookies" below for
       more information.
 
     * ``test_cookie_worked()``
+
       Returns either ``True`` or ``False``, depending on whether the user's
       browser accepted the test cookie. Due to the way cookies work, you'll
       have to call ``set_test_cookie()`` on a previous, separate page request.
       See "Setting test cookies" below for more information.
 
     * ``delete_test_cookie()``
+
       Deletes the test cookie. Use this to clean up after yourself.
 
+    * ``set_expiry(value)``
+
+      Sets a custom expiration for the session.
+
+      If ``value`` is an integer, the session will expire after that many
+      seconds of inactivity. If set to ``0`` then the session will expire when
+      the user's browser is closed.
+
+      If ``value`` is a ``datetime`` or ``timedelta`` object, the session will
+      expire at that specific time.
+
+      If ``value`` is ``None``, the session reverts to using the global session
+      expiry policy.
+
+    * ``get_max_age()``
+
+      Returns the number of seconds until this session expires. For sessions
+      with no custom expiration (or those set to expire at browser close), this
+      will equal ``settings.SESSION_COOKIE_AGE``.
+
+    * ``get_expiry_date()``
+
+      Returns the date this session will expire. For sessions with no custom
+      expiration (or those set to expire at browser close), this will equal the
+      date ``settings.SESSION_COOKIE_AGE`` seconds from now.
+
+    * ``get_expire_at_browser_close()``
+
+      Returns either ``True`` or ``False``, depending on whether this session
+      will expire when the user's browser is closed.
+
 You can edit ``request.session`` at any point in your view. You can edit it
 multiple times.
 
@@ -217,6 +256,10 @@
 her browser. Use this if you want people to have to log in every time they open
 a browser.
 
+This setting is a global default and can be overwritten by explicitly calling
+``request.session.set_expiry()`` as described above in
+`using sessions in views`_.
+
 Clearing the session table
 ==========================
 
