{ "info": { "author": "Rhett Garber", "author_email": "UNKNOWN", "bugtrack_url": null, "classifiers": [], "description": "dmc\n========\n\n`dmc` is python library for date and time manipulation.\n\nUsage of standard library modules such as `datetime`, `time`, `pytz` etc is dangerous and error prone.\n\nThis library is very opinionated about how to treat dates, times and intervals\nso as to prevent developers from shooting themselves in the foot. Also, the API\nis quote a bit more convinient for most use cases.\n\nYou can think of dmc like datetime with training wheels. Except these training\nwheels flipped around and became rocket boosters. Where we're going, we don't\nneed timezones...\n\nOverview\n---------\n\ndmc is really just a wrapper around several other python libraries, but does so\nin a way that makes using these libraries safe.\n\nSome things to keep in mind:\n\n * There is no such thing as a naive time. ALL times involve a timezone, and that timezone is UTC.\n * The only time we deal with non-UTC timezones is when we parse or display a time.\n * We support math with dates, and math with times, but you can't do date math with times.\n For example: You can't add \"1 day\" to 2014-03-28 02:00:00, because that\n doesn't actually make sense. Do you mean add 24 hours? Then do that. Did\n you mean you want 02:00:00 on 3/29, sure we can do that, but you have to\n decide.\n\nWe have a few primary objects:\n\n * Time - Represents a precise time in UTC.\n * TimeInterval - Represents a change in time, like 10 minutes, or 4000 hours.\n * TimeSpan - a range of Times of some TimeInterval length\n * TimeIterator - generates time instances based on a TimeSpan and a TimeInterval\n * Date - Represents a calendar date. Only the modern Gregorian calendar is supported.\n * DateInterval - Represents a change in calendar date, like '2 days', 'next month', or 'next monday'\n * DateSpan - a range of dates of some DateInterval.\n * DateIterator - genreates dates based on DateSpan and an DateInterval\n\n\n\nUsage\n----------\n\n\n >> start_t = dmc.Time.now()\n >> print start_t\n \"2014-03-29T20:16:58.265249Z\"\n\n >> print start.to_str(tz='Americas/Los_Angeles')\n \"2014-03-29T12:16:58.265249-7:00\"\n\n >> print start.to_str(local=True)\n \"2014-03-29T12:16:58.265249-7:00\"\n\n >> start.to_str(\"YYYY-MM-DD HH:MM\", tz='Americas/Los_Angeles')\n \"2014-03-29 12:16\"\n\n >> print start_t.to_timestamp()\n 1396120755.748726\n\n >> print start_t.to_human()\n \"10 minutes ago\"\n\n >> start_t = dmc.Time.from_timestamp(1396120755.748726)\n\n >> print start_t.to_datetime()\n datetime.datetime(2014, 3, 29, 12, 16, 58, 0, tzinfo=)\n\n >> d = dmc.Date(2014, 3, 28)\n >> print d\n \"2014-03-28\"\n\n >> dmc.Date.from_str(\"3/28/2014\")\n dmc.Date(2014, 28, 3)\n\n >> start_t, _ = dmc.TimeSpan.from_date(d)\n >> print start_t\n \"2014-03-28T00:00:00Z\"\n\n # 3 weeks from now\n >> d += dmc.DateInterval(weeks=3)\n\n # Next Sunday\n >> d += dmc.DateInterval(weekday=0)\n\n >> start_t, end_t = dmc.TimeSpan.from_date(d)\n\n >> t = dmc.Time.now()\n >> t += dmc.TimeInterval(minutes=30)\n\n >> today_span = dmc.Date.today().to_timespan()\n >> for t in dmc.TimeIterator(today_span, dmc.TimeInterval(hours=1)):\n >> print t\n \"2014-03-28T00:00:00Z\"\n \"2014-03-28T01:00:00Z\"\n \"2014-03-28T02:00:00Z\"\n ....\n\n\nTesting\n----------\n\nWhen you're working with date and time sensitive code, it's often very helpful\nto be able to mock out the current time or date. `dmc` makes this easy:\n\n >> dmc.set_mock_time(dmc.Time().now() - dmc.TimeInterval(hours=1))\n >> dmc.clear_mock_time()\n\nOr, with a friendly context manager:\n\n >> with dmc.MockTime(...):\n .... pass\n\n\nWhat's wrong with datetime\n-----------\n\nThis fun old wiki page is enlightening: https://wiki.python.org/moin/WorkingWithTime\n\nDatetime was supposed to be the solution. I still remember when I stumbed\nacross mx.Datetime. Mind blown. Such a better world. (datetime was added in\npython 2.3, in 2003, over 10 years ago). You'd think that dealing with dates\nand times would be solved problem. But twice a year I wake up to the collective\n\"oh shit\" as developers remember daylight savings time.\n\nThere is also [PEP-431](http://legacy.python.org/dev/peps/pep-0431/) that\nattempts to fix datetime timezones. Or is it just patching over how insane it\nis to handle timezones in this way?\n\nThere are also a lot of glaring holes in the API datetime provides us. We fill\nthose holes with a cornocopia of several other modules. Basically, if you're\ndoing anything remotely complex with dates and times you need to understand and\nmake us of:\n\n * datetime\n * time\n * calendar\n * pytz\n * dateutil\n\n`dmc` attempts to combine all these together into a consistent interface so you don't have to.\n\nFor specific examples, try these:\n\n#### timestamps\n\nIt's easy to create a a datetime from a timestamp:\n\n >> d = datetime.datetime.fromtimestamp(12312412.0)\n\nOh wait, what timezone was that in? What we should have done was\n\n >> d = datetime.datetime.utcfromtimestamp(12312412.0)\n\nPop, quiz, how do you convert back?\n\n >> import time\n >> time.mktime(d.timetuple())\n\nOr wait, did I mean:\n\n >> time.mktime(d.utctimetuple())\n\n#### formatting / parsing\n\n >> d.isoformat()\n '2014-04-17T15:32:01.219333'\n\nNow how do I parse an isoformat? There are several solutions on stackoverflow.\n\n >> datetime.datetime.strptime(\"2014-04-17T15:32:01\", \"%Y-%m-%dT%H:%M:%S\" )\n\nBut of course parsing iso8601 is more complicated than that:\n\n >> import re\n >> s = \"2008-09-03T20:56:35.450686Z\"\n >> d = datetime.datetime(*map(int, re.split('[^\\d]', s)[:-1]))\n\nOk, there are some 3rd party libraries:\n\n >> dateutil.parser.parse('2014-04-17T15:32:01.219333Z')\n\nOf course you need to be real careful with this library, if there is something\nit doesn't recognize you'll just get a datetime filled in with values from the\ncurrent time (!!!! wtf)\n\nOh good, there is a python module for JUST THIS ONE FORMAT:\n\n >> import iso8601\n >> iso8601.parse_date(\"2007-01-25T12:00:00Z\")\n datetime.datetime(2007, 1, 25, 12, 0, tzinfo=)\n\nI don't even want to think about whether `iso8601.iso8601.Utc == pytz.UTC`.\n\n#### timezones\n\n >> datetime.datetime.now()\n\nWhat timezone is this in?\n\nOh right, what I actually need to do is:\n\n >> import pytz\n >> d = pytz.UTC.localize(datetime.datetime.utcnow())\n\nHow about some arithmetic:\n\n >> now() + datetime.timedelta(days=1)\n\nIs this 24 hours from now? Is this the same number hours since midnight but the\nnext calendar day? Unfortunately the difference between these interpretations\nonly becomes obvious twice a year, in only some political regions of the world.\n\nWhat if I wanted to enumerate time ranges for a day in localtime, and then run\nsome queries that are in UTC.\n\n >> import pytz\n >> tz = pytz.timezone('US/Pacific')\n >> start_dt = datetime.datetime(2014, 3, 9, 0, 0, 0, tzinfo=tz)\n >> end_dt = start_dt + datetime.timedelta(days=1)\n >> d = start_dt\n\n while d < end_dt:\n run_query(d.astimezone(pytz.UTC), d.astimezone(pytz.UTC) + datetime.timedelta(hours=1))\n d += datetime.timedelta(hours=1)\n\nHow many errors can you spot?\n\nOh, my favorite:\n\n >> import pytz\n >> tz = pytz.timezone('US/Pacific')\n >> start_dt = datetime.datetime(2014, 3, 6, 0, 0, 0, tzinfo=tz)\n\n >> d = start_dt\n >> for _ in range(7):\n ... print d\n ... d += datetime.timedelta(days=1\n 2014-03-06 00:00:00-08:00\n 2014-03-07 00:00:00-08:00\n 2014-03-08 00:00:00-08:00\n 2014-03-09 00:00:00-08:00\n 2014-03-10 00:00:00-08:00\n 2014-03-11 00:00:00-08:00\n 2014-03-12 00:00:00-08:00\n\nSee anything wrong here? 2014-03-10 00:00:00-08:00 doesn't make any sense.\nBecause on the 2014-03-10, the timezone should be -07:00. There are fun\nswitcheroo ways around this:\n\n >> d = start_dt\n >> for _ in range(7):\n ... print d.astimezone(pytz.UTC).astimezone(pytz.timezone('US/Pacific'))\n ... d += datetime.timedelta(days=1)\n\nThis is common enough that pytz actually provides a function for it:\n\n\n >> print pytz.timezone('US/Pacific').normalize(d)", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/rhettg/dmc", "keywords": null, "license": "ISC", "maintainer": null, "maintainer_email": null, "name": "dmc", "package_url": "https://pypi.org/project/dmc/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/dmc/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/rhettg/dmc" }, "release_url": "https://pypi.org/project/dmc/0.0.1/", "requires_dist": null, "requires_python": null, "summary": "Date and Time handling, the right way.", "version": "0.0.1" }, "last_serial": 1181920, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "fc634447c2b1cf74d974d2884eb6b3d4", "sha256": "d622261149a0ca2e75493d0d8ea0e4c15f671913d58b5490d7444851a10f8b89" }, "downloads": -1, "filename": "dmc-0.0.1.tar.gz", "has_sig": false, "md5_digest": "fc634447c2b1cf74d974d2884eb6b3d4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9291, "upload_time": "2014-08-06T16:24:09", "url": "https://files.pythonhosted.org/packages/b3/aa/422548b54766b6a7bb64823a9b33f77d0edf9a1ef263a5fbd94eb9581bb9/dmc-0.0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "fc634447c2b1cf74d974d2884eb6b3d4", "sha256": "d622261149a0ca2e75493d0d8ea0e4c15f671913d58b5490d7444851a10f8b89" }, "downloads": -1, "filename": "dmc-0.0.1.tar.gz", "has_sig": false, "md5_digest": "fc634447c2b1cf74d974d2884eb6b3d4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9291, "upload_time": "2014-08-06T16:24:09", "url": "https://files.pythonhosted.org/packages/b3/aa/422548b54766b6a7bb64823a9b33f77d0edf9a1ef263a5fbd94eb9581bb9/dmc-0.0.1.tar.gz" } ] }