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.2' 
  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 = Z - (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 isscalar: 640 if date.month == 2 and date.day == 29: 641 raise ValueError, 'there is no leap day in the noleap calendar' 642 jdelta = _NoLeapDayFromDate(date) - self._jd0 643 else: 644 jdelta = [] 645 for d in date.flat: 646 if d.month == 2 and d.day == 29: 647 raise ValueError, 'there is no leap day in the noleap calendar' 648 jdelta.append(_NoLeapDayFromDate(d)-self._jd0) 649 elif self.calendar in ['all_leap','366_day']: 650 if isscalar: 651 jdelta = _AllLeapFromDate(date) - self._jd0 652 else: 653 jdelta = [_AllLeapFromDate(d)-self._jd0 for d in date.flat] 654 elif self.calendar == '360_day': 655 if self.calendar == '360_day' and date.day > 30: 656 raise ValueError, 'there are only 30 days in every month with the 360_day calendar' 657 if isscalar: 658 jdelta = _360DayFromDate(date) - self._jd0 659 else: 660 jdelta = [_360DayFromDate(d)-self._jd0 for d in date.flat] 661 if not isscalar: 662 jdelta = numpy.array(jdelta) 663 # convert to desired units, add time zone offset. 664 if self.units in ['second','seconds']: 665 jdelta = jdelta*86400. + self.tzoffset*60. 666 elif self.units in ['minute','minutes']: 667 jdelta = jdelta*1440. + self.tzoffset 668 elif self.units in ['hour','hours']: 669 jdelta = jdelta*24. + self.tzoffset/60. 670 elif self.units in ['day','days']: 671 jdelta = jdelta + self.tzoffset/1440. 672 if isscalar: 673 return jdelta 674 else: 675 return numpy.reshape(jdelta,shape)
676
677 - def num2date(self,time_value):
678 """ 679 Return a 'datetime-like' object given a C{time_value} in units 680 described by L{unit_string}, using L{calendar}. 681 682 dates are in UTC with no offset, even if L{unit_string} contains 683 a time zone offset from UTC. 684 685 Resolution is 1 second. 686 687 Works for scalars, sequences and numpy arrays. 688 Returns a scalar if input is a scalar, else returns a numpy array. 689 690 The datetime instances returned by C{num2date} are 'real' python datetime 691 objects if the date falls in the Gregorian calendar (i.e. 692 C{calendar='proleptic_gregorian'}, or C{calendar = 'standard'/'gregorian'} and 693 the date is after 1582-10-15). Otherwise, they are 'phony' datetime 694 objects which are actually instances of netcdftime.datetime. This is 695 because the python datetime module cannot handle the weird dates in some 696 calendars (such as C{'360_day'} and C{'all_leap'}) which 697 do not exist in any real world calendar. 698 """ 699 isscalar = False 700 try: 701 time_value[0] 702 except: 703 isscalar = True 704 if not isscalar: 705 time_value = numpy.array(time_value, dtype='d') 706 shape = time_value.shape 707 # convert to desired units, remove time zone offset. 708 if self.units in ['second','seconds']: 709 jdelta = time_value/86400. - self.tzoffset/1440. 710 elif self.units in ['minute','minutes']: 711 jdelta = time_value/1440. - self.tzoffset/1440. 712 elif self.units in ['hour','hours']: 713 jdelta = time_value/24. - self.tzoffset/1440. 714 elif self.units in ['day','days']: 715 jdelta = time_value - self.tzoffset/1440. 716 jd = self._jd0 + jdelta 717 if self.calendar in ['julian','standard','gregorian','proleptic_gregorian']: 718 if not isscalar: 719 date = [DateFromJulianDay(j,self.calendar) for j in jd.flat] 720 else: 721 date = DateFromJulianDay(jd,self.calendar) 722 elif self.calendar in ['noleap','365_day']: 723 if not isscalar: 724 date = [_DateFromNoLeapDay(j) for j in jd.flat] 725 else: 726 date = _DateFromNoLeapDay(jd) 727 elif self.calendar in ['all_leap','366_day']: 728 if not isscalar: 729 date = [_DateFromAllLeap(j) for j in jd.flat] 730 else: 731 date = _DateFromAllLeap(jd) 732 elif self.calendar == '360_day': 733 if not isscalar: 734 date = [_DateFrom360Day(j) for j in jd.flat] 735 else: 736 date = _DateFrom360Day(jd) 737 if isscalar: 738 return date 739 else: 740 return numpy.reshape(numpy.array(date),shape)
741
742 -def _parse_date(origin):
743 """Parses a date string and returns a tuple 744 (year,month,day,hour,minute,second,utc_offset). 745 utc_offset is in minutes. 746 747 This function parses the 'origin' part of the time unit. It should be 748 something like:: 749 750 2004-11-03 14:42:27.0 +2:00 751 752 Lots of things are optional; just the date is mandatory. 753 754 by Roberto De Almeida 755 756 excerpted from coards.py - http://cheeseshop.python.org/pypi/coards/ 757 """ 758 # yyyy-mm-dd [hh:mm:ss[.s][ [+-]hh[:][mm]]] 759 p = re.compile( r'''(?P<year>\d{1,4}) # yyyy 760 - # 761 (?P<month>\d{1,2}) # mm or m 762 - # 763 (?P<day>\d{1,2}) # dd or d 764 # 765 (?: # [optional time and timezone] 766 \s # 767 (?P<hour>\d{1,2}) # hh or h 768 : # 769 (?P<min>\d{1,2}) # mm or m 770 (?: 771 \: 772 (?P<sec>\d{1,2}) # ss or s (optional) 773 )? 774 # 775 (?: # [optional decisecond] 776 \. # . 777 (?P<dsec>\d) # s 778 )? # 779 (?: # [optional timezone] 780 \s # 781 (?P<ho>[+-]? # [+ or -] 782 \d{1,2}) # hh or h 783 :? # [:] 784 (?P<mo>\d{2})? # [mm] 785 )? # 786 )? # 787 $ # EOL 788 ''', re.VERBOSE) 789 790 m = p.match(origin.strip()) 791 if m: 792 c = m.groupdict(0) 793 # UTC offset. 794 offset = int(c['ho'])*60 + int(c['mo']) 795 return int(c['year']),int(c['month']),int(c['day']),int(c['hour']),int(c['min']),int(c['sec']),offset 796 797 raise Exception('Invalid date origin: %s' % origin)
798 799 # remove the unsupposed "%s" command. But don't 800 # do it if there's an even number of %s before the s 801 # because those are all escaped. Can't simply 802 # remove the s because the result of 803 # %sY 804 # should be %Y if %s isn't supported, not the 805 # 4 digit year. 806 _illegal_s = re.compile(r"((^|[^%])(%%)*%s)") 807
808 -def _findall(text, substr):
809 # Also finds overlaps 810 sites = [] 811 i = 0 812 while 1: 813 j = text.find(substr, i) 814 if j == -1: 815 break 816 sites.append(j) 817 i=j+1 818 return sites
819 820 # Every 28 years the calendar repeats, except through century leap 821 # years where it's 6 years. But only if you're using the Gregorian 822 # calendar. ;) 823
824 -def _strftime(dt, fmt):
825 if _illegal_s.search(fmt): 826 raise TypeError("This strftime implementation does not handle %s") 827 # don't use strftime method at all. 828 #if dt.year > 1900: 829 # return dt.strftime(fmt) 830 831 year = dt.year 832 # For every non-leap year century, advance by 833 # 6 years to get into the 28-year repeat cycle 834 delta = 2000 - year 835 off = 6*(delta // 100 + delta // 400) 836 year = year + off 837 838 # Move to around the year 2000 839 year = year + ((2000 - year)//28)*28 840 timetuple = dt.timetuple() 841 s1 = time.strftime(fmt, (year,) + timetuple[1:]) 842 sites1 = _findall(s1, str(year)) 843 844 s2 = time.strftime(fmt, (year+28,) + timetuple[1:]) 845 sites2 = _findall(s2, str(year+28)) 846 847 sites = [] 848 for site in sites1: 849 if site in sites2: 850 sites.append(site) 851 852 s = s1 853 syear = "%4d" % (dt.year,) 854 for site in sites: 855 s = s[:site] + syear + s[site+4:] 856 return s
857
858 -def date2num(dates,units,calendar='standard'):
859 """ 860 date2num(dates,units,calendar='standard') 861 862 Return numeric time values given datetime objects. The units 863 of the numeric time values are described by the L{units} argument 864 and the L{calendar} keyword. The datetime objects must 865 be in UTC with no time-zone offset. If there is a 866 time-zone offset in C{units}, it will be applied to the 867 returned numeric values. 868 869 Like the matplotlib C{date2num} function, except that it allows 870 for different units and calendars. Behaves the same if 871 C{units = 'days since 0001-01-01 00:00:00'} and 872 C{calendar = 'proleptic_gregorian'}. 873 874 @param dates: A datetime object or a sequence of datetime objects. 875 The datetime objects should not include a time-zone offset. 876 877 @param units: a string of the form C{'B{time units} since B{reference time}}' 878 describing the time units. B{C{time units}} can be days, hours, minutes 879 or seconds. B{C{reference time}} is the time origin. A valid choice 880 would be units=C{'hours since 1800-01-01 00:00:00 -6:00'}. 881 882 @param calendar: describes the calendar used in the time calculations. 883 All the values currently defined in the U{CF metadata convention 884 <http://cf-pcmdi.llnl.gov/documents/cf-conventions/>} are supported. 885 Valid calendars C{'standard', 'gregorian', 'proleptic_gregorian' 886 'noleap', '365_day', '360_day', 'julian', 'all_leap', '366_day'}. 887 Default is C{'standard'}, which is a mixed Julian/Gregorian calendar. 888 889 @return: a numeric time value, or an array of numeric time values. 890 891 The maximum resolution of the numeric time values is 1 second. 892 """ 893 cdftime = utime(units,calendar=calendar) 894 return cdftime.date2num(dates)
895
896 -def num2date(times,units,calendar='standard'):
897 """ 898 num2date(times,units,calendar='standard') 899 900 Return datetime objects given numeric time values. The units 901 of the numeric time values are described by the C{units} argument 902 and the C{calendar} keyword. The returned datetime objects represent 903 UTC with no time-zone offset, even if the specified 904 C{units} contain a time-zone offset. 905 906 Like the matplotlib C{num2date} function, except that it allows 907 for different units and calendars. Behaves the same if 908 C{units = 'days since 001-01-01 00:00:00'} and 909 C{calendar = 'proleptic_gregorian'}. 910 911 @param times: numeric time values. Maximum resolution is 1 second. 912 913 @param units: a string of the form C{'B{time units} since B{reference time}}' 914 describing the time units. B{C{time units}} can be days, hours, minutes 915 or seconds. B{C{reference time}} is the time origin. A valid choice 916 would be units=C{'hours since 1800-01-01 00:00:00 -6:00'}. 917 918 @param calendar: describes the calendar used in the time calculations. 919 All the values currently defined in the U{CF metadata convention 920 <http://cf-pcmdi.llnl.gov/documents/cf-conventions/>} are supported. 921 Valid calendars C{'standard', 'gregorian', 'proleptic_gregorian' 922 'noleap', '365_day', '360_day', 'julian', 'all_leap', '366_day'}. 923 Default is C{'standard'}, which is a mixed Julian/Gregorian calendar. 924 925 @return: a datetime instance, or an array of datetime instances. 926 927 The datetime instances returned are 'real' python datetime 928 objects if the date falls in the Gregorian calendar (i.e. 929 C{calendar='proleptic_gregorian'}, or C{calendar = 'standard'} or C{'gregorian'} 930 and the date is after 1582-10-15). Otherwise, they are 'phony' datetime 931 objects which support some but not all the methods of 'real' python 932 datetime objects. This is because the python datetime module cannot 933 the uses the C{'proleptic_gregorian'} calendar, even before the switch 934 occured from the Julian calendar in 1582. The datetime instances 935 do not contain a time-zone offset, even if the specified C{units} 936 contains one. 937 """ 938 cdftime = utime(units,calendar=calendar) 939 return cdftime.num2date(times)
940
941 -def _check_index(indices, dates, nctime, calendar):
942 """Return True if the time indices given correspond to the given dates, 943 False otherwise. 944 945 Parameters: 946 947 indices : sequence of integers 948 Positive integers indexing the time variable. 949 950 dates : sequence of datetime objects 951 Reference dates. 952 953 nctime : netCDF Variable object 954 NetCDF time object. 955 956 calendar : string 957 Calendar of nctime. 958 """ 959 if (indices <0).any(): 960 return False 961 962 if (indices >= nctime.shape[0]).any(): 963 return False 964 965 t = nctime[indices] 966 # if fancy indexing not available, fall back on this. 967 # t=[] 968 # for ind in indices: 969 # t.append(nctime[ind]) 970 return numpy.all( num2date(t, nctime.units, calendar) == dates)
971 972 973
974 -def date2index(dates, nctime, calendar=None, select='exact'):
975 """ 976 date2index(dates, nctime, calendar=None, select='exact') 977 978 Return indices of a netCDF time variable corresponding to the given dates. 979 980 @param dates: A datetime object or a sequence of datetime objects. 981 The datetime objects should not include a time-zone offset. 982 983 @param nctime: A netCDF time variable object. The nctime object must have a 984 C{units} attribute. The entries are assumed to be stored in increasing 985 order. 986 987 @param calendar: Describes the calendar used in the time calculation. 988 Valid calendars C{'standard', 'gregorian', 'proleptic_gregorian' 989 'noleap', '365_day', '360_day', 'julian', 'all_leap', '366_day'}. 990 Default is C{'standard'}, which is a mixed Julian/Gregorian calendar 991 If C{calendar} is None, its value is given by C{nctime.calendar} or 992 C{standard} if no such attribute exists. 993 994 @param select: C{'exact', 'before', 'after', 'nearest'} 995 The index selection method. C{exact} will return the indices perfectly 996 matching the dates given. C{before} and C{after} will return the indices 997 corresponding to the dates just before or just after the given dates if 998 an exact match cannot be found. C{nearest} will return the indices that 999 correpond to the closest dates. 1000 """ 1001 # Setting the calendar. 1002 if calendar == None: 1003 calendar = getattr(nctime, 'calendar', 'standard') 1004 1005 num = numpy.atleast_1d(date2num(dates, nctime.units, calendar)) 1006 N = len(nctime) 1007 1008 # Trying to infer the correct index from the starting time and the stride. 1009 # This assumes that the times are increasing uniformly. 1010 t0, t1 = nctime[:2] 1011 dt = t1 - t0 1012 index = numpy.array((num-t0)/dt, int) 1013 1014 # Checking that the index really corresponds to the given date. 1015 # If the times do not correspond, then it means that the times 1016 # are not increasing uniformly and we try the bisection method. 1017 if not _check_index(index, dates, nctime, calendar): 1018 1019 # Use the bisection method. Assumes the dates are ordered. 1020 import bisect 1021 index = numpy.array([bisect.bisect_left(nctime, n) for n in num], int) 1022 1023 after = index == N 1024 before = index == 0 1025 1026 if select in ['before', 'exact'] and numpy.any(before): 1027 raise ValueError, 'At least one of the dates given is before the first date in `nctime`.' 1028 1029 if select in ['after', 'exact'] and numpy.any(after): 1030 raise ValueError, 'At least one of the dates given is after the last date in `nctime`.' 1031 1032 1033 # Find the dates for which the match is not perfect. 1034 # Use list comprehension instead of the simpler `nctime[index]` since 1035 # not all time objects support numpy integer indexing (eg dap). 1036 index[after] = N-1 1037 ncnum = numpy.squeeze([nctime[i] for i in index]) 1038 mismatch = numpy.nonzero(ncnum != num)[0] 1039 1040 if select == 'exact': 1041 if len(mismatch) > 0: 1042 raise ValueError, 'Some of the dates specified were not found in the `nctime` variable.' 1043 1044 elif select == 'before': 1045 index[after] = N 1046 index[mismatch] -= 1 1047 1048 elif select == 'after': 1049 pass 1050 1051 elif select == 'nearest': 1052 nearest_to_left = num[mismatch] < numpy.array( [nctime[i-1] + nctime[i] for i in index[mismatch]]) / 2. 1053 index[mismatch] = index[mismatch] - 1 * nearest_to_left 1054 1055 else: 1056 raise ValueError("%s is not an option for the `select` argument."%select) 1057 1058 1059 # Correct for indices equal to -1 1060 index[before] = 0 1061 1062 # convert numpy scalars or single element arrays to python ints. 1063 return _toscalar(index)
1064 1065
1066 -def _toscalar(a):
1067 if a.shape in [(),(1,)]: 1068 return a.item() 1069 else: 1070 return a
1071