# -*- coding: utf-8 -*-
"""Date and time precision helpers."""
import decimal
from dfdatetime import definitions
[docs]
class DateTimePrecisionHelper(object):
"""Date time precision helper interface.
This is the super class of different date and time precision helpers.
Time precision helpers provide functionality for converting date and time
values between different precisions.
"""
# pylint: disable=missing-raises-doc,redundant-returns-doc
[docs]
@classmethod
def CopyNanosecondsToFractionOfSecond(cls, nanoseconds):
"""Copies the number of nanoseconds to a fraction of second value.
Args:
nanoseconds (int): number of nanoseconds.
Returns:
decimal.Decimal: fraction of second, which must be a value between 0.0 and
1.0.
"""
raise NotImplementedError()
[docs]
@classmethod
def CopyToDateTimeString(cls, time_elements_tuple, fraction_of_second):
"""Copies the time elements and fraction of second to a string.
Args:
time_elements_tuple (tuple[int, int, int, int, int, int]):
time elements, contains year, month, day of month, hours, minutes and
seconds.
fraction_of_second (decimal.Decimal): fraction of second, which must be a
value between 0.0 and 1.0.
Returns:
str: date and time value formatted as: YYYY-MM-DD hh:mm:ss with fraction
of second part that corresponds to the precision.
"""
raise NotImplementedError()
[docs]
class SecondsPrecisionHelper(DateTimePrecisionHelper):
"""Seconds precision helper."""
[docs]
@classmethod
def CopyNanosecondsToFractionOfSecond(cls, nanoseconds):
"""Copies the number of nanoseconds to a fraction of second value.
Args:
nanoseconds (int): number of nanoseconds.
Returns:
decimal.Decimal: fraction of second, which must be a value between 0.0 and
1.0. For the seconds precision helper this will always be 0.0.
Raises:
ValueError: if the number of nanoseconds is out of bounds.
"""
if nanoseconds < 0 or nanoseconds >= definitions.NANOSECONDS_PER_SECOND:
raise ValueError(
f'Number of nanoseconds value: {nanoseconds:d} out of bounds.')
return decimal.Decimal(0.0)
[docs]
@classmethod
def CopyToDateTimeString(cls, time_elements_tuple, fraction_of_second):
"""Copies the time elements and fraction of second to a string.
Args:
time_elements_tuple (tuple[int, int, int, int, int, int]):
time elements, contains year, month, day of month, hours, minutes and
seconds.
fraction_of_second (decimal.Decimal): fraction of second, which must be a
value between 0.0 and 1.0.
Returns:
str: date and time value formatted as:
YYYY-MM-DD hh:mm:ss
Raises:
ValueError: if the fraction of second is out of bounds.
"""
if fraction_of_second < 0.0 or fraction_of_second >= 1.0:
raise ValueError(
f'Fraction of second value: {fraction_of_second:f} out of bounds.')
year, month, day_of_month, hours, minutes, seconds = time_elements_tuple
return (f'{year:04d}-{month:02d}-{day_of_month:02d} '
f'{hours:02d}:{minutes:02d}:{seconds:02d}')
[docs]
class CentisecondsPrecisionHelper(DateTimePrecisionHelper):
"""Centiseconds (10 ms) precision helper."""
[docs]
@classmethod
def CopyNanosecondsToFractionOfSecond(cls, nanoseconds):
"""Copies the number of nanoseconds to a fraction of second value.
Args:
nanoseconds (int): number of nanoseconds.
Returns:
decimal.Decimal: fraction of second, which must be a value between 0.0 and
1.0.
Raises:
ValueError: if the number of nanoseconds is out of bounds.
"""
if nanoseconds < 0 or nanoseconds >= definitions.NANOSECONDS_PER_SECOND:
raise ValueError(
f'Number of nanoseconds value: {nanoseconds:d} out of bounds.')
centiseconds, _ = divmod(
nanoseconds, definitions.NANOSECONDS_PER_CENTISECOND)
return decimal.Decimal(centiseconds) / definitions.CENTISECONDS_PER_SECOND
[docs]
@classmethod
def CopyToDateTimeString(cls, time_elements_tuple, fraction_of_second):
"""Copies the time elements and fraction of second to a string.
Args:
time_elements_tuple (tuple[int, int, int, int, int, int]):
time elements, contains year, month, day of month, hours, minutes and
seconds.
fraction_of_second (decimal.Decimal): fraction of second, which must be a
value between 0.0 and 1.0.
Returns:
str: date and time value formatted as:
YYYY-MM-DD hh:mm:ss.##
Raises:
ValueError: if the fraction of second is out of bounds.
"""
if fraction_of_second < 0.0 or fraction_of_second >= 1.0:
raise ValueError(
f'Fraction of second value: {fraction_of_second:f} out of bounds.')
year, month, day_of_month, hours, minutes, seconds = time_elements_tuple
centiseconds = int(fraction_of_second * definitions.CENTISECONDS_PER_SECOND)
return (f'{year:04d}-{month:02d}-{day_of_month:02d} '
f'{hours:02d}:{minutes:02d}:{seconds:02d}.{centiseconds:02d}')
[docs]
class MillisecondsPrecisionHelper(DateTimePrecisionHelper):
"""Milliseconds precision helper."""
[docs]
@classmethod
def CopyNanosecondsToFractionOfSecond(cls, nanoseconds):
"""Copies the number of nanoseconds to a fraction of second value.
Args:
nanoseconds (int): number of nanoseconds.
Returns:
decimal.Decimal: fraction of second, which must be a value between 0.0 and
1.0.
Raises:
ValueError: if the number of nanoseconds is out of bounds.
"""
if nanoseconds < 0 or nanoseconds >= definitions.NANOSECONDS_PER_SECOND:
raise ValueError(
f'Number of nanoseconds value: {nanoseconds:d} out of bounds.')
milliseconds, _ = divmod(
nanoseconds, definitions.NANOSECONDS_PER_MILLISECOND)
return decimal.Decimal(milliseconds) / definitions.MILLISECONDS_PER_SECOND
[docs]
@classmethod
def CopyToDateTimeString(cls, time_elements_tuple, fraction_of_second):
"""Copies the time elements and fraction of second to a string.
Args:
time_elements_tuple (tuple[int, int, int, int, int, int]):
time elements, contains year, month, day of month, hours, minutes and
seconds.
fraction_of_second (decimal.Decimal): fraction of second, which must be a
value between 0.0 and 1.0.
Returns:
str: date and time value formatted as:
YYYY-MM-DD hh:mm:ss.###
Raises:
ValueError: if the fraction of second is out of bounds.
"""
if fraction_of_second < 0.0 or fraction_of_second >= 1.0:
raise ValueError(
f'Fraction of second value: {fraction_of_second:f} out of bounds.')
year, month, day_of_month, hours, minutes, seconds = time_elements_tuple
milliseconds = int(fraction_of_second * definitions.MILLISECONDS_PER_SECOND)
return (f'{year:04d}-{month:02d}-{day_of_month:02d} '
f'{hours:02d}:{minutes:02d}:{seconds:02d}.{milliseconds:03d}')
[docs]
class DecimillisecondsPrecisionHelper(DateTimePrecisionHelper):
"""Decimilliseconds (100 microseconds) precision helper."""
[docs]
@classmethod
def CopyNanosecondsToFractionOfSecond(cls, nanoseconds):
"""Copies the number of nanoseconds to a fraction of second value.
Args:
nanoseconds (int): number of nanoseconds.
Returns:
decimal.Decimal: fraction of second, which must be a value between 0.0
and 1.0.
Raises:
ValueError: if the number of nanoseconds is out of bounds.
"""
if nanoseconds < 0 or nanoseconds >= definitions.NANOSECONDS_PER_SECOND:
raise ValueError(
f'Number of nanoseconds value: {nanoseconds:d} out of bounds.')
decimiliseconds, _ = divmod(
nanoseconds, definitions.NANOSECONDS_PER_DECIMILISECOND)
return (
decimal.Decimal(decimiliseconds) /
definitions.DECIMICROSECONDS_PER_SECOND)
[docs]
@classmethod
def CopyToDateTimeString(cls, time_elements_tuple, fraction_of_second):
"""Copies the time elements and fraction of second to a string.
Args:
time_elements_tuple (tuple[int, int, int, int, int, int]):
time elements, contains year, month, day of month, hours, minutes and
seconds.
fraction_of_second (decimal.Decimal): fraction of second, which must be a
value between 0.0 and 1.0.
Returns:
str: date and time value formatted as:
YYYY-MM-DD hh:mm:ss.####
Raises:
ValueError: if the fraction of second is out of bounds.
"""
if fraction_of_second < 0.0 or fraction_of_second >= 1.0:
raise ValueError(
f'Fraction of second value: {fraction_of_second:f} out of bounds.')
year, month, day_of_month, hours, minutes, seconds = time_elements_tuple
decimicroseconds = int(
fraction_of_second * definitions.DECIMICROSECONDS_PER_SECOND)
return (f'{year:04d}-{month:02d}-{day_of_month:02d} '
f'{hours:02d}:{minutes:02d}:{seconds:02d}.{decimicroseconds:04d}')
[docs]
class MicrosecondsPrecisionHelper(DateTimePrecisionHelper):
"""Microseconds precision helper."""
[docs]
@classmethod
def CopyNanosecondsToFractionOfSecond(cls, nanoseconds):
"""Copies the number of nanoseconds to a fraction of second value.
Args:
nanoseconds (int): number of nanoseconds.
Returns:
decimal.Decimal: fraction of second, which must be a value between 0.0 and
1.0.
Raises:
ValueError: if the number of nanoseconds is out of bounds.
"""
if nanoseconds < 0 or nanoseconds >= definitions.NANOSECONDS_PER_SECOND:
raise ValueError(
f'Number of nanoseconds value: {nanoseconds:d} out of bounds.')
microseconds, _ = divmod(
nanoseconds, definitions.NANOSECONDS_PER_MICROSECOND)
return decimal.Decimal(microseconds) / definitions.MICROSECONDS_PER_SECOND
[docs]
@classmethod
def CopyToDateTimeString(cls, time_elements_tuple, fraction_of_second):
"""Copies the time elements and fraction of second to a string.
Args:
time_elements_tuple (tuple[int, int, int, int, int, int]):
time elements, contains year, month, day of month, hours, minutes and
seconds.
fraction_of_second (decimal.Decimal): fraction of second, which must be a
value between 0.0 and 1.0.
Returns:
str: date and time value formatted as:
YYYY-MM-DD hh:mm:ss.######
Raises:
ValueError: if the fraction of second is out of bounds.
"""
if fraction_of_second < 0.0 or fraction_of_second >= 1.0:
raise ValueError(
f'Fraction of second value: {fraction_of_second:f} out of bounds.')
year, month, day_of_month, hours, minutes, seconds = time_elements_tuple
microseconds = int(fraction_of_second * definitions.MICROSECONDS_PER_SECOND)
return (f'{year:04d}-{month:02d}-{day_of_month:02d} '
f'{hours:02d}:{minutes:02d}:{seconds:02d}.{microseconds:06d}')
[docs]
class NanosecondsPrecisionHelper(DateTimePrecisionHelper):
"""Nanoseconds precision helper."""
[docs]
@classmethod
def CopyNanosecondsToFractionOfSecond(cls, nanoseconds):
"""Copies the number of nanoseconds to a fraction of second value.
Args:
nanoseconds (int): number of nanoseconds.
Returns:
decimal.Decimal: fraction of second, which must be a value between 0.0 and
1.0.
Raises:
ValueError: if the number of nanoseconds is out of bounds.
"""
if nanoseconds < 0 or nanoseconds >= definitions.NANOSECONDS_PER_SECOND:
raise ValueError(
f'Number of nanoseconds value: {nanoseconds:d} out of bounds.')
return decimal.Decimal(nanoseconds) / definitions.NANOSECONDS_PER_SECOND
[docs]
@classmethod
def CopyToDateTimeString(cls, time_elements_tuple, fraction_of_second):
"""Copies the time elements and fraction of second to a string.
Args:
time_elements_tuple (tuple[int, int, int, int, int, int]):
time elements, contains year, month, day of month, hours, minutes and
seconds.
fraction_of_second (decimal.Decimal): fraction of second, which must be a
value between 0.0 and 1.0.
Returns:
str: date and time value formatted as:
YYYY-MM-DD hh:mm:ss.######
Raises:
ValueError: if the fraction of second is out of bounds.
"""
if fraction_of_second < 0.0 or fraction_of_second >= 1.0:
raise ValueError(
f'Fraction of second value: {fraction_of_second:f} out of bounds.')
year, month, day_of_month, hours, minutes, seconds = time_elements_tuple
nanoseconds = int(fraction_of_second * definitions.NANOSECONDS_PER_SECOND)
return (f'{year:04d}-{month:02d}-{day_of_month:02d} '
f'{hours:02d}:{minutes:02d}:{seconds:02d}.{nanoseconds:09d}')
[docs]
class PrecisionHelperFactory(object):
"""Date time precision helper factory."""
_PRECISION_CLASSES = {
definitions.PRECISION_10_MILLISECONDS: CentisecondsPrecisionHelper,
definitions.PRECISION_100_MICROSECONDS: DecimillisecondsPrecisionHelper,
definitions.PRECISION_1_MICROSECOND: MicrosecondsPrecisionHelper,
definitions.PRECISION_1_MILLISECOND: MillisecondsPrecisionHelper,
definitions.PRECISION_1_NANOSECOND: NanosecondsPrecisionHelper,
definitions.PRECISION_1_SECOND: SecondsPrecisionHelper}
[docs]
@classmethod
def CreatePrecisionHelper(cls, precision):
"""Creates a precision helper.
Args:
precision (str): precision of the date and time value, which should
be one of the PRECISION_VALUES in definitions.
Returns:
class: date time precision helper class.
Raises:
ValueError: if the precision value is unsupported.
"""
precision_helper_class = cls._PRECISION_CLASSES.get(precision, None)
if not precision_helper_class:
raise ValueError(f'Unsupported precision: {precision!s}')
return precision_helper_class