5
\$\begingroup\$

In my python code, date formats are stored as "strftime and strptime syntax". For instance %d/%m/%Y %H:%M.

On the client side, I use pickadate.js which format is rather dd/mm/yyyy HH:i.

So I have to translate %d/%m/%Y %H:%M to dd/mm/yyyy HH:i before sending a datetime format to the client.

I first thought str.translate was the right tool for this. But actually, this is only for characters translation.

Then I thought about str.replace but it looks awful and inefficient to loop over str.replace:

def format_to_pickadate_format(fmt):
 trans_table = [
 ('%d', 'dd'),
 ('%a', 'ddd'),
 ('%A', 'dddd'),
 ('%m', 'mm'),
 ('%b', 'mmm'),
 ('%B', 'mmmm'),
 ('%y', 'yy'),
 ('%Y', 'yyyy'),
 ('%H', 'HH'),
 ('%M', 'i'),
 ('%p', 'A'),
 ]
 for trans in trans_table:
 fmt = fmt.replace(*trans)
 return fmt

So, to make it more elegant I used functools.reduce:

from functools import reduce
def format_to_pickadate_format(fmt):
 trans_table = [
 ('%d', 'dd'),
 ('%a', 'ddd'),
 ('%A', 'dddd'),
 ('%m', 'mm'),
 ('%b', 'mmm'),
 ('%B', 'mmmm'),
 ('%y', 'yy'),
 ('%Y', 'yyyy'),
 ('%H', 'HH'),
 ('%M', 'i'),
 ('%p', 'A'),
 ]
 return reduce(lambda x, y: x.replace(*y), trans_table, fmt)

But I think it still screams inefficiency. The string is browsed once for each sequence to replace if found.

Is there a way make it more efficient (ie. make the translation in only one iteration)?

Note: This question is mainly for the sake of curiosity and learning. This is probably one of the least important thing I have to care about on my project, especially concerning performance.

Obviously in this case, making it more efficient would be ridiculously imperceptible. If this helps you find an interest in this question, imagine another use case where I'd have a string of the size of an entire book, and a translation table of 10,000 or 100,000 entries. Doesn't it look dumb to read the book 10,000 or 100,000 times to make the translation?

asked Jul 13, 2016 at 21:01
\$\endgroup\$
5
  • 1
    \$\begingroup\$ What is the purpose of this? Why do you need to transfer date formats between the client and the server? Can't you just choose one format and stick to it, even if it is represented differently on each side? \$\endgroup\$ Commented Jul 14, 2016 at 8:12
  • \$\begingroup\$ The main reason is that the format is different according to the locale of the client. So the serveur and the client must be synced on which format to use. If you have a more intelligent way to do it, I open to it. But I am still interested in the main question. :P \$\endgroup\$ Commented Jul 14, 2016 at 8:18
  • \$\begingroup\$ pickadate.js provides au way to use a field for displaying the date and another hidden field to submit the data. I originally tried to use this feature, but it was troublesome to provide initial data / repopulate the form with latest submitted data. \$\endgroup\$ Commented Jul 14, 2016 at 8:23
  • \$\begingroup\$ In such cases, it is often easier to use a common intermediate representation, such as timestamps. Is it possible to convert the date to timestamp in pickadate.js before sending? \$\endgroup\$ Commented Jul 14, 2016 at 9:12
  • \$\begingroup\$ Not as far as I know, it would require additional JS to convert the data, which is something I'd like to avoid as much as possible and is one of the reason why I didn't want to use the "hidden submit field" feature. Anyway, we are going a bit out of the scope of this question. My code above works, and I have no issue in keeping it as-is. But I'd like to know if there is a way to make it look more efficient (though given the small size of the string and the translation table, it would be imperceptible). \$\endgroup\$ Commented Jul 14, 2016 at 9:32

1 Answer 1

3
\$\begingroup\$

The strftime function is relatively well known, even if the formatting directives are not completely standardized across platforms. On the other hand, pickadate.js is a relatively unknown library. It would therefore be helpful to cite its specification, preferably in a docstring. The use of doctests would also be a good idea here.

According to the pickadate.js documentation, you should escape any "rule" characters with an exclamation mark (!).

Doing string substitutions in multiple passes is, in general, a bad idea. Not only is it more work to traverse the string several times, there is also the possibility that operating on the output leads to incorrect results. Fortunately, you seem to be safe here, since the pickadate.js directives don't start with % like the strftime directives. Still, a one-pass solution is preferable, and Python gives you a way to do that with re.sub():

re.sub(pattern, repl, string, count=0, flags=0)

...

If repl is a function, it is called for every non-overlapping occurrence of pattern. The function takes a single match object argument, and returns the replacement string.

Using re.sub(), and handling special cases where strings that appear to be strftime and pickadate.js directives should actually be treated literally...

import re
def format_to_pickadate_format(fmt):
 """
 Convert a strftime format string to a pickadate.js template.
 http://amsul.ca/pickadate.js/date/#formats
 http://amsul.ca/pickadate.js/time/#formats
 >>> format_to_pickadate_format('%d/%m/%Y %H:%M')
 'dd/mm/yyyy HH:i'
 >>> format_to_pickadate_format('%Y %% d')
 'yyyy % !d'
 """
 TRANS = {
 '%d': 'dd',
 '%a': 'ddd',
 '%A': 'dddd',
 '%m': 'mm',
 '%b': 'mmm',
 '%B': 'mmmm',
 '%y': 'yy',
 '%Y': 'yyyy',
 '%H': 'HH',
 '%M': 'i',
 '%p': 'A',
 }
 return re.sub(
 r'%(.)|([^dmyhHiaA])|(.)',
 lambda m: TRANS.get(m.group(), m.group(1)) or m.group(2) or '!' + m.group(3),
 fmt
 )
answered Jul 15, 2016 at 7:00
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.