Format.py :  » Database » PyDO » skunkweb-3.4.4 » pylibs » Date » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Database » PyDO 
PyDO » skunkweb 3.4.4 » pylibs » Date » Format.py
#  
#  Copyright (C) 2001 Andrew T. Csillag <drew_csillag@geocities.com>
#  
#      You may distribute under the terms of either the GNU General
#      Public License or the SkunkWeb License, as specified in the
#      README file.
#   
# date format funclets

import types
import time
import string
import re
try:
    from mx import DateTime
except:
    import DateTime
import Locales

# _DateFormatter is a class whose instances produce
# a string for a single date format string.
# for each 'format element' found in a format
# string, a _FormatElement instance is created.
# Then the main formatting function calls
# the format() method on the instance, and
# works with the result.

def isDateTime(x):
    return isinstance ( x, DateTime.DateTimeType )

class _FormatElement:

    def __init__(self, part):
        self.part = part

  # decide if element needs case considerations
  self.initcap = 0
  self.allcaps = 0
  self.lowercase = 0

  pl = string.lower(part)
        # is it allcaps?
  if part == string.upper(part):
      self.allcaps = 1
  # else is it initcap? compare part to pl capitalized
        elif part == string.capitalize(pl):
      self.initcap = 1
        # if not allcaps or initcap, we consider it lowercase,
  # even if it's in stuDlyCaPS
  else: self.lowercase = 1

        # link thyself to the proper rendering function
  # try it with the case as is, so that the uppercase
  # numeric formats will get their leading zeroes stripped
  func = _format_parts.get(part) or _format_parts.get(pl)
  if func is None:
      raise ValueError, "'%s' not a valid date format element" % part
  # self.func will not be a bound method, just a func.
        self.func = func
      
    def format(self, d, l):
  result = self.func(d, l)

  if self.allcaps:
      result = string.upper(result)
  elif self.initcap:
      result = string.capitalize(result)
  else:
      result = string.lower(result)
        return result

_FormatElementCache = {}

def _GetFormatElement(eltname):
    if _FormatElementCache.has_key(eltname):
  return _FormatElementCache[eltname]
    else:
  elt = _FormatElement(eltname)
  _FormatElementCache[eltname] = elt
  return elt

# STRFTIME EQUIVALENCE
#
# For extra speed, we want a date format string to be implemented
# via time.strftime() if possible. A date format string is implementable
# in strftime() if the following conditions are met:
#
# - all of its format elements are present in _format_strftime_equiv
# - the Date.Locale object matches the machine locale
#   (until I figure out how to map Date.Locale to POSIX,
#   this means, "Date.Locale is English, and machine locale is English")
#   if any of the elements are locale-dependent
# - all of the format elements which are case-sensitive
#   are in lowercase (because strftime formatting does 
#   not allow playing with case)

# the format of the values in this dict are 3-tuples:
#    (strftime format, is_locale_dependent, is_case_sensitive)
#
# The values for case-sensitive, if true, mean as follows:
#
#  'allcaps': element will have a leading zero 
#             or be uppercase in strftime,
#             and that is only correct iff element is ALLCAPS
#
#  'initcap': element will generate an initcapped string in
#             strftime, and that is correct iff element is Initcap
#
#  'lowercase': element will be a lowercase string in strftime,
#               only correct iff element is not initcap or ALLCAPS

_format_strftime_equiv = {
   'dd': ('%d', 0, 'lowercase'),
   'dy': ('%a', 1, 'initcap'),
   'day': ('%A', 1, 'initcap'),
   'mm': ('%m', 0, 'lowercase'),
   'mon': ('%b', 1, 'initcap'),
   'month': ('%B', 1, 'initcap'),
   'yy': ('%y', 0, 'lowercase'),
   'yyyy': ('%Y', 0, 'lowercase'),
   'hh12': ('%I', 0, 'lowercase'),
   'hh24': ('%H', 0, 'lowercase'),
   'hh': ('%H', 0, 'lowercase'),
   'mi': ('%M', 0, 'lowercase'),
   'min': ('%M', 0, 'lowercase'),
   'ss': ('%S', 0, 'lowercase'),
   'am': ('%p', 1, 'lowercase'),
   'pm': ('%p', 1, 'lowercase'),
}

# RENDERING FUNCTION DICTIONARY
# A dictionary to look up rendering functions for different 
# date format parts. Note that the keys are all in lowercase;
# thus any code using this dictionary must convert the date format
# part to lowercase before trying the lookup.

_format_parts = {}

# RENDERING FUNCTIONS
#
# These functions server to render a "part" of a date format
# for a particular date, in a linguistic locale and for a 
# particular timezone. Each function takes two arguments:
# d, the Date object; and l, the locale object.
#
# The names of these functions coincide with the names of the
# date format parts: __dd() is the function for 'dd', etc.
#
# If you want to add a new date format part, write its rendering function
# below to accept the same three arguments. Have it return a string, always.
# Make it a lambda of three arguments if at all possible.
#
# Then add a mapping entry (or entries) to _format_parts, as below, 
# for all of the
# lowercase strings that indicate the part. (For instance, both "hh" and
# "hh24" indicate the 24-hour hour element. 'hh24' gets a lambda assigned
# to it, and then 'hh' gets the same lambda.
#
# Special note on numeric format elements: the convention here
# is that if the format is allcaps, e.g. 'MM', then leading zeroes
# are preserved. If the format is not allcaps, then leading zeroes
# are stripped. If you add a numeric format element, add one
# lambda for the allcaps name and have it preserve leading zeroes.
# Then add another in lowercase that does not preserve the zeroes.

_format_parts['DD'] = lambda d,l: '%i' % d.day
_format_parts['dd'] = lambda d,l: '%02i' % d.day
_format_parts['dy'] = lambda d,l: l.DayAbbr[d.day_of_week]
_format_parts['day'] = lambda d,l: l.Day[d.day_of_week]
_format_parts['MM'] = lambda d,l: '%i' % d.month
_format_parts['mm'] = lambda d,l: '%02i' % d.month
_format_parts['mon'] = lambda d,l: l.MonthAbbr[d.month]
_format_parts['month'] = lambda d,l: l.Month[d.month]
_format_parts['YY'] = lambda d,l: '%i' % (d.year % 100)
_format_parts['yy'] = lambda d,l: '%02i' % (d.year % 100)
_format_parts['YYYY'] = lambda d,l: '%i' % d.year
_format_parts['yyyy'] = lambda d,l: '%04i' % d.year
_format_parts['HH12'] = lambda d,l: '%i' % (d.hour - ( (d.hour > 12) * 12 ) + ( (d.hour == 0) * 12 ) )
_format_parts['hh12'] = lambda d,l: '%02i' % (d.hour - ( (d.hour > 12) * 12 ) + ( (d.hour == 0) * 12 ) )
_format_parts['HH24'] = lambda d,l: '%i' % d.hour
_format_parts['hh24'] = lambda d,l: '%02i' % d.hour
_format_parts['HH'] = _format_parts['HH24']
_format_parts['hh'] = _format_parts['hh24']
_format_parts['MI'] = lambda d,l: '%i' % d.minute
_format_parts['mi'] = lambda d,l: '%02i' % d.minute
_format_parts['MIN'] = _format_parts['MI']
_format_parts['min'] = _format_parts['mi']
_format_parts['SS'] = lambda d,l: '%i' % d.second
_format_parts['ss'] = lambda d,l: '%02i' % d.second
_format_parts['am'] = lambda d,l: '%s' % l.AmPm[ ( d.hour >= 12 ) ]
_format_parts['pm'] = _format_parts['am']

# ORDER OF DATE FORMAT PARTS
#
# make a list of the format names, and sort them
# by longest to shortest

_format_elts = _format_parts.keys()
_format_elts.sort( lambda x, y: cmp(len(y), len(x)) )

# REGULAR EXPRESSION TO RECOGNIZE A DATE FORMAT PART
# the monster regex made of all format elements

_format_rex = re.compile(
    r'(?xi)' +    # a verbose regex, and case insensitive
    r'(' +       # start of group
    string.join(_format_elts, '|') + # all format elts in big OR group
    r')'
    )
    
# DATE FORMAT "COMPILATION" FUNCTION
#
# _match_elements() takes a string, which ostensibly contains
# a date format. The function decomposes the string into a list
# of date format parts (_FormatElement instances) and filler strings.
# Example:
#
# 'hey dd' -> ['hey ', <_FormatElement for 'dd'>]

def _match_elements(origst):
    result = []
    start = 0
    end = len(origst)
    match = _format_rex.search(origst, start)
    while match:
  mstart = match.start()
  mend = match.end()
        snippet = origst[start:mstart]
  if snippet: result.append(snippet)
  result.append( _GetFormatElement(origst[mstart:mend]) )
  start = mend
        match = _format_rex.search(origst, start)
    tail = origst[start:end]
    if tail: result.append(tail)
    return result


# the date formatter.
# here's the real _DateFormatter class...

class _DateFormatter:
    """
    _DateFormatter is a class whose instances produce
    a string for a single date format string.
    for each 'format element' found in a format
    string, a _FormatElement instance is created.
    Then the main formatting function calls
    the format() method on the instance, and
    works with the result.

    This is an internal class only. Users
    of the Date formatting functions don't
    need to mess with this.
    """
    def __init__(self, format):
        self.format_list = _match_elements(format)
  # perform a check for strftime equivalence.
  self.check_for_strftime()

    def check_for_strftime(self):
  strf = []
  # fse is shorthand
  fse = _format_strftime_equiv
        # for each element in list...
  for elt in self.format_list:
      # if string, escape any % signs
      if type(elt) == types.StringType:
          strf.append( string.replace(elt, '%', '%%') )
      # else _FormatElement...
      else:
          # if not lowercase, will not be in fse dict
    # if not in fse dict, bail
    lowerpart = string.lower(elt.part)
    if not fse.has_key(lowerpart): return
    strfmt, locsens, casesens = fse[lowerpart]
    # if casesensitive, use the fact that
    # casesens values correspond to boolean
    # attribute names in _FormatElement
    if casesens and not getattr(elt, casesens): return
    # append value in _format_strftime_equiv
    strf.append( strfmt )

        # if we got here, the format has a strftime equivalent.
  # put the strftime format in the object
  self.strftime = string.join(strf, '')

    # RENDERING METHOD
    #
    # Function takes a list of _FormatElements and filler strings,
    # plus a date object, plus optional locale and timezone objects,
    # and constructs a string representation of the date in the locale
    # and timezone according to the _FormatElements and filler strings.

    def format(self, date, locale=Locales.English):
        """
        Function takes a list of _FormatElements and filler strings,
        plus a date object, plus optional locale and timezone objects,
        and constructs a string representation of the date in the locale
        and timezone according to the _FormatElements and filler strings.
        """
  # strftime shortcut, if locale is English
  if locale == Locales.English and hasattr(self, 'strftime'):
            return date.strftime(self.strftime)
            # this was silly to begin with, and at the moment
            # it triggers a DeprecationWarning with Python 2.3 and egenix 2.0.5.
      #return time.strftime(self.strftime, date.tuple())
  result = []
  for elt in self.format_list:
      if type(elt) == types.StringType: result.append(elt)
      else: result.append( elt.format(date, locale) )

  return string.join(result, '')

# since _DateFormatter objects are more or less immutable,
# it's best to make only one instance per unique format string.
# The dict below caches _DateFormatter objects for easy use
# by _MakeDateFormat().

_FormatterCache = {}

# the format factory; use to make your own formatter libraries

def _MakeDateFormat(format):
    """the format factory; use to make your own formatter libraries"""
    if type(format) != types.StringType:
  raise 'DateFormatStringIsNotStringError', format
    if not _FormatterCache.has_key(format):
        fmter = _DateFormatter(format)
  _FormatterCache[format] = fmter
  return fmter
    else: return _FormatterCache[format]

# the default iso format

_iso_format = _MakeDateFormat('yyyy-mm-dd hh24:mi:ss')

DEFAULT_FORMAT = 'yyyy-mm-dd hh24:mi:ss'


# the format function used by Date module as DateString

def DateString(date, format=_iso_format, lang='eng'):
    """
    Function accepts a DateTime object in date, and a 
    date format string in format. format
    defaults to an ISO standard format "yyyy-mm-dd hh:mi:ss".

    The string may contain any combination of the 
    following format elements. Anything not recognized 
    as a format is included in the output as static text:
    
    Element      Meaning
    ----------   --------------
    yyyy         four-digit year
    yy           two-digit year
    mm           two-digit month
    dd           two-digit day
    hh24         24-hour hour
    hh12         12-hour hour
    hh           equivalent to hh24
    mi or min    minutes
    SS           seconds

    For all of the above, leading zeroes are included 
    as necessary: "01" instead of 1, etc. However, 
    if the format element is in UPPERCASE, leading zeroes 
    are not included: "MM" -> "1", "mm" -> "01".
    
    Element    Meaning
    ---------  ------------------------
    mon        abbreviated name of month
    month      full name of month
    dy         abbreviated weekday name
    day        full weekday name
    am or pm   whether time is AM or PM
    
    For all of the above, if the element is in UPPERCASE, 
    the result will be in ALL CAPS: "MONTH" -> "JANUARY". 
    If the element is Initcap, the result will be initcapped: 
    "Month" -> "January". Any other combination of case will 
    be considered lowercase: "month" or "moNth" -> "january".
    """
    if not isDateTime(date):
  raise 'CannotDateFormatNonDateError', date

    elif not isinstance(format, _DateFormatter):
  if type(format) != types.StringType:
      raise 'DateFormatStringIsNotStringError', format
        format = _MakeDateFormat(format)

    locale = Locales.lang_lookup(lang)

    return format.format(date, locale)

#
# A helper function to return date in RFC 1123 format, suitable
# for using in 'Last-Modified' like fields
#
# XXX Modified to return a string, and take seconds only - for performance
# considerations inside AED
def HTTPDate ( secs ):
    """
    Returns a DateString object with format set to RFC 1123,
    for use by code needing to return dates in the format expected
    in email, web, or other net-based formats. This format corresponds
    to the Unix strftime format: 
    '%a, %d %b %Y %H:%M:%S GMT'
    This function is used by the SkunkWeb server to make
    date strings extra quickly.
    """
    return time.strftime ('%a, %d %b %Y %H:%M:%S GMT' , time.gmtime ( secs ))
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.