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