Package netcdftime :: Module netcdftime

Source Code for Module netcdftime.netcdftime

   1  """ 
   2  Performs conversions of netCDF time coordinate data to/from datetime objects. 
   3  """ 
   4  import math, numpy, re, time 
   5  from datetime import datetime as real_datetime 
   6   
   7  _units = ['days','hours','minutes','seconds','day','hour','minute','second'] 
   8  _calendars = ['standard','gregorian','proleptic_gregorian','noleap','julian','all_leap','365_day','366_day','360_day'] 
   9   
  10  __version__ = '0.7.1' 
  11   
12 -class datetime:
13 """ 14 Phony datetime object which mimics the python datetime object, 15 but allows for dates that don't exist in the proleptic gregorian calendar. 16 Doesn't do timedelta operations, doesn't overload + and -. 17 18 Has strftime, timetuple and __repr__ methods. The format 19 of the string produced by __repr__ is controlled by self.format 20 (default %Y-%m-%d %H:%M:%S). 21 22 Instance variables are year,month,day,hour,minute,second,dayofwk,dayofyr 23 and format. 24 """
25 - def __init__(self,year,month,day,hour=0,minute=0,second=0,dayofwk=-1,dayofyr=1):
26 """dayofyr set to 1 by default - otherwise time.strftime will complain""" 27 self.year=year 28 self.month=month 29 self.day=day 30 self.hour=hour 31 self.minute=minute 32 self.dayofwk=dayofwk 33 self.dayofyr=dayofyr 34 self.second=second 35 self.format='%Y-%m-%d %H:%M:%S'
36 - def strftime(self,format=None):
37 if format is None: 38 format = self.format 39 return _strftime(self,format)
40 - def timetuple(self):
41 return (self.year,self.month,self.day,self.hour,self.minute,self.second,self.dayofwk,self.dayofyr,-1)
42 - def __repr__(self):
43 return self.strftime(self.format)
44
45 -def JulianDayFromDate(date,calendar='standard'):
46 47 """ 48 49 creates a Julian Day from a 'datetime-like' object. Returns the fractional 50 Julian Day (resolution 1 second). 51 52 if calendar='standard' or 'gregorian' (default), Julian day follows Julian 53 Calendar on and before 1582-10-5, Gregorian calendar after 1582-10-15. 54 55 if calendar='proleptic_gregorian', Julian Day follows gregorian calendar. 56 57 if calendar='julian', Julian Day follows julian calendar. 58 59 Algorithm: 60 61 Meeus, Jean (1998) Astronomical Algorithms (2nd Edition). Willmann-Bell, 62 Virginia. p. 63 63 64 """ 65 66 # based on redate.py by David Finlayson. 67 68 year=date.year; month=date.month; day=date.day 69 hour=date.hour; minute=date.minute; second=date.second 70 # Convert time to fractions of a day 71 day = day + hour/24.0 + minute/1440.0 + second/86400.0 72 73 # Start Meeus algorithm (variables are in his notation) 74 if (month < 3): 75 month = month + 12 76 year = year - 1 77 78 A = int(year/100) 79 80 jd = int(365.25 * (year + 4716)) + int(30.6001 * (month + 1)) + \ 81 day - 1524.5 82 83 # optionally adjust the jd for the switch from 84 # the Julian to Gregorian Calendar 85 # here assumed to have occurred the day after 1582 October 4 86 if calendar in ['standard','gregorian']: 87 if jd >= 2299170.5: 88 # 1582 October 15 (Gregorian Calendar) 89 B = 2 - A + int(A/4) 90 elif jd < 2299160.5: 91 # 1582 October 5 (Julian Calendar) 92 B = 0 93 else: 94 raise ValueError, 'impossible date (falls in gap between end of Julian calendar and beginning of Gregorian calendar' 95 elif calendar == 'proleptic_gregorian': 96 B = 2 - A + int(A/4) 97 elif calendar == 'julian': 98 B = 0 99 else: 100 raise ValueError, 'unknown calendar, must be one of julian,standard,gregorian,proleptic_gregorian, got %s' % calendar 101 102 # adjust for Julian calendar if necessary 103 jd = jd + B 104 105 return jd
106
107 -def _NoLeapDayFromDate(date):
108 109 """ 110 111 creates a Julian Day for a calendar with no leap years from a datetime 112 instance. Returns the fractional Julian Day (resolution 1 second). 113 114 """ 115 116 year=date.year; month=date.month; day=date.day 117 hour=date.hour; minute=date.minute; second=date.second 118 # Convert time to fractions of a day 119 day = day + hour/24.0 + minute/1440.0 + second/86400.0 120 121 # Start Meeus algorithm (variables are in his notation) 122 if (month < 3): 123 month = month + 12 124 year = year - 1 125 126 jd = int(365. * (year + 4716)) + int(30.6001 * (month + 1)) + \ 127 day - 1524.5 128 129 return jd
130
131 -def _AllLeapFromDate(date):
132 133 """ 134 135 creates a Julian Day for a calendar where all years have 366 days from 136 a 'datetime-like' object. 137 Returns the fractional Julian Day (resolution 1 second). 138 139 """ 140 141 year=date.year; month=date.month; day=date.day 142 hour=date.hour; minute=date.minute; second=date.second 143 # Convert time to fractions of a day 144 day = day + hour/24.0 + minute/1440.0 + second/86400.0 145 146 # Start Meeus algorithm (variables are in his notation) 147 if (month < 3): 148 month = month + 12 149 year = year - 1 150 151 jd = int(366. * (year + 4716)) + int(30.6001 * (month + 1)) + \ 152 day - 1524.5 153 154 return jd
155
156 -def _360DayFromDate(date):
157 158 """ 159 160 creates a Julian Day for a calendar where all months have 30 daysfrom 161 a 'datetime-like' object. 162 Returns the fractional Julian Day (resolution 1 second). 163 164 """ 165 166 year=date.year; month=date.month; day=date.day 167 hour=date.hour; minute=date.minute; second=date.second 168 # Convert time to fractions of a day 169 day = day + hour/24.0 + minute/1440.0 + second/86400.0 170 171 jd = int(360. * (year + 4716)) + int(30. * (month - 1)) + day 172 173 return jd
174
175 -def DateFromJulianDay(JD,calendar='standard'):
176 """ 177 178 returns a 'datetime-like' object given Julian Day. Julian Day is a 179 fractional day with a resolution of 1 second. 180 181 if calendar='standard' or 'gregorian' (default), Julian day follows Julian 182 Calendar on and before 1582-10-5, Gregorian calendar after 1582-10-15. 183 184 if calendar='proleptic_gregorian', Julian Day follows gregorian calendar. 185 186 if calendar='julian', Julian Day follows julian calendar. 187 188 The datetime object is a 'real' datetime object if the date falls in 189 the Gregorian calendar (i.e. calendar='proleptic_gregorian', or 190 calendar = 'standard'/'gregorian' and the date is after 1582-10-15). 191 Otherwise, it's a 'phony' datetime object which is actually an instance 192 of netcdftime.datetime. 193 194 195 Algorithm: 196 197 Meeus, Jean (1998) Astronomical Algorithms (2nd Edition). Willmann-Bell, 198 Virginia. p. 63 199 200 """ 201 202 # based on redate.py by David Finlayson. 203 204 if JD < 0: 205 raise ValueError, 'Julian Day must be positive' 206 207 dayofwk = int(math.fmod(int(JD + 1.5),7)) 208 (F, Z) = math.modf(JD + 0.5) 209 Z = int(Z) 210 if calendar in ['standard','gregorian']: 211 if JD < 2299160.5: 212 A = Z 213 else: 214 alpha = int((Z - 1867216.25)/36524.25) 215 A = Z + 1 + alpha - int(alpha/4) 216 217 elif calendar == 'proleptic_gregorian': 218 alpha = int((Z - 1867216.25)/36524.25) 219 A = Z + 1 + alpha - int(alpha/4) 220 elif calendar == 'julian': 221 A = Z 222 else: 223 raise ValueError, 'unknown calendar, must be one of julian,standard,gregorian,proleptic_gregorian, got %s' % calendar 224 225 B = A + 1524 226 C = int((B - 122.1)/365.25) 227 D = int(365.25 * C) 228 E = int((B - D)/30.6001) 229 230 # Convert to date 231 day = B - D - int(30.6001 * E) + F 232 nday = B-D-123 233 if nday <= 305: 234 dayofyr = nday+60 235 else: 236 dayofyr = nday-305 237 if E < 14: 238 month = E - 1 239 else: 240 month = E - 13 241 242 if month > 2: 243 year = C - 4716 244 else: 245 year = C - 4715 246 247 # a leap year? 248 leap = 0 249 if year % 4 == 0: 250 leap = 1 251 if calendar == 'proleptic_gregorian' or \ 252 (calendar in ['standard','gregorian'] and JD >= 2299160.5): 253 if year % 100 == 0 and year % 400 != 0: 254 print year % 100, year % 400 255 leap = 0 256 if leap and month > 2: 257 dayofyr = dayofyr + leap 258 259 # Convert fractions of a day to time 260 (dfrac, days) = math.modf(day/1.0) 261 (hfrac, hours) = math.modf(dfrac * 24.0) 262 (mfrac, minutes) = math.modf(hfrac * 60.0) 263 seconds = round(mfrac * 60.0) # seconds are rounded 264 265 if seconds > 59: 266 seconds = 0 267 minutes = minutes + 1 268 if minutes > 59: 269 minutes = 0 270 hours = hours + 1 271 if hours > 23: 272 hours = 0 273 days = days + 1 274 275 # return a 'real' datetime instance if calendar is gregorian. 276 if calendar == 'proleptic_gregorian' or \ 277 (calendar in ['standard','gregorian'] and JD >= 2299160.5): 278 return real_datetime(year,month,int(days),int(hours),int(minutes),int(seconds)) 279 else: 280 # or else, return a 'datetime-like' instance. 281 return datetime(year,month,int(days),int(hours),int(minutes),int(seconds),dayofwk,dayofyr)
282
283 -def _DateFromNoLeapDay(JD):
284 """ 285 286 returns a 'datetime-like' object given Julian Day for a calendar with no leap 287 days. Julian Day is a fractional day with a resolution of 1 second. 288 289 """ 290 291 # based on redate.py by David Finlayson. 292 293 if JD < 0: 294 raise ValueError, 'Julian Day must be positive' 295 296 dayofwk = int(math.fmod(int(JD + 1.5),7)) 297 (F, Z) = math.modf(JD + 0.5) 298 Z = int(Z) 299 A = Z 300 B = A + 1524 301 C = int((B - 122.1)/365.) 302 D = int(365. * C) 303 E = int((B - D)/30.6001) 304 305 # Convert to date 306 day = B - D - int(30.6001 * E) + F 307 nday = B-D-123 308 if nday <= 305: 309 dayofyr = nday+60 310 else: 311 dayofyr = nday-305 312 if E < 14: 313 month = E - 1 314 else: 315 month = E - 13 316 317 if month > 2: 318 year = C - 4716 319 else: 320 year = C - 4715 321 322 # Convert fractions of a day to time 323 (dfrac, days) = math.modf(day/1.0) 324 (hfrac, hours) = math.modf(dfrac * 24.0) 325 (mfrac, minutes) = math.modf(hfrac * 60.0) 326 seconds = round(mfrac * 60.0) # seconds are rounded 327 328 if seconds > 59: 329 seconds = 0 330 minutes = minutes + 1 331 if minutes > 59: 332 minutes = 0 333 hours = hours + 1 334 if hours > 23: 335 hours = 0 336 days = days + 1 337 338 return datetime(year,month,int(days),int(hours),int(minutes),int(seconds), dayofwk, dayofyr)
339
340 -def _DateFromAllLeap(JD):
341 """ 342 343 returns a 'datetime-like' object given Julian Day for a calendar where all 344 years have 366 days. 345 Julian Day is a fractional day with a resolution of 1 second. 346 347 """ 348 349 # based on redate.py by David Finlayson. 350 351 if JD < 0: 352 raise ValueError, 'Julian Day must be positive' 353 354 dayofwk = int(math.fmod(int(JD + 1.5),7)) 355 (F, Z) = math.modf(JD + 0.5) 356 Z = int(Z) 357 A = Z 358 B = A + 1524 359 C = int((B - 122.1)/366.) 360 D = int(366. * C) 361 E = int((B - D)/30.6001) 362 363 # Convert to date 364 day = B - D - int(30.6001 * E) + F 365 nday = B-D-123 366 if nday <= 305: 367 dayofyr = nday+60 368 else: 369 dayofyr = nday-305 370 if E < 14: 371 month = E - 1 372 else: 373 month = E - 13 374 if month > 2: 375 dayofyr = dayofyr+1 376 377 if month > 2: 378 year = C - 4716 379 else: 380 year = C - 4715 381 382 # Convert fractions of a day to time 383 (dfrac, days) = math.modf(day/1.0) 384 (hfrac, hours) = math.modf(dfrac * 24.0) 385 (mfrac, minutes) = math.modf(hfrac * 60.0) 386 seconds = round(mfrac * 60.0) # seconds are rounded 387 388 if seconds > 59: 389 seconds = 0 390 minutes = minutes + 1 391 if minutes > 59: 392 minutes = 0 393 hours = hours + 1 394 if hours > 23: 395 hours = 0 396 days = days + 1 397 398 return datetime(year,month,int(days),int(hours),int(minutes),int(seconds), dayofwk, dayofyr)
399
400 -def _DateFrom360Day(JD):
401 """ 402 403 returns a 'datetime-like' object given Julian Day for a calendar where all 404 months have 30 days. 405 Julian Day is a fractional day with a resolution of 1 second. 406 407 """ 408 409 if JD < 0: 410 raise ValueError, 'Julian Day must be positive' 411 412 #jd = int(360. * (year + 4716)) + int(30. * (month - 1)) + day 413 (F, Z) = math.modf(JD) 414 year = int((Z-0.5)/360.) - 4716 415 dayofyr = JD - (year+4716)*360 416 month = int((dayofyr-0.5)/30)+1 417 day = dayofyr - (month-1)*30 + F 418 419 # Convert fractions of a day to time 420 (dfrac, days) = math.modf(day/1.0) 421 (hfrac, hours) = math.modf(dfrac * 24.0) 422 (mfrac, minutes) = math.modf(hfrac * 60.0) 423 seconds = round(mfrac * 60.0) # seconds are rounded 424 425 if seconds > 59: 426 seconds = 0 427 minutes = minutes + 1 428 if minutes > 59: 429 minutes = 0 430 hours = hours + 1 431 if hours > 23: 432 hours = 0 433 days = days + 1 434 435 return datetime(year,month,int(days),int(hours),int(minutes),int(seconds),-1, int(dayofyr))
436
437 -def _dateparse(timestr):
438 """parse a string of the form time-units since yyyy-mm-dd hh:mm:ss 439 return a tuple (units, datetimeinstance)""" 440 timestr_split = timestr.split() 441 units = timestr_split[0].lower() 442 if units not in _units: 443 raise ValueError,"units must be one of 'seconds', 'minutes', 'hours' or 'days' (or singular version of these), got '%s'" % units 444 if timestr_split[1].lower() != 'since': 445 raise ValueError,"no 'since' in unit_string" 446 # parse the date string. 447 n = timestr.find('since')+6 448 year,month,day,hour,minute,second,utc_offset = _parse_date(timestr[n:]) 449 return units, utc_offset, datetime(year, month, day, hour, minute, second)
450
451 -class utime:
452 """ 453 Performs conversions of netCDF time coordinate 454 data to/from datetime objects. 455 456 To initialize: C{t = utime(unit_string,calendar='standard')} 457 458 where 459 460 B{C{unit_string}} is a string of the form 461 C{'time-units since <time-origin>'} defining the time units. 462 463 Valid time-units are days, hours, minutes and seconds (the singular forms 464 are also accepted). An example unit_string would be C{'hours 465 since 0001-01-01 00:00:00'}. 466 467 The B{C{calendar}} keyword describes the calendar used in the time calculations. 468 All the values currently defined in the U{CF metadata convention 469 <http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.1/cf-conventions.html#time-coordinate>} 470 are accepted. The default is C{'standard'}, which corresponds to the mixed 471 Gregorian/Julian calendar used by the C{udunits library}. Valid calendars 472 are: 473 474 C{'gregorian'} or C{'standard'} (default): 475 476 Mixed Gregorian/Julian calendar as defined by udunits. 477 478 C{'proleptic_gregorian'}: 479 480 A Gregorian calendar extended to dates before 1582-10-15. That is, a year 481 is a leap year if either (i) it is divisible by 4 but not by 100 or (ii) 482 it is divisible by 400. 483 484 C{'noleap'} or C{'365_day'}: 485 486 Gregorian calendar without leap years, i.e., all years are 365 days long. 487 all_leap or 366_day Gregorian calendar with every year being a leap year, 488 i.e., all years are 366 days long. 489 490 C{'360_day'}: 491 492 All years are 360 days divided into 30 day months. 493 494 C{'julian'}: 495 496 Proleptic Julian calendar, extended to dates after 1582-10-5. A year is a 497 leap year if it is divisible by 4. 498 499 The C{L{num2date}} and C{L{date2num}} class methods can used to convert datetime 500 instances to/from the specified time units using the specified calendar. 501 502 The datetime instances returned by C{num2date} are 'real' python datetime 503 objects if the date falls in the Gregorian calendar (i.e. 504 C{calendar='proleptic_gregorian', 'standard'} or C{'gregorian'} and 505 the date is after 1582-10-15). Otherwise, they are 'phony' datetime 506 objects which are actually instances of C{L{netcdftime.datetime}}. This is 507 because the python datetime module cannot handle the weird dates in some 508 calendars (such as C{'360_day'} and C{'all_leap'}) which don't exist in any real 509 world calendar. 510 511 512 Example usage: 513 514 >>> from netcdftime import utime 515 >>> from datetime import datetime 516 >>> cdftime = utime('hours since 0001-01-01 00:00:00') 517 >>> date = datetime.now() 518 >>> print date 519 2006-03-17 16:04:02.561678 520 >>> 521 >>> t = cdftime.date2num(date) 522 >>> print t 523 17577328.0672 524 >>> 525 >>> date = cdftime.num2date(t) 526 >>> print date 527 2006-03-17 16:04:02 528 >>> 529 530 The resolution of the transformation operation is 1 second. 531 532 Warning: Dates between 1582-10-5 and 1582-10-15 do not exist in the 533 C{'standard'} or C{'gregorian'} calendars. An exception will be raised if you pass 534 a 'datetime-like' object in that range to the C{L{date2num}} class method. 535 536 Words of Wisdom from the British MetOffice concerning reference dates: 537 538 "udunits implements the mixed Gregorian/Julian calendar system, as 539 followed in England, in which dates prior to 1582-10-15 are assumed to use 540 the Julian calendar. Other software cannot be relied upon to handle the 541 change of calendar in the same way, so for robustness it is recommended 542 that the reference date be later than 1582. If earlier dates must be used, 543 it should be noted that udunits treats 0 AD as identical to 1 AD." 544 545 @ivar origin: datetime instance defining the origin of the netCDF time variable. 546 @ivar calendar: the calendar used (as specified by the C{calendar} keyword). 547 @ivar unit_string: a string defining the the netCDF time variable. 548 @ivar units: the units part of C{unit_string} (i.e. 'days', 'hours', 'seconds'). 549 """
550 - def __init__(self,unit_string,calendar='standard'):
551 """ 552 @param unit_string: a string of the form 553 C{'time-units since <time-origin>'} defining the time units. 554 555 Valid time-units are days, hours, minutes and seconds (the singular forms 556 are also accepted). An example unit_string would be C{'hours 557 since 0001-01-01 00:00:00'}. 558 559 @keyword calendar: describes the calendar used in the time calculations. 560 All the values currently defined in the U{CF metadata convention 561 <http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.1/cf-conventions.html#time-coordinate>} 562 are accepted. The default is C{'standard'}, which corresponds to the mixed 563 Gregorian/Julian calendar used by the C{udunits library}. Valid calendars 564 are: 565 - C{'gregorian'} or C{'standard'} (default): 566 Mixed Gregorian/Julian calendar as defined by udunits. 567 - C{'proleptic_gregorian'}: 568 A Gregorian calendar extended to dates before 1582-10-15. That is, a year 569 is a leap year if either (i) it is divisible by 4 but not by 100 or (ii) 570 it is divisible by 400. 571 - C{'noleap'} or C{'365_day'}: 572 Gregorian calendar without leap years, i.e., all years are 365 days long. 573 - C{'all_leap'} or C{'366_day'}: 574 Gregorian calendar with every year being a leap year, i.e., 575 all years are 366 days long. 576 -C{'360_day'}: 577 All years are 360 days divided into 30 day months. 578 -C{'julian'}: 579 Proleptic Julian calendar, extended to dates after 1582-10-5. A year is a 580 leap year if it is divisible by 4. 581 582 @returns: A class instance which may be used for converting times from netCDF 583 units to datetime objects. 584 """ 585 if calendar in _calendars: 586 self.calendar = calendar 587 else: 588 raise ValueError, "calendar must be one of %s, got '%s'" % (str(_calendars),calendar) 589 units, tzoffset, self.origin = _dateparse(unit_string) 590 self.tzoffset = tzoffset # time zone offset in minutes 591 self.units = units 592 self.unit_string = unit_string 593 if self.calendar in ['noleap','365_day'] and self.origin.month == 2 and self.origin.day == 29: 594 raise ValueError, 'cannot specify a leap day as the reference time with the noleap calendar' 595 if self.calendar == '360_day' and self.origin.day > 30: 596 raise ValueError, 'there are only 30 days in every month with the 360_day calendar' 597 if self.calendar in ['noleap','365_day']: 598 self._jd0 = _NoLeapDayFromDate(self.origin) 599 elif self.calendar in ['all_leap','366_day']: 600 self._jd0 = _AllLeapFromDate(self.origin) 601 elif self.calendar == '360_day': 602 self._jd0 = _360DayFromDate(self.origin) 603 else: 604 self._jd0 = JulianDayFromDate(self.origin,calendar=self.calendar)
605
606 - def date2num(self,date):
607 """ 608 Returns C{time_value} in units described by L{unit_string}, using 609 the specified L{calendar}, given a 'datetime-like' object. 610 611 The datetime object must represent UTC with no time-zone offset. 612 If there is a time-zone offset implied by L{unit_string}, it will 613 be applied to the returned numeric values. 614 615 Resolution is 1 second. 616 617 If C{calendar = 'standard'} or C{'gregorian'} (indicating 618 that the mixed Julian/Gregorian calendar is to be used), an 619 exception will be raised if the 'datetime-like' object describes 620 a date between 1582-10-5 and 1582-10-15. 621 622 Works for scalars, sequences and numpy arrays. 623 Returns a scalar if input is a scalar, else returns a numpy array. 624 """ 625 isscalar = False 626 try: 627 date[0] 628 except: 629 isscalar = True 630 if not isscalar: 631 date = numpy.array(date) 632 shape = date.shape 633 if self.calendar in ['julian','standard','gregorian','proleptic_gregorian']: 634 if isscalar: 635 jdelta = JulianDayFromDate(date,self.calendar)-self._jd0 636 else: 637 jdelta = [JulianDayFromDate(d,self.calendar)-self._jd0 for d in date.flat] 638 elif self.calendar in ['noleap','365_day']: 639 if date.month == 2 and date.day == 29: 640 raise ValueError, 'there is no leap day in the noleap calendar' 641 if isscalar: 642 jdelta = _NoLeapDayFromDate(date) - self._jd0 643 else: 644 jdelta = [_NoLeapDayFromDate(d)-self._jd0 for d in date.flat] 645 elif self.calendar in ['all_leap','366_day']: 646 if isscalar: 647 jdelta = _AllLeapFromDate(date) - self._jd0 648 else: 649 jdelta = [_AllLeapFromDate(d)-self._jd0 for d in date.flat] 650 elif self.calendar == '360_day': 651 if self.calendar == '360_day' and date.day > 30: 652 raise ValueError, 'there are only 30 days in every month with the 360_day calendar' 653 if isscalar: 654 jdelta = _360DayFromDate(date) - self._jd0 655 else: 656 jdelta = [_360DayFromDate(d)-self._jd0 for d in date.flat] 657 if not isscalar: 658 jdelta = numpy.array(jdelta) 659 # convert to desired units, add time zone offset. 660 if self.units in ['second','seconds']: 661 jdelta = jdelta*86400. + self.tzoffset*60. 662 elif self.units in ['minute','minutes']: 663 jdelta = jdelta*1440. + self.tzoffset 664 elif self.units in ['hour','hours']: 665 jdelta = jdelta*24. + self.tzoffset/60. 666 elif self.units in ['day','days']: 667 jdelta = jdelta + self.tzoffset/1440. 668 if isscalar: 669 return jdelta 670 else: 671 return numpy.reshape(jdelta,shape)
672
673 - def num2date(self,time_value):
674 """ 675 Return a 'datetime-like' object given a C{time_value} in units 676 described by L{unit_string}, using L{calendar}. 677 678 dates are in UTC with no offset, even if L{unit_string} contains 679 a time zone offset from UTC. 680 681 Resolution is 1 second. 682 683 Works for scalars, sequences and numpy arrays. 684 Returns a scalar if input is a scalar, else returns a numpy array. 685 686 The datetime instances returned by C{num2date} are 'real' python datetime 687 objects if the date falls in the Gregorian calendar (i.e. 688 C{calendar='proleptic_gregorian'}, or C{calendar = 'standard'/'gregorian'} and 689 the date is after 1582-10-15). Otherwise, they are 'phony' datetime 690 objects which are actually instances of netcdftime.datetime. This is 691 because the python datetime module cannot handle the weird dates in some 692 calendars (such as C{'360_day'} and C{'all_leap'}) which 693 do not exist in any real world calendar. 694 """ 695 isscalar = False 696 try: 697 time_value[0] 698 except: 699 isscalar = True 700 if not isscalar: 701 time_value = numpy.array(time_value, dtype='d') 702 shape = time_value.shape 703 # convert to desired units, remove time zone offset. 704 if self.units in ['second','seconds']: 705 jdelta = time_value/86400. - self.tzoffset/1440. 706 elif self.units in ['minute','minutes']: 707 jdelta = time_value/1440. - self.tzoffset/1440. 708 elif self.units in ['hour','hours']: 709 jdelta = time_value/24. - self.tzoffset/1440. 710 elif self.units in ['day','days']: 711 jdelta = time_value - self.tzoffset/1440. 712 jd = self._jd0 + jdelta 713 if self.calendar in ['julian','standard','gregorian','proleptic_gregorian']: 714 if not isscalar: 715 date = [DateFromJulianDay(j,self.calendar) for j in jd.flat] 716 else: 717 date = DateFromJulianDay(jd,self.calendar) 718 elif self.calendar in ['noleap','365_day']: 719 if not isscalar: 720 date = [_DateFromNoLeapDay(j) for j in jd.flat] 721 else: 722 date = _DateFromNoLeapDay(jd) 723 elif self.calendar in ['all_leap','366_day']: 724 if not isscalar: 725 date = [_DateFromAllLeap(j) for j in jd.flat] 726 else: 727 date = _DateFromAllLeap(jd) 728 elif self.calendar == '360_day': 729 if not isscalar: 730 date = [_DateFrom360Day(j) for j in jd.flat] 731 else: 732 date = _DateFrom360Day(jd) 733 if isscalar: 734 return date 735 else: 736 return numpy.reshape(numpy.array(date),shape)
737
738 -def _parse_date(origin):
739 """Parses a date string and returns a tuple 740 (year,month,day,hour,minute,second,utc_offset). 741 utc_offset is in minutes. 742 743 This function parses the 'origin' part of the time unit. It should be 744 something like:: 745 746 2004-11-03 14:42:27.0 +2:00 747 748 Lots of things are optional; just the date is mandatory. 749 750 by Roberto De Almeida 751 752 excerpted from coards.py - http://cheeseshop.python.org/pypi/coards/ 753 """ 754 # yyyy-mm-dd [hh:mm:ss[.s][ [+-]hh[:][mm]]] 755 p = re.compile( r'''(?P<year>\d{1,4}) # yyyy 756 - # 757 (?P<month>\d{1,2}) # mm or m 758 - # 759 (?P<day>\d{1,2}) # dd or d 760 # 761 (?: # [optional time and timezone] 762 \s # 763 (?P<hour>\d{1,2}) # hh or h 764 : # 765 (?P<min>\d{1,2}) # mm or m 766 (?: 767 \: 768 (?P<sec>\d{1,2}) # ss or s (optional) 769 )? 770 # 771 (?: # [optional decisecond] 772 \. # . 773 (?P<dsec>\d) # s 774 )? # 775 (?: # [optional timezone] 776 \s # 777 (?P<ho>[+-]? # [+ or -] 778 \d{1,2}) # hh or h 779 :? # [:] 780 (?P<mo>\d{2})? # [mm] 781 )? # 782 )? # 783 $ # EOL 784 ''', re.VERBOSE) 785 786 m = p.match(origin.strip()) 787 if m: 788 c = m.groupdict(0) 789 # UTC offset. 790 offset = int(c['ho'])*60 + int(c['mo']) 791 return int(c['year']),int(c['month']),int(c['day']),int(c['hour']),int(c['min']),int(c['sec']),offset 792 793 raise Exception('Invalid date origin: %s' % origin)
794 795 # remove the unsupposed "%s" command. But don't 796 # do it if there's an even number of %s before the s 797 # because those are all escaped. Can't simply 798 # remove the s because the result of 799 # %sY 800 # should be %Y if %s isn't supported, not the 801 # 4 digit year. 802 _illegal_s = re.compile(r"((^|[^%])(%%)*%s)") 803
804 -def _findall(text, substr):
805 # Also finds overlaps 806 sites = [] 807 i = 0 808 while 1: 809 j = text.find(substr, i) 810 if j == -1: 811 break 812 sites.append(j) 813 i=j+1 814 return sites
815 816 # Every 28 years the calendar repeats, except through century leap 817 # years where it's 6 years. But only if you're using the Gregorian 818 # calendar. ;) 819
820 -def _strftime(dt, fmt):
821 if _illegal_s.search(fmt): 822 raise TypeError("This strftime implementation does not handle %s") 823 # don't use strftime method at all. 824 #if dt.year > 1900: 825 # return dt.strftime(fmt) 826 827 year = dt.year 828 # For every non-leap year century, advance by 829 # 6 years to get into the 28-year repeat cycle 830 delta = 2000 - year 831 off = 6*(delta // 100 + delta // 400) 832 year = year + off 833 834 # Move to around the year 2000 835 year = year + ((2000 - year)//28)*28 836 timetuple = dt.timetuple() 837 s1 = time.strftime(fmt, (year,) + timetuple[1:]) 838 sites1 = _findall(s1, str(year)) 839 840 s2 = time.strftime(fmt, (year+28,) + timetuple[1:]) 841 sites2 = _findall(s2, str(year+28)) 842 843 sites = [] 844 for site in sites1: 845 if site in sites2: 846 sites.append(site) 847 848 s = s1 849 syear = "%4d" % (dt.year,) 850 for site in sites: 851 s = s[:site] + syear + s[site+4:] 852 return s
853
854 -def date2num(dates,units,calendar='standard'):
855 """ 856 date2num(dates,units,calendar='standard') 857 858 Return numeric time values given datetime objects. The units 859 of the numeric time values are described by the L{units} argument 860 and the L{calendar} keyword. The datetime objects must 861 be in UTC with no time-zone offset. If there is a 862 time-zone offset in C{units}, it will be applied to the 863 returned numeric values. 864 865 Like the matplotlib C{date2num} function, except that it allows 866 for different units and calendars. Behaves the same if 867 C{units = 'days since 0001-01-01 00:00:00'} and 868 C{calendar = 'proleptic_gregorian'}. 869 870 @param dates: A datetime object or a sequence of datetime objects. 871 The datetime objects should not include a time-zone offset. 872 873 @param units: a string of the form C{'B{time units} since B{reference time}}' 874 describing the time units. B{C{time units}} can be days, hours, minutes 875 or seconds. B{C{reference time}} is the time origin. A valid choice 876 would be units=C{'hours since 1800-01-01 00:00:00 -6:00'}. 877 878 @param calendar: describes the calendar used in the time calculations. 879 All the values currently defined in the U{CF metadata convention 880 <http://cf-pcmdi.llnl.gov/documents/cf-conventions/>} are supported. 881 Valid calendars C{'standard', 'gregorian', 'proleptic_gregorian' 882 'noleap', '365_day', '360_day', 'julian', 'all_leap', '366_day'}. 883 Default is C{'standard'}, which is a mixed Julian/Gregorian calendar. 884 885 @return: a numeric time value, or an array of numeric time values. 886 887 The maximum resolution of the numeric time values is 1 second. 888 """ 889 cdftime = utime(units,calendar=calendar) 890 return cdftime.date2num(dates)
891
892 -def num2date(times,units,calendar='standard'):
893 """ 894 num2date(times,units,calendar='standard') 895 896 Return datetime objects given numeric time values. The units 897 of the numeric time values are described by the C{units} argument 898 and the C{calendar} keyword. The returned datetime objects represent 899 UTC with no time-zone offset, even if the specified 900 C{units} contain a time-zone offset. 901 902 Like the matplotlib C{num2date} function, except that it allows 903 for different units and calendars. Behaves the same if 904 C{units = 'days since 001-01-01 00:00:00'} and 905 C{calendar = 'proleptic_gregorian'}. 906 907 @param times: numeric time values. Maximum resolution is 1 second. 908 909 @param units: a string of the form C{'B{time units} since B{reference time}}' 910 describing the time units. B{C{time units}} can be days, hours, minutes 911 or seconds. B{C{reference time}} is the time origin. A valid choice 912 would be units=C{'hours since 1800-01-01 00:00:00 -6:00'}. 913 914 @param calendar: describes the calendar used in the time calculations. 915 All the values currently defined in the U{CF metadata convention 916 <http://cf-pcmdi.llnl.gov/documents/cf-conventions/>} are supported. 917 Valid calendars C{'standard', 'gregorian', 'proleptic_gregorian' 918 'noleap', '365_day', '360_day', 'julian', 'all_leap', '366_day'}. 919 Default is C{'standard'}, which is a mixed Julian/Gregorian calendar. 920 921 @return: a datetime instance, or an array of datetime instances. 922 923 The datetime instances returned are 'real' python datetime 924 objects if the date falls in the Gregorian calendar (i.e. 925 C{calendar='proleptic_gregorian'}, or C{calendar = 'standard'} or C{'gregorian'} 926 and the date is after 1582-10-15). Otherwise, they are 'phony' datetime 927 objects which support some but not all the methods of 'real' python 928 datetime objects. This is because the python datetime module cannot 929 the uses the C{'proleptic_gregorian'} calendar, even before the switch 930 occured from the Julian calendar in 1582. The datetime instances 931 do not contain a time-zone offset, even if the specified C{units} 932 contains one. 933 """ 934 cdftime = utime(units,calendar=calendar) 935 return cdftime.num2date(times)
936 937
938 -def _check_index(indices, dates, nctime, calendar):
939 """Assert that the time indices given correspond to the given dates.""" 940 t = numpy.empty(len(indices), nctime.dtype) 941 for n,i in enumerate(indices): 942 t[n] = nctime[i] 943 assert numpy.all( num2date(t, nctime.units, calendar) == dates)
944 945
946 -def date2index(dates, nctime, calendar=None):
947 """ 948 date2index(dates, nctime, calendar=None) 949 950 Return indices of a netCDF time variable corresponding to the given dates. 951 952 @param dates: A datetime object or a sequence of datetime objects. 953 The datetime objects should not include a time-zone offset. 954 955 @param nctime: A netCDF time variable object. The nctime object must have a 956 C{units} attribute. 957 958 @param calendar: Describes the calendar used in the time calculation. 959 Valid calendars C{'standard', 'gregorian', 'proleptic_gregorian' 960 'noleap', '365_day', '360_day', 'julian', 'all_leap', '366_day'}. 961 Default is C{'standard'}, which is a mixed Julian/Gregorian calendar 962 If C{calendar} is None, its value is given by C{nctime.calendar} or 963 C{standard} if no such attribute exists. 964 """ 965 # Setting the calendar. 966 if calendar is None: 967 calendar = getattr(nctime, 'calendar', 'standard') 968 969 num = numpy.atleast_1d(date2num(dates, nctime.units, calendar)) 970 971 index = numpy.empty(numpy.alen(dates), int) 972 973 # Trying to infer the correct index from the starting time and the stride. 974 try: 975 t0, t1 = nctime[:2] 976 dt = t1 - t0 977 index[:] = (num-t0)/dt 978 979 # Checking that the index really corresponds to the given date. 980 _check_index(index, dates, nctime, calendar) 981 982 except AssertionError: 983 984 # If check fails, use brute force method. 985 index[:] = numpy.digitize(num, nctime[:]) - 1 986 987 # Perform check again. 988 _check_index(index, dates, nctime, calendar) 989 990 # convert numpy scalars or single element arrays to python ints. 991 index = _toscalar(index) 992 993 return index
994 995
996 -def _toscalar(a):
997 if a.shape in [(),(1,)]: 998 return a.item() 999 else: 1000 return a
1001