~ubuntu-branches/ubuntu/trusty/python-babel/trusty

« back to all changes in this revision

Viewing changes to babel/dates.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2013-10-28 10:11:31 UTC
  • mfrom: (4.1.2 sid)
  • Revision ID: package-import@ubuntu.com-20131028101131-zwbmm8sc29iemmlr
Tags: 1.3-2ubuntu1
* Merge from Debian unstable.  Remaining changes:
  - debian/rules: Run the testsuite during builds.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# -*- coding: utf-8 -*-
2
 
#
3
 
# Copyright (C) 2007 Edgewall Software
4
 
# All rights reserved.
5
 
#
6
 
# This software is licensed as described in the file COPYING, which
7
 
# you should have received as part of this distribution. The terms
8
 
# are also available at http://babel.edgewall.org/wiki/License.
9
 
#
10
 
# This software consists of voluntary contributions made by many
11
 
# individuals. For the exact contribution history, see the revision
12
 
# history and logs, available at http://babel.edgewall.org/log/.
13
 
 
14
 
"""Locale dependent formatting and parsing of dates and times.
15
 
 
16
 
The default locale for the functions in this module is determined by the
17
 
following environment variables, in that order:
18
 
 
19
 
 * ``LC_TIME``,
20
 
 * ``LC_ALL``, and
21
 
 * ``LANG``
22
 
"""
23
 
 
24
 
from datetime import date, datetime, time, timedelta, tzinfo
 
2
"""
 
3
    babel.dates
 
4
    ~~~~~~~~~~~
 
5
 
 
6
    Locale dependent formatting and parsing of dates and times.
 
7
 
 
8
    The default locale for the functions in this module is determined by the
 
9
    following environment variables, in that order:
 
10
 
 
11
     * ``LC_TIME``,
 
12
     * ``LC_ALL``, and
 
13
     * ``LANG``
 
14
 
 
15
    :copyright: (c) 2013 by the Babel Team.
 
16
    :license: BSD, see LICENSE for more details.
 
17
"""
 
18
 
 
19
from __future__ import division
 
20
 
25
21
import re
 
22
import pytz as _pytz
 
23
 
 
24
from datetime import date, datetime, time, timedelta
 
25
from bisect import bisect_right
26
26
 
27
27
from babel.core import default_locale, get_global, Locale
28
 
from babel.util import UTC
 
28
from babel.util import UTC, LOCALTZ
 
29
from babel._compat import string_types, integer_types, number_types
29
30
 
30
 
__all__ = ['format_date', 'format_datetime', 'format_time',
31
 
           'get_timezone_name', 'parse_date', 'parse_datetime', 'parse_time']
32
 
__docformat__ = 'restructuredtext en'
33
31
 
34
32
LC_TIME = default_locale('LC_TIME')
35
33
 
38
36
datetime_ = datetime
39
37
time_ = time
40
38
 
 
39
 
 
40
def get_timezone(zone=None):
 
41
    """Looks up a timezone by name and returns it.  The timezone object
 
42
    returned comes from ``pytz`` and corresponds to the `tzinfo` interface and
 
43
    can be used with all of the functions of Babel that operate with dates.
 
44
 
 
45
    If a timezone is not known a :exc:`LookupError` is raised.  If `zone`
 
46
    is ``None`` a local zone object is returned.
 
47
 
 
48
    :param zone: the name of the timezone to look up.  If a timezone object
 
49
                 itself is passed in, mit's returned unchanged.
 
50
    """
 
51
    if zone is None:
 
52
        return LOCALTZ
 
53
    if not isinstance(zone, string_types):
 
54
        return zone
 
55
    try:
 
56
        return _pytz.timezone(zone)
 
57
    except _pytz.UnknownTimeZoneError:
 
58
        raise LookupError('Unknown timezone %s' % zone)
 
59
 
 
60
 
 
61
def get_next_timezone_transition(zone=None, dt=None):
 
62
    """Given a timezone it will return a :class:`TimezoneTransition` object
 
63
    that holds the information about the next timezone transition that's going
 
64
    to happen.  For instance this can be used to detect when the next DST
 
65
    change is going to happen and how it looks like.
 
66
 
 
67
    The transition is calculated relative to the given datetime object.  The
 
68
    next transition that follows the date is used.  If a transition cannot
 
69
    be found the return value will be `None`.
 
70
 
 
71
    Transition information can only be provided for timezones returned by
 
72
    the :func:`get_timezone` function.
 
73
 
 
74
    :param zone: the timezone for which the transition should be looked up.
 
75
                 If not provided the local timezone is used.
 
76
    :param dt: the date after which the next transition should be found.
 
77
               If not given the current time is assumed.
 
78
    """
 
79
    zone = get_timezone(zone)
 
80
    if dt is None:
 
81
        dt = datetime.utcnow()
 
82
    else:
 
83
        dt = dt.replace(tzinfo=None)
 
84
 
 
85
    if not hasattr(zone, '_utc_transition_times'):
 
86
        raise TypeError('Given timezone does not have UTC transition '
 
87
                        'times.  This can happen because the operating '
 
88
                        'system fallback local timezone is used or a '
 
89
                        'custom timezone object')
 
90
 
 
91
    try:
 
92
        idx = max(0, bisect_right(zone._utc_transition_times, dt))
 
93
        old_trans = zone._transition_info[idx - 1]
 
94
        new_trans = zone._transition_info[idx]
 
95
        old_tz = zone._tzinfos[old_trans]
 
96
        new_tz = zone._tzinfos[new_trans]
 
97
    except (LookupError, ValueError):
 
98
        return None
 
99
 
 
100
    return TimezoneTransition(
 
101
        activates=zone._utc_transition_times[idx],
 
102
        from_tzinfo=old_tz,
 
103
        to_tzinfo=new_tz,
 
104
        reference_date=dt
 
105
    )
 
106
 
 
107
 
 
108
class TimezoneTransition(object):
 
109
    """A helper object that represents the return value from
 
110
    :func:`get_next_timezone_transition`.
 
111
    """
 
112
 
 
113
    def __init__(self, activates, from_tzinfo, to_tzinfo, reference_date=None):
 
114
        #: the time of the activation of the timezone transition in UTC.
 
115
        self.activates = activates
 
116
        #: the timezone from where the transition starts.
 
117
        self.from_tzinfo = from_tzinfo
 
118
        #: the timezone for after the transition.
 
119
        self.to_tzinfo = to_tzinfo
 
120
        #: the reference date that was provided.  This is the `dt` parameter
 
121
        #: to the :func:`get_next_timezone_transition`.
 
122
        self.reference_date = reference_date
 
123
 
 
124
    @property
 
125
    def from_tz(self):
 
126
        """The name of the timezone before the transition."""
 
127
        return self.from_tzinfo._tzname
 
128
 
 
129
    @property
 
130
    def to_tz(self):
 
131
        """The name of the timezone after the transition."""
 
132
        return self.to_tzinfo._tzname
 
133
 
 
134
    @property
 
135
    def from_offset(self):
 
136
        """The UTC offset in seconds before the transition."""
 
137
        return int(self.from_tzinfo._utcoffset.total_seconds())
 
138
 
 
139
    @property
 
140
    def to_offset(self):
 
141
        """The UTC offset in seconds after the transition."""
 
142
        return int(self.to_tzinfo._utcoffset.total_seconds())
 
143
 
 
144
    def __repr__(self):
 
145
        return '<TimezoneTransition %s -> %s (%s)>' % (
 
146
            self.from_tz,
 
147
            self.to_tz,
 
148
            self.activates,
 
149
        )
 
150
 
 
151
 
41
152
def get_period_names(locale=LC_TIME):
42
153
    """Return the names for day periods (AM/PM) used by the locale.
43
 
    
 
154
 
44
155
    >>> get_period_names(locale='en_US')['am']
45
156
    u'AM'
46
 
    
 
157
 
47
158
    :param locale: the `Locale` object, or a locale string
48
 
    :return: the dictionary of period names
49
 
    :rtype: `dict`
50
159
    """
51
160
    return Locale.parse(locale).periods
52
161
 
 
162
 
53
163
def get_day_names(width='wide', context='format', locale=LC_TIME):
54
164
    """Return the day names used by the locale for the specified format.
55
 
    
 
165
 
56
166
    >>> get_day_names('wide', locale='en_US')[1]
57
167
    u'Tuesday'
58
168
    >>> get_day_names('abbreviated', locale='es')[1]
59
169
    u'mar'
60
170
    >>> get_day_names('narrow', context='stand-alone', locale='de_DE')[1]
61
171
    u'D'
62
 
    
 
172
 
63
173
    :param width: the width to use, one of "wide", "abbreviated", or "narrow"
64
174
    :param context: the context, either "format" or "stand-alone"
65
175
    :param locale: the `Locale` object, or a locale string
66
 
    :return: the dictionary of day names
67
 
    :rtype: `dict`
68
176
    """
69
177
    return Locale.parse(locale).days[context][width]
70
178
 
 
179
 
71
180
def get_month_names(width='wide', context='format', locale=LC_TIME):
72
181
    """Return the month names used by the locale for the specified format.
73
 
    
 
182
 
74
183
    >>> get_month_names('wide', locale='en_US')[1]
75
184
    u'January'
76
185
    >>> get_month_names('abbreviated', locale='es')[1]
77
186
    u'ene'
78
187
    >>> get_month_names('narrow', context='stand-alone', locale='de_DE')[1]
79
188
    u'J'
80
 
    
 
189
 
81
190
    :param width: the width to use, one of "wide", "abbreviated", or "narrow"
82
191
    :param context: the context, either "format" or "stand-alone"
83
192
    :param locale: the `Locale` object, or a locale string
84
 
    :return: the dictionary of month names
85
 
    :rtype: `dict`
86
193
    """
87
194
    return Locale.parse(locale).months[context][width]
88
195
 
 
196
 
89
197
def get_quarter_names(width='wide', context='format', locale=LC_TIME):
90
198
    """Return the quarter names used by the locale for the specified format.
91
 
    
 
199
 
92
200
    >>> get_quarter_names('wide', locale='en_US')[1]
93
201
    u'1st quarter'
94
202
    >>> get_quarter_names('abbreviated', locale='de_DE')[1]
95
203
    u'Q1'
96
 
    
 
204
 
97
205
    :param width: the width to use, one of "wide", "abbreviated", or "narrow"
98
206
    :param context: the context, either "format" or "stand-alone"
99
207
    :param locale: the `Locale` object, or a locale string
100
 
    :return: the dictionary of quarter names
101
 
    :rtype: `dict`
102
208
    """
103
209
    return Locale.parse(locale).quarters[context][width]
104
210
 
 
211
 
105
212
def get_era_names(width='wide', locale=LC_TIME):
106
213
    """Return the era names used by the locale for the specified format.
107
 
    
 
214
 
108
215
    >>> get_era_names('wide', locale='en_US')[1]
109
216
    u'Anno Domini'
110
217
    >>> get_era_names('abbreviated', locale='de_DE')[1]
111
218
    u'n. Chr.'
112
 
    
 
219
 
113
220
    :param width: the width to use, either "wide", "abbreviated", or "narrow"
114
221
    :param locale: the `Locale` object, or a locale string
115
 
    :return: the dictionary of era names
116
 
    :rtype: `dict`
117
222
    """
118
223
    return Locale.parse(locale).eras[width]
119
224
 
 
225
 
120
226
def get_date_format(format='medium', locale=LC_TIME):
121
227
    """Return the date formatting patterns used by the locale for the specified
122
228
    format.
123
 
    
 
229
 
124
230
    >>> get_date_format(locale='en_US')
125
 
    <DateTimePattern u'MMM d, yyyy'>
 
231
    <DateTimePattern u'MMM d, y'>
126
232
    >>> get_date_format('full', locale='de_DE')
127
 
    <DateTimePattern u'EEEE, d. MMMM yyyy'>
128
 
    
 
233
    <DateTimePattern u'EEEE, d. MMMM y'>
 
234
 
129
235
    :param format: the format to use, one of "full", "long", "medium", or
130
236
                   "short"
131
237
    :param locale: the `Locale` object, or a locale string
132
 
    :return: the date format pattern
133
 
    :rtype: `DateTimePattern`
134
238
    """
135
239
    return Locale.parse(locale).date_formats[format]
136
240
 
 
241
 
137
242
def get_datetime_format(format='medium', locale=LC_TIME):
138
243
    """Return the datetime formatting patterns used by the locale for the
139
244
    specified format.
140
 
    
 
245
 
141
246
    >>> get_datetime_format(locale='en_US')
142
 
    u'{1} {0}'
143
 
    
 
247
    u'{1}, {0}'
 
248
 
144
249
    :param format: the format to use, one of "full", "long", "medium", or
145
250
                   "short"
146
251
    :param locale: the `Locale` object, or a locale string
147
 
    :return: the datetime format pattern
148
 
    :rtype: `unicode`
149
252
    """
150
253
    patterns = Locale.parse(locale).datetime_formats
151
254
    if format not in patterns:
152
255
        format = None
153
256
    return patterns[format]
154
257
 
 
258
 
155
259
def get_time_format(format='medium', locale=LC_TIME):
156
260
    """Return the time formatting patterns used by the locale for the specified
157
261
    format.
158
 
    
 
262
 
159
263
    >>> get_time_format(locale='en_US')
160
264
    <DateTimePattern u'h:mm:ss a'>
161
265
    >>> get_time_format('full', locale='de_DE')
162
 
    <DateTimePattern u'HH:mm:ss v'>
163
 
    
 
266
    <DateTimePattern u'HH:mm:ss zzzz'>
 
267
 
164
268
    :param format: the format to use, one of "full", "long", "medium", or
165
269
                   "short"
166
270
    :param locale: the `Locale` object, or a locale string
167
 
    :return: the time format pattern
168
 
    :rtype: `DateTimePattern`
169
271
    """
170
272
    return Locale.parse(locale).time_formats[format]
171
273
 
 
274
 
172
275
def get_timezone_gmt(datetime=None, width='long', locale=LC_TIME):
173
276
    """Return the timezone associated with the given `datetime` object formatted
174
277
    as string indicating the offset from GMT.
175
 
    
 
278
 
176
279
    >>> dt = datetime(2007, 4, 1, 15, 30)
177
280
    >>> get_timezone_gmt(dt, locale='en')
178
281
    u'GMT+00:00'
179
 
    
180
 
    >>> from pytz import timezone
181
 
    >>> tz = timezone('America/Los_Angeles')
 
282
 
 
283
    >>> tz = get_timezone('America/Los_Angeles')
182
284
    >>> dt = datetime(2007, 4, 1, 15, 30, tzinfo=tz)
183
285
    >>> get_timezone_gmt(dt, locale='en')
184
286
    u'GMT-08:00'
185
287
    >>> get_timezone_gmt(dt, 'short', locale='en')
186
288
    u'-0800'
187
 
    
 
289
 
188
290
    The long format depends on the locale, for example in France the acronym
189
291
    UTC string is used instead of GMT:
190
 
    
 
292
 
191
293
    >>> get_timezone_gmt(dt, 'long', locale='fr_FR')
192
294
    u'UTC-08:00'
193
 
    
 
295
 
 
296
    .. versionadded:: 0.9
 
297
 
194
298
    :param datetime: the ``datetime`` object; if `None`, the current date and
195
299
                     time in UTC is used
196
300
    :param width: either "long" or "short"
197
301
    :param locale: the `Locale` object, or a locale string
198
 
    :return: the GMT offset representation of the timezone
199
 
    :rtype: `unicode`
200
 
    :since: version 0.9
201
302
    """
202
303
    if datetime is None:
203
304
        datetime = datetime_.utcnow()
204
 
    elif isinstance(datetime, (int, long)):
 
305
    elif isinstance(datetime, integer_types):
205
306
        datetime = datetime_.utcfromtimestamp(datetime).time()
206
307
    if datetime.tzinfo is None:
207
308
        datetime = datetime.replace(tzinfo=UTC)
216
317
        pattern = locale.zone_formats['gmt'] % '%+03d:%02d'
217
318
    return pattern % (hours, seconds // 60)
218
319
 
 
320
 
219
321
def get_timezone_location(dt_or_tzinfo=None, locale=LC_TIME):
220
322
    """Return a representation of the given timezone using "location format".
221
 
    
 
323
 
222
324
    The result depends on both the local display name of the country and the
223
325
    city associated with the time zone:
224
 
    
225
 
    >>> from pytz import timezone
226
 
    >>> tz = timezone('America/St_Johns')
227
 
    >>> get_timezone_location(tz, locale='de_DE')
228
 
    u"Kanada (St. John's)"
229
 
    >>> tz = timezone('America/Mexico_City')
230
 
    >>> get_timezone_location(tz, locale='de_DE')
231
 
    u'Mexiko (Mexiko-Stadt)'
232
 
    
 
326
 
 
327
    >>> tz = get_timezone('America/St_Johns')
 
328
    >>> get_timezone_location(tz, locale='de_DE')
 
329
    u"Kanada (St. John's) Zeit"
 
330
    >>> tz = get_timezone('America/Mexico_City')
 
331
    >>> get_timezone_location(tz, locale='de_DE')
 
332
    u'Mexiko (Mexiko-Stadt) Zeit'
 
333
 
233
334
    If the timezone is associated with a country that uses only a single
234
335
    timezone, just the localized country name is returned:
235
 
    
236
 
    >>> tz = timezone('Europe/Berlin')
 
336
 
 
337
    >>> tz = get_timezone('Europe/Berlin')
237
338
    >>> get_timezone_name(tz, locale='de_DE')
238
 
    u'Deutschland'
239
 
    
 
339
    u'Mitteleurop\\xe4ische Zeit'
 
340
 
 
341
    .. versionadded:: 0.9
 
342
 
240
343
    :param dt_or_tzinfo: the ``datetime`` or ``tzinfo`` object that determines
241
344
                         the timezone; if `None`, the current date and time in
242
345
                         UTC is assumed
243
346
    :param locale: the `Locale` object, or a locale string
244
347
    :return: the localized timezone name using location format
245
 
    :rtype: `unicode`
246
 
    :since: version 0.9
247
348
    """
248
 
    if dt_or_tzinfo is None or isinstance(dt_or_tzinfo, (int, long)):
 
349
    if dt_or_tzinfo is None:
 
350
        dt = datetime.now()
 
351
        tzinfo = LOCALTZ
 
352
    elif isinstance(dt_or_tzinfo, string_types):
 
353
        dt = None
 
354
        tzinfo = get_timezone(dt_or_tzinfo)
 
355
    elif isinstance(dt_or_tzinfo, integer_types):
249
356
        dt = None
250
357
        tzinfo = UTC
251
358
    elif isinstance(dt_or_tzinfo, (datetime, time)):
287
394
        metazone = get_global('meta_zones').get(zone)
288
395
        metazone_info = locale.meta_zones.get(metazone, {})
289
396
        if 'city' in metazone_info:
290
 
            city_name = metainfo['city']
 
397
            city_name = metazone_info['city']
291
398
        elif '/' in zone:
292
399
            city_name = zone.split('/', 1)[1].replace('_', ' ')
293
400
        else:
298
405
        '1': territory_name
299
406
    })
300
407
 
 
408
 
301
409
def get_timezone_name(dt_or_tzinfo=None, width='long', uncommon=False,
302
 
                      locale=LC_TIME):
 
410
                      locale=LC_TIME, zone_variant=None):
303
411
    r"""Return the localized display name for the given timezone. The timezone
304
412
    may be specified using a ``datetime`` or `tzinfo` object.
305
 
    
306
 
    >>> from pytz import timezone
307
 
    >>> dt = time(15, 30, tzinfo=timezone('America/Los_Angeles'))
 
413
 
 
414
    >>> dt = time(15, 30, tzinfo=get_timezone('America/Los_Angeles'))
308
415
    >>> get_timezone_name(dt, locale='en_US')
309
416
    u'Pacific Standard Time'
310
417
    >>> get_timezone_name(dt, width='short', locale='en_US')
311
418
    u'PST'
312
 
    
 
419
 
313
420
    If this function gets passed only a `tzinfo` object and no concrete
314
421
    `datetime`,  the returned display name is indenpendent of daylight savings
315
422
    time. This can be used for example for selecting timezones, or to set the
316
423
    time of events that recur across DST changes:
317
 
    
318
 
    >>> tz = timezone('America/Los_Angeles')
 
424
 
 
425
    >>> tz = get_timezone('America/Los_Angeles')
319
426
    >>> get_timezone_name(tz, locale='en_US')
320
427
    u'Pacific Time'
321
428
    >>> get_timezone_name(tz, 'short', locale='en_US')
322
429
    u'PT'
323
 
    
 
430
 
324
431
    If no localized display name for the timezone is available, and the timezone
325
432
    is associated with a country that uses only a single timezone, the name of
326
433
    that country is returned, formatted according to the locale:
327
 
    
328
 
    >>> tz = timezone('Europe/Berlin')
 
434
 
 
435
    >>> tz = get_timezone('Europe/Berlin')
329
436
    >>> get_timezone_name(tz, locale='de_DE')
330
 
    u'Deutschland'
 
437
    u'Mitteleurop\xe4ische Zeit'
331
438
    >>> get_timezone_name(tz, locale='pt_BR')
332
 
    u'Hor\xe1rio Alemanha'
333
 
    
 
439
    u'Hor\xe1rio da Europa Central'
 
440
 
334
441
    On the other hand, if the country uses multiple timezones, the city is also
335
442
    included in the representation:
336
 
    
337
 
    >>> tz = timezone('America/St_Johns')
 
443
 
 
444
    >>> tz = get_timezone('America/St_Johns')
338
445
    >>> get_timezone_name(tz, locale='de_DE')
339
 
    u"Kanada (St. John's)"
340
 
    
341
 
    The `uncommon` parameter can be set to `True` to enable the use of timezone
342
 
    representations that are not commonly used by the requested locale. For
343
 
    example, while in French the central European timezone is usually
344
 
    abbreviated as "HEC", in Canadian French, this abbreviation is not in
345
 
    common use, so a generic name would be chosen by default:
346
 
    
347
 
    >>> tz = timezone('Europe/Paris')
348
 
    >>> get_timezone_name(tz, 'short', locale='fr_CA')
349
 
    u'France'
350
 
    >>> get_timezone_name(tz, 'short', uncommon=True, locale='fr_CA')
351
 
    u'HEC'
352
 
    
 
446
    u'Neufundland-Zeit'
 
447
 
 
448
    Note that short format is currently not supported for all timezones and
 
449
    all locales.  This is partially because not every timezone has a short
 
450
    code in every locale.  In that case it currently falls back to the long
 
451
    format.
 
452
 
 
453
    For more information see `LDML Appendix J: Time Zone Display Names
 
454
    <http://www.unicode.org/reports/tr35/#Time_Zone_Fallback>`_
 
455
 
 
456
    .. versionadded:: 0.9
 
457
 
 
458
    .. versionchanged:: 1.0
 
459
       Added `zone_variant` support.
 
460
 
353
461
    :param dt_or_tzinfo: the ``datetime`` or ``tzinfo`` object that determines
354
462
                         the timezone; if a ``tzinfo`` object is used, the
355
463
                         resulting display name will be generic, i.e.
356
464
                         independent of daylight savings time; if `None`, the
357
465
                         current date in UTC is assumed
358
466
    :param width: either "long" or "short"
359
 
    :param uncommon: whether even uncommon timezone abbreviations should be used
 
467
    :param uncommon: deprecated and ignored
 
468
    :param zone_variant: defines the zone variation to return.  By default the
 
469
                           variation is defined from the datetime object
 
470
                           passed in.  If no datetime object is passed in, the
 
471
                           ``'generic'`` variation is assumed.  The following
 
472
                           values are valid: ``'generic'``, ``'daylight'`` and
 
473
                           ``'standard'``.
360
474
    :param locale: the `Locale` object, or a locale string
361
 
    :return: the timezone display name
362
 
    :rtype: `unicode`
363
 
    :since: version 0.9
364
 
    :see:  `LDML Appendix J: Time Zone Display Names
365
 
            <http://www.unicode.org/reports/tr35/#Time_Zone_Fallback>`_
366
475
    """
367
 
    if dt_or_tzinfo is None or isinstance(dt_or_tzinfo, (int, long)):
 
476
    if dt_or_tzinfo is None:
 
477
        dt = datetime.now()
 
478
        tzinfo = LOCALTZ
 
479
    elif isinstance(dt_or_tzinfo, string_types):
 
480
        dt = None
 
481
        tzinfo = get_timezone(dt_or_tzinfo)
 
482
    elif isinstance(dt_or_tzinfo, integer_types):
368
483
        dt = None
369
484
        tzinfo = UTC
370
485
    elif isinstance(dt_or_tzinfo, (datetime, time)):
383
498
    else:
384
499
        zone = tzinfo.tzname(dt)
385
500
 
 
501
    if zone_variant is None:
 
502
        if dt is None:
 
503
            zone_variant = 'generic'
 
504
        else:
 
505
            dst = tzinfo.dst(dt)
 
506
            if dst:
 
507
                zone_variant = 'daylight'
 
508
            else:
 
509
                zone_variant = 'standard'
 
510
    else:
 
511
        if zone_variant not in ('generic', 'standard', 'daylight'):
 
512
            raise ValueError('Invalid zone variation')
 
513
 
386
514
    # Get the canonical time-zone code
387
515
    zone = get_global('zone_aliases').get(zone, zone)
388
516
 
389
517
    info = locale.time_zones.get(zone, {})
390
518
    # Try explicitly translated zone names first
391
519
    if width in info:
392
 
        if dt is None:
393
 
            field = 'generic'
394
 
        else:
395
 
            dst = tzinfo.dst(dt)
396
 
            if dst is None:
397
 
                field = 'generic'
398
 
            elif dst == 0:
399
 
                field = 'standard'
400
 
            else:
401
 
                field = 'daylight'
402
 
        if field in info[width]:
403
 
            return info[width][field]
 
520
        if zone_variant in info[width]:
 
521
            return info[width][zone_variant]
404
522
 
405
523
    metazone = get_global('meta_zones').get(zone)
406
524
    if metazone:
407
525
        metazone_info = locale.meta_zones.get(metazone, {})
408
 
        if width in metazone_info and (uncommon or metazone_info.get('common')):
409
 
            if dt is None:
410
 
                field = 'generic'
411
 
            else:
412
 
                field = tzinfo.dst(dt) and 'daylight' or 'standard'
413
 
            if field in metazone_info[width]:
414
 
                return metazone_info[width][field]
 
526
        if width in metazone_info:
 
527
            if zone_variant in metazone_info[width]:
 
528
                return metazone_info[width][zone_variant]
415
529
 
416
530
    # If we have a concrete datetime, we assume that the result can't be
417
531
    # independent of daylight savings time, so we return the GMT offset
420
534
 
421
535
    return get_timezone_location(dt_or_tzinfo, locale=locale)
422
536
 
 
537
 
423
538
def format_date(date=None, format='medium', locale=LC_TIME):
424
539
    """Return a date formatted according to the given pattern.
425
 
    
 
540
 
426
541
    >>> d = date(2007, 04, 01)
427
542
    >>> format_date(d, locale='en_US')
428
543
    u'Apr 1, 2007'
429
544
    >>> format_date(d, format='full', locale='de_DE')
430
545
    u'Sonntag, 1. April 2007'
431
 
    
 
546
 
432
547
    If you don't want to use the locale default formats, you can specify a
433
548
    custom date pattern:
434
 
    
 
549
 
435
550
    >>> format_date(d, "EEE, MMM d, ''yy", locale='en')
436
551
    u"Sun, Apr 1, '07"
437
 
    
 
552
 
438
553
    :param date: the ``date`` or ``datetime`` object; if `None`, the current
439
554
                 date is used
440
555
    :param format: one of "full", "long", "medium", or "short", or a custom
441
556
                   date/time pattern
442
557
    :param locale: a `Locale` object or a locale identifier
443
 
    :rtype: `unicode`
444
 
    
445
 
    :note: If the pattern contains time fields, an `AttributeError` will be
446
 
           raised when trying to apply the formatting. This is also true if
447
 
           the value of ``date`` parameter is actually a ``datetime`` object,
448
 
           as this function automatically converts that to a ``date``.
449
558
    """
450
559
    if date is None:
451
560
        date = date_.today()
458
567
    pattern = parse_pattern(format)
459
568
    return pattern.apply(date, locale)
460
569
 
 
570
 
461
571
def format_datetime(datetime=None, format='medium', tzinfo=None,
462
572
                    locale=LC_TIME):
463
 
    """Return a date formatted according to the given pattern.
464
 
    
 
573
    r"""Return a date formatted according to the given pattern.
 
574
 
465
575
    >>> dt = datetime(2007, 04, 01, 15, 30)
466
576
    >>> format_datetime(dt, locale='en_US')
467
 
    u'Apr 1, 2007 3:30:00 PM'
468
 
    
 
577
    u'Apr 1, 2007, 3:30:00 PM'
 
578
 
469
579
    For any pattern requiring the display of the time-zone, the third-party
470
580
    ``pytz`` package is needed to explicitly specify the time-zone:
471
 
    
472
 
    >>> from pytz import timezone
473
 
    >>> format_datetime(dt, 'full', tzinfo=timezone('Europe/Paris'),
 
581
 
 
582
    >>> format_datetime(dt, 'full', tzinfo=get_timezone('Europe/Paris'),
474
583
    ...                 locale='fr_FR')
475
 
    u'dimanche 1 avril 2007 17:30:00 HEC'
 
584
    u'dimanche 1 avril 2007 17:30:00 heure avanc\xe9e d\u2019Europe centrale'
476
585
    >>> format_datetime(dt, "yyyy.MM.dd G 'at' HH:mm:ss zzz",
477
 
    ...                 tzinfo=timezone('US/Eastern'), locale='en')
 
586
    ...                 tzinfo=get_timezone('US/Eastern'), locale='en')
478
587
    u'2007.04.01 AD at 11:30:00 EDT'
479
 
    
 
588
 
480
589
    :param datetime: the `datetime` object; if `None`, the current date and
481
590
                     time is used
482
591
    :param format: one of "full", "long", "medium", or "short", or a custom
483
592
                   date/time pattern
484
593
    :param tzinfo: the timezone to apply to the time for display
485
594
    :param locale: a `Locale` object or a locale identifier
486
 
    :rtype: `unicode`
487
595
    """
488
596
    if datetime is None:
489
597
        datetime = datetime_.utcnow()
490
 
    elif isinstance(datetime, (int, long)):
 
598
    elif isinstance(datetime, number_types):
491
599
        datetime = datetime_.utcfromtimestamp(datetime)
492
600
    elif isinstance(datetime, time):
493
601
        datetime = datetime_.combine(date.today(), datetime)
494
602
    if datetime.tzinfo is None:
495
603
        datetime = datetime.replace(tzinfo=UTC)
496
604
    if tzinfo is not None:
497
 
        datetime = datetime.astimezone(tzinfo)
 
605
        datetime = datetime.astimezone(get_timezone(tzinfo))
498
606
        if hasattr(tzinfo, 'normalize'): # pytz
499
607
            datetime = tzinfo.normalize(datetime)
500
608
 
501
609
    locale = Locale.parse(locale)
502
610
    if format in ('full', 'long', 'medium', 'short'):
503
611
        return get_datetime_format(format, locale=locale) \
 
612
            .replace("'", "") \
504
613
            .replace('{0}', format_time(datetime, format, tzinfo=None,
505
614
                                        locale=locale)) \
506
615
            .replace('{1}', format_date(datetime, format, locale=locale))
507
616
    else:
508
617
        return parse_pattern(format).apply(datetime, locale)
509
618
 
 
619
 
510
620
def format_time(time=None, format='medium', tzinfo=None, locale=LC_TIME):
511
 
    """Return a time formatted according to the given pattern.
512
 
    
 
621
    r"""Return a time formatted according to the given pattern.
 
622
 
513
623
    >>> t = time(15, 30)
514
624
    >>> format_time(t, locale='en_US')
515
625
    u'3:30:00 PM'
516
626
    >>> format_time(t, format='short', locale='de_DE')
517
627
    u'15:30'
518
 
    
 
628
 
519
629
    If you don't want to use the locale default formats, you can specify a
520
630
    custom time pattern:
521
 
    
 
631
 
522
632
    >>> format_time(t, "hh 'o''clock' a", locale='en')
523
633
    u"03 o'clock PM"
524
 
    
525
 
    For any pattern requiring the display of the time-zone, the third-party
526
 
    ``pytz`` package is needed to explicitly specify the time-zone:
527
 
    
528
 
    >>> from pytz import timezone
 
634
 
 
635
    For any pattern requiring the display of the time-zone a
 
636
    timezone has to be specified explicitly:
 
637
 
529
638
    >>> t = datetime(2007, 4, 1, 15, 30)
530
 
    >>> tzinfo = timezone('Europe/Paris')
 
639
    >>> tzinfo = get_timezone('Europe/Paris')
531
640
    >>> t = tzinfo.localize(t)
532
641
    >>> format_time(t, format='full', tzinfo=tzinfo, locale='fr_FR')
533
 
    u'15:30:00 HEC'
534
 
    >>> format_time(t, "hh 'o''clock' a, zzzz", tzinfo=timezone('US/Eastern'),
 
642
    u'15:30:00 heure avanc\xe9e d\u2019Europe centrale'
 
643
    >>> format_time(t, "hh 'o''clock' a, zzzz", tzinfo=get_timezone('US/Eastern'),
535
644
    ...             locale='en')
536
645
    u"09 o'clock AM, Eastern Daylight Time"
537
 
    
 
646
 
538
647
    As that example shows, when this function gets passed a
539
648
    ``datetime.datetime`` value, the actual time in the formatted string is
540
649
    adjusted to the timezone specified by the `tzinfo` parameter. If the
541
650
    ``datetime`` is "naive" (i.e. it has no associated timezone information),
542
651
    it is assumed to be in UTC.
543
 
    
 
652
 
544
653
    These timezone calculations are **not** performed if the value is of type
545
654
    ``datetime.time``, as without date information there's no way to determine
546
655
    what a given time would translate to in a different timezone without
547
656
    information about whether daylight savings time is in effect or not. This
548
657
    means that time values are left as-is, and the value of the `tzinfo`
549
658
    parameter is only used to display the timezone name if needed:
550
 
    
 
659
 
551
660
    >>> t = time(15, 30)
552
 
    >>> format_time(t, format='full', tzinfo=timezone('Europe/Paris'),
 
661
    >>> format_time(t, format='full', tzinfo=get_timezone('Europe/Paris'),
553
662
    ...             locale='fr_FR')
554
 
    u'15:30:00 HEC'
555
 
    >>> format_time(t, format='full', tzinfo=timezone('US/Eastern'),
 
663
    u'15:30:00 heure normale de l\u2019Europe centrale'
 
664
    >>> format_time(t, format='full', tzinfo=get_timezone('US/Eastern'),
556
665
    ...             locale='en_US')
557
 
    u'3:30:00 PM ET'
558
 
    
 
666
    u'3:30:00 PM Eastern Standard Time'
 
667
 
559
668
    :param time: the ``time`` or ``datetime`` object; if `None`, the current
560
669
                 time in UTC is used
561
670
    :param format: one of "full", "long", "medium", or "short", or a custom
562
671
                   date/time pattern
563
672
    :param tzinfo: the time-zone to apply to the time for display
564
673
    :param locale: a `Locale` object or a locale identifier
565
 
    :rtype: `unicode`
566
 
    
567
 
    :note: If the pattern contains date fields, an `AttributeError` will be
568
 
           raised when trying to apply the formatting. This is also true if
569
 
           the value of ``time`` parameter is actually a ``datetime`` object,
570
 
           as this function automatically converts that to a ``time``.
571
674
    """
572
675
    if time is None:
573
676
        time = datetime.utcnow()
574
 
    elif isinstance(time, (int, long)):
 
677
    elif isinstance(time, number_types):
575
678
        time = datetime.utcfromtimestamp(time)
576
679
    if time.tzinfo is None:
577
680
        time = time.replace(tzinfo=UTC)
589
692
        format = get_time_format(format, locale=locale)
590
693
    return parse_pattern(format).apply(time, locale)
591
694
 
 
695
 
 
696
TIMEDELTA_UNITS = (
 
697
    ('year',   3600 * 24 * 365),
 
698
    ('month',  3600 * 24 * 30),
 
699
    ('week',   3600 * 24 * 7),
 
700
    ('day',    3600 * 24),
 
701
    ('hour',   3600),
 
702
    ('minute', 60),
 
703
    ('second', 1)
 
704
)
 
705
 
 
706
 
 
707
def format_timedelta(delta, granularity='second', threshold=.85,
 
708
                     add_direction=False, format='medium',
 
709
                     locale=LC_TIME):
 
710
    """Return a time delta according to the rules of the given locale.
 
711
 
 
712
    >>> format_timedelta(timedelta(weeks=12), locale='en_US')
 
713
    u'3 months'
 
714
    >>> format_timedelta(timedelta(seconds=1), locale='es')
 
715
    u'1 segundo'
 
716
 
 
717
    The granularity parameter can be provided to alter the lowest unit
 
718
    presented, which defaults to a second.
 
719
 
 
720
    >>> format_timedelta(timedelta(hours=3), granularity='day',
 
721
    ...                  locale='en_US')
 
722
    u'1 day'
 
723
 
 
724
    The threshold parameter can be used to determine at which value the
 
725
    presentation switches to the next higher unit. A higher threshold factor
 
726
    means the presentation will switch later. For example:
 
727
 
 
728
    >>> format_timedelta(timedelta(hours=23), threshold=0.9, locale='en_US')
 
729
    u'1 day'
 
730
    >>> format_timedelta(timedelta(hours=23), threshold=1.1, locale='en_US')
 
731
    u'23 hours'
 
732
 
 
733
    In addition directional information can be provided that informs
 
734
    the user if the date is in the past or in the future:
 
735
 
 
736
    >>> format_timedelta(timedelta(hours=1), add_direction=True)
 
737
    u'In 1 hour'
 
738
    >>> format_timedelta(timedelta(hours=-1), add_direction=True)
 
739
    u'1 hour ago'
 
740
 
 
741
    :param delta: a ``timedelta`` object representing the time difference to
 
742
                  format, or the delta in seconds as an `int` value
 
743
    :param granularity: determines the smallest unit that should be displayed,
 
744
                        the value can be one of "year", "month", "week", "day",
 
745
                        "hour", "minute" or "second"
 
746
    :param threshold: factor that determines at which point the presentation
 
747
                      switches to the next higher unit
 
748
    :param add_direction: if this flag is set to `True` the return value will
 
749
                          include directional information.  For instance a
 
750
                          positive timedelta will include the information about
 
751
                          it being in the future, a negative will be information
 
752
                          about the value being in the past.
 
753
    :param format: the format (currently only "medium" and "short" are supported)
 
754
    :param locale: a `Locale` object or a locale identifier
 
755
    """
 
756
    if format not in ('short', 'medium'):
 
757
        raise TypeError('Format can only be one of "short" or "medium"')
 
758
    if isinstance(delta, timedelta):
 
759
        seconds = int((delta.days * 86400) + delta.seconds)
 
760
    else:
 
761
        seconds = delta
 
762
    locale = Locale.parse(locale)
 
763
 
 
764
    def _iter_choices(unit):
 
765
        if add_direction:
 
766
            if seconds >= 0:
 
767
                yield unit + '-future'
 
768
            else:
 
769
                yield unit + '-past'
 
770
        yield unit + ':' + format
 
771
        yield unit
 
772
 
 
773
    for unit, secs_per_unit in TIMEDELTA_UNITS:
 
774
        value = abs(seconds) / secs_per_unit
 
775
        if value >= threshold or unit == granularity:
 
776
            if unit == granularity and value > 0:
 
777
                value = max(1, value)
 
778
            value = int(round(value))
 
779
            plural_form = locale.plural_form(value)
 
780
            pattern = None
 
781
            for choice in _iter_choices(unit):
 
782
                patterns = locale._data['unit_patterns'].get(choice)
 
783
                if patterns is not None:
 
784
                    pattern = patterns[plural_form]
 
785
                    break
 
786
            # This really should not happen
 
787
            if pattern is None:
 
788
                return u''
 
789
            return pattern.replace('{0}', str(value))
 
790
 
 
791
    return u''
 
792
 
 
793
 
592
794
def parse_date(string, locale=LC_TIME):
593
795
    """Parse a date from a string.
594
 
    
 
796
 
595
797
    This function uses the date format for the locale as a hint to determine
596
798
    the order in which the date fields appear in the string.
597
 
    
 
799
 
598
800
    >>> parse_date('4/1/04', locale='en_US')
599
801
    datetime.date(2004, 4, 1)
600
802
    >>> parse_date('01.04.2004', locale='de_DE')
601
803
    datetime.date(2004, 4, 1)
602
 
    
 
804
 
603
805
    :param string: the string containing the date
604
806
    :param locale: a `Locale` object or a locale identifier
605
 
    :return: the parsed date
606
 
    :rtype: `date`
607
807
    """
608
808
    # TODO: try ISO format first?
609
809
    format = get_date_format(locale=locale).pattern.lower()
632
832
        month, day = day, month
633
833
    return date(year, month, day)
634
834
 
635
 
def parse_datetime(string, locale=LC_TIME):
636
 
    """Parse a date and time from a string.
637
 
    
638
 
    This function uses the date and time formats for the locale as a hint to
639
 
    determine the order in which the time fields appear in the string.
640
 
    
641
 
    :param string: the string containing the date and time
642
 
    :param locale: a `Locale` object or a locale identifier
643
 
    :return: the parsed date/time
644
 
    :rtype: `datetime`
645
 
    """
646
 
    raise NotImplementedError
647
835
 
648
836
def parse_time(string, locale=LC_TIME):
649
837
    """Parse a time from a string.
650
 
    
 
838
 
651
839
    This function uses the time format for the locale as a hint to determine
652
840
    the order in which the time fields appear in the string.
653
 
    
 
841
 
654
842
    >>> parse_time('15:30:00', locale='en_US')
655
843
    datetime.time(15, 30)
656
 
    
 
844
 
657
845
    :param string: the string containing the time
658
846
    :param locale: a `Locale` object or a locale identifier
659
847
    :return: the parsed time
695
883
        return self.pattern
696
884
 
697
885
    def __mod__(self, other):
698
 
        assert type(other) is DateTimeFormat
 
886
        if type(other) is not DateTimeFormat:
 
887
            return NotImplemented
699
888
        return self.format % other
700
889
 
701
890
    def apply(self, datetime, locale):
824
1013
        return self.format(self.get_day_of_year(), num)
825
1014
 
826
1015
    def format_day_of_week_in_month(self):
827
 
        return '%d' % ((self.value.day - 1) / 7 + 1)
 
1016
        return '%d' % ((self.value.day - 1) // 7 + 1)
828
1017
 
829
1018
    def format_period(self, char):
830
1019
        period = {0: 'am', 1: 'pm'}[int(self.value.hour >= 12)]
860
1049
    def get_day_of_year(self, date=None):
861
1050
        if date is None:
862
1051
            date = self.value
863
 
        return (date - date_(date.year, 1, 1)).days + 1
 
1052
        return (date - date.replace(month=1, day=1)).days + 1
864
1053
 
865
1054
    def get_week_number(self, day_of_period, day_of_week=None):
866
1055
        """Return the number of the week of a day within a period. This may be
867
1056
        the week number in a year or the week number in a month.
868
 
        
 
1057
 
869
1058
        Usually this will return a value equal to or greater than 1, but if the
870
1059
        first week of the period is so short that it actually counts as the last
871
1060
        week of the previous period, this function will return 0.
872
 
        
 
1061
 
873
1062
        >>> format = DateTimeFormat(date(2006, 1, 8), Locale.parse('de_DE'))
874
1063
        >>> format.get_week_number(6)
875
1064
        1
876
 
        
 
1065
 
877
1066
        >>> format = DateTimeFormat(date(2006, 1, 8), Locale.parse('en_US'))
878
1067
        >>> format.get_week_number(6)
879
1068
        2
880
 
        
 
1069
 
881
1070
        :param day_of_period: the number of the day in the period (usually
882
1071
                              either the day of month or the day of year)
883
1072
        :param day_of_week: the week day; if ommitted, the week day of the
889
1078
                     day_of_period + 1) % 7
890
1079
        if first_day < 0:
891
1080
            first_day += 7
892
 
        week_number = (day_of_period + first_day - 1) / 7
 
1081
        week_number = (day_of_period + first_day - 1) // 7
893
1082
        if 7 - first_day >= self.locale.min_week_days:
894
1083
            week_number += 1
895
1084
        return week_number
910
1099
    'z': [1, 2, 3, 4], 'Z': [1, 2, 3, 4], 'v': [1, 4], 'V': [1, 4]  # zone
911
1100
}
912
1101
 
 
1102
 
913
1103
def parse_pattern(pattern):
914
1104
    """Parse date, time, and datetime format patterns.
915
 
    
 
1105
 
916
1106
    >>> parse_pattern("MMMMd").format
917
1107
    u'%(MMMM)s%(d)s'
918
1108
    >>> parse_pattern("MMM d, yyyy").format
919
1109
    u'%(MMM)s %(d)s, %(yyyy)s'
920
 
    
 
1110
 
921
1111
    Pattern can contain literal strings in single quotes:
922
 
    
 
1112
 
923
1113
    >>> parse_pattern("H:mm' Uhr 'z").format
924
1114
    u'%(H)s:%(mm)s Uhr %(z)s'
925
 
    
 
1115
 
926
1116
    An actual single quote can be used by using two adjacent single quote
927
1117
    characters:
928
 
    
 
1118
 
929
1119
    >>> parse_pattern("hh' o''clock'").format
930
1120
    u"%(hh)s o'clock"
931
 
    
 
1121
 
932
1122
    :param pattern: the formatting pattern to parse
933
1123
    """
934
1124
    if type(pattern) is DateTimePattern: