/****************************************************************************
 *
 * Licensed Material - Property Of GBase
 *
 * "Restricted Materials of GBase"
 *
 * GBase Client SDK
 * Copyright GBASE  1997, 2008. All rights reserved.
 *
 *  Title:       datetime.h
 *  Description: DATETIME and INTERVAL types
 *
 ***************************************************************************
 */

#ifndef _DATETIME_H
#define _DATETIME_H

#include "ifxtypes.h"

#include "decimal.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct dtime
	{
	int2 dt_qual;
	dec_t dt_dec;
	}	dtime_t;

typedef struct intrvl
	{
	int2 in_qual;
	dec_t in_dec;
	}	intrvl_t;

/*
 * ** SQL (ISO/IEC 9075-2:2003) follows ISO/IEC 8601:2000 for time zones.
 * ** Positive time zones are east of UTC (Universal Time, Coordinated or
 * ** Coordinated Universal Time), and negative time zones are west of UTC.
 * ** By contrast, POSIX (IEEE 1003.1:2003, ISO/IEC 9945:2003) uses the
 * ** convention established by Unix long before ISO 8601 was first
 * ** released.  The TZ environment variable uses a positive time zone to
 * ** indicate a time zone west of UTC (eg TZ=EST5EDT).
 * **
 * ** UTC   + TZ  = Local
 * ** Local - TZ  = UTC
 * ** Local - UTC = TZ
 * */

/*
 * ** The disk format of TIME WITH TIME ZONE, TIMESTAMP WITH TIME ZONE
 * ** consists of the corresponding type WITHOUT TIME ZONE, followed by the
 * ** time zone.  (TIME(n) is equivalent to DATETIME HOUR TO SECOND(n),
 * ** which is stored in a DECIMAL(6+n,n).  TIMESTAMP(n) is equivalent to
 * ** DATETIME YEAR TO SECOND(n), which is stored in a DECIMAL(14+n,n).)
 * **
 * ** The disk format for a time zone uses 2 bytes for timezone offset:
 * ** Byte 0: 000svvvv - s is sign bit (0 positive, 1 negative), vvvv is
 * **                    hours part of offset (0..15, with 15 verboten).
 * ** Byte 1: 00mmmmmm - mmmmmm is minutes part of offset (0..63, with
 * **                    60..63 verboten).
 * **
 * ** The decimal value of a TIME WITH TIME ZONE or TIMESTAMP WITH TIME
 * ** ZONE is always normalized to UTC, both on disk and in memory.  Most
 * ** comparisons need not adjust the value and can ignore the time zone.
 * ** The code that formats or scans values with time zones adjusts the
 * ** formatted value so that local time plus time zone offset is entered
 * ** or displayed.
 * **
 * ** Memory format keeps the TZ in the same notation as on disk, but uses
 * ** the in-memory structure for the TIME or TIMESTAMP component, which in
 * ** turn uses the in-memory DECIMAL value.
 * */

typedef uint2 tzoffset_t;

typedef struct dtimetz_t
{
    dtime_t dt;
    tzoffset_t tz;
} dtimetz_t;


/* time units for datetime qualifier */

#define TU_YEAR 0
#define TU_MONTH 2
#define TU_DAY 4
#define TU_HOUR 6
#define TU_MINUTE 8
#define TU_SECOND 10
#define TU_FRAC 12
#define TU_F1 11
#define TU_F2 12
#define TU_F3 13
#define TU_F4 14
#define TU_F5 15

/* TU_END - end time unit in datetime qualifier */
/* TU_START - start time unit in datetime qualifier */
/* TU_LEN - length in  digits of datetime qualifier */

#define TU_END(qual) (qual & 0xf)
#define TU_START(qual) ((qual>>4) & 0xf)
#define TU_LEN(qual) ((qual>>8) & 0xff)

/* TU_ENCODE - encode length, start and end time unit to form qualifier */
/* TU_DTENCODE - encode datetime qual */
/* TU_IENCODE - encode interval qual */

#define TU_ENCODE(len,s,e) (((len)<<8) | ((s)<<4) | (e))
#define TU_DTENCODE(s,e) TU_ENCODE(((e)-(s)+((s)==TU_YEAR?4:2)), s, e)
#define TU_IENCODE(len,s,e) TU_ENCODE(((e)-(s)+(len)),s,e)
#define TU_FLEN(len) (TU_LEN(len)-(TU_END(len)-TU_START(len)))

/* TU_FIELDNO - field number of the given TU_ macro.
   (e.g. year is 0, month is 1, day is 2, TU_F1 is  7, TU_F5 is 11)
 */

#define TU_FIELDNO(f)   (f > TU_SECOND ? (5+(f-TU_SECOND)) : (f/2))

/* TU_CURRQUAL - default qualifier used by current */

#define TU_CURRQUAL TU_ENCODE(17,TU_YEAR,TU_F3)

/* -- TIME ZONE values and functions -- */

enum
{
    TZ_SIGNBIT = 0x2000,
    TZ_HOURFIELDMASK = 0x1F00,
    TZ_MINUTEFIELDMASK = 0x003F,
    TZ_HOURSHIFT = 8,
    TZ_POSSIGN = +1,
    TZ_NEGSIGN = -1
};

#define TZ_SIGNVAL(sign)    (((sign)==TZ_POSSIGN)?0:TZ_SIGNBIT)
#define TZ_HOURVAL(hh)      (((hh)<<TZ_HOURSHIFT)&TZ_HOURFIELDMASK)
#define TZ_MINUTEVAL(mm)    ((mm)&TZ_MINUTEFIELDMASK)

/* Create TIME ZONE literal - use TZ_POSSIGN or TZ_NEGSIGN for first component */
#define TZ_VALUE(sign,hh,mm)    (TZ_SIGNVAL(sign)|TZ_HOURVAL(hh)|TZ_MINUTEVAL(mm))

/* ISO/IEC 9075-2:2003 section 4.6.2 permits time zone offsets between -12:59 and +14:00 */
enum
{
    TZ_MINVALUE  = 0x2c3B,
    TZ_MAXVALUE  = 0xE00,
    TZ_MINHOUR   = -12,
    TZ_MAXHOUR   = +14,
    TZ_MAXMINUTE = +59
};

enum ifx_fmtopts
{
    DTIV_FMT_TZ_DropZeroMinutes = 0x0001, /* If timezone is +hh:00, omit :00 */
    DTIV_FMT_IV_BlankPad        = 0x0002, /* Blank pad leading section of interval to full width - sign floats */
    DTIV_FMT_IV_ZeroPad         = 0x0004, /* Zero pad leading section of interval with zeroes instead of blanks */
    DTIV_FMT_IV_PositiveSign    = 0x0008, /* Include '+' sign on non-negative intervals - inc zero */
    DTIV_FMT_FR_BlankPad        = 0x0010, /* Blank pad fraction to full width */
    DTIV_FMT_FR_DropZeroes      = 0x0020, /* Drop trailing zeroes from fraction */
    DTIV_FMT_NullFullWidth      = 0x0040, /* Format NULL values at full width (all blank) */
    DTIV_FMT_None               = 0x0000  /* None of the above */
};

typedef uint4 ifx_fmtopts_t;            /* Bit mask of values from ifx_fmtopts enumeration */


/* For any valid tz value, tz == TZ_VALUE(TZ_GETSIGN(tz),TZ_GETHOUR(tz),TZ_GETMINUTE(tz)) */
#define TZ_GETSIGN(tz)   (((tz) & TZ_SIGNBIT) ? TZ_NEGSIGN : TZ_POSSIGN)
#define TZ_GETHOUR(tz)   (((tz) & TZ_HOURFIELDMASK) >> TZ_HOURSHIFT)
#define TZ_GETMINUTE(tz) ((tz) & TZ_MINUTEFIELDMASK)

/* Equivalent to ifx_tz_tominutes() without any error checking */
#define TZ_TOMINUTES(tz)    (TZ_GETSIGN(tz)*(TZ_GETHOUR(tz)*60+TZ_GETMINUTE(tz)))
#define TZ_NEGATE(tz)       ((tz)^TZ_SIGNBIT)

/* tz_setvalue() returns UTC == 0x0000 if data is invalid */

#define DECTZLENGTH(len)	(DECLEN(PRECTOT(len),PRECDEC(len)) + sizeof(tzoffset_t))


/* -- End of TIME ZONE values and functions -- */


mint __cdecl dtaddinv(struct dtime *d, struct intrvl *i, struct dtime *r);
void __cdecl dtcurrent(struct dtime *d);
mint __cdecl dtcvasc(char *str, struct dtime *d);
mint __cdecl dttzcvasc(char *str, struct dtimetz_t *d);
mint __cdecl ifx_dtcvasc(char *str,struct dtime *d, char db_century);
mint __cdecl dtcvfmtasc(char *input, char *fmt, struct dtime *d);
mint __cdecl ifx_dtcvfmtasc(char *input,char *fmt,struct dtime *d, char db_century);
mint __cdecl dtextend(struct dtime *id, struct dtime *od);
mint __cdecl dtsub(struct dtime *d1, struct dtime *d2, struct intrvl *i);
mint __cdecl dtsubinv(struct dtime *d, struct intrvl *i, struct dtime *r);
mint __cdecl dttoasc(struct dtime *d, char *str);
mint __cdecl dttztoasc(struct dtimetz_t *d, char *str);
mint __cdecl dttofmtasc(struct dtime *d, char *output, mint str_len, char *fmtstr);
mint __cdecl ifx_dttofmtasc(struct dtime *d,char *output,mint str_len,char *fmtstr, char db_century);
mint __cdecl incvasc(char *str, struct intrvl *i);
mint __cdecl incvfmtasc(char *input, char *fmt, struct intrvl *intvl);
mint __cdecl intoasc(struct intrvl *i, char *str);
mint __cdecl intofmtasc(struct intrvl *i, char *output, mint str_len, char *fmtstr);
mint __cdecl invdivdbl(struct intrvl *iv, double dbl, struct intrvl *ov);
mint __cdecl invdivinv(struct intrvl *i1, struct intrvl *i2, double *res);
mint __cdecl invextend(struct intrvl *i, struct intrvl *o);
mint __cdecl invmuldbl(struct intrvl *iv, double dbl, struct intrvl *ov);
mint __cdecl ifx_to_gl_datetime(char *source_str, char *target_str, mint maxlen);

#ifdef __cplusplus
}
#endif

/* GL_DATETIME length */
#define GLDT_LEN        80

#endif /* _DATETIME_H */
