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
Datetimes for Humans™

Maya: Datetimes for Humans™ Datetimes are very frustrating to work with in Python, especially when dealing with different locales on different systems

Timo Furrer 3.4k Dec 28, 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
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

30 Dec 30, 2022
⌚️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 02, 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 05, 2023
TimeTagger is a web-based time-tracking solution that can be run locally or on a server

TimeTagger is a web-based time-tracking solution that can be run locally or on a server. In the latter case, you'll want to add authentication, and also be aware of the license restrictions.

Almar Klein 626 Jan 06, 2023
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
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 03, 2023
The Terasic DECA board as a mandelbrot acceleerator

deca-mandelbrot The Terasic DECA board as a mandelbrot accelerator. This is a hobby project to explore parallel computation/pipelining on a FPGA. curr

Hans Baier 11 Aug 29, 2022
Make Python datetime formatting human readable

Make Python datetime formatting human readable

James Timmins 0 Oct 03, 2021
Cross Platform Application for Calculating Render Time

mdsanima-rt-go Cross Platform Application for Calculating Render Time. Testing This is a base application build on Windows Android and Linux. All buil

MDSANIMA DEV 2 Mar 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 06, 2023
Jalali (Shamsi) date and datetime (based on python datetime's module)

PersianTools Jalali (Shamsi) date and datetime (based on python datetime's module) Convert Jalali to Gregorian date/datetime and vice versa Support co

Majid Hajiloo 66 Dec 18, 2022
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
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
UNIX time from NTP or short UtfN is a simple CLI tool to set the time from an NTP-Server.

UNIX ⌚ from NTP UNIX time from NTP or short UtfN is a simple CLI tool to set the time from an NTP-Server. Sets time and date using the date command pr

Alexander 1 Jan 02, 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 09, 2023
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
An python based Timer and Digital Clock

Python-based-Timer- An python based Timer and Digital Clock How to contribute to this repo ❓ Step 1: Fork the this repository Step 2: Clone your fork

Bauddhik-Geeks 3 Sep 16, 2022
Croniter provides iteration for the datetime object with a cron like format

Introduction Contents Introduction Travis badge Usage About DST About second repeats Testing if a date matches a crontab Gaps between date matches Ite

kiorky 152 Dec 30, 2022