Revision: 4584
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4584&view=rev
Author:   jswhit
Date:     2007-12-04 11:18:03 -0800 (Tue, 04 Dec 2007)

Log Message:
-----------
move netcdftime.py

Modified Paths:
--------------
    trunk/toolkits/basemap/MANIFEST.in
    trunk/toolkits/basemap/examples/fcstmaps.py
    trunk/toolkits/basemap/lib/matplotlib/toolkits/basemap/basemap.py
    trunk/toolkits/basemap/setup.py

Added Paths:
-----------
    trunk/toolkits/basemap/lib/matplotlib/toolkits/basemap/netcdftime.py

Modified: trunk/toolkits/basemap/MANIFEST.in
===================================================================
--- trunk/toolkits/basemap/MANIFEST.in  2007-12-04 19:16:57 UTC (rev 4583)
+++ trunk/toolkits/basemap/MANIFEST.in  2007-12-04 19:18:03 UTC (rev 4584)
@@ -68,6 +68,7 @@
 include lib/matplotlib/toolkits/basemap/pyproj.py
 include lib/matplotlib/toolkits/basemap/cm.py
 include lib/matplotlib/toolkits/basemap/pupynere.py
+include lib/matplotlib/toolkits/basemap/netcdftime.py
 include pyshapelib/README pyshapelib/COPYING pyshapelib/ChangeLog 
pyshapelib/NEWS
 include pyshapelib/*.i pyshapelib/*.c pyshapelib/*.py pyshapelib/*.h
 include pyshapelib/*.shp pyshapelib/*.shx pyshapelib/*.dbf
@@ -78,7 +79,6 @@
 recursive-include lib/httplib2 *
 recursive-include lib/dbflib *
 recursive-include lib/shapelib *
-recursive-include lib/netcdftime *
 include lib/matplotlib/toolkits/basemap/data/5minmask.bin
 include lib/matplotlib/toolkits/basemap/data/GL27
 include lib/matplotlib/toolkits/basemap/data/countries_c.dat

Modified: trunk/toolkits/basemap/examples/fcstmaps.py
===================================================================
--- trunk/toolkits/basemap/examples/fcstmaps.py 2007-12-04 19:16:57 UTC (rev 
4583)
+++ trunk/toolkits/basemap/examples/fcstmaps.py 2007-12-04 19:18:03 UTC (rev 
4584)
@@ -36,9 +36,6 @@
 longitudes = data.variables['lon']
 fcsttimes = data.variables['time']
 times = fcsttimes[0:6] # first 6 forecast times.
-# change 0.0 to 00 at end of time units string
-# (so strptime will understand it).
-timeunits = fcsttimes.units[:-2]+'0'
 ntimes = len(times)
 # put forecast times in YYYYMMDDHH format.
 verifdates = []
@@ -46,7 +43,7 @@
 for time in times:
     print time, times[0]
     fcsthrs.append(int((time-times[0])*24))
-    fdate = num2date(time,'days since 0001-01-01 00:00:00')
+    fdate = num2date(time,fcsttimes.units)
     verifdates.append(fdate.strftime('%Y%m%d%H'))
 print fcsthrs
 print verifdates

Modified: trunk/toolkits/basemap/lib/matplotlib/toolkits/basemap/basemap.py
===================================================================
--- trunk/toolkits/basemap/lib/matplotlib/toolkits/basemap/basemap.py   
2007-12-04 19:16:57 UTC (rev 4583)
+++ trunk/toolkits/basemap/lib/matplotlib/toolkits/basemap/basemap.py   
2007-12-04 19:18:03 UTC (rev 4584)
@@ -2858,28 +2858,21 @@
             f = pupynere._LocalFile(file,maskandscale)
         return f
 
-def num2date(times,units,unit_format='%Y-%m-%d %H:%M:%S',calendar='standard'):
+def num2date(times,units,calendar='standard'):
     """
     Return datetime objects given numeric time values. The units
     of the numeric time values are described by the units argument
-    and the unit_format and calendar keywords.
+    and the calendar keyword.
 
     Arguments:
 
     times - numeric time values. Maximum resolution is 1 second.
+
     units - a string of the form '<time-units> since <reference time>'
      describing the time units. <time-units> can be days, hours, minutes
-     or seconds.  <reference-time> is the time origin, defined by the format
-     keyword (see below). For example, a valid choice would be
-     units='hours since 0001-01-01 00:00:00'.
+     or seconds.  <reference-time> is the time origin. A valid choice
+     would be units='hours since 0001-01-01 00:00:00'.
 
-    Keyword Arguments:
-
-    format - a string describing a reference time. This string is converted 
-     to a year,month,day,hour,minute,second tuple by strptime. The default 
-     format is '%Y-%m-%d %H:%M:%S'. See the time.strptime docstring for other 
-     valid formats.
-
     calendar - describes the calendar used in the time calculations. 
      All the values currently defined in the CF metadata convention 
      (http://cf-pcmdi.llnl.gov/documents/cf-conventions/) are supported.
@@ -2897,31 +2890,24 @@
     the weird dates in some calendars (such as '360_day' and 'all_leap'
     which don't exist in any real world calendar.
     """
-    cdftime = netcdftime.utime(units,calendar=calendar,format=unit_format)
+    cdftime = netcdftime.utime(units,calendar=calendar)
     return cdftime.num2date(times)
 
-def date2num(dates,units,unit_format='%Y-%m-%d %H:%M:%S',calendar='standard'):
+def date2num(dates,units,calendar='standard'):
     """
     Return numeric time values given datetime objects. The units
     of the numeric time values are described by the units argument
-    and the unit_format and calendar keywords.
+    and the calendar keyword.
 
     Arguments:
 
     dates - A datetime object or a sequence of datetime objects.
+
     units - a string of the form '<time-units> since <reference time>'
      describing the time units. <time-units> can be days, hours, minutes
-     or seconds.  <reference-time> is the time origin, defined by the format
-     keyword (see below). For example, a valid choice would be
-     units='hours since 0001-01-01 00:00:00'.
+     or seconds.  <reference-time> is the time origin. A valid choice
+     would be units='hours since 0001-01-01 00:00:00'.
 
-    Keyword Arguments:
-
-    format - a string describing a reference time. This string is converted 
-     to a year,month,day,hour,minute,second tuple by strptime. The default 
-     format is '%Y-%m-%d %H:%M:%S'. See the time.strptime docstring for other 
-     valid formats.
-
     calendar - describes the calendar used in the time calculations. 
      All the values currently defined in the CF metadata convention 
      (http://cf-pcmdi.llnl.gov/documents/cf-conventions/) are supported.
@@ -2932,5 +2918,5 @@
 
     The maximum resolution of the numeric time values is 1 second.
     """
-    cdftime = netcdftime.utime(units,calendar=calendar,format=unit_format)
+    cdftime = netcdftime.utime(units,calendar=calendar)
     return cdftime.date2num(dates)

Added: trunk/toolkits/basemap/lib/matplotlib/toolkits/basemap/netcdftime.py
===================================================================
--- trunk/toolkits/basemap/lib/matplotlib/toolkits/basemap/netcdftime.py        
                        (rev 0)
+++ trunk/toolkits/basemap/lib/matplotlib/toolkits/basemap/netcdftime.py        
2007-12-04 19:18:03 UTC (rev 4584)
@@ -0,0 +1,859 @@
+"""
+Performs conversions of netCDF time coordinate data to/from datetime objects.
+"""
+import math, numpy, re, time
+from datetime import datetime as real_datetime
+
+_units = ['days','hours','minutes','seconds','day','hour','minute','second']
+_calendars = 
['standard','gregorian','proleptic_gregorian','noleap','julian','all_leap','365_day','366_day','360_day']
+
+__version__ = '0.6'
+
+class datetime:
+    """
+Phony datetime object which mimics the python datetime object,
+but allows for dates that don't exist in the proleptic gregorian calendar.
+Doesn't do timedelta operations, doesn't overload + and -.
+
+Has strftime, timetuple and __repr__ methods.  The format
+of the string produced by __repr__ is controlled by self.format
+(default %Y-%m-%d %H:%M:%S).
+
+Instance variables are year,month,day,hour,minute,second,dayofwk,dayofyr
+and format.
+    """
+    def 
__init__(self,year,month,day,hour=0,minute=0,second=0,dayofwk=-1,dayofyr=1):
+        """dayofyr set to 1 by default - otherwise time.strftime will 
complain"""
+        self.year=year
+        self.month=month
+        self.day=day
+        self.hour=hour
+        self.minute=minute
+        self.dayofwk=dayofwk
+        self.dayofyr=dayofyr
+        self.second=second
+        self.format='%Y-%m-%d %H:%M:%S'
+    def strftime(self,format=None):
+        if format is None:
+            format = self.format
+        return _strftime(self,format)
+    def timetuple(self):
+        return 
(self.year,self.month,self.day,self.hour,self.minute,self.second,self.dayofwk,self.dayofyr,-1)
+    def __repr__(self):
+        return self.strftime(self.format)
+
+def JulianDayFromDate(date,calendar='standard'):
+
+    """
+
+creates a Julian Day from a 'datetime-like' object.  Returns the fractional
+Julian Day (resolution 1 second).
+
+if calendar='standard' or 'gregorian' (default), Julian day follows Julian 
+Calendar on and before 1582-10-5, Gregorian calendar after 1582-10-15.
+
+if calendar='proleptic_gregorian', Julian Day follows gregorian calendar.
+
+if calendar='julian', Julian Day follows julian calendar.
+
+Algorithm:
+
+Meeus, Jean (1998) Astronomical Algorithms (2nd Edition). Willmann-Bell,
+Virginia. p. 63
+
+    """
+    
+    # based on redate.py by David Finlayson.
+
+    year=date.year; month=date.month; day=date.day
+    hour=date.hour; minute=date.minute; second=date.second
+    # Convert time to fractions of a day
+    day = day + hour/24.0 + minute/1440.0 + second/86400.0
+
+    # Start Meeus algorithm (variables are in his notation)
+    if (month < 3):
+        month = month + 12
+        year = year - 1
+        
+    A = int(year/100)
+
+    jd = int(365.25 * (year + 4716)) + int(30.6001 * (month + 1)) + \
+         day - 1524.5
+
+    # optionally adjust the jd for the switch from 
+    # the Julian to Gregorian Calendar
+    # here assumed to have occurred the day after 1582 October 4
+    if calendar in ['standard','gregorian']:
+        if jd >= 2299170.5:
+            # 1582 October 15 (Gregorian Calendar)
+            B = 2 - A + int(A/4)
+        elif jd < 2299160.5:
+            # 1582 October 5 (Julian Calendar)
+            B = 0
+        else:
+            raise ValueError, 'impossible date (falls in gap between end of 
Julian calendar and beginning of Gregorian calendar'
+    elif calendar == 'proleptic_gregorian':
+        B = 2 - A + int(A/4)
+    elif calendar == 'julian':
+        B = 0
+    else:
+        raise ValueError, 'unknown calendar, must be one of 
julian,standard,gregorian,proleptic_gregorian, got %s' % calendar
+    
+    # adjust for Julian calendar if necessary
+    jd = jd + B
+    
+    return jd 
+
+def _NoLeapDayFromDate(date):
+
+    """
+
+creates a Julian Day for a calendar with no leap years from a datetime 
+instance.  Returns the fractional Julian Day (resolution 1 second).
+
+    """
+    
+    year=date.year; month=date.month; day=date.day
+    hour=date.hour; minute=date.minute; second=date.second
+    # Convert time to fractions of a day
+    day = day + hour/24.0 + minute/1440.0 + second/86400.0
+
+    # Start Meeus algorithm (variables are in his notation)
+    if (month < 3):
+        month = month + 12
+        year = year - 1
+        
+    jd = int(365. * (year + 4716)) + int(30.6001 * (month + 1)) + \
+         day - 1524.5
+    
+    return jd 
+
+def _AllLeapFromDate(date):
+
+    """
+
+creates a Julian Day for a calendar where all years have 366 days from
+a 'datetime-like' object.
+Returns the fractional Julian Day (resolution 1 second).
+
+    """
+    
+    year=date.year; month=date.month; day=date.day
+    hour=date.hour; minute=date.minute; second=date.second
+    # Convert time to fractions of a day
+    day = day + hour/24.0 + minute/1440.0 + second/86400.0
+
+    # Start Meeus algorithm (variables are in his notation)
+    if (month < 3):
+        month = month + 12
+        year = year - 1
+        
+    jd = int(366. * (year + 4716)) + int(30.6001 * (month + 1)) + \
+         day - 1524.5
+    
+    return jd 
+
+def _360DayFromDate(date):
+
+    """
+
+creates a Julian Day for a calendar where all months have 30 daysfrom
+a 'datetime-like' object.
+Returns the fractional Julian Day (resolution 1 second).
+
+    """
+    
+    year=date.year; month=date.month; day=date.day
+    hour=date.hour; minute=date.minute; second=date.second
+    # Convert time to fractions of a day
+    day = day + hour/24.0 + minute/1440.0 + second/86400.0
+
+    jd = int(360. * (year + 4716)) + int(30. * (month - 1)) + day
+    
+    return jd 
+
+def DateFromJulianDay(JD,calendar='standard'):
+    """
+
+returns a 'datetime-like' object given Julian Day. Julian Day is a 
+fractional day with a resolution of 1 second.
+
+if calendar='standard' or 'gregorian' (default), Julian day follows Julian 
+Calendar on and before 1582-10-5, Gregorian calendar after  1582-10-15.
+
+if calendar='proleptic_gregorian', Julian Day follows gregorian calendar.
+
+if calendar='julian', Julian Day follows julian calendar.
+
+The datetime object is a 'real' datetime object if the date falls in
+the Gregorian calendar (i.e. calendar='proleptic_gregorian', or
+calendar = 'standard'/'gregorian' and the date is after 1582-10-15).
+Otherwise, it's a 'phony' datetime object which is actually an instance
+of netcdftime.datetime.
+
+
+Algorithm:
+
+Meeus, Jean (1998) Astronomical Algorithms (2nd Edition). Willmann-Bell,
+Virginia. p. 63
+
+    """
+
+    # based on redate.py by David Finlayson.
+    
+    if JD < 0:
+        raise ValueError, 'Julian Day must be positive'
+
+    dayofwk = int(math.fmod(int(JD + 1.5),7))
+    (F, Z) = math.modf(JD + 0.5)
+    Z = int(Z)
+    if calendar in ['standard','gregorian']:
+        if JD < 2299160.5:
+            A = Z
+        else:
+            alpha = int((Z - 1867216.25)/36524.25)
+            A = Z + 1 + alpha - int(alpha/4)
+
+    elif calendar == 'proleptic_gregorian':
+        alpha = int((Z - 1867216.25)/36524.25)
+        A = Z + 1 + alpha - int(alpha/4)
+    elif calendar == 'julian':
+        A = Z
+    else:
+        raise ValueError, 'unknown calendar, must be one of 
julian,standard,gregorian,proleptic_gregorian, got %s' % calendar
+
+    B = A + 1524
+    C = int((B - 122.1)/365.25)
+    D = int(365.25 * C)
+    E = int((B - D)/30.6001)
+
+    # Convert to date
+    day = B - D - int(30.6001 * E) + F
+    nday = B-D-123
+    if nday <= 305:
+        dayofyr = nday+60
+    else:
+        dayofyr = nday-305
+    if E < 14:
+        month = E - 1
+    else:
+        month = E - 13
+
+    if month > 2:
+        year = C - 4716
+    else:
+        year = C - 4715
+
+    # a leap year?
+    leap = 0
+    if year % 4 == 0:
+        leap = 1
+    if calendar == 'proleptic_gregorian' or \
+       (calendar in ['standard','gregorian'] and JD >= 2299160.5):
+        if year % 100 == 0 and year % 400 != 0: 
+            print year % 100, year % 400
+            leap = 0
+    if leap and month > 2:
+       dayofyr = dayofyr + leap
+    
+    # Convert fractions of a day to time    
+    (dfrac, days) = math.modf(day/1.0)
+    (hfrac, hours) = math.modf(dfrac * 24.0)
+    (mfrac, minutes) = math.modf(hfrac * 60.0)
+    seconds = round(mfrac * 60.0) # seconds are rounded
+    
+    if seconds > 59:
+        seconds = 0
+        minutes = minutes + 1
+    if minutes > 59:
+        minutes = 0
+        hours = hours + 1
+    if hours > 23:
+        hours = 0
+        days = days + 1
+    
+    # return a 'real' datetime instance if calendar is gregorian.
+    if calendar == 'proleptic_gregorian' or \
+            (calendar in ['standard','gregorian'] and JD >= 2299160.5):
+        return 
real_datetime(year,month,int(days),int(hours),int(minutes),int(seconds))
+    else:
+    # or else, return a 'datetime-like' instance.
+        return 
datetime(year,month,int(days),int(hours),int(minutes),int(seconds),dayofwk,dayofyr)
+
+def _DateFromNoLeapDay(JD):
+    """
+
+returns a 'datetime-like' object given Julian Day for a calendar with no leap 
+days. Julian Day is a fractional day with a resolution of 1 second.
+
+    """
+
+    # based on redate.py by David Finlayson.
+    
+    if JD < 0:
+        raise ValueError, 'Julian Day must be positive'
+
+    dayofwk = int(math.fmod(int(JD + 1.5),7))
+    (F, Z) = math.modf(JD + 0.5)
+    Z = int(Z)
+    A = Z
+    B = A + 1524
+    C = int((B - 122.1)/365.)
+    D = int(365. * C)
+    E = int((B - D)/30.6001)
+
+    # Convert to date
+    day = B - D - int(30.6001 * E) + F
+    nday = B-D-123
+    if nday <= 305:
+        dayofyr = nday+60
+    else:
+        dayofyr = nday-305
+    if E < 14:
+        month = E - 1
+    else:
+        month = E - 13
+
+    if month > 2:
+        year = C - 4716
+    else:
+        year = C - 4715
+    
+    # Convert fractions of a day to time    
+    (dfrac, days) = math.modf(day/1.0)
+    (hfrac, hours) = math.modf(dfrac * 24.0)
+    (mfrac, minutes) = math.modf(hfrac * 60.0)
+    seconds = round(mfrac * 60.0) # seconds are rounded
+    
+    if seconds > 59:
+        seconds = 0
+        minutes = minutes + 1
+    if minutes > 59:
+        minutes = 0
+        hours = hours + 1
+    if hours > 23:
+        hours = 0
+        days = days + 1
+    
+    return datetime(year,month,int(days),int(hours),int(minutes),int(seconds), 
dayofwk, dayofyr)
+
+def _DateFromAllLeap(JD):
+    """
+
+returns a 'datetime-like' object given Julian Day for a calendar where all
+years have 366 days.
+Julian Day is a fractional day with a resolution of 1 second.
+
+    """
+
+    # based on redate.py by David Finlayson.
+    
+    if JD < 0:
+        raise ValueError, 'Julian Day must be positive'
+
+    dayofwk = int(math.fmod(int(JD + 1.5),7))
+    (F, Z) = math.modf(JD + 0.5)
+    Z = int(Z)
+    A = Z
+    B = A + 1524
+    C = int((B - 122.1)/366.)
+    D = int(366. * C)
+    E = int((B - D)/30.6001)
+
+    # Convert to date
+    day = B - D - int(30.6001 * E) + F
+    nday = B-D-123
+    if nday <= 305:
+        dayofyr = nday+60
+    else:
+        dayofyr = nday-305
+    if E < 14:
+        month = E - 1
+    else:
+        month = E - 13
+    if month > 2:
+       dayofyr = dayofyr+1
+
+    if month > 2:
+        year = C - 4716
+    else:
+        year = C - 4715
+    
+    # Convert fractions of a day to time    
+    (dfrac, days) = math.modf(day/1.0)
+    (hfrac, hours) = math.modf(dfrac * 24.0)
+    (mfrac, minutes) = math.modf(hfrac * 60.0)
+    seconds = round(mfrac * 60.0) # seconds are rounded
+    
+    if seconds > 59:
+        seconds = 0
+        minutes = minutes + 1
+    if minutes > 59:
+        minutes = 0
+        hours = hours + 1
+    if hours > 23:
+        hours = 0
+        days = days + 1
+    
+    return datetime(year,month,int(days),int(hours),int(minutes),int(seconds), 
dayofwk, dayofyr)
+
+def _DateFrom360Day(JD):
+    """
+
+returns a 'datetime-like' object given Julian Day for a calendar where all
+months have 30 days.
+Julian Day is a fractional day with a resolution of 1 second.
+
+    """
+
+    if JD < 0:
+        raise ValueError, 'Julian Day must be positive'
+
+    #jd = int(360. * (year + 4716)) + int(30. * (month - 1)) + day
+    (F, Z) = math.modf(JD)
+    year = int((Z-0.5)/360.) - 4716
+    dayofyr =  JD - (year+4716)*360  
+    month = int((dayofyr-0.5)/30)+1
+    day = dayofyr - (month-1)*30 + F  
+    
+    # Convert fractions of a day to time    
+    (dfrac, days) = math.modf(day/1.0)
+    (hfrac, hours) = math.modf(dfrac * 24.0)
+    (mfrac, minutes) = math.modf(hfrac * 60.0)
+    seconds = round(mfrac * 60.0) # seconds are rounded
+    
+    if seconds > 59:
+        seconds = 0
+        minutes = minutes + 1
+    if minutes > 59:
+        minutes = 0
+        hours = hours + 1
+    if hours > 23:
+        hours = 0
+        days = days + 1
+    
+    return 
datetime(year,month,int(days),int(hours),int(minutes),int(seconds),-1, 
int(dayofyr))
+
+def _dateparse(timestr,format='%Y-%m-%d %H:%M:%S'):
+    """parse a string of the form time-units since yyyy-mm-dd hh:mm:ss
+    return a tuple (units, datetimeinstance)"""
+    timestr_split = timestr.split()
+    units = timestr_split[0].lower()
+    if units not in _units:
+        raise ValueError,"units must be one of 'seconds', 'minutes', 'hours' 
or 'days' (or singular version of these), got '%s'" % units
+    if timestr_split[1].lower() != 'since':
+        raise ValueError,"no 'since' in unit_string"
+    # use strptime to parse the date string.
+    n = timestr.find('since')+6
+    #year,month,day,hour,minute,second,daywk,dayyr,tz = 
strptime(timestr[n:],format)
+    year,month,day,hour,minute,second = _parse_date(timestr[n:])
+    #if dayyr == -1: dayyr=1 # must have valid day of year for strftime to work
+    #return units, datetime(year, month, day, hour, minute, second, daywk, 
dayyr)
+    return units, datetime(year, month, day, hour, minute, second)
+
+class utime:
+    """
+Performs conversions of netCDF time coordinate
+data to/from datetime objects.
+
+To initialize: C{t = utime(unit_string,format='%Y-%m-%d 
%H:%M:%S',calendar='standard')}
+
+where 
+
+B{C{unit_string}} is a string of the form
+C{'time-units since <format>'} defining the time units.
+
+B{C{format}} is a string describing a reference time. This string is converted 
+to a year,month,day,hour,minute,second tuple by strptime. The default 
+format is C{'%Y-%m-%d %H:%M:%S'}. See the C{time.strptime} docstring for other 
+valid formats.
+
+Valid time-units are days, hours, minutes and seconds (the singular forms 
+are also accepted). An example unit_string would be C{'hours 
+since 0001-01-01 00:00:00'}.
+
+The B{C{calendar}} keyword describes the calendar used in the time 
calculations. 
+All the values currently defined in the U{CF metadata convention 
+<http://www.cgd.ucar.edu/cms/eaton/cf-metadata/CF-1.0.html#time>} are 
+accepted. The default is C{'standard'}, which corresponds to the mixed 
+Gregorian/Julian calendar used by the C{udunits library}. Valid calendars 
+are:
+
+C{'gregorian'} or C{'standard'} (default):
+
+Mixed Gregorian/Julian calendar as defined by udunits.
+
+C{'proleptic_gregorian'}:
+
+A Gregorian calendar extended to dates before 1582-10-15. That is, a year 
+is a leap year if either (i) it is divisible by 4 but not by 100 or (ii) 
+it is divisible by 400.
+
+C{'noleap'} or C{'365_day'}:
+
+Gregorian calendar without leap years, i.e., all years are 365 days long. 
+all_leap or 366_day Gregorian calendar with every year being a leap year, 
+i.e., all years are 366 days long.
+
+C{'360_day'}:
+
+All years are 360 days divided into 30 day months. 
+
+C{'julian'}:
+
+Proleptic Julian calendar, extended to dates after 1582-10-5. A year is a 
+leap year if it is divisible by 4.
+
+The C{L{num2date}} and C{L{date2num}} class methods can used to convert 
datetime 
+instances to/from the specified time units using the specified calendar.
+
+The datetime instances returned by C{num2date} are 'real' python datetime 
+objects if the date falls in the Gregorian calendar (i.e. 
+C{calendar='proleptic_gregorian', 'standard'} or C{'gregorian'} and 
+the date is after 1582-10-15). Otherwise, they are 'phony' datetime 
+objects which are actually instances of C{L{netcdftime.datetime}}.  This is 
+because the python datetime module cannot handle the weird dates in some 
+calendars (such as C{'360_day'} and C{'all_leap'}) which don't exist in any 
real 
+world calendar.
+
+
+Example usage:
+
+>>> from netcdftime import utime
+>>> from datetime import  datetime
+>>> cdftime = utime('hours since 0001-01-01 00:00:00')
+>>> date = datetime.now()
+>>> print date
+2006-03-17 16:04:02.561678
+>>>
+>>> t = cdftime.date2num(date)
+>>> print t
+17577328.0672
+>>>
+>>> date = cdftime.num2date(t)
+>>> print date
+2006-03-17 16:04:02
+>>>
+
+The resolution of the transformation operation is 1 second.
+        
+Warning:  Dates between 1582-10-5 and 1582-10-15 do not exist in the 
+C{'standard'} or C{'gregorian'} calendars.  An exception will be raised if you 
pass 
+a 'datetime-like' object in that range to the C{L{date2num}} class method.
+
+Words of Wisdom from the British MetOffice concerning reference dates 
+U{http://www.metoffice.com/research/hadleycentre/models/GDT/ch26.html}:
+
+"udunits implements the mixed Gregorian/Julian calendar system, as 
+followed in England, in which dates prior to 1582-10-15 are assumed to use 
+the Julian calendar. Other software cannot be relied upon to handle the 
+change of calendar in the same way, so for robustness it is recommended 
+that the reference date be later than 1582. If earlier dates must be used, 
+it should be noted that udunits treats 0 AD as identical to 1 AD."
+
[EMAIL PROTECTED] origin: datetime instance defining the origin of the netCDF 
time variable.
[EMAIL PROTECTED] calendar:  the calendar used (as specified by the C{calendar} 
keyword).
[EMAIL PROTECTED] unit_string:  a string defining the the netCDF time variable.
[EMAIL PROTECTED] units:  the units part of C{unit_string} (i.e. 'days', 
'hours', 'seconds').
+    """
+    def __init__(self,unit_string,format='%Y-%m-%d 
%H:%M:%S',calendar='standard'):
+        """
[EMAIL PROTECTED] unit_string: a string of the form
+C{'time-units since <format>'} defining the time units.
+
[EMAIL PROTECTED] format: a string describing a reference time. This string is 
converted 
+to a year,month,day,hour,minute,second tuple by strptime. The default 
+format is C{'%Y-%m-%d %H:%M:%S'}. See the C{time.strptime} docstring for other 
+valid formats.
+Valid time-units are days, hours, minutes and seconds (the singular forms 
+are also accepted). An example unit_string would be C{'hours 
+since 0001-01-01 00:00:00'}.
+
[EMAIL PROTECTED] calendar: describes the calendar used in the time 
calculations. 
+All the values currently defined in the U{CF metadata convention 
+<http://www.cgd.ucar.edu/cms/eaton/cf-metadata/CF-1.0.html#time>} are 
+accepted. The default is C{'standard'}, which corresponds to the mixed 
+Gregorian/Julian calendar used by the C{udunits library}. Valid calendars 
+are:
+ - C{'gregorian'} or C{'standard'} (default):
+ Mixed Gregorian/Julian calendar as defined by udunits.
+ - C{'proleptic_gregorian'}:
+ A Gregorian calendar extended to dates before 1582-10-15. That is, a year 
+ is a leap year if either (i) it is divisible by 4 but not by 100 or (ii) 
+ it is divisible by 400.
+ - C{'noleap'} or C{'365_day'}:
+ Gregorian calendar without leap years, i.e., all years are 365 days long. 
+ all_leap or 366_day Gregorian calendar with every year being a leap year, 
+ i.e., all years are 366 days long.
+ -C{'360_day'}:
+ All years are 360 days divided into 30 day months. 
+ -C{'julian'}:
+ Proleptic Julian calendar, extended to dates after 1582-10-5. A year is a 
+ leap year if it is divisible by 4.
+
[EMAIL PROTECTED]: A class instance which may be used for converting times from 
netCDF
+units to datetime objects.
+        """
+        if calendar in _calendars:
+            self.calendar = calendar
+        else:
+            raise ValueError, "calendar must be one of %s, got '%s'" % 
(str(_calendars),calendar)
+        units, self.origin = _dateparse(unit_string,format=format)
+        self.units = units
+        self.unit_string = unit_string
+        if self.calendar in ['noleap','365_day'] and self.origin.month == 2 
and self.origin.day == 29:
+            raise ValueError, 'cannot specify a leap day as the reference time 
with the noleap calendar'
+        if self.calendar == '360_day' and self.origin.day > 30:
+            raise ValueError, 'there are only 30 days in every month with the 
360_day calendar'
+        if self.calendar in ['noleap','365_day']:
+            self._jd0 = _NoLeapDayFromDate(self.origin)
+        elif self.calendar in ['all_leap','366_day']:
+            self._jd0 = _AllLeapFromDate(self.origin)
+        elif self.calendar == '360_day':
+            self._jd0 = _360DayFromDate(self.origin)
+        else:
+            self._jd0 = JulianDayFromDate(self.origin,calendar=self.calendar)
+
+    def date2num(self,date):
+        """
+Returns C{time_value} in units described by L{unit_string}, using
+the specified L{calendar}, given a 'datetime-like' object.
+
+Resolution is 1 second.
+
+If C{calendar = 'standard'} or C{'gregorian'} (indicating
+that the mixed Julian/Gregorian calendar is to be used), an
+exception will be raised if the 'datetime-like' object describes
+a date between 1582-10-5 and 1582-10-15.
+
+Works for scalars, sequences and numpy arrays.
+Returns a scalar if input is a scalar, else returns a numpy array.
+        """
+        isscalar = False
+        try:
+            date[0]
+        except:
+            isscalar = True
+        if not isscalar:
+            date = numpy.array(date)
+            shape = date.shape
+        if self.calendar in 
['julian','standard','gregorian','proleptic_gregorian']:
+            if isscalar:
+                jdelta = JulianDayFromDate(date,self.calendar)-self._jd0
+            else:
+                jdelta = [JulianDayFromDate(d,self.calendar)-self._jd0 for d 
in date.flat]
+        elif self.calendar in ['noleap','365_day']:
+            if date.month == 2 and date.day == 29:
+                raise ValueError, 'there is no leap day in the noleap calendar'
+            if isscalar:
+                jdelta = _NoLeapDayFromDate(date) - self._jd0
+            else:
+                jdelta = [_NoLeapDayFromDate(d)-self._jd0 for d in date.flat]
+        elif self.calendar in ['all_leap','366_day']:
+            if isscalar:
+                jdelta = _AllLeapFromDate(date) - self._jd0
+            else:
+                jdelta = [_AllLeapFromDate(d)-self._jd0 for d in date.flat]
+        elif self.calendar == '360_day':
+            if self.calendar == '360_day' and date.day > 30:
+                raise ValueError, 'there are only 30 days in every month with 
the 360_day calendar'
+            if isscalar:
+                jdelta = _360DayFromDate(date) - self._jd0
+            else:
+                jdelta = [_360DayFromDate(d)-self._jd0 for d in date.flat]
+        if not isscalar:
+            jdelta = numpy.array(jdelta)
+        if self.units in ['second','seconds']:
+            jdelta = jdelta*86400.
+        elif self.units in ['minute','minutes']:
+            jdelta = jdelta*1440.
+        elif self.units in ['hours','hours']:
+            jdelta = jdelta*24.
+        if isscalar:
+            return jdelta
+        else:
+            return numpy.reshape(jdelta,shape)
+
+    def num2date(self,time_value):
+        """
+Return a 'datetime-like' object given a C{time_value} in units
+described by L{unit_string}, using L{calendar}.
+
+Resolution is 1 second.
+
+Works for scalars, sequences and numpy arrays.
+Returns a scalar if input is a scalar, else returns a numpy array.
+
+The datetime instances returned by C{num2date} are 'real' python datetime 
+objects if the date falls in the Gregorian calendar (i.e. 
+C{calendar='proleptic_gregorian'}, or C{calendar = 'standard'/'gregorian'} and 
+the date is after 1582-10-15). Otherwise, they are 'phony' datetime 
+objects which are actually instances of netcdftime.datetime.  This is 
+because the python datetime module cannot handle the weird dates in some 
+calendars (such as C{'360_day'} and C{'all_leap'}) which don't exist in any 
real 
+world calendar.
+        """
+        isscalar = False
+        try:
+            time_value[0]
+        except:
+            isscalar = True
+        if not isscalar:
+            time_value = numpy.array(time_value)
+            shape = time_value.shape
+        if self.units in ['second','seconds']:
+            jdelta = time_value/86400.
+        elif self.units in ['minute','minutes']:
+            jdelta = time_value/1440.
+        elif self.units in ['hours','hours']:
+            jdelta = time_value/24.
+        elif self.units in ['day','days']:
+            jdelta = time_value
+        jd = self._jd0 + jdelta
+        if self.calendar in 
['julian','standard','gregorian','proleptic_gregorian']:
+            if not isscalar:
+                date = [DateFromJulianDay(j,self.calendar) for j in jd.flat]
+            else:
+                date = DateFromJulianDay(jd,self.calendar)
+        elif self.calendar in ['noleap','365_day']:
+            if not isscalar:
+                date = [_DateFromNoLeapDay(j) for j in jd.flat]
+            else:
+                date = _DateFromNoLeapDay(jd)
+        elif self.calendar in ['all_leap','366_day']:
+            if not isscalar:
+                date = [_DateFromAllLeap(j) for j in jd.flat]
+            else:
+                date = _DateFromAllLeap(jd)
+        elif self.calendar == '360_day':
+            if not isscalar:
+                date = [_DateFrom360Day(j) for j in jd.flat]
+            else:
+                date = _DateFrom360Day(jd)
+        if isscalar:
+            return date
+        else:
+            return numpy.reshape(numpy.array(date),shape)
+
+def _parse_date(origin):
+    """Parses a date string and returns a datetime object.
+
+    This function parses the 'origin' part of the time unit. It should be
+    something like::
+
+        2004-11-03 14:42:27.0 +2:00
+
+    Lots of things are optional; just the date is mandatory.
+
+    by Roberto D'Almeida 
+
+    excerpted from coards.py - http://cheeseshop.python.org/pypi/coards/
+    """
+    # yyyy-mm-dd [hh:mm:ss[.s][ [+-]hh[:][mm]]]
+    p = re.compile( r'''(?P<year>\d{1,4})           # yyyy
+                        -                           #
+                        (?P<month>\d{1,2})          # mm or m
+                        -                           #
+                        (?P<day>\d{1,2})            # dd or d
+                                                    #
+                        (?:                         # [optional time and 
timezone]
+                            \s                      #
+                            (?P<hour>\d{1,2})       #   hh or h
+                            :                       #
+                            (?P<min>\d{1,2})        #   mm or m
+                            :                       #
+                            (?P<sec>\d{1,2})        #   ss or s
+                                                    #
+                            (?:                     #   [optional decisecond]
+                                \.                  #       .
+                                (?P<dsec>\d)        #       s
+                            )?                      #
+                            (?:                     #   [optional timezone]
+                                \s                  #
+                                (?P<ho>[+-]?        #       [+ or -]
+                                \d{1,2})            #       hh or h
+                                :?                  #       [:]
+                                (?P<mo>\d{2})?      #       [mm]
+                            )?                      #
+                        )?                          #
+                        $                           # EOL
+                    ''', re.VERBOSE)
+
+    m = p.match(origin.strip())
+    if m:
+        c = m.groupdict(0)
+        
+        # Instantiate timezone object.
+        #offset = int(c['ho'])*60 + int(c['mo'])
+        #tz = FixedOffset(offset, 'Unknown')
+
+        #return datetime(int(c['year']),
+        #                int(c['month']),
+        #                int(c['day']),
+        #                int(c['hour']),
+        #                int(c['min']),
+        #                int(c['sec']),
+        #                int(c['dsec']) * 100000,
+        #                tz)
+        return 
int(c['year']),int(c['month']),int(c['day']),int(c['hour']),int(c['min']),int(c['sec'])
+    
+    raise Exception('Invalid date origin: %s' % origin)
+
+# remove the unsupposed "%s" command.  But don't
+# do it if there's an even number of %s before the s
+# because those are all escaped.  Can't simply
+# remove the s because the result of
+#  %sY
+# should be %Y if %s isn't supported, not the
+# 4 digit year.
+_illegal_s = re.compile(r"((^|[^%])(%%)*%s)")
+
+def _findall(text, substr):
+     # Also finds overlaps
+     sites = []
+     i = 0
+     while 1:
+         j = text.find(substr, i)
+         if j == -1:
+             break
+         sites.append(j)
+         i=j+1
+     return sites
+
+# Every 28 years the calendar repeats, except through century leap
+# years where it's 6 years.  But only if you're using the Gregorian
+# calendar.  ;)
+
+def _strftime(dt, fmt):
+    if _illegal_s.search(fmt):
+        raise TypeError("This strftime implementation does not handle %s")
+    # don't use strftime method at all.
+    #if dt.year > 1900:
+    #    return dt.strftime(fmt)
+
+    year = dt.year
+    # For every non-leap year century, advance by
+    # 6 years to get into the 28-year repeat cycle
+    delta = 2000 - year
+    off = 6*(delta // 100 + delta // 400)
+    year = year + off
+
+    # Move to around the year 2000
+    year = year + ((2000 - year)//28)*28
+    timetuple = dt.timetuple()
+    s1 = time.strftime(fmt, (year,) + timetuple[1:])
+    sites1 = _findall(s1, str(year))
+    
+    s2 = time.strftime(fmt, (year+28,) + timetuple[1:])
+    sites2 = _findall(s2, str(year+28))
+
+    sites = []
+    for site in sites1:
+        if site in sites2:
+            sites.append(site)
+            
+    s = s1
+    syear = "%4d" % (dt.year,)
+    for site in sites:
+        s = s[:site] + syear + s[site+4:]
+    return s

Modified: trunk/toolkits/basemap/setup.py
===================================================================
--- trunk/toolkits/basemap/setup.py     2007-12-04 19:16:57 UTC (rev 4583)
+++ trunk/toolkits/basemap/setup.py     2007-12-04 19:18:03 UTC (rev 4584)
@@ -114,10 +114,6 @@
     packages = packages + ['httplib2']
     package_dirs['httlib2'] = os.path.join('lib','httplib2')
 
-# install netcdftime
-packages = packages + ['netcdftime']
-package_dirs['httlib2'] = os.path.join('lib','netcdftime')
-
 if 'setuptools' in sys.modules:
 # Are we running with setuptools?
 # if so, need to specify all the packages in heirarchy


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

-------------------------------------------------------------------------
SF.Net email is sponsored by: The Future of Linux Business White Paper
from Novell.  From the desktop to the data center, Linux is going
mainstream.  Let it simplify your IT future.
http://altfarm.mediaplex.com/ad/ck/8857-50307-18918-4
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to