A Python 3 library for parsing human-written times and dates

Overview

Chronyk

Build Status Code Coverage PyPi License

A small Python 3 library containing some handy tools for handling time, especially when it comes to interfacing with those pesky humans.

Features

  • Parsing human-written strings ("10 minutes ago", "10. April 2015", "2014-02-15"...)
  • Relative time string creation ("in 2 hours", "5 hours ago")
  • Various input formats
  • Easy to use

Installation

$ pip install chronyk

Usage

Basic:

>>> from chronyk import Chronyk
>>> t = Chronyk(1410531179.0)
>>> t = Chronyk("May 2nd, 2016 12:51 am")
>>> t = Chronyk("yesterday")
>>> t = Chronyk("21. 8. 1976 23:18")
>>> t = Chronyk("2 days and 30 hours ago")
>>> t.ctime()
'Tue Sep  9 05:59:39 2014'
>>> t.timestamp()
1410235179.0
>>> t.timestring()
'2014-09-09 05:59:39'
>>> t.timestring("%Y-%m-%d")
'2014-09-09'
>>> t.relativestring()
'3 days ago'
>>> t.date()
datetime.date(2014, 9, 9)
>>> t.datetime()
datetime.datetime(2014, 9, 9, 5, 59, 39)

Input validation:

import sys
import chronyk

timestr = input("Please enter the date you were born: ")
try:
    date = chronyk.Chronyk(timestr, allowfuture=False)
except chronyk.DateRangeError:
    print("Yeah, right.")
    sys.exit(1)
except ValueError:
    print("Failed to parse birthdate.")
    sys.exit(1)
else:
    print("You were born {}".format(date.relativestring()))

Timezones:

By default, the Chronyk constructor uses local time, and every method by default uses whatever was passed to the constructor as well.

Almost all methods, however, have a timezone keyargument that you can use to define your local offset from UTC in seconds (positive for west, negative for east).

If you want to use a certain timezone for more than one method, you can also change the timezone instance attribute itself:

>>> t = Chronyk("4 hours ago", timezone=0) # using UTC
>>> t.ctime()
'Tue Sep  9 10:53:42 2014'
>>> t.timezone = -3600 # changes to CET (UTC+1)
>>> t.relativeTime()
'5 hours ago'
>>> t.ctime()
'Tue Sep  9 09:53:42 2014'

This uses the local relative time and returns a time string relative to current UTC:

>>> t = Chronyk("4 hours ago")
>>> t.relativestring(timezone=0)
'3 hours ago'

This uses a UTC timestamp and returns a time string relative to local time:

>>> t = Chronyk(1410524713.69, timezone=0)
>>> t.relativestring(timezone=chronyk.LOCALTZ)
'2 hours ago'
Comments
  • Dates pre-1970 epoch result in OverflowError

    Dates pre-1970 epoch result in OverflowError

    On windows machines, any date entered that is prior to the Jan 1 1970 epoch date results in the following error:

    OverflowError: mktime argument out of range
    

    The operation throwing the error is:

    timestamp = time.mktime(struct)
    

    Seems to be a limitation of the built in mktime function that only applies to Windows machines -- see here.

    bug 
    opened by walkerdb 8
  • Support for abbreviated months with trailing periods

    Support for abbreviated months with trailing periods

    Strings such as "Jan. 1, 2013" fail to parse, due to the presence of the trailing period in the abbreviated month. Since this is a very common way to write out dates, it seems worthwhile to support.

    The proposed fix is very simple - just remove all periods from the input text when they're followed by a space.

    This does not break any of the current tests, and looking through the current pattern list I don't think it would break any functionality. Due to the requirement of a space after the period, formats such as "d.m.y" are preserved.

    Any thoughts?

    opened by walkerdb 3
  • Add method for datetime.date object return

    Add method for datetime.date object return

    Using this for a local project and found myself wanting datetime.date() versions of Chronyk objects. Since datetime.datetime() functionality is already there, I just patched in a version for date().

    In case the change seems worthwhile, I also built a test and dropped in a demo for the readme.

    Thanks for building a great module!

    opened by walkerdb 3
  • relative month parsing fails when target month has fewer days than the current date

    relative month parsing fails when target month has fewer days than the current date

    As of 5/31, the strings "in 4 months" and "3 months ago" fail to parse, raising the following error:

    ValueError: day is out of range for month
    

    Presumably because neither September nor February have 31 days.

    bug 
    opened by walkerdb 2
  • [Question] Timezone/Geolocation parsing

    [Question] Timezone/Geolocation parsing

    I'm not sure if you've implemented this already - I haven't looked at the code itself, nor did I see it in the documentation (README.md), hence why I'm asking here.

    For instance, "4 hours ago, in GMT+5" or (could be possible with the geolocation-python module) "4 hours ago in Copenhagen, Denmark" ...

    ... and then there's other languages, ha.

    Edit: Changed title and a removed/changed a few sentences around to A) keep myself from looking silly and B) be more accurate with what I'm trying to ask. My apologies for the numerous edits.

    opened by sutt0n 0
  • Incorrect Results for Parssing Data Time

    Incorrect Results for Parssing Data Time

    Hi, I am a high school student just starting to learn about testing techniques. I found a bug in the latest version (0.9.1) of chronyk while doing some fuzzing using the fuzzing tool Atheris. The reproducing process is shown below:

    from chronyk import Chronyk
    Chronyk("1970-01-01t00:00:00Z").datetime()
    

    Result datetime.datetime(1969, 12, 31, 20, 0) My Environment

    Python 3.8.10 (default, Mar 15 2022, 12:22:08) 
    [GCC 9.4.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import chronyk
    >>> print(chronyk.__version__)
    0.9.1
    >>> 
    

    I am not very sure about the bug so I would very much appreciate any kind of feedback regarding it. Thanks.

    opened by OrianeK 0
  • Tests fail

    Tests fail

    See

    $ python test_chronyk.py 
    ========================================================================= test session starts ==========================================================================
    platform linux2 -- Python 2.7.10, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
    rootdir: /home/moose/GitHub/chr/Chronyk, inifile: 
    collected 72 items 
    
    test_chronyk.py .F.................FF.FFFF......................F..FFFF..........FFFFF.F
    
    =============================================================================== FAILURES ===============================================================================
    ____________________________________________________________________________ test_guesstype ____________________________________________________________________________
    
        def test_guesstype():
    >       assert guesstype(9001) == Chronyk(9001)
    E       assert Chronyk(1970-01-01 04:30:01) == Chronyk(1970-01-01 04:30:01)
    E        +  where Chronyk(1970-01-01 04:30:01) = guesstype(9001)
    E        +  and   Chronyk(1970-01-01 04:30:01) = Chronyk(9001)
    
    test_chronyk.py:21: AssertionError
    _______________________________________________________________________ test_relative_seconds_1 ________________________________________________________________________
    
        def test_relative_seconds_1():
    >       assert Chronyk("2 seconds ago").relativestring() == "just now"
    E       assert 'in 1 hour' == 'just now'
    E         - in 1 hour
    E         + just now
    
    test_chronyk.py:97: AssertionError
    _______________________________________________________________________ test_relative_seconds_2 ________________________________________________________________________
    
        def test_relative_seconds_2():
    >       assert Chronyk("in 5 seconds").relativestring() == "just now"
    E       assert 'in 1 hour' == 'just now'
    E         - in 1 hour
    E         + just now
    
    test_chronyk.py:100: AssertionError
    _______________________________________________________________________ test_relative_minutes_1 ________________________________________________________________________
    
        def test_relative_minutes_1():
    >       assert Chronyk("1 minute ago").relativestring() == "1 minute ago"
    E       assert 'in 1 hour' == '1 minute ago'
    E         - in 1 hour
    E         + 1 minute ago
    
    test_chronyk.py:107: AssertionError
    _______________________________________________________________________ test_relative_minutes_2 ________________________________________________________________________
    
        def test_relative_minutes_2():
    >       assert Chronyk("in 2 minutes").relativestring() == "in 2 minutes"
    E       assert 'in 1 hour' == 'in 2 minutes'
    E         - in 1 hour
    E         + in 2 minutes
    
    test_chronyk.py:110: AssertionError
    ________________________________________________________________________ test_relative_hours_1 _________________________________________________________________________
    
        def test_relative_hours_1():
    >       assert Chronyk("1 hour ago").relativestring() == "1 hour ago"
    E       assert 'just now' == '1 hour ago'
    E         - just now
    E         + 1 hour ago
    
    test_chronyk.py:113: AssertionError
    ________________________________________________________________________ test_relative_hours_2 _________________________________________________________________________
    
        def test_relative_hours_2():
    >       assert Chronyk("in 2 hours").relativestring() == "in 2 hours"
    E       assert 'in 3 hours' == 'in 2 hours'
    E         - in 3 hours
    E         ?    ^
    E         + in 2 hours
    E         ?    ^
    
    test_chronyk.py:116: AssertionError
    __________________________________________________________________________ test_operators_eq ___________________________________________________________________________
    
        def test_operators_eq():
            timest = time.time()
    >       assert Chronyk(timest) - 5 == Chronyk(timest - 5) and Chronyk(timest, timezone=0) == timest
    E       assert ((Chronyk(2016-01-07 10:59:02) - 5) == Chronyk(2016-01-07 10:58:57))
    E        +  where Chronyk(2016-01-07 10:59:02) = Chronyk(1452157142.589508)
    E        +  and   Chronyk(2016-01-07 10:58:57) = Chronyk((1452157142.589508 - 5))
    
    test_chronyk.py:208: AssertionError
    __________________________________________________________________________ test_operators_ne ___________________________________________________________________________
    
        def test_operators_ne():
            timest = time.time()
    >       assert Chronyk(timest) != Chronyk(timest - 2)
    E       assert Chronyk(2016-01-07 10:59:02) != Chronyk(2016-01-07 10:59:00)
    E        +  where Chronyk(2016-01-07 10:59:02) = Chronyk(1452157142.605754)
    E        +  and   Chronyk(2016-01-07 10:59:00) = Chronyk((1452157142.605754 - 2))
    
    test_chronyk.py:221: AssertionError
    _________________________________________________________________________ test_operators_ltgt __________________________________________________________________________
    
        def test_operators_ltgt():
            timest = time.time()
    >       assert Chronyk(timest) > Chronyk(timest - 5)
    E       assert Chronyk(2016-01-07 10:59:02) > Chronyk(2016-01-07 10:58:57)
    E        +  where Chronyk(2016-01-07 10:59:02) = Chronyk(1452157142.620057)
    E        +  and   Chronyk(2016-01-07 10:58:57) = Chronyk((1452157142.620057 - 5))
    
    test_chronyk.py:225: AssertionError
    __________________________________________________________________________ test_operators_add __________________________________________________________________________
    
        def test_operators_add():
            timest = time.time()
    >       assert Chronyk(timest) + ChronykDelta(5) == Chronyk(timest + 5)
    E       TypeError: unsupported operand type(s) for +: 'instance' and 'instance'
    
    test_chronyk.py:236: TypeError
    __________________________________________________________________________ test_operators_sub __________________________________________________________________________
    
        def test_operators_sub():
            timest = time.time()
    >       assert Chronyk(timest) - Chronyk(timest - 5) == ChronykDelta(5)
    E       TypeError: unsupported operand type(s) for -: 'instance' and 'instance'
    
    test_chronyk.py:241: TypeError
    _______________________________________________________________________ test_delta_operators_eq ________________________________________________________________________
    
        def test_delta_operators_eq():
    >       assert ChronykDelta(5) == ChronykDelta(5) and ChronykDelta(5) == 5
    E       assert (ChronykDelta(5 seconds) == ChronykDelta(5 seconds))
    E        +  where ChronykDelta(5 seconds) = ChronykDelta(5)
    E        +  and   ChronykDelta(5 seconds) = ChronykDelta(5)
    
    test_chronyk.py:278: AssertionError
    _______________________________________________________________________ test_delta_operators_neq _______________________________________________________________________
    
        def test_delta_operators_neq():
    >       assert ChronykDelta(5) != ChronykDelta(6) and ChronykDelta(5) != 3
    E       assert (ChronykDelta(5 seconds) != ChronykDelta(6 seconds))
    E        +  where ChronykDelta(5 seconds) = ChronykDelta(5)
    E        +  and   ChronykDelta(6 seconds) = ChronykDelta(6)
    
    test_chronyk.py:281: AssertionError
    ______________________________________________________________________ test_delta_operators_ltgt _______________________________________________________________________
    
        def test_delta_operators_ltgt():
            assert ChronykDelta(5) > ChronykDelta(4) and ChronykDelta(5) > 3
            assert ChronykDelta(5) < ChronykDelta(7) and ChronykDelta(5) < 9
    >       assert ChronykDelta(5) >= ChronykDelta(5) and ChronykDelta(5) >= 3
    E       assert (ChronykDelta(5 seconds) >= ChronykDelta(5 seconds))
    E        +  where ChronykDelta(5 seconds) = ChronykDelta(5)
    E        +  and   ChronykDelta(5 seconds) = ChronykDelta(5)
    
    test_chronyk.py:286: AssertionError
    _______________________________________________________________________ test_delta_operators_add _______________________________________________________________________
    
        def test_delta_operators_add():
            timest = time.time()
    >       assert ChronykDelta(5) + ChronykDelta(-5) == 0
    E       TypeError: unsupported operand type(s) for +: 'instance' and 'instance'
    
    test_chronyk.py:291: TypeError
    _______________________________________________________________________ test_delta_operators_sub _______________________________________________________________________
    
        def test_delta_operators_sub():
            assert ChronykDelta(5) - 5 == 0
    >       assert ChronykDelta(5) - ChronykDelta(1) == 4
    E       TypeError: unsupported operand type(s) for -: 'instance' and 'instance'
    
    test_chronyk.py:297: TypeError
    _______________________________________________________________________ test_delta_operators_div _______________________________________________________________________
    
        def test_delta_operators_div():
    >       assert ChronykDelta(10) / 2 == 5
    E       TypeError: unsupported operand type(s) for /: 'instance' and 'int'
    
    test_chronyk.py:303: TypeError
    ================================================================= 18 failed, 54 passed in 4.23 seconds =================================================================
    
    opened by MartinThoma 2
  • Weekday combinations.

    Weekday combinations.

    Apparently it is possible to parse the following expressions, although they all evaluate to the current time:

    >>> Chronyk("friday ago")
    Chronyk(2015-12-17 13:37:00)
    >>> Chronyk("in saturday")
    Chronyk(2015-12-17 13:37:00)
    >>> Chronyk("in one friday")
    Chronyk(2015-12-17 13:37:00)
    >>> Chronyk("friday in two weeks")
    Chronyk(2015-12-17 13:37:00)
    

    On the other hand, expressions like these cannot be parsed (you may take this as a feature request :D):

    >>> Chronyk("friday")
    ValueError: Failed to parse time string.
    >>> Chronyk("on friday")
    ValueError: Failed to parse time string.
    >>> Chronyk("next friday")
    ValueError: Failed to parse time string.
    

    as well as Chronyk("next week") which does not include a weekday, but it could evaluate to the same as Chronyk("in one week").

    Cheers, Lumiukko

    opened by Lumiukko 0
  • Unicode doesn't work

    Unicode doesn't work

    >>> import chronyk
    >>> chronyk.Chronyk("2015-5-5")
    <chronyk.chronyk.Chronyk instance at 0x7fc70d03c3b0>
    >>> chronyk.Chronyk(u"2015-5-5")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/mallyvai/.virtualenvs/webdev4/local/lib/python2.7/site-packages/chronyk/chronyk.py", line 77, in __init__
        raise TypeError("Invalid type specified.")
    TypeError: Invalid type specified.
    
    $ python --version
    Python 2.7.6
    
    $ pip show chronyk
    
    ---
    Metadata-Version: 2.0
    Name: Chronyk
    Version: 0.9.1
    Summary: A library for parsing human-written times and dates.
    Home-page: https://github.com/KoffeinFlummi/PyGHI
    Author: Felix "KoffeinFlummi" Wiegand
    Author-email: [email protected]
    [...]
    
    opened by mallyvai 1
Releases(v1.0.1)
Owner
Felix Wiegand
Felix Wiegand
python parser for human readable dates

Python parser for human readable dates Key Features • How To Use • Installation • Common use cases • You may also like... • License Key Features Suppo

Scrapinghub 2.2k Jan 8, 2023
A Python library for dealing with dates

moment A Python library for dealing with dates/times. Inspired by Moment.js and Kenneth Reitz's Requests library. Ideas were also taken from the Times

Zach Williams 709 Dec 9, 2022
Friendly Python Dates

When.py: Friendly Dates and Times Production: Development: User-friendly functions to help perform common date and time actions. Usage To get the syst

Andy Dirnberger 191 Oct 14, 2022
Make Python datetime formatting human readable

Make Python datetime formatting human readable

James Timmins 0 Oct 3, 2021
Parse human-readable date/time strings

parsedatetime Parse human-readable date/time strings. Python 2.6 or greater is required for parsedatetime version 1.0 or greater. While we still test

Mike Taylor 651 Dec 23, 2022
A simple in-process python scheduler library, designed to be integrated seamlessly with the `datetime` standard library.

scheduler A simple in-process python scheduler library, designed to be integrated seamlessly with the datetime standard library. Due to the support of

null 30 Dec 30, 2022
pytz Python historical timezone library and database

pytz Brings the IANA tz database into Python. This library allows accurate and cross platform timezone calculations. pytz contains generated code, and

Stub 236 Jan 3, 2023
darts is a Python library for easy manipulation and forecasting of time series.

A python library for easy manipulation and forecasting of time series.

Unit8 5.2k Jan 1, 2023
Generate and work with holidays in Python

python-holidays A fast, efficient Python library for generating country, province and state specific sets of holidays on the fly. It aims to make dete

Maurizio Montel 881 Dec 29, 2022
A datetime parser in Python by Ari24-cb24 and NekoFantic

datetimeparser A datetime parser in Python by Ari24-cb24 and NekoFantic V 1.0 Erinnerung für den Parser Auf falsche Eingaben überprüfen Liste an Event

AriDevelopment 13 Dec 30, 2022
Useful extensions to the standard Python datetime features

dateutil - powerful extensions to datetime The dateutil module provides powerful extensions to the standard datetime module, available in Python. Inst

null 2k Dec 29, 2022
Python datetimes made easy

Pendulum Python datetimes made easy. Supports Python 2.7 and 3.4+. >>> import pendulum >>> now_in_paris = pendulum.now('Europe/Paris') >>> now_in_par

Sébastien Eustace 5.3k Jan 6, 2023
PyTime is an easy-use Python module which aims to operate date/time/datetime by string.

PyTime PyTime is an easy-use Python module which aims to operate date/time/datetime by string. PyTime allows you using nonregular datetime string to g

Sinux 148 Dec 9, 2022
A Python module that tries to figure out what your local timezone is

tzlocal This Python module returns a tzinfo object with the local timezone information under Unix and Windows. It requires either Python 3.9+ or the b

Lennart Regebro 159 Dec 16, 2022
A simple digital clock made with the help of python

Digital-Clock ⏰ Description ?? ✔️ A simple digital clock made with the help of python. The code is easy to understand and implement. With this reposit

Mohit 0 Dec 10, 2021
E-Ink Magic Calendar that automatically syncs to Google Calendar and runs off a battery powered Raspberry Pi Zero

E-Ink Magic Calendar that automatically syncs to Google Calendar and runs off a battery powered Raspberry Pi Zero

null 2.8k Jan 6, 2023
⌚️Internet Time reference and (eventually) converter site, for planning things with your internet friends who aren't (yet) obsessed with Internet Time 😉

Internet-Ti.me Internet Time reference and (eventually) converter site, for planning things with your internet friends who aren't (yet) obsessed with

Jessica Stokes 17 Nov 2, 2022
Better dates & times for Python

Arrow: Better dates & times for Python Arrow is a Python library that offers a sensible and human-friendly approach to creating, manipulating, formatt

Arrow 8.2k Jan 5, 2023
Formatting of dates and times in Flask templates using moment.js.

Flask-Moment This extension enhances Jinja2 templates with formatting of dates and times using moment.js. Quick Start Step 1: Initialize the extension

Miguel Grinberg 358 Nov 28, 2022
Formatting of dates and times in Flask templates using moment.js.

Flask-Moment This extension enhances Jinja2 templates with formatting of dates and times using moment.js. Quick Start Step 1: Initialize the extension

Miguel Grinberg 318 Feb 17, 2021