Skip to content

Commit 1ad952e

Browse files
committed
Merge branch 'alternative-formatter' into develop
2 parents 9d5057f + 3e2fa63 commit 1ad952e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1012
-72
lines changed

pendulum/formatting/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# -*- coding: utf-8 -*-
22

33
from .classic_formatter import ClassicFormatter
4+
from .alternative_formatter import AlternativeFormatter
45

56

67
FORMATTERS = {
7-
'classic': ClassicFormatter()
8+
'classic': ClassicFormatter(),
9+
'alternative': AlternativeFormatter(),
810
}
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import re
4+
import datetime
5+
6+
from .formatter import Formatter
7+
8+
9+
class AlternativeFormatter(Formatter):
10+
11+
_TOKENS = '\[([^\[]*)\]|\\\(.)|' \
12+
'(' \
13+
'Mo|MM?M?M?' \
14+
'|Do|DDDo|DD?D?D?|ddd?d?|do?' \
15+
'|w[o|w]?|W[o|W]?|Qo?' \
16+
'|YYYY|YY|Y' \
17+
'|gg(ggg?)?|GG(GGG?)?' \
18+
'|e|E|a|A' \
19+
'|hh?|HH?|kk?' \
20+
'|mm?|ss?|S{1,9}' \
21+
'|x|X' \
22+
'|zz?|ZZ?' \
23+
'|LTS|LT|LL?L?L?' \
24+
')'
25+
26+
_FORMAT_RE = re.compile(_TOKENS)
27+
28+
_LOCALIZABLE_TOKENS = (
29+
'Mo', 'MMM', 'MMMM',
30+
'Qo',
31+
'Do',
32+
'DDDo',
33+
'do', 'dd', 'ddd', 'dddd',
34+
'wo',
35+
'Wo',
36+
'A', 'a',
37+
)
38+
39+
_TOKENS_RULES = {
40+
# Year
41+
'YYYY': lambda dt: '{:d}'.format(dt.year),
42+
'YY': lambda dt: '{:d}'.format(dt.year)[2:],
43+
'Y': lambda dt: '{:d}'.format(dt.year),
44+
45+
# Quarter
46+
'Q': lambda dt: '{:d}'.format(dt.quarter),
47+
48+
# Month
49+
'MM': lambda dt: '{:02d}'.format(dt.month),
50+
'M': lambda dt: '{:d}'.format(dt.month),
51+
52+
# Day
53+
'DD': lambda dt: '{:02d}'.format(dt.day),
54+
'D': lambda dt: '{:d}'.format(dt.day),
55+
56+
# Day of Year
57+
'DDDD': lambda dt: '{:03d}'.format(dt.day_of_year),
58+
'DDD': lambda dt: '{:d}'.format(dt.day_of_year),
59+
60+
# Day of Week
61+
'd': lambda dt: '{:d}'.format(dt.day_of_week),
62+
63+
# Hour
64+
'HH': lambda dt: '{:02d}'.format(dt.hour),
65+
'H': lambda dt: '{:d}'.format(dt.hour),
66+
'hh': lambda dt: '{:02d}'.format(dt.hour % 12 or 12),
67+
'h': lambda dt: '{:d}'.format(dt.hour % 12 or 12),
68+
69+
# Minute
70+
'mm': lambda dt: '{:02d}'.format(dt.minute),
71+
'm': lambda dt: '{:d}'.format(dt.minute),
72+
73+
# Second
74+
'ss': lambda dt: '{:02d}'.format(dt.second),
75+
's': lambda dt: '{:d}'.format(dt.second),
76+
77+
# Fractional second
78+
'S': lambda dt: '{:1d}'.format(dt.microsecond // 100000),
79+
'SS': lambda dt: '{:2d}'.format(dt.microsecond // 10000),
80+
'SSS': lambda dt: '{:3d}'.format(dt.microsecond // 1000),
81+
'SSSS': lambda dt: '{:4d}'.format(dt.microsecond // 100),
82+
'SSSSS': lambda dt: '{:5d}'.format(dt.microsecond // 10),
83+
'SSSSSS': lambda dt: '{:6d}'.format(dt.microsecond),
84+
85+
# Timestamp
86+
'X': lambda dt: '{:d}'.format(dt.timestamp),
87+
88+
# Timezone
89+
'z': lambda dt: '{}'.format(dt.tzinfo.abbrev),
90+
'zz': lambda dt: '{}'.format(dt.timezone_name),
91+
}
92+
93+
_DEFAULT_DATE_FORMATS = {
94+
'LTS': 'h:mm:ss A',
95+
'LT': 'h:mm A',
96+
'L': 'MM/DD/YYYY',
97+
'LL': 'MMMM D, YYYY',
98+
'LLL': 'MMMM D, YYYY h:mm A',
99+
'LLLL': 'dddd, MMMM D, YYYY h:mm A',
100+
}
101+
102+
def format(self, dt, fmt, locale=None):
103+
"""
104+
Formats a Pendulum instance with a given format and locale.
105+
106+
:param dt: The instance to format
107+
:type dt: Pendulum
108+
109+
:param fmt: The format to use
110+
:type fmt: str
111+
112+
:param locale: The locale to use
113+
:type locale: str or None
114+
115+
:rtype: str
116+
"""
117+
if not locale:
118+
locale = dt.get_locale()
119+
120+
return self._FORMAT_RE.sub(
121+
lambda m: m.group(1)
122+
if m.group(1)
123+
else m.group(2)
124+
if m.group(2)
125+
else self._format_token(dt, m.group(3), locale),
126+
fmt
127+
)
128+
129+
def _format_token(self, dt, token, locale):
130+
"""
131+
Formats a Pendulum instance with a given token and locale.
132+
133+
:param dt: The instance to format
134+
:type dt: Pendulum
135+
136+
:param token: The token to use
137+
:type token: str
138+
139+
:param locale: The locale to use
140+
:type locale: str or None
141+
142+
:rtype: str
143+
"""
144+
if token in self._DEFAULT_DATE_FORMATS:
145+
fmt = dt.translator().transchoice('date_formats', token, locale=locale)
146+
if fmt == 'date_formats':
147+
fmt = self._DEFAULT_DATE_FORMATS[token]
148+
149+
return self.format(dt, fmt, locale)
150+
if token in self._LOCALIZABLE_TOKENS:
151+
return self._format_localizable_token(dt, token, locale)
152+
153+
if token in self._TOKENS_RULES:
154+
return self._TOKENS_RULES[token](dt)
155+
156+
# Timezone
157+
if token in ['ZZ', 'Z']:
158+
separator = ':' if token == 'ZZ' else ''
159+
offset = dt.utcoffset() or datetime.timedelta()
160+
minutes = offset.total_seconds() / 60
161+
162+
if minutes >= 0:
163+
sign = '+'
164+
else:
165+
sign = '-'
166+
167+
hour, minute = divmod(abs(int(minutes)), 60)
168+
169+
return '{}{:02d}{}{:02d}'.format(sign, hour, separator, minute)
170+
171+
return token
172+
173+
def _format_localizable_token(self, dt, token, locale):
174+
"""
175+
Formats a Pendulum instance
176+
with a given localizable token and locale.
177+
178+
:param dt: The instance to format
179+
:type dt: Pendulum
180+
181+
:param token: The token to use
182+
:type token: str
183+
184+
:param locale: The locale to use
185+
:type locale: str or None
186+
187+
:rtype: str
188+
"""
189+
if token == 'MMM':
190+
count = dt.month
191+
trans_id = 'months_abbrev'
192+
elif token == 'MMMM':
193+
count = dt.month
194+
trans_id = 'months'
195+
elif token in ('dd', 'ddd'):
196+
count = dt.day_of_week
197+
trans_id = 'days_abbrev'
198+
elif token == 'dddd':
199+
count = dt.day_of_week
200+
trans_id = 'days'
201+
elif token == 'Do':
202+
count = dt.day
203+
trans_id = 'ordinal'
204+
elif token == 'do':
205+
count = dt.day_of_week
206+
trans_id = 'ordinal'
207+
elif token == 'Mo':
208+
count = dt.month
209+
trans_id = 'ordinal'
210+
elif token == 'Qo':
211+
count = dt.quarter
212+
trans_id = 'ordinal'
213+
elif token == 'wo':
214+
count = dt.week_of_year
215+
trans_id = 'ordinal'
216+
elif token == 'DDDo':
217+
count = dt.day_of_year
218+
trans_id = 'ordinal'
219+
elif token == 'A':
220+
count = (dt.hour, dt.minute)
221+
trans_id = 'meridian'
222+
else:
223+
return token
224+
225+
trans = dt.translator().transchoice(trans_id, count, locale=locale)
226+
227+
if trans_id == 'ordinal':
228+
trans = '{:d}{}'.format(count, trans)
229+
230+
if trans_id == trans:
231+
# Unable to find the corresponding translation
232+
# Defaulting to english
233+
return self._format_localizable_token(dt, token, 'en')
234+
235+
return trans

pendulum/formatting/classic_formatter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def _localize_directive(self, dt, directive, locale):
6666
number = dt.month
6767
elif directive == 'p':
6868
id = 'meridian'
69-
number = dt.hour
69+
number = (dt.hour, dt.minute)
7070
else:
7171
raise ValueError('Unlocalizable directive [{}]'.format(directive))
7272

pendulum/lang/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from .mk import translations as mk_translations
3535
from .ms import translations as ms_translations
3636
from .nl import translations as nl_translations
37-
from .no import translations as no_translations
37+
from .nn import translations as nn_translations
3838
from .pl import translations as pl_translations
3939
from .pt_br import translations as pt_br_translations
4040
from .ro import translations as ro_translations
@@ -88,7 +88,7 @@
8888
"mk": mk_translations,
8989
"ms": ms_translations,
9090
"nl": nl_translations,
91-
"no": no_translations,
91+
"nn": nn_translations,
9292
"pl": pl_translations,
9393
"pt_br": pt_br_translations,
9494
"ro": ro_translations,

pendulum/lang/af.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,16 @@
6767
'before': '{time} voor',
6868

6969
# Meridians
70-
'meridian': lambda hour: 'VM' if 0 <= hour < 12 else 'NM',
70+
'meridian': lambda time: 'VM' if 0 <= time[0] < 12 else 'NM',
71+
72+
# Date formats
73+
'date_formats': {
74+
'LTS': 'HH:mm:ss',
75+
'LT': 'HH:mm',
76+
'LLLL': 'dddd, D MMMM YYYY HH:mm',
77+
'LLL': 'D MMMM YYYY HH:mm',
78+
'LL': 'D MMMM YYYY',
79+
'L': 'DD/MM/YYYY',
80+
},
7181
}
7282

pendulum/lang/ar.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,14 @@
107107
'from_now': 'من الآن {time}',
108108
'after': 'بعد {time}',
109109
'before': 'قبل {time}',
110+
111+
# Date formats
112+
'date_formats': {
113+
'LTS': 'HH:mm:ss',
114+
'LT': 'HH:mm',
115+
'LLLL': 'dddd D MMMM YYYY HH:mm',
116+
'LLL': 'D MMMM YYYY HH:mm',
117+
'LL': 'D MMMM YYYY',
118+
'L': 'D/\u200FM/\u200FYYYY',
119+
},
110120
}

pendulum/lang/az.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@
6464
'ago': '{time} öncə',
6565
'from_now': '{time} sonra',
6666
'after': '{time} sonra',
67-
'before': '{time} öncə'
68-
}
67+
'before': '{time} öncə',
6968

69+
# Date formats
70+
'date_formats': {
71+
'LTS': 'HH:mm:ss',
72+
'LT': 'HH:mm',
73+
'LLLL': 'dddd, D MMMM YYYY HH:mm',
74+
'LLL': 'D MMMM YYYY HH:mm',
75+
'LL': 'D MMMM YYYY',
76+
'L': 'DD.MM.YYYY',
77+
},
78+
}

pendulum/lang/bg.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,14 @@
6565
'from_now': '{time} от сега',
6666
'after': 'след {time}',
6767
'before': 'преди {time}',
68-
}
6968

69+
# Date formats
70+
'date_formats': {
71+
'LTS': 'H:mm:ss',
72+
'LT': 'H:mm',
73+
'LLLL': 'dddd, D MMMM YYYY H:mm',
74+
'LLL': 'D MMMM YYYY H:mm',
75+
'LL': 'D MMMM YYYY',
76+
'L': 'D.MM.YYYY',
77+
},
78+
}

pendulum/lang/bn.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@
6565
'before': '{time} আগে',
6666

6767
# Meridians
68-
'meridian': lambda hour: 'সকাল' if 0 <= hour < 12 else 'বিকাল',
69-
}
68+
'meridian': lambda time: 'সকাল' if 0 <= time[0] < 12 else 'বিকাল',
7069

70+
# Date formats
71+
'date_formats': {
72+
'LTS': 'A h:mm:ss সময়',
73+
'LT': 'A h:mm সময়',
74+
'LLLL': 'dddd, D MMMM YYYY, A h:mm সময়',
75+
'LLL': 'D MMMM YYYY, A h:mm সময়',
76+
'LL': 'D MMMM YYYY',
77+
'L': 'DD/MM/YYYY',
78+
},
79+
}

pendulum/lang/ca.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,14 @@
6565
'from_now': 'Dins de {time}',
6666
'after': '{time} després',
6767
'before': '{time} abans',
68-
}
6968

69+
# Date formats
70+
'date_formats': {
71+
'LTS': 'H:mm:ss',
72+
'LT': 'H:mm',
73+
'LLLL': 'dddd D MMMM YYYY H:mm',
74+
'LLL': 'D MMMM YYYY H:mm',
75+
'LL': 'D MMMM YYYY',
76+
'L': 'DD/MM/YYYY',
77+
},
78+
}

0 commit comments

Comments
 (0)