1

datetime format is quite a pain, I keep having to deal with different formats.

I am validating the format to contain date and time in a specific format like this:

def validate_datetime(value):
    try:
        checktype = datetime.strptime(value, '%Y-%m-%dT%H:%M')
        if not isinstance(checktype, datetime):
            return False
    except Exception as e:
        return False
    return True

now if this value also contains seconds, such as '2020-01-01T10:00:00', the method will return False. I could nest multiple try: -blocks within each other and only return False if ALL the checks fail, but I feel like there must be a better way.

How do you check if a string is a date / datetime / with / without seconds, and not having maximum overhead and checking for every case possible?

c8999c 3f964f64
  • 1,430
  • 1
  • 12
  • 25
  • You can parse both formats conveniently `2020-01-01T10:00:00` and `2020-01-01T10:00` with [datetime.fromisoformat](https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat). Also, you could have a look at dateutil's [parser](https://dateutil.readthedocs.io/en/stable/parser.html). – FObersteiner Oct 01 '20 at 09:54
  • thats very helpful, thanks! is there a way to REQUIRE a time this way, because it seems like the parsing accepts a date which doesnt include one. I'll gladly accept an answer if you make one – c8999c 3f964f64 Oct 01 '20 at 15:06

2 Answers2

1

in case it is an option to allow all input formats that can be parsed by datetime.fromisoformat but must contain a time (not parse-able by date.fromisoformat), you could check that like

from datetime import date, datetime

def validate_datetime(string):
        try:
            dt = date.fromisoformat(string)
        except ValueError: # it's not ISO date-only, so...
            try:
                dt = datetime.fromisoformat(string)
            except ValueError: # invalid/other format
                return False
            else:
                return dt # could be parsed by datetime.fromisoformat
        else: # was parsed by date.fromisoformat, which is forbidden
            return False


tests = ('2020-01-01T10:00:00',  '2020-01-01T10:00',  '2020-01-01')
for t in tests:
    print(t, '->', validate_datetime(t))
    
# 2020-01-01T10:00:00 -> 2020-01-01 10:00:00
# 2020-01-01T10:00 -> 2020-01-01 10:00:00
# 2020-01-01 -> False
FObersteiner
  • 22,500
  • 8
  • 42
  • 72
0

to enforce a certain format, I suppose fromisoformat (ref. my comment) is not of any help; you'll need to try specific strptime format codes, e.g. like

from datetime import datetime

def validate_datetime(string, whitelist=('%Y-%m-%dT%H:%M', '%Y-%m-%dT%H:%M:%S')):
    for fmt in whitelist:
        try:
            dt = datetime.strptime(string, fmt)
        except ValueError:
            pass
        else: # if a defined format is found, datetime object will be returned
            return dt
    else: # all formats done, none did work...
        return False # could also raise an exception here

tests = ('2020-01-01T10:00:00',  '2020-01-01T10:00',  '2020-01-01')
for t in tests:
    print(t, '->', validate_datetime(t))

# 2020-01-01T10:00:00 -> 2020-01-01 10:00:00
# 2020-01-01T10:00 -> 2020-01-01 10:00:00
# 2020-01-01 -> False
FObersteiner
  • 22,500
  • 8
  • 42
  • 72
  • Thats pretty elegant, however, it will fail once again if someone sends me milliseconds (or days before years, or uses dots instead of dashes, etc... etc...). I'd rather have the fromisoformat solution as a whitelist + enforce time on top of that, but this works just fine for now. Thank you very much! – c8999c 3f964f64 Oct 02 '20 at 07:05
  • @c8999c3f964f64: right, not knowing upfront what format to expect is a bit of a pain. dateutil's parser works nice but can be resource-demanding if you call it often. Also, in some cases, it can be simply ambiguous, e.g. if you don't know if month or day are provided first. In general, I think it is good to allow formats parse-able with `fromisoformat`, since it is [pretty efficient](https://stackoverflow.com/q/13468126/10197418). Unfortunately, this method is not strictly compliant with ISO8601... – FObersteiner Oct 02 '20 at 07:32
  • the problem with `fromisoformat` is that it adds a time to a valid date which previously didnt have one, and checking if the time exists will always return true after that. I actually did implement your answer, although it does follow the approach I didnt hope to go: whitelisting every single format which is valid... If anyone can solve this problem in a different answer, I might accept that one instead. – c8999c 3f964f64 Oct 02 '20 at 08:59