Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create datetime.time() object in yaml?

Tags:

python

yaml

I know that I can create a datetime.datetime object and then use .time() method, like this:

my_file.yaml

MyDateTime: 2015-08-11 10:08:37

and then:

In [58]: f = open("my_file.yaml", "rU")

In [59]: temp = yaml.safe_load(f)

In [60]: f.close()

In [61]: temp['MyDateTime'].time()
Out[61]: datetime.time(10, 8, 37)

but that seems a little redundant, is there a way to create datetime.time() object to begin with?

like image 640
Akavall Avatar asked Jul 29 '26 12:07

Akavall


1 Answers

You can patch the SafeLoader to do your bidding, but it is nicer to make a new Loader that does the same as the SafeLoader used by safe_load(). The only thing you need to provide for that is a method that is invoked (construct_yaml_time()) when a timestamp scalar is matched and specify it by calling add_constructor.

import ruamel.yaml as yaml

from ruamel.yaml.loader import Reader, Scanner, Composer, SafeConstructor, \
     Resolver, Parser

class TimeConstructor(SafeConstructor):
    def __init__(self):
        SafeConstructor.__init__(self)

    def construct_yaml_time(self, node):
        x = SafeConstructor.construct_yaml_timestamp(self, node)
        return x.time()


TimeConstructor.add_constructor(
    u'tag:yaml.org,2002:timestamp',
    TimeConstructor.construct_yaml_time)

class TimeLoader(Reader, Scanner, Parser, Composer, TimeConstructor, Resolver):
    def __init__(self, stream):
        Reader.__init__(self, stream)
        Scanner.__init__(self)
        Parser.__init__(self)
        Composer.__init__(self)
        TimeConstructor.__init__(self)
        Resolver.__init__(self)

yaml_str = """\
MyDateTime: 2015-08-11 10:08:37
x: 23423
"""

data = yaml.load(yaml_str, Loader=TimeLoader)
print data

gives you:

<type 'datetime.time'>
{'x': 23423, 'MyDateTime': datetime.time(10, 8, 37)}

You can also specify your time objects as 10:08:37 but this will load them as sexagesimal number and that will convert them to an integer (36517 in this case). You can, of course, change this behaviour as well:

import ruamel.yaml as yaml

from ruamel.yaml.loader import Reader, Scanner, Composer, SafeConstructor, \
     Resolver, Parser

import datetime

class TimeConstructor(SafeConstructor):
    def __init__(self):
        SafeConstructor.__init__(self)

    def construct_yaml_time(self, node):
        value = self.construct_scalar(node)
        if ':' not in value:
            return SafeConstructor.construct_yaml_int(self, node)
        return datetime.time(*[int(x) for x in value.split(':')])



TimeConstructor.add_constructor(
    u'tag:yaml.org,2002:int',
    TimeConstructor.construct_yaml_time)

class TimeLoader(Reader, Scanner, Parser, Composer, TimeConstructor, Resolver):
    def __init__(self, stream):
        Reader.__init__(self, stream)
        Scanner.__init__(self)
        Parser.__init__(self)
        Composer.__init__(self)
        TimeConstructor.__init__(self)
        Resolver.__init__(self)

yaml_str = """\
MyDateTime: 10:08:37
x: 12345
"""

data = yaml.load(yaml_str, Loader=TimeLoader)
print data

which gives:

{'x': 12345, 'MyDateTime': datetime.time(10, 8, 37)}

This relies on the time to be recognised by the normal int recogniser. You can also recognise the sexagesimal itself, but that is more complex to implement in my experience.


Other alternatives exists, including creating datetime.time objects by tagging them explicitly and providing loading (and possible saving) routines.

like image 151
Anthon Avatar answered Jul 31 '26 02:07

Anthon