3

I'm trying to port an application from C# to Python. The application allows the user to choose their datetime format using C# String.Format DateTime formatting. Python's datetime formatting is not even close to the same, so I'm having to jump my code through a few hoops.

Is there any way Python can parse strings like yyyy-MM-dd HH-mm-ss instead of %Y-%m-%d %H-%M-%S?

4 Answers 4

3

You can get a fair distance by using simple replacement to convert the format strings.

_format_changes = (
    ('MMMM', '%B'),
    ('MMM',  '%b'), # note: the order in this list is critical
    ('MM',   '%m'),
    ('M',    '%m'), # note: no exact equivalent
    # etc etc
    )

def conv_format(s):
    for c, p in _format_changes:
        # s.replace(c, p) #### typo/braino
        s = s.replace(c, p)
    return s

I presume that your "hoops" means something similar. Note that there are complications:
(1) the C# format can have literal text enclosed in single quotes (examples in the link you quoted)
(2) it probably allows a single character to be made a literal by escaping it with (e.g.) \
(3) The 12- or 24-hour clock stuff may need extra work (I haven't delved into the C# spec; this comment is based another similar exercise that I've been involved in).
You can end up writing a compiler and a byte-code interpreter to get around all the gotchas (like M, F, FF, FFF, ...).

An alternative to look at is using ctypes or something similar to call the C# RTL directly.

Update The original code was overly simplistic and had a typo/braino. The following new code shows how to address some of the issues (like literal text, and ensuring that a literal % in the input doesn't make strftime unhappy). It doesn't attempt to give accurate answers where there's no direct conversion (M, F, etc). Places where an Exception could be raised are noted but the code operates on a laissez-faire basis.

_format_changes = (
    ('yyyy', '%Y'), ('yyy', '%Y'), ('yy', '%y'),('y', '%y'),
    ('MMMM', '%B'), ('MMM', '%b'), ('MM', '%m'),('M', '%m'),
    ('dddd', '%A'), ('ddd', '%a'), ('dd', '%d'),('d', '%d'),
    ('HH', '%H'), ('H', '%H'), ('hh', '%I'), ('h', '%I'),
    ('mm', '%M'), ('m', '%M'),
    ('ss', '%S'), ('s', '%S'),
    ('tt', '%p'), ('t', '%p'),
    ('zzz', '%z'), ('zz', '%z'), ('z', '%z'),
    )

def cnv_csharp_date_fmt(in_fmt):
    ofmt = ""
    fmt = in_fmt
    while fmt:
        if fmt[0] == "'":
            # literal text enclosed in ''
            apos = fmt.find("'", 1)
            if apos == -1:
                # Input format is broken.
                apos = len(fmt)
            ofmt += fmt[1:apos].replace("%", "%%")
            fmt = fmt[apos+1:]
        elif fmt[0] == "\\":
            # One escaped literal character.
            # Note graceful behaviour when \ is the last character.
            ofmt += fmt[1:2].replace("%", "%%")
            fmt = fmt[2:]
        else:
            # This loop could be done with a regex "(yyyy)|(yyy)|etc".
            for intok, outtok in _format_changes:
                if fmt.startswith(intok):
                    ofmt += outtok
                    fmt = fmt[len(intok):]
                    break
            else:
                # Hmmmm, what does C# do here?
                # What do *you* want to do here?
                # I'll just emit one character as literal text
                # and carry on. Alternative: raise an exception.
                ofmt += fmt[0].replace("%", "%%")
                fmt = fmt[1:]
    return ofmt

Tested to the following extent:

>>> from cnv_csharp_date_fmt import cnv_csharp_date_fmt as cv
>>> cv("yyyy-MM-dd hh:mm:ss")
'%Y-%m-%d %I:%M:%S'
>>> cv("3pcts %%% yyyy-MM-dd hh:mm:ss")
'3pc%p%S %%%%%% %Y-%m-%d %I:%M:%S'
>>> cv("'3pcts' %%% yyyy-MM-dd hh:mm:ss")
'3pcts %%%%%% %Y-%m-%d %I:%M:%S'
>>> cv(r"3pc\t\s %%% yyyy-MM-dd hh:mm:ss")
'3pcts %%%%%% %Y-%m-%d %I:%M:%S'
>>>
Sign up to request clarification or add additional context in comments.

10 Comments

+1. Derivative of J V's answer, but more clearly formatted and more clearly explained.
@Steven Rumbalski: Derivative of his initial answer, which contained only the "just run a few replaces" remark, with no code, the latter evidently being added before SO's "don't log edits" period timed out.
By hoops I meant that I have a second, hidden, text box that I use my button clicks/text box change events add the Python format. It's messy, and doing double the work it seems, but it works. Your answer, however, makes it seem like this would be possible. How does it handle the text if it's all mashed together with no spaces or delimiters? Example: yyyyMMddHHmmss I haven't looked much into the string replace docs, but I guess I should.
@Mike: "Second hidden text box": yikes! "all mashed together": well, you'd get the Python string all mashed together %Y%m%d... which would produced a mashed-together output e.g. 19991231... just like the C#-produced output, wouldn't you? The str.replace docs don't need much looking.
@Mike: "mashed together": also read the advice from Steven Rumbalski and myself that the order that replacements are done is significant -- you don't want yyyy to be changed into %y%y, for example.
|
2

Just run a few replaces first:

replacelist = [["yyyy","%Y"], ["MM","%m"]] # Etc etc
for replacer in replacelist:
    string.replace(replacer[0],replacer[1])

8 Comments

What would you do about ones that don't map? For example C#'s "M" is the month's number without leading zeros. Python doesn't have a time format option for that.
I would use this but with tuples
Also, make sure to replace 'yyyy' before you replace 'yyy'.
Nothing you can do about formats that are completely incompatible, I would rather put a listbox of preconfigured options there than allow raw user input, it's bound to cause trouble anyway...
(1) What you can do is implement your own formatter as mentioned in my answer (2) You don't have to explain strftime format to users, all you have to do is ask them to type a day number, a month number, and a year number -- a task that the average user can do much faster than mousing about (especially when the listbox for year of birth contains 70 to 100 entries).
|
1

In addition to the selected answer, this is for anyone that would want the same but in c# where formats are converted from python (Convert python datetime format to C# convert-able datetime format ), below is an extension that would do the job

public static string PythonToCSharpDateFormat(this string dateFormat)
    {
        string[][] changes = new string[][]
        {
            new string[]{"yyyy", "%Y"},new string[] {"yyy", "%Y"}, new string[]{"yy", "%y"},
            new string[]{"y", "%y"}, new string[]{"MMMM", "%B"}, new string[]{"MMM", "%b"},
            new string[]{"MM", "%m"}, new string[]{"M", "%m"}, new string[]{"dddd", "%A"},
            new string[]{"ddd", "%a"}, new string[]{"dd", "%d"}, new string[]{"d", "%d"},
            new string[]{"HH", "%H"}, new string[]{"H", "%H"}, new string[]{"hh", "%I"},
            new string[]{"h", "%I"}, new string[]{"mm", "%M"}, new string[]{"m", "%M"},
            new string[]{"ss", "%S"}, new string[]{"s", "%S"}, new string[]{"tt", "%p"},
            new string[]{"t", "%p"}, new string[]{"zzz", "%z"}, new string[]{"zz", "%z"},
            new string[]{"z", "%z"}
        };

        foreach (var change in changes)
        {
            //REPLACE PYTHON FORMAT WITH C# FORMAT
            dateFormat = dateFormat.Replace(change[1], change[0]);
        }
        return dateFormat;
    }

Comments

0

I'm afraid you can't. strftime() calls the underlying C library's strftime() function, which in turn takes formatting directives exactly in the %X form. You'll have to write a few lines of code to do a conversion.

Comments

Your Answer

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.