Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
66e598b7
Commit
66e598b7
authored
Feb 14, 2014
by
Ned Batchelder
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add strftime_localized function, not called yet.
parent
229f3522
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
417 additions
and
3 deletions
+417
-3
common/djangoapps/util/date_utils.py
+301
-0
common/djangoapps/util/tests/test_date_utils.py
+116
-3
No files found.
common/djangoapps/util/date_utils.py
View file @
66e598b7
"""
Convenience methods for working with datetime objects
"""
from
datetime
import
timedelta
import
re
from
pytz
import
timezone
,
UTC
,
UnknownTimeZoneError
from
django.utils.translation
import
pgettext
,
ugettext
def
get_default_time_display
(
dtime
):
"""
...
...
@@ -62,3 +67,299 @@ def almost_same_datetime(dt1, dt2, allowed_delta=timedelta(minutes=1)):
:param dt2:
"""
return
abs
(
dt1
-
dt2
)
<
allowed_delta
DEFAULT_SHORT_DATE_FORMAT
=
"
%
b
%
d,
%
Y"
DEFAULT_LONG_DATE_FORMAT
=
"
%
A,
%
B
%
d,
%
Y"
DEFAULT_TIME_FORMAT
=
"
%
I:
%
M:
%
S
%
p"
DEFAULT_DATE_TIME_FORMAT
=
"
%
b
%
d,
%
Y at
%
H:
%
M"
def
strftime_localized
(
dtime
,
format
):
# pylint: disable=redefined-builtin
"""
Format a datetime, just like the built-in strftime, but with localized words.
The format string can also be one of:
* "SHORT_DATE" for a date in brief form, localized.
* "LONG_DATE" for a longer form of date, localized.
* "DATE_TIME" for a date and time together, localized.
* "TIME" for just the time, localized.
The localization is based on the current language Django is using for the
request. The exact format strings used for each of the names above is
determined by the translator for each language.
Args:
dtime (datetime): The datetime value to format.
format (str): The format string to use, as specified by
:ref:`datetime.strftime`.
Returns:
A unicode string with the formatted datetime.
"""
if
format
==
"SHORT_DATE"
:
format
=
"
%
x"
elif
format
==
"LONG_DATE"
:
# Translators: the translation for "LONG_DATE_FORMAT" must be a format
# string for formatting dates in a long form. For example, the
# American English form is "%A, %B %d %Y".
# See http://strftime.org for details.
format
=
ugettext
(
"LONG_DATE_FORMAT"
)
if
format
==
"LONG_DATE_FORMAT"
:
format
=
DEFAULT_LONG_DATE_FORMAT
elif
format
==
"DATE_TIME"
:
# Translators: the translation for "DATE_TIME_FORMAT" must be a format
# string for formatting dates with times. For example, the American
# English form is "%b %d, %Y at %H:%M".
# See http://strftime.org for details.
format
=
ugettext
(
"DATE_TIME_FORMAT"
)
if
format
==
"DATE_TIME_FORMAT"
:
format
=
DEFAULT_DATE_TIME_FORMAT
elif
format
==
"TIME"
:
format
=
"
%
X"
def
process_percent_code
(
match
):
"""
Convert one percent-prefixed code in the format string.
Called by re.sub just below.
"""
code
=
match
.
group
()
if
code
==
"
%
"
:
# This only happens if the string ends with a %, which is not legal.
raise
ValueError
(
"strftime format ends with raw
%
"
)
if
code
==
"
%
a"
:
part
=
pgettext
(
'abbreviated weekday name'
,
WEEKDAYS_ABBREVIATED
[
dtime
.
weekday
()])
elif
code
==
"
%
A"
:
part
=
pgettext
(
'weekday name'
,
WEEKDAYS
[
dtime
.
weekday
()])
elif
code
==
"
%
b"
:
part
=
pgettext
(
'abbreviated month name'
,
MONTHS_ABBREVIATED
[
dtime
.
month
])
elif
code
==
"
%
B"
:
part
=
pgettext
(
'month name'
,
MONTHS
[
dtime
.
month
])
elif
code
==
"
%
p"
:
part
=
pgettext
(
'am/pm indicator'
,
AM_PM
[
dtime
.
hour
//
12
])
elif
code
==
"
%
x"
:
# Get the localized short date format, and recurse.
# Translators: the translation for "SHORT_DATE_FORMAT" must be a
# format string for formatting dates in a brief form. For example,
# the American English form is "%b %d %Y".
# See http://strftime.org for details.
actual_format
=
ugettext
(
"SHORT_DATE_FORMAT"
)
if
actual_format
==
"SHORT_DATE_FORMAT"
:
actual_format
=
DEFAULT_SHORT_DATE_FORMAT
if
"
%
x"
in
actual_format
:
# Prevent infinite accidental recursion.
actual_format
=
DEFAULT_SHORT_DATE_FORMAT
part
=
strftime_localized
(
dtime
,
actual_format
)
elif
code
==
"
%
X"
:
# Get the localized time format, and recurse.
# Translators: the translation for "TIME_FORMAT" must be a format
# string for formatting times. For example, the American English
# form is "%H:%M:%S". See http://strftime.org for details.
actual_format
=
ugettext
(
"TIME_FORMAT"
)
if
actual_format
==
"TIME_FORMAT"
:
actual_format
=
DEFAULT_TIME_FORMAT
if
"
%
X"
in
actual_format
:
# Prevent infinite accidental recursion.
actual_format
=
DEFAULT_TIME_FORMAT
part
=
strftime_localized
(
dtime
,
actual_format
)
else
:
# All the other format codes: just let built-in strftime take
# care of them.
part
=
dtime
.
strftime
(
code
)
return
part
formatted_date
=
re
.
sub
(
r"
%
.|
%
"
,
process_percent_code
,
format
)
return
formatted_date
# In order to extract the strings below, we have to mark them with pgettext.
# But we'll do the actual pgettext later, so use a no-op for now, and save the
# real pgettext so we can assign it back to the global name later.
real_pgettext
=
pgettext
pgettext
=
lambda
context
,
text
:
text
# pylint: disable=invalid-name
AM_PM
=
{
# Translators: This is an AM/PM indicator for displaying times. It is
# used for the %p directive in date-time formats. See http://strftime.org
# for details.
0
:
pgettext
(
'am/pm indicator'
,
'AM'
),
# Translators: This is an AM/PM indicator for displaying times. It is
# used for the %p directive in date-time formats. See http://strftime.org
# for details.
1
:
pgettext
(
'am/pm indicator'
,
'PM'
),
}
WEEKDAYS
=
{
# Translators: this is a weekday name that will be used when displaying
# dates, as in "Monday Februrary 10, 2014". It is used for the %A
# directive in date-time formats. See http://strftime.org for details.
0
:
pgettext
(
'weekday name'
,
'Monday'
),
# Translators: this is a weekday name that will be used when displaying
# dates, as in "Tuesday Februrary 11, 2014". It is used for the %A
# directive in date-time formats. See http://strftime.org for details.
1
:
pgettext
(
'weekday name'
,
'Tuesday'
),
# Translators: this is a weekday name that will be used when displaying
# dates, as in "Wednesday Februrary 12, 2014". It is used for the %A
# directive in date-time formats. See http://strftime.org for details.
2
:
pgettext
(
'weekday name'
,
'Wednesday'
),
# Translators: this is a weekday name that will be used when displaying
# dates, as in "Thursday Februrary 13, 2014". It is used for the %A
# directive in date-time formats. See http://strftime.org for details.
3
:
pgettext
(
'weekday name'
,
'Thursday'
),
# Translators: this is a weekday name that will be used when displaying
# dates, as in "Friday Februrary 14, 2014". It is used for the %A
# directive in date-time formats. See http://strftime.org for details.
4
:
pgettext
(
'weekday name'
,
'Friday'
),
# Translators: this is a weekday name that will be used when displaying
# dates, as in "Saturday Februrary 15, 2014". It is used for the %A
# directive in date-time formats. See http://strftime.org for details.
5
:
pgettext
(
'weekday name'
,
'Saturday'
),
# Translators: this is a weekday name that will be used when displaying
# dates, as in "Sunday Februrary 16, 2014". It is used for the %A
# directive in date-time formats. See http://strftime.org for details.
6
:
pgettext
(
'weekday name'
,
'Sunday'
),
}
WEEKDAYS_ABBREVIATED
=
{
# Translators: this is an abbreviated weekday name that will be used when
# displaying dates, as in "Mon Feb 10, 2014". It is used for the %a
# directive in date-time formats. See http://strftime.org for details.
0
:
pgettext
(
'abbreviated weekday name'
,
'Mon'
),
# Translators: this is an abbreviated weekday name that will be used when
# displaying dates, as in "Tue Feb 11, 2014". It is used for the %a
# directive in date-time formats. See http://strftime.org for details.
1
:
pgettext
(
'abbreviated weekday name'
,
'Tue'
),
# Translators: this is an abbreviated weekday name that will be used when
# displaying dates, as in "Wed Feb 12, 2014". It is used for the %a
# directive in date-time formats. See http://strftime.org for details.
2
:
pgettext
(
'abbreviated weekday name'
,
'Wed'
),
# Translators: this is an abbreviated weekday name that will be used when
# displaying dates, as in "Thu Feb 13, 2014". It is used for the %a
# directive in date-time formats. See http://strftime.org for details.
3
:
pgettext
(
'abbreviated weekday name'
,
'Thu'
),
# Translators: this is an abbreviated weekday name that will be used when
# displaying dates, as in "Fri Feb 14, 2014". It is used for the %a
# directive in date-time formats. See http://strftime.org for details.
4
:
pgettext
(
'abbreviated weekday name'
,
'Fri'
),
# Translators: this is an abbreviated weekday name that will be used when
# displaying dates, as in "Sat Feb 15, 2014". It is used for the %a
# directive in date-time formats. See http://strftime.org for details.
5
:
pgettext
(
'abbreviated weekday name'
,
'Sat'
),
# Translators: this is an abbreviated weekday name that will be used when
# displaying dates, as in "Sun Feb 16, 2014". It is used for the %a
# directive in date-time formats. See http://strftime.org for details.
6
:
pgettext
(
'abbreviated weekday name'
,
'Sun'
),
}
MONTHS_ABBREVIATED
=
{
# Translators: this is an abbreviated month name that will be used when
# displaying dates, as in "Jan 10, 2014". It is used for the %b
# directive in date-time formats. See http://strftime.org for details.
1
:
pgettext
(
'abbreviated month name'
,
'Jan'
),
# Translators: this is an abbreviated month name that will be used when
# displaying dates, as in "Feb 10, 2014". It is used for the %b
# directive in date-time formats. See http://strftime.org for details.
2
:
pgettext
(
'abbreviated month name'
,
'Feb'
),
# Translators: this is an abbreviated month name that will be used when
# displaying dates, as in "Mar 10, 2014". It is used for the %b
# directive in date-time formats. See http://strftime.org for details.
3
:
pgettext
(
'abbreviated month name'
,
'Mar'
),
# Translators: this is an abbreviated month name that will be used when
# displaying dates, as in "Apr 10, 2014". It is used for the %b
# directive in date-time formats. See http://strftime.org for details.
4
:
pgettext
(
'abbreviated month name'
,
'Apr'
),
# Translators: this is an abbreviated month name that will be used when
# displaying dates, as in "May 10, 2014". It is used for the %b
# directive in date-time formats. See http://strftime.org for details.
5
:
pgettext
(
'abbreviated month name'
,
'May'
),
# Translators: this is an abbreviated month name that will be used when
# displaying dates, as in "Jun 10, 2014". It is used for the %b
# directive in date-time formats. See http://strftime.org for details.
6
:
pgettext
(
'abbreviated month name'
,
'Jun'
),
# Translators: this is an abbreviated month name that will be used when
# displaying dates, as in "Jul 10, 2014". It is used for the %b
# directive in date-time formats. See http://strftime.org for details.
7
:
pgettext
(
'abbreviated month name'
,
'Jul'
),
# Translators: this is an abbreviated month name that will be used when
# displaying dates, as in "Aug 10, 2014". It is used for the %b
# directive in date-time formats. See http://strftime.org for details.
8
:
pgettext
(
'abbreviated month name'
,
'Aug'
),
# Translators: this is an abbreviated month name that will be used when
# displaying dates, as in "Sep 10, 2014". It is used for the %b
# directive in date-time formats. See http://strftime.org for details.
9
:
pgettext
(
'abbreviated month name'
,
'Sep'
),
# Translators: this is an abbreviated month name that will be used when
# displaying dates, as in "Oct 10, 2014". It is used for the %b
# directive in date-time formats. See http://strftime.org for details.
10
:
pgettext
(
'abbreviated month name'
,
'Oct'
),
# Translators: this is an abbreviated month name that will be used when
# displaying dates, as in "Nov 10, 2014". It is used for the %b
# directive in date-time formats. See http://strftime.org for details.
11
:
pgettext
(
'abbreviated month name'
,
'Nov'
),
# Translators: this is an abbreviated month name that will be used when
# displaying dates, as in "Dec 10, 2014". It is used for the %b
# directive in date-time formats. See http://strftime.org for details.
12
:
pgettext
(
'abbreviated month name'
,
'Dec'
),
}
MONTHS
=
{
# Translators: this is a month name that will be used when displaying
# dates, as in "January 10, 2014". It is used for the %B directive in
# date-time formats. See http://strftime.org for details.
1
:
pgettext
(
'month name'
,
'January'
),
# Translators: this is a month name that will be used when displaying
# dates, as in "February 10, 2014". It is used for the %B directive in
# date-time formats. See http://strftime.org for details.
2
:
pgettext
(
'month name'
,
'February'
),
# Translators: this is a month name that will be used when displaying
# dates, as in "March 10, 2014". It is used for the %B directive in
# date-time formats. See http://strftime.org for details.
3
:
pgettext
(
'month name'
,
'March'
),
# Translators: this is a month name that will be used when displaying
# dates, as in "April 10, 2014". It is used for the %B directive in
# date-time formats. See http://strftime.org for details.
4
:
pgettext
(
'month name'
,
'April'
),
# Translators: this is a month name that will be used when displaying
# dates, as in "May 10, 2014". It is used for the %B directive in
# date-time formats. See http://strftime.org for details.
5
:
pgettext
(
'month name'
,
'May'
),
# Translators: this is a month name that will be used when displaying
# dates, as in "June 10, 2014". It is used for the %B directive in
# date-time formats. See http://strftime.org for details.
6
:
pgettext
(
'month name'
,
'June'
),
# Translators: this is a month name that will be used when displaying
# dates, as in "July 10, 2014". It is used for the %B directive in
# date-time formats. See http://strftime.org for details.
7
:
pgettext
(
'month name'
,
'July'
),
# Translators: this is a month name that will be used when displaying
# dates, as in "August 10, 2014". It is used for the %B directive in
# date-time formats. See http://strftime.org for details.
8
:
pgettext
(
'month name'
,
'August'
),
# Translators: this is a month name that will be used when displaying
# dates, as in "September 10, 2014". It is used for the %B directive in
# date-time formats. See http://strftime.org for details.
9
:
pgettext
(
'month name'
,
'September'
),
# Translators: this is a month name that will be used when displaying
# dates, as in "October 10, 2014". It is used for the %B directive in
# date-time formats. See http://strftime.org for details.
10
:
pgettext
(
'month name'
,
'October'
),
# Translators: this is a month name that will be used when displaying
# dates, as in "November 10, 2014". It is used for the %B directive in
# date-time formats. See http://strftime.org for details.
11
:
pgettext
(
'month name'
,
'November'
),
# Translators: this is a month name that will be used when displaying
# dates, as in "December 10, 2014". It is used for the %B directive in
# date-time formats. See http://strftime.org for details.
12
:
pgettext
(
'month name'
,
'December'
),
}
common/djangoapps/util/tests/test_date_utils.py
View file @
66e598b7
"""Tests for util.date_utils"""
# -*- coding: utf-8 -*-
"""
Tests for util.date_utils
"""
from
datetime
import
datetime
,
timedelta
,
tzinfo
from
functools
import
partial
import
unittest
import
ddt
from
mock
import
patch
from
nose.tools
import
assert_equals
,
assert_false
# pylint: disable=E0611
from
pytz
import
UTC
,
timezone
from
pytz
import
UTC
from
util.date_utils
import
get_default_time_display
,
get_time_display
,
almost_same_datetime
from
util.date_utils
import
(
get_default_time_display
,
get_time_display
,
almost_same_datetime
,
strftime_localized
,
)
def
test_get_default_time_display
():
...
...
@@ -106,3 +116,106 @@ def test_almost_same_datetime():
timedelta
(
minutes
=
10
)
)
)
def
fake_ugettext
(
text
,
translations
):
"""
A fake implementation of ugettext, for testing.
"""
return
translations
.
get
(
text
,
text
)
def
fake_pgettext
(
context
,
text
,
translations
):
"""
A fake implementation of pgettext, for testing.
"""
return
translations
.
get
((
context
,
text
),
text
)
@ddt.ddt
class
StrftimeLocalizedTest
(
unittest
.
TestCase
):
"""
Tests for strftime_localized.
"""
@ddt.data
(
(
"
%
Y"
,
"2013"
),
(
"
%
m/
%
d/
%
y"
,
"02/14/13"
),
(
"hello"
,
"hello"
),
(
u'
%
Y년
%
m월
%
d일'
,
u"2013년 02월 14일"
),
(
"
%
a,
%
b
%
d,
%
Y"
,
"Thu, Feb 14, 2013"
),
(
"
%
I:
%
M:
%
S
%
p"
,
"04:41:17 PM"
),
)
def
test_usual_strftime_behavior
(
self
,
(
fmt
,
expected
)):
dtime
=
datetime
(
2013
,
02
,
14
,
16
,
41
,
17
)
self
.
assertEqual
(
expected
,
strftime_localized
(
dtime
,
fmt
))
# strftime doesn't like Unicode, so do the work in UTF8.
self
.
assertEqual
(
expected
,
dtime
.
strftime
(
fmt
.
encode
(
'utf8'
))
.
decode
(
'utf8'
))
@ddt.data
(
(
"SHORT_DATE"
,
"Feb 14, 2013"
),
(
"LONG_DATE"
,
"Thursday, February 14, 2013"
),
(
"TIME"
,
"04:41:17 PM"
),
(
"
%
x
%
X!"
,
"Feb 14, 2013 04:41:17 PM!"
),
)
def
test_shortcuts
(
self
,
(
fmt
,
expected
)):
dtime
=
datetime
(
2013
,
02
,
14
,
16
,
41
,
17
)
self
.
assertEqual
(
expected
,
strftime_localized
(
dtime
,
fmt
))
@patch
(
'util.date_utils.pgettext'
,
partial
(
fake_pgettext
,
translations
=
{
(
"abbreviated month name"
,
"Feb"
):
"XXfebXX"
,
(
"month name"
,
"February"
):
"XXfebruaryXX"
,
(
"abbreviated weekday name"
,
"Thu"
):
"XXthuXX"
,
(
"weekday name"
,
"Thursday"
):
"XXthursdayXX"
,
(
"am/pm indicator"
,
"PM"
):
"XXpmXX"
,
}))
@ddt.data
(
(
"SHORT_DATE"
,
"XXfebXX 14, 2013"
),
(
"LONG_DATE"
,
"XXthursdayXX, XXfebruaryXX 14, 2013"
),
(
"DATE_TIME"
,
"XXfebXX 14, 2013 at 16:41"
),
(
"TIME"
,
"04:41:17 XXpmXX"
),
(
"
%
x
%
X!"
,
"XXfebXX 14, 2013 04:41:17 XXpmXX!"
),
)
def
test_translated_words
(
self
,
(
fmt
,
expected
)):
dtime
=
datetime
(
2013
,
02
,
14
,
16
,
41
,
17
)
self
.
assertEqual
(
expected
,
strftime_localized
(
dtime
,
fmt
))
@patch
(
'util.date_utils.ugettext'
,
partial
(
fake_ugettext
,
translations
=
{
"SHORT_DATE_FORMAT"
:
"date(
%
Y.
%
m.
%
d)"
,
"LONG_DATE_FORMAT"
:
"date(
%
A.
%
Y.
%
B.
%
d)"
,
"DATE_TIME_FORMAT"
:
"date(
%
Y.
%
m.
%
d@
%
H.
%
M)"
,
"TIME_FORMAT"
:
"
%
Hh.
%
Mm.
%
Ss"
,
}))
@ddt.data
(
(
"SHORT_DATE"
,
"date(2013.02.14)"
),
(
"Look:
%
x"
,
"Look: date(2013.02.14)"
),
(
"LONG_DATE"
,
"date(Thursday.2013.February.14)"
),
(
"DATE_TIME"
,
"date(2013.02.14@16.41)"
),
(
"TIME"
,
"16h.41m.17s"
),
(
"The time is:
%
X"
,
"The time is: 16h.41m.17s"
),
(
"
%
x
%
X"
,
"date(2013.02.14) 16h.41m.17s"
),
)
def
test_translated_formats
(
self
,
(
fmt
,
expected
)):
dtime
=
datetime
(
2013
,
02
,
14
,
16
,
41
,
17
)
self
.
assertEqual
(
expected
,
strftime_localized
(
dtime
,
fmt
))
@patch
(
'util.date_utils.ugettext'
,
partial
(
fake_ugettext
,
translations
=
{
"SHORT_DATE_FORMAT"
:
"oops date(
%
Y.
%
x.
%
d)"
,
"TIME_FORMAT"
:
"oops
%
Hh.
%
Xm.
%
Ss"
,
}))
@ddt.data
(
(
"SHORT_DATE"
,
"Feb 14, 2013"
),
(
"TIME"
,
"04:41:17 PM"
),
)
def
test_recursion_protection
(
self
,
(
fmt
,
expected
)):
dtime
=
datetime
(
2013
,
02
,
14
,
16
,
41
,
17
)
self
.
assertEqual
(
expected
,
strftime_localized
(
dtime
,
fmt
))
@ddt.data
(
"
%
"
,
"Hello
%
"
"
%
Y/
%
m/
%
d
%
"
,
)
def
test_invalid_format_strings
(
self
,
fmt
):
dtime
=
datetime
(
2013
,
02
,
14
,
16
,
41
,
17
)
with
self
.
assertRaises
(
ValueError
):
strftime_localized
(
dtime
,
fmt
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment