# KInterbasDB Python Package - Type Conv : DateTime/eGenix mx.DateTime
#
# Version 3.1
#
# The following contributors hold Copyright (C) over their respective
# portions of code (see license.txt for details):
#
# [Original Author (maintained through version 2.0-0.3.1):]
#   1998-2001 [alex]  Alexander Kuznetsov   <alexan@users.sourceforge.net>
# [Maintainers (after version 2.0-0.3.1):]
#   2001-2002 [maz]   Marek Isalski         <kinterbasdb@maz.nu>
#   2002-2004 [dsr]   David Rushby          <woodsplitter@rocketmail.com>
# [Contributors:]
#   2001      [eac]   Evgeny A. Cherkashin  <eugeneai@icc.ru>
#   2001-2002 [janez] Janez Jere            <janez.jere@void.si>

__all__ = (
    # kinterbasdb-native date and time converters:
    'date_conv_in', 'date_conv_out',
    'time_conv_in', 'time_conv_out',
    'timestamp_conv_in', 'timestamp_conv_out',

    # DB API 2.0 standard date and time type constructors:
    'Date', 'Time', 'Timestamp',
    'DateFromTicks', 'TimeFromTicks', 'TimestampFromTicks',
  )

import sys, types
from kinterbasdb.k_exceptions import *

if sys.version_info < (2,2):
    from kinterbasdb.typeconv_util_isinstance import isinstance

# This conversion module uses mx.DateTime for its date/time operations.
from mx import DateTime as mxDT

################################################################################
## DATE AND TIME
################################################################################

# kinterbasdb-native date and time converters:
def date_conv_in(mxDateTimeObj):
    # Allow implicit param conv:
    if mxDateTimeObj is None or isinstance(mxDateTimeObj, types.StringType):
        return mxDateTimeObj

    if not isinstance(mxDateTimeObj, mxDT.DateTimeType):
        raise InterfaceError(
            'Required type: %s ; supplied type: %s'
            % ( str(mxDT.DateTimeType), str(type(mxDateTimeObj)) )
          )
    return mxDateTimeObj.tuple()[:3]

def date_conv_out(dateTuple):
    if dateTuple is None:
        return None
    return mxDT.DateTime(*dateTuple)


def time_conv_in(mxDateTimeOrTimeDeltaObj):
    # Allow implicit param conv:
    if (    mxDateTimeOrTimeDeltaObj is None
         or isinstance(mxDateTimeOrTimeDeltaObj, types.StringType)
      ):
        return mxDateTimeOrTimeDeltaObj

    if isinstance(mxDateTimeOrTimeDeltaObj, mxDT.DateTimeType):
        timeTuple = mxDateTimeOrTimeDeltaObj.tuple()[3:6]
    elif isinstance(mxDateTimeOrTimeDeltaObj, mxDT.DateTimeDeltaType):
        timeTuple = mxDateTimeOrTimeDeltaObj.tuple()[1:]
    else:
        raise InterfaceError(
            'Cannot convert object of type %s to native kinterbasdb tuple.'
            % str(type(mxDateTimeOrTimeDeltaObj))
          )
    timeTuple = timeTuple[:2] + (int(timeTuple[2]),)
    return timeTuple

def time_conv_out(timeTuple):
    if timeTuple is None:
        return None
    mxDeltaTuple = (0,) + timeTuple
    return mxDT.DateTimeDelta(*mxDeltaTuple)


def timestamp_conv_in(mxDateTimeObj):
    # Allow implicit param conv:
    if mxDateTimeObj is None or isinstance(mxDateTimeObj, types.StringType):
        return mxDateTimeObj

    if not isinstance(mxDateTimeObj, mxDT.DateTimeType):
        raise InterfaceError(
            'Required type: %s ; supplied type: %s'
            % ( str(mxDT.DateTimeType), str(type(mxDateTimeObj)) )
          )

    timestampTuple = mxDateTimeObj.tuple()
    timestampTuple = timestampTuple[:5] + (int(timestampTuple[5]),)
    return timestampTuple

def timestamp_conv_out(timestampTuple):
    if timestampTuple is None:
        return None
    return mxDT.DateTime(*timestampTuple)


# DB API 2.0 standard date and time type constructors:
def Date(year, month, day):
    try:
        theDate = mxDT.DateTime(year, month, day)
    except mxDT.Error, e:
        raise DataError(str(e))

    return theDate


def Time(hour, minute, second):
    # mx DateTimeDeltas roll over when provided with an hour greater than
    # 23, a minute greater than 59, and so on.  That is not acceptable for our
    # purposes.
    if hour < 0 or hour > 23:
        raise DataError("hour must be between 0 and 23")
    if minute < 0 or minute > 59:
        raise DataError("minute must be between 0 and 59")
    if second < 0 or second > 59:
        raise DataError("second must be between 0 and 59")

    try:
        theTime = mxDT.TimeDelta(hour, minute, second)
    except mxDT.Error, e:
        raise DataError(str(e))

    return theTime


def Timestamp(year, month, day, hour, minute, second):
    args = (year, month, day, hour, minute, second) # Yes, I know about the
      # *args syntactical shortcut, but it's not particularly readable.

    # mx mxDT's Timestamp constructor accepts negative values in the
    # spirit of Python's negative list indexes, but I see no reason to allow
    # that behavior in this DB API-compliance-obsessed module.
    if 0 < len(filter(lambda x: x < 0, args)):
        raise DataError("Values less than zero not allowed in Timestamp."
            " (Received arguments %s)"
            % repr(args)
          )

    try:
        theStamp = mxDT.DateTime(*args)
    except mxDT.Error, e:
        raise DataError(str(e))

    return theStamp


DateFromTicks = mxDT.DateFromTicks
TimeFromTicks = mxDT.TimeFromTicks
TimestampFromTicks = mxDT.TimestampFromTicks
