Package netcdftime ::
Module 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
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'
37 if format is None:
38 format = self.format
39 return _strftime(self,format)
41 return (self.year,self.month,self.day,self.hour,self.minute,self.second,self.dayofwk,self.dayofyr,-1)
44
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
67
68 year=date.year; month=date.month; day=date.day
69 hour=date.hour; minute=date.minute; second=date.second
70
71 day = day + hour/24.0 + minute/1440.0 + second/86400.0
72
73
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
84
85
86 if calendar in ['standard','gregorian']:
87 if jd >= 2299170.5:
88
89 B = 2 - A + int(A/4)
90 elif jd < 2299160.5:
91
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
103 jd = jd + B
104
105 return jd
106
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
119 day = day + hour/24.0 + minute/1440.0 + second/86400.0
120
121
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
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
144 day = day + hour/24.0 + minute/1440.0 + second/86400.0
145
146
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
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
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
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
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
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
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
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)
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
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
281 return datetime(year,month,int(days),int(hours),int(minutes),int(seconds),dayofwk,dayofyr)
282
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
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
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
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)
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
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
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
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
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)
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
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
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
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)
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
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
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
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
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
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
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
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
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
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
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
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
800
801
802
803
804
805
806 _illegal_s = re.compile(r"((^|[^%])(%%)*%s)")
807
809
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
821
822
823
825 if _illegal_s.search(fmt):
826 raise TypeError("This strftime implementation does not handle %s")
827
828
829
830
831 year = dt.year
832
833
834 delta = 2000 - year
835 off = 6*(delta // 100 + delta // 400)
836 year = year + off
837
838
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
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
967
968
969
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
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
1009
1010 t0, t1 = nctime[:2]
1011 dt = t1 - t0
1012 index = numpy.array((num-t0)/dt, int)
1013
1014
1015
1016
1017 if not _check_index(index, dates, nctime, calendar):
1018
1019
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
1034
1035
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
1060 index[before] = 0
1061
1062
1063 return _toscalar(index)
1064
1065
1067 if a.shape in [(),(1,)]:
1068 return a.item()
1069 else:
1070 return a
1071