[Python-modules-commits] [python-udatetime] 01/01: New upstream version 0.0.9
Ilias Tsitsimpis
iliastsi-guest at moszumanska.debian.org
Sun Sep 11 20:58:30 UTC 2016
This is an automated email from the git hooks/post-receive script.
iliastsi-guest pushed a commit to branch upstream
in repository python-udatetime.
commit edceebc5a4163cae34a906944e1aa838cbe69f37
Author: Ilias Tsitsimpis <i.tsitsimpis at gmail.com>
Date: Sun Sep 11 13:41:52 2016 +0300
New upstream version 0.0.9
---
MANIFEST.in | 1 +
PKG-INFO | 23 +
README.md | 285 +++++++++
requirements.txt | 0
scripts/bench_udatetime.py | 135 ++++
setup.cfg | 5 +
setup.py | 69 +++
src/rfc3339.c | 1034 +++++++++++++++++++++++++++++++
test/test_udatetime.py | 189 ++++++
udatetime.egg-info/PKG-INFO | 23 +
udatetime.egg-info/SOURCES.txt | 15 +
udatetime.egg-info/dependency_links.txt | 1 +
udatetime.egg-info/not-zip-safe | 1 +
udatetime.egg-info/top_level.txt | 2 +
udatetime/__init__.py | 35 ++
udatetime/_pure.py | 205 ++++++
version.txt | 1 +
17 files changed, 2024 insertions(+)
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..fa30486
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+include *.txt *.cfg *.md *.py
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..38d2455
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,23 @@
+Metadata-Version: 1.1
+Name: udatetime
+Version: 0.0.9
+Summary: Fast RFC3339 compliant date-time library
+Home-page: https://github.com/freach/udatetime
+Author: Simon Pirschel
+Author-email: simon at aboutsimon.com
+License: Apache 2.0
+Description: # udatetime: Fast RFC3339 compliant date-time library
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Natural Language :: English
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: MacOS
+Classifier: Programming Language :: C
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c327ec5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,285 @@
+# udatetime: Fast RFC3339 compliant date-time library
+
+Handling date-times is a painful act because of the sheer endless amount
+of formats used by people. Luckily there are a couple of specified standards
+out there like [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601). But even
+ISO 8601 leaves to many options on how to define date and time. That's why I
+encourage using the [RFC3339](https://www.ietf.org/rfc/rfc3339.txt) specified
+date-time format.
+
+`udatetime` offers on average 76% faster `datetime` object instantiation,
+serialization and deserialization of RFC3339 date-time strings. `udatetime` is
+using Python's [datetime class](https://docs.python.org/2/library/datetime.html)
+under the hood and code already using `datetime` should be able to easily
+switch to `udatetime`. All `datetime` objects created by `udatetime` are timezone
+aware.
+
+| | Support | Performance optimized | Implementation |
+| -------- |:------------------:|:---------------------:| -------------- |
+| Python 2 | :heavy_check_mark: | :heavy_check_mark: | C |
+| Python 3 | :heavy_check_mark: | :heavy_check_mark: | C |
+| PyPy | :heavy_check_mark: | :heavy_check_mark: | Pure Python |
+
+```python
+>>> udatetime.from_string("2016-07-15T12:33:20.123000+02:00")
+datetime.datetime(2016, 7, 15, 12, 33, 20, 123000, tzinfo=+02:00)
+
+>>> dt = udatetime.from_string("2016-07-15T12:33:20.123000+02:00")
+>>> udatetime.to_string(dt)
+'2016-07-15T12:33:20.123000+02:00'
+
+>>> udatetime.now()
+datetime.datetime(2016, 7, 29, 10, 15, 24, 472467, tzinfo=+02:00)
+
+>>> udatetime.utcnow()
+datetime.datetime(2016, 7, 29, 8, 15, 36, 184762, tzinfo=+00:00)
+
+>>> udatetime.now_to_string()
+'2016-07-29T10:15:46.641233+02:00'
+
+>>> udatetime.utcnow_to_string()
+'2016-07-29T08:15:56.129798+00:00'
+
+>>> udatetime.to_string(udatetime.utcnow() - timedelta(hours=6))
+'2016-07-29T02:16:05.770358+00:00'
+
+>>> udatetime.fromtimestamp(time.time())
+datetime.datetime(2016, 7, 30, 17, 45, 1, 536586, tzinfo=+02:00)
+
+>>> udatetime.utcfromtimestamp(time.time())
+datetime.datetime(2016, 8, 1, 10, 14, 53, tzinfo=+00:00)
+```
+
+## Installation
+
+Currently only **POSIX** compliant systems are supported.
+Working on cross-platform support.
+
+```
+$ pip install udatetime
+```
+
+You might need to install the header files of your Python installation and
+some essential tools to execute the build like a C compiler.
+
+**Python 2**
+
+```
+$ sudo apt-get install python-dev build-essential
+```
+
+or
+
+```
+$ sudo yum install python-devel gcc
+```
+
+**Python 3**
+
+```
+$ sudo apt-get install python3-dev build-essential
+```
+
+or
+
+```
+$ sudo yum install python3-devel gcc
+```
+
+## Benchmark
+
+The benchmarks compare the performance of equivalent code of `datetime` and
+`udatetime`. The benchmark measures the time needed for 1 million executions
+and takes the `min` of 3 repeats. You can run the benchmark yourself and see
+the results on your machine by executing the `bench_udatetime.py` script.
+
+![Benchmark interpreter summary](/extras/benchmark_interpreter_summary.png?raw=true "datetime vs. udatetime summary")
+
+### Python 2.7
+
+```
+$ python scripts/bench_udatetime.py
+Executing benchmarks ...
+
+============ benchmark_parse
+datetime_strptime 10.6306970119
+udatetime_parse 1.109801054
+udatetime is 9.6 times faster
+
+============ benchmark_format
+datetime_strftime 2.08363199234
+udatetime_format 0.654432058334
+udatetime is 3.2 times faster
+
+============ benchmark_utcnow
+datetime_utcnow 0.485884904861
+udatetime_utcnow 0.237742185593
+udatetime is 2.0 times faster
+
+============ benchmark_now
+datetime_now 1.37059998512
+udatetime_now 0.235424041748
+udatetime is 5.8 times faster
+
+============ benchmark_utcnow_to_string
+datetime_utcnow_to_string 2.56599593163
+udatetime_utcnow_to_string 0.685483932495
+udatetime is 3.7 times faster
+
+============ benchmark_now_to_string
+datetime_now_to_string 3.68989396095
+udatetime_now_to_string 0.687911987305
+udatetime is 5.4 times faster
+
+============ benchmark_fromtimestamp
+datetime_fromtimestamp 1.38640713692
+udatetime_fromtimestamp 0.287910938263
+udatetime is 4.8 times faster
+
+============ benchmark_utcfromtimestamp
+datetime_utcfromtimestamp 0.533131837845
+udatetime_utcfromtimestamp 0.279694080353
+udatetime is 1.9 times faster
+```
+
+### Python 3.5
+
+```
+$ python scripts/bench_udatetime.py
+Executing benchmarks ...
+
+============ benchmark_parse
+datetime_strptime 9.90670353400003
+udatetime_parse 1.1668808249999074
+udatetime is 8.5 times faster
+
+============ benchmark_format
+datetime_strftime 3.0286041580000074
+udatetime_format 0.7153575119999687
+udatetime is 4.2 times faster
+
+============ benchmark_utcnow
+datetime_utcnow 0.5638177430000724
+udatetime_utcnow 0.2548112540000602
+udatetime is 2.2 times faster
+
+============ benchmark_now
+datetime_now 1.457822759999999
+udatetime_now 0.26195338699994863
+udatetime is 5.6 times faster
+
+============ benchmark_utcnow_to_string
+datetime_utcnow_to_string 3.5347913849999486
+udatetime_utcnow_to_string 0.750341161999927
+udatetime is 4.7 times faster
+
+============ benchmark_now_to_string
+datetime_now_to_string 4.854975383999999
+udatetime_now_to_string 0.7411948169999505
+udatetime is 6.6 times faster
+
+============ benchmark_fromtimestamp
+datetime_fromtimestamp 1.4233373309999706
+udatetime_fromtimestamp 0.31758270299997093
+udatetime is 4.5 times faster
+
+============ benchmark_utcfromtimestamp
+datetime_utcfromtimestamp 0.5633522409999614
+udatetime_utcfromtimestamp 0.305099536000057
+udatetime is 1.8 times faster
+```
+
+### PyPy 5.3.1
+
+```
+$ python scripts/bench_udatetime.py
+Executing benchmarks ...
+
+============ benchmark_parse
+datetime_strptime 2.31050491333
+udatetime_parse 0.838973045349
+udatetime is 2.8 times faster
+
+============ benchmark_format
+datetime_strftime 0.957178115845
+udatetime_format 0.162060976028
+udatetime is 5.9 times faster
+
+============ benchmark_utcnow
+datetime_utcnow 0.149839878082
+udatetime_utcnow 0.149217844009
+udatetime is as fast as datetime
+
+============ benchmark_now
+datetime_now 0.967023134232
+udatetime_now 0.15003991127
+udatetime is 6.4 times faster
+
+============ benchmark_utcnow_to_string
+datetime_utcnow_to_string 1.24988698959
+udatetime_utcnow_to_string 0.632546901703
+udatetime is 2.0 times faster
+
+============ benchmark_now_to_string
+datetime_now_to_string 2.13041496277
+udatetime_now_to_string 0.607964038849
+udatetime is 3.5 times faster
+
+============ benchmark_fromtimestamp
+datetime_fromtimestamp 0.903736114502
+udatetime_fromtimestamp 0.0907990932465
+udatetime is 10.0 times faster
+
+============ benchmark_utcfromtimestamp
+datetime_utcfromtimestamp 0.0890419483185
+udatetime_utcfromtimestamp 0.0907027721405
+udatetime is as fast as datetime
+```
+
+## Why RFC3339
+
+The RFC3339 specification has the following advantages:
+
+- Defined date, time, timezone, date-time format
+- 4 digit year
+- Fractional seconds
+- Human readable
+- No redundant information like weekday name
+- Simple specification, easily machine readable
+
+### RFC3339 format specification
+
+```
+date-fullyear = 4DIGIT
+date-month = 2DIGIT ; 01-12
+date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on
+ ; month/year
+time-hour = 2DIGIT ; 00-23
+time-minute = 2DIGIT ; 00-59
+time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second
+ ; rules
+time-secfrac = "." 1*DIGIT
+time-numoffset = ("+" / "-") time-hour ":" time-minute
+time-offset = "Z" / time-numoffset
+
+partial-time = time-hour ":" time-minute ":" time-second [time-secfrac]
+
+full-date = date-fullyear "-" date-month "-" date-mday
+full-time = partial-time time-offset
+
+date-time = full-date "T" full-time
+```
+
+`udatetime` specific format addons:
+
+- time-secfrac can be either 3DIGIT for milliseconds or 6DIGIT for microseconds
+- time-secfrac will be normalized to microseconds
+- time-offset is optional. Missing time-offset will be treated as UTC.
+- spaces will be eliminated
+
+## Why in C?
+
+The Python `datetime` library is flexible but painfully slow, when it comes to
+parsing and formating. High performance applications like web services or
+logging benefit from fast underlying libraries. Restricting the input format
+to one standard allows for optimization and results in speed improvements.
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..e69de29
diff --git a/scripts/bench_udatetime.py b/scripts/bench_udatetime.py
new file mode 100644
index 0000000..0ba7aab
--- /dev/null
+++ b/scripts/bench_udatetime.py
@@ -0,0 +1,135 @@
+from __future__ import print_function
+from datetime import datetime
+from time import time
+import udatetime
+
+RFC3339_DATE = '2016-07-18'
+RFC3339_TIME = '12:58:26.485897+02:00'
+RFC3339_DATE_TIME = RFC3339_DATE + 'T' + RFC3339_TIME
+RFC3339_DATE_TIME_DTLIB = RFC3339_DATE_TIME[:-6]
+DATE_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%f'
+DATETIME_OBJ = datetime.strptime(RFC3339_DATE_TIME_DTLIB, DATE_TIME_FORMAT)
+TIME = time()
+
+
+def benchmark_parse():
+ def datetime_strptime():
+ datetime.strptime(RFC3339_DATE_TIME_DTLIB, DATE_TIME_FORMAT)
+
+ def udatetime_parse():
+ udatetime.from_string(RFC3339_DATE_TIME)
+
+ return (datetime_strptime, udatetime_parse)
+
+
+def benchmark_format():
+ def datetime_strftime():
+ DATETIME_OBJ.strftime(DATE_TIME_FORMAT)
+
+ def udatetime_format():
+ udatetime.to_string(DATETIME_OBJ)
+
+ return (datetime_strftime, udatetime_format)
+
+
+def benchmark_utcnow():
+ def datetime_utcnow():
+ datetime.utcnow()
+
+ def udatetime_utcnow():
+ udatetime.utcnow()
+
+ return (datetime_utcnow, udatetime_utcnow)
+
+
+def benchmark_now():
+ def datetime_now():
+ datetime.now()
+
+ def udatetime_now():
+ udatetime.now()
+
+ return (datetime_now, udatetime_now)
+
+
+def benchmark_utcnow_to_string():
+ def datetime_utcnow_to_string():
+ datetime.utcnow().strftime(DATE_TIME_FORMAT)
+
+ def udatetime_utcnow_to_string():
+ udatetime.utcnow_to_string()
+
+ return (datetime_utcnow_to_string, udatetime_utcnow_to_string)
+
+
+def benchmark_now_to_string():
+ def datetime_now_to_string():
+ datetime.now().strftime(DATE_TIME_FORMAT)
+
+ def udatetime_now_to_string():
+ udatetime.now_to_string()
+
+ return (datetime_now_to_string, udatetime_now_to_string)
+
+
+def benchmark_fromtimestamp():
+ def datetime_fromtimestamp():
+ datetime.fromtimestamp(TIME)
+
+ def udatetime_fromtimestamp():
+ udatetime.fromtimestamp(TIME)
+
+ return (datetime_fromtimestamp, udatetime_fromtimestamp)
+
+
+def benchmark_utcfromtimestamp():
+ def datetime_utcfromtimestamp():
+ datetime.utcfromtimestamp(TIME)
+
+ def udatetime_utcfromtimestamp():
+ udatetime.utcfromtimestamp(TIME)
+
+ return (datetime_utcfromtimestamp, udatetime_utcfromtimestamp)
+
+if __name__ == '__main__':
+ import timeit
+
+ benchmarks = [
+ benchmark_parse,
+ benchmark_format,
+
+ benchmark_utcnow,
+ benchmark_now,
+
+ benchmark_utcnow_to_string,
+ benchmark_now_to_string,
+
+ benchmark_fromtimestamp,
+ benchmark_utcfromtimestamp,
+ ]
+
+ print('Executing benchmarks ...')
+
+ for k in benchmarks:
+ print('\n============ %s' % k.__name__)
+ mins = []
+
+ for func in k():
+ times =\
+ timeit.repeat('func()', setup='from __main__ import func')
+ t = min(times)
+ mins.append(t)
+
+ print(func.__name__, t)
+
+ win = False
+ if mins[0] > mins[1]:
+ win = True
+
+ mins = sorted(mins)
+ diff = mins[1] / mins[0]
+
+ if win:
+ print('udatetime is %.01f times faster' % diff)
+ else:
+ print('udatetime is %.01f times slower' % diff)
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..861a9f5
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build =
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..97ddf04
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,69 @@
+from setuptools import setup, find_packages, Extension
+import os
+import sys
+
+try:
+ import __pypy__
+except ImportError:
+ __pypy__ = None
+
+__version__ = None
+here = os.path.abspath(os.path.dirname(__file__))
+name = 'udatetime'
+
+with open('%s/requirements.txt' % here) as f:
+ requires = f.readlines()
+
+with open('%s/version.txt' % here) as f:
+ __version__ = f.readline().strip()
+
+with open('%s/README.md' % here) as f:
+ readme = f.readline().strip()
+
+macros = []
+
+if __pypy__ is not None:
+ macros.append(('_PYPY', '1'))
+elif sys.version_info.major == 2:
+ macros.append(('_PYTHON2', '1'))
+elif sys.version_info.major == 3:
+ macros.append(('_PYTHON3', '1'))
+
+rfc3339 = Extension(
+ 'rfc3339',
+ ['./src/rfc3339.c'],
+ define_macros=macros,
+ extra_compile_args=['-Ofast', '-std=c99']
+)
+
+setup(
+ name=name,
+ version=__version__,
+ description='Fast RFC3339 compliant date-time library',
+ long_description=readme,
+ license='Apache 2.0',
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Apache Software License',
+ 'Natural Language :: English',
+ 'Operating System :: POSIX',
+ 'Operating System :: MacOS',
+ 'Programming Language :: C',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: Implementation :: CPython',
+ 'Programming Language :: Python :: Implementation :: PyPy',
+ ],
+ author='Simon Pirschel',
+ author_email='simon at aboutsimon.com',
+ url='https://github.com/freach/udatetime',
+ packages=find_packages(),
+ include_package_data=True,
+ zip_safe=False,
+ install_requires=requires,
+ ext_modules=[rfc3339],
+ scripts=['scripts/bench_udatetime.py'],
+)
diff --git a/src/rfc3339.c b/src/rfc3339.c
new file mode 100644
index 0000000..722cc4b
--- /dev/null
+++ b/src/rfc3339.c
@@ -0,0 +1,1034 @@
+#define _GNU_SOURCE 1
+
+#if defined(_PYTHON2) || defined(_PYTHON3)
+#include <Python.h>
+#include <datetime.h>
+#include <structmember.h>
+#endif
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+
+#define RFC3339_VERSION "0.0.6"
+#define DAY_IN_SECS 86400
+#define HOUR_IN_SECS 3600
+#define MINUTE_IN_SECS 60
+#define HOUR_IN_MINS 60
+
+typedef struct {
+ unsigned int year;
+ unsigned int month;
+ unsigned int day;
+ unsigned int wday;
+ char ok;
+} date_struct;
+
+typedef struct {
+ unsigned int hour;
+ unsigned int minute;
+ unsigned int second;
+ unsigned int fraction;
+ int offset; // UTC offset in minutes
+ char ok;
+} time_struct;
+
+typedef struct {
+ date_struct date;
+ time_struct time;
+ char ok;
+} date_time_struct;
+
+static int local_utc_offset; // local's system offset to UTC, init later
+
+/*
+ * Remove space characters from source
+ */
+static void _strip_spaces(char *source) {
+ char *i = source;
+
+ while(*source != 0) {
+ *i = *source++;
+ if (*i != ' ') i++;
+ }
+
+ *i = 0;
+}
+
+/* Get the local time zone's offset to UTC
+ *
+ * Uses tm_gmtoff in tm struct, which requires a POSIX system.
+ * TODO: Cross platform compatibility
+ */
+static int _get_local_utc_offset(void) {
+ if (!local_utc_offset) {
+#ifdef HAVE_STRUCT_TM_TM_ZONE
+ struct tm info = {0};
+ time_t n = time(NULL);
+ localtime_r(&n, &info);
+
+ // tm_gmtoff requires POSIX
+ local_utc_offset = (int)info.tm_gmtoff / HOUR_IN_MINS;
+#else
+ local_utc_offset = 0;
+#endif
+ }
+
+ return local_utc_offset;
+}
+
+/* Get current time using gettimeofday(), ftime() or time() depending on
+ * support.
+ */
+static double _gettime(void) {
+#if defined(HAVE_GETTIMEOFDAY)
+ // => Use gettimeofday() in usec
+ struct timeval t;
+#if defined(GETTIMEOFDAY_NO_TZ)
+ if (gettimeofday(&t) == 0)
+ return ((double)t.tv_sec) + ((double)t.tv_usec * 0.000001);
+#else
+ struct timezone *tz = NULL;
+ if (gettimeofday(&t, tz) == 0)
+ return ((double)t.tv_sec) + ((double)t.tv_usec * 0.000001);
+#endif
+#elif defined(HAVE_FTIME)
+ // => Use ftime() in msec
+ struct timeb t;
+ ftime(&t);
+ return ((double)t.time) + ((double)t.millitm * 0.001);
+#else
+ // Fallback to time() in sec
+ time_t t;
+ time(&t);
+ return (double)t;
+#endif
+
+ // -Wreturn-type
+ return 0.0;
+}
+
+/*
+ * Parse a RFC3339 full-date
+ * full-date = date-fullyear "-" date-month "-" date-mday
+ * Ex. 2007-08-31
+ *
+ * Characters after date-mday are being ignored, so you can pass a
+ * date-time string and parse out only the full-date part.
+ */
+static void _parse_date(char *date_string, date_struct *d) {
+ // operate on string copy
+ char* const tokens = strdup(date_string);
+
+ // remove spaces
+ _strip_spaces(tokens);
+
+ // invalidate date_struct
+ (*d).ok = 0;
+
+ if (strlen(tokens) < 10)
+ return;
+
+ int status = sscanf(
+ tokens, "%04d-%02d-%02d", &((*d).year), &((*d).month), &((*d).day)
+ );
+
+ // Validate parsed tokens
+ if (status != 3) return;
+ if ((*d).year < 1 || (*d).year > 9999) return;
+ if ((*d).month < 1 || (*d).month > 12) return;
+ if ((*d).day < 1 || (*d).day > 31) return;
+
+ unsigned int leap = ((*d).year % 4 == 0) &&\
+ ((*d).year % 100 || ((*d).year % 400 == 0));
+
+ // Validate max day based on month
+ switch((*d).month) {
+ case 1:
+ if ((*d).day > 31)
+ return;
+ break;
+ case 2:
+ if (leap > 0) {
+ if ((*d).day > 29)
+ return;
+ } else {
+ if ((*d).day > 28)
+ return;
+ }
+ break;
+ case 3:
+ if ((*d).day > 31)
+ return;
+ break;
+ case 4:
+ if ((*d).day > 30)
+ return;
+ break;
+ case 5:
+ if ((*d).day > 31)
+ return;
+ break;
+ case 6:
+ if ((*d).day > 30)
+ return;
+ break;
+ case 7:
+ if ((*d).day > 31)
+ return;
+ break;
+ case 8:
+ if ((*d).day > 31)
+ return;
+ break;
+ case 9:
+ if ((*d).day > 30)
+ return;
+ break;
+ case 10:
+ if ((*d).day > 31)
+ return;
+ break;
+ case 11:
+ if ((*d).day > 30)
+ return;
+ break;
+ case 12:
+ if ((*d).day > 31)
+ return;
+ break;
+ }
+
+ (*d).ok = 1;
+}
+
+/*
+ * Parse a RFC3339 partial-time or full-time
+ * partial-time = time-hour ":" time-minute ":" time-second [time-secfrac]
+ * full-time = partial-time time-offset
+ * Ex. 16:47:31.123+00:00, 18:21:00.123, 18:21:00
+ *
+ * If time_string is partial-time timezone will be UTC.
+ * If time_string is date-time, full-date part will be ignored.
+ */
+static void _parse_time(char *time_string, time_struct *t) {
+ // operate on string copy
+ char *tokens = strdup(time_string);
+ char *token_ptr = tokens;
+
+ // remove spaces
+ _strip_spaces(tokens);
+
+ // invalidate time_struct
+ (*t).ok = 0;
+
+ // must be at least hh:mm:ss, no timezone implicates UTC
+ if (strlen(tokens) < 8)
+ goto cleanup;
+
+ // check if time_string is date-time string, for convenience reasons
+ if ((strlen(tokens) > 11) && ((*(tokens + 10 ) == 'T') || (*(tokens + 10 ) == 't'))) {
+ tokens += 11;
+ }
+
+ int status = sscanf(
+ tokens, "%02d:%02d:%02d", &((*t).hour), &((*t).minute), &((*t).second)
+ );
+
+ // Validate parsed tokens
+ if (status != 3) goto cleanup;
+ if ((*t).hour < 0 || (*t).hour > 23) goto cleanup;
+ if ((*t).minute < 0 || (*t).minute > 59) goto cleanup;
+ if ((*t).second < 0 || (*t).second > 59) goto cleanup;
+
+ // dealt with hh:mm:ss
+ if (strlen(tokens) == 8) {
+ (*t).offset = 0;
+ (*t).ok = 1;
+ goto cleanup;
+ } else {
+ tokens += 8;
+ }
+
+ // check for fractions
+ if (*tokens == '.') {
+ tokens++;
+ char fractions[6] = {0};
+
+ // Substring fractions, max 6 digits for usec
+ for (unsigned int i = 0; i < 6; i++) {
+ if ((*(tokens + i) >= 48) && (*(tokens + i) <= 57)) {
+ fractions[i] = *(tokens + i);
+ } else {
+ break;
+ }
+ }
+
+ // convert fractions to uint
+ status = sscanf(fractions, "%d", &((*t).fraction));
+
+ if (strlen(fractions) == 3) {
+ (*t).fraction = (*t).fraction * 1000; // convert msec to usec
+ } else if (strlen(fractions) == 6) {
+ // all fine, already in usec
+ } else {
+ goto cleanup; // Invalid fractions must be msec or usec
+ }
+
+ // validate
+ if (status != 1) goto cleanup;
+ if ((*t).fraction < 0 || (*t).fraction > 999999) goto cleanup;
+
+ tokens += strlen(fractions);
+
+ // no timezone provided
+ if (strlen(tokens) == 0) {
+ (*t).offset = 0;
+ (*t).ok = 1;
+ goto cleanup;
+ }
+ }
+
+ // parse timezone
+ if ((*tokens == 'Z') || (*tokens == 'z')) {
+ (*t).offset = 0;
+ (*t).ok = 1;
+ goto cleanup;
+ } else if ((*tokens == '+') || (*tokens == '-')) {
+ unsigned int tz_hour, tz_minute;
+
+ status = sscanf(tokens + 1, "%02d:%02d", &tz_hour, &tz_minute);
+
+ // validate
+ if (status != 2) goto cleanup;
+ if ((tz_hour < 0) || (tz_hour > 23)) goto cleanup;
+ if ((tz_minute < 0) || (tz_minute > 59)) goto cleanup;
+
+ // final offset
+ int tz_offset = (tz_hour * HOUR_IN_MINS) + tz_minute;
+
+ // make final offset negative
+ if (*tokens == '-') {
+ tz_offset = tz_offset * -1;
+ }
+
+ (*t).offset = tz_offset;
+ (*t).ok = 1;
+ }
+
+cleanup:
+ free(token_ptr);
+ tokens = NULL;
+ token_ptr = NULL;
+ return;
+}
+
+/*
+ * Parse a RFC3339 date-time
+ * date-time = full-date "T" full-time
+ * Ex. 2007-08-31T16:47:31+00:00 or 2007-12-24T18:21:00.123Z
+ *
+ * Using " " instead of "T" is NOT supported.
+ */
+static void _parse_date_time(char *datetime_string, date_time_struct *dt) {
+ _parse_date(datetime_string, &((*dt).date));
+ if ((*dt).date.ok == 0)
+ return;
+
+ _parse_time(datetime_string, &((*dt).time));
+ if ((*dt).time.ok == 0)
+ return;
+
+ (*dt).ok = 1;
+}
+
+/*
+ * Convert positive and negative timestamp double to date_time_struct
+ * based on gmtime
+ */
+static void _timestamp_to_date_time(double timestamp, date_time_struct *now,
+ int offset) {
+ timestamp += (offset * MINUTE_IN_SECS);
+
+ time_t t = (time_t)timestamp;
+ double fraction = (double)((timestamp - (int)timestamp) * 1000000);
+ int usec = fraction >= 0.0 ?\
+ (int)floor(fraction + 0.5) : (int)ceil(fraction - 0.5);
+
+ if (usec < 0) {
+ t -= 1;
+ usec += 1000000;
+ }
+
+ if (usec == 1000000) {
+ t += 1;
+ usec = 0;
+ }
+
+ struct tm *ts = NULL;
+ ts = gmtime(&t);
+
+ (*now).date.year = (*ts).tm_year + 1900;
+ (*now).date.month = (*ts).tm_mon + 1;
+ (*now).date.day = (*ts).tm_mday;
+ (*now).date.wday = (*ts).tm_wday + 1;
+ (*now).date.ok = 1;
+
+ (*now).time.hour = (*ts).tm_hour;
+ (*now).time.minute = (*ts).tm_min;
+ (*now).time.second = (*ts).tm_sec;
+ (*now).time.fraction = (int)usec; // sec fractions in microseconds
+ (*now).time.offset = offset;
+ (*now).time.ok = 1;
+
+ (*now).ok = 1;
+}
+
+/*
+ * Convert positive and negative timestamp double to date_time_struct
+ * based on localtime
+ */
+static void _local_timestamp_to_date_time(double timestamp, date_time_struct *now) {
+ time_t t = (time_t)timestamp;
+ double fraction = (double)((timestamp - (int)timestamp) * 1000000);
+ int usec = fraction >= 0.0 ?\
+ (int)floor(fraction + 0.5) : (int)ceil(fraction - 0.5);
+
+ if (usec < 0) {
+ t -= 1;
+ usec += 1000000;
+ }
+
+ if (usec == 1000000) {
+ t += 1;
+ usec = 0;
+ }
+
+ struct tm *ts = NULL;
+ ts = localtime(&t);
+
... 1149 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-udatetime.git
More information about the Python-modules-commits
mailing list