module firebird_date; private import ibase; private import std.c.time; private import std.string; const const_Dec31_1899 = 693595; const const_min16 = short.min; const const_max16 = short.max; const const_min32 = int.min; const const_max32 = int.max; const MinDate = -693594; // 1 JAN 0001 const MaxDate = 2958464; // 31 DEC 9999 /* Class Time represent purely a Time. It is usefull in interactions with the SQL TIME type of Interbase. */ class Time { protected: int mTime = 0; // The time, in ten-thousandths of seconds since midnight public: void encodeTime(ISC_TIME *isc_tm, Time *tm) { *isc_tm = cast(ISC_TIME) tm.GetTime(); } void decodeTime(Time *tm, ISC_TIME *isc_tm) { tm.SetTime(cast(int)isc_tm); } void Clear() { mTime = 0; } void ClearTime(){ this.Clear(); } void Now() { time_t systime = time(null); tm* loctime = localtime(&systime); itot(mTime, loctime.tm_hour, loctime.tm_min, loctime.tm_sec, 0); } void SetTime(int hour, int minute, int second, int tenthousandths = 0) { if(hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59 || tenthousandths < 0 || tenthousandths > 9999) throw new Exception("Time::SetTime Invalid hour, minute, second values"); itot(mTime, hour, minute, second, tenthousandths); } void SetTime(int tm) { if(tm < 0 || tm > 863999999) throw new Exception("Time::SetTime Invalid time value"); mTime = tm; } void GetTime(ref int hour, ref int minute, ref int second) { int d1=0; ttoi(mTime, hour, minute, second, d1); } void GetTime(ref int hour, ref int minute, ref int second, ref int tenthousandths) { ttoi(mTime, hour, minute, second, tenthousandths); } int GetTime() { return mTime; } int Hours() { int hours; int d1=0,d2=0,d3=0; ttoi(mTime, hours, d1, d2, d3); return hours; } int Minutes() { int minutes; int d1=0,d2=0,d3=0; ttoi(mTime, d1, minutes, d2, d3); return minutes; } int Seconds() { int seconds; int d1=0,d2=0,d3=0; ttoi(mTime, d1, d2, seconds, d3); return seconds; } int SubSeconds() { // Actually tenthousandths of seconds int tenthousandths; int d1=0,d2=0,d3=0; ttoi(mTime, d1, d2, d3, tenthousandths); return tenthousandths; } override string toString() { int h,mi,s,t; GetTime(h, mi, s, t); return zfill(std.string.toString(h),2)~":"~ zfill(std.string.toString(mi),2)~":" ~ zfill(std.string.toString(s),2)~"."~ zfill(std.string.toString(t),2); } this() { Clear(); } this(int tm) { SetTime(tm); } this(int hour, int minute, int second, int tenthousandths = 0) { SetTime(hour, minute, second, tenthousandths); } this(ref Time copied) { mTime = copied.mTime; } /* Time opAssign(ref Timestamp assigned) { mTime = assigned.GetTime(); return this; } */ Time opAssign(Time *assigned) { mTime = assigned.mTime; return this; } bool opEquals(Time *rv) { return mTime == rv.GetTime(); } // bool opEquals(ref Time rv) { return mTime != rv.GetTime(); } bool opCmp(Time *rv) { if(mTime < rv.GetTime()) return true; if(mTime > rv.GetTime()) return true; return false; } // bool opCmp(ref Time rv) { return mTime > rv.GetTime(); } void ttoi(int itime, ref int h, ref int m, ref int s, ref int t) { int hh = cast(int)(itime / 36000000); itime = itime - hh * 36000000; int mm = cast(int)(itime / 600000); itime = itime - mm * 600000; int ss = cast(int)(itime / 10000); int tt = cast(int)(itime - ss * 10000); h = hh; m = mm; s = ss; t = tt; return; } // Get the internal time format, given hour, minute, second. void itot(ref int ptime, int hour, int minute, int second, int tenthousandths) { ptime = hour * 36000000 + minute * 600000 + second * 10000 + tenthousandths; } ~this() { }; } class Date { protected: int mDate = 0; // The date : 1 == 1 Jan 1900 public: void encodeDate(ISC_DATE *isc_dt, Date *dt) { // There simply has a shift of 15019 between the native Firebird date model and the IBPP model. *isc_dt = cast(ISC_DATE)(dt.GetDate() + 15019); } void decodeDate(Date *dt, ISC_DATE *isc_dt) { // There simply has a shift of 15019 between the native Firebird date model and the IBPP model. dt.SetDate(cast(int) *isc_dt - 15019); } void Clear() { mDate = MinDate - 1; } void ClearDate() { this.Clear(); } void Today() { time_t systime = time(null); tm* loctime = localtime(&systime); if(!itod(mDate, loctime.tm_year + 1900, loctime.tm_mon + 1, loctime.tm_mday)) throw new Exception("Date::Today Out of range"); } void SetDate(int year, int month, int day) { if(! itod(mDate, year, month, day)) throw new Exception("Date::SetDate Out of range"); } void SetDate(int dt) { int d1=0,d2=0,d3=0; if(!dtoi(dt, d1, d2, d3)) throw new Exception("Date::SetDate Out of range"); mDate = dt; } void GetDate(ref int year, ref int month, ref int day) { if(!dtoi(mDate, year, month, day)) throw new Exception("Date::GetDate Out of range"); } int GetDate() { return mDate; } int Year() { int year; int d1=0,d2=0; if(! dtoi(mDate, year, d1, d2)) throw new Exception("Date::Year Out of range"); return year; } int Month() { int month; int d1=0,d2=0; if(!dtoi(mDate, d1, month, d2)) throw new Exception("Date::Month Out of range"); return month; } int Day() { int day; int d1=0,d2=0; if(! dtoi(mDate, d1, d2, day)) throw new Exception("Date::Day Out of range"); return day; } void Add(int days) { int d1=0,d2=0,d3=0; int newdate = mDate + days; // days can be signed if(! dtoi(newdate, d1, d2, d3)) throw new Exception("Date::Add() Out of range"); mDate = newdate; } void StartOfMonth() { int year, month; int d1=0; if(! dtoi(mDate, year, month, d1)) throw new Exception("Date::StartOfMonth() Out of range"); if(! itod(mDate, year, month, 1)) // First of same month throw new Exception("Date::StartOfMonth() Out of range"); } void EndOfMonth() { int year, month; int d1=0; if(! dtoi(mDate, year, month, d1)) throw new Exception("Date::EndOfMonth() Out of range"); if(++month > 12) { month = 1; year++; } if(!itod(mDate, year, month, 1)) // First of next month throw new Exception("Date::EndOfMonth() Out of range"); mDate--; // Last day of original month, all weird cases accounted for } override string toString() { int y,mo,d; GetDate(y, mo, d); return zfill(std.string.toString(d),2)~"."~ zfill(std.string.toString(mo),2)~"."~ std.string.toString(y); } this() { Clear(); }; this(int dt){ SetDate(dt); } this(int year, int month, int day) { SetDate(year, month, day); } this(ref Date copied) { mDate = copied.mDate; } //Date& operator=(const Timestamp&); // Timestamp Assignment operator //Date& operator=(const Date&); // Date Assignment operator /* Date opAssign(ref Timestamp assigned) { mDate = assigned.date.GetDate(); return this; } */ Date opAssign(Date *assigned) { mDate = assigned.mDate; return this; } bool opEquals(Date *rv) { return mDate == rv.GetDate(); } // bool opEquals(Date rv) { return mDate != rv.GetDate(); } bool opCmp(Date *rv) { if(mDate < rv.GetDate()) return true; if(mDate > rv.GetDate()) return true; return false; } // bool opCmp(Date *rv) { return mDate > rv.GetDate(); } // The following date calculations were inspired by web pages found on // Peter Baum web homepage at 'http://www.capecod.net/~pbaum/'. // His contact info is at : 'http://home.capecod.net/~pbaum/contact.htm'. // Please, understand that Peter Baum is not related to this IBPP project. // So __please__, do not contact him regarding IBPP matters. // Take a date, in its integer format as used in IBPP internals and splits it in year(4 digits), month(1-12), day(1-31) bool dtoi (int date, ref int y, ref int m, ref int d) { // Validity control. if (date < MinDate || date > MaxDate) return false; // The "Rata Die" is the date specified as the number of days elapsed since // 31 Dec of year 0. So 1 Jan 0001 is 1. int RataDie = date + const_Dec31_1899; // Because IBPP sets the '0' on 31 Dec 1899. int Z = RataDie + 306; int H = 100*Z - 25; int A = H/3652425; int B = A - A/4; int year = (100*B + H) / 36525; int C = B + Z - 365*year - year / 4; int month = (5*C + 456) / 153; int day = C - (153*month - 457) / 5; if(month > 12) { year += 1; month -= 12; } y = cast(int)year; m = cast(int)month; d = cast(int)day; return true; } // Take a date from its components year, month, day and convert it to the integer representation used internally in IBPP. bool itod (ref int pdate, int year, int month, int day) { int d = day; int m = month; int y = year; if (m < 3) { m += 12; y -= 1; } int RataDie = d + (153*m - 457) / 5 + 365*y + y/4 - y/100 + y/400 - 306; int result = RataDie - const_Dec31_1899; // Because IBPP sets the '0' on 31 Dec 1899 if(result < MinDate || result > MaxDate) // Validity control return false; pdate = result; return true; } ~this() { } } /* Class Timestamp represent a date AND a time. It is usefull in * interactions with the SQL TIMESTAMP type of Interbase. This class * inherits from Date and Time and completely inline implements its small * specific details. */ class Timestamp { private: int mDate; int mTime; public: Date dat; Time tim; this() { dat = new Date(); tim = new Time(); Clear(); } this(int y, int m, int d) { dat = new Date(); tim = new Time(); dat.ClearDate(); dat.SetDate(y, m, d); } this(int y, int mo, int d, int h, int mi, int s, int t = 0) { dat = new Date(); tim = new Time(); dat.SetDate(y, mo, d); tim.SetTime(h, mi, s, t); } this(Timestamp *rv) { dat = new Date(); tim = new Time(); mDate = rv.mDate; mTime = rv.mTime; } // Copy Constructor this(Date *rv) { dat = new Date(); tim = new Time(); mDate = rv.GetDate(); mTime = 0; } this(Time *rv) { dat = new Date(); tim = new Time(); mDate = 0; mTime = rv.GetTime(); } void Clear() { dat.ClearDate(); tim.ClearTime(); } Timestamp opAssign(Timestamp *rv) {// Timestamp Assignment operator mDate = rv.mDate; mTime = rv.mTime; return this; } Timestamp opAssign(Date *rv) { // Date Assignment operator mDate = rv.GetDate(); return this; } Timestamp opAssign(Time *rv) { // Time Assignment operator mTime = rv.GetTime(); return this; } bool Now(ref int y, ref int mo, ref int d, ref int h, ref int mi, ref int s, ref int t = 0) { if(dat is null && tim is null) return false; dat.GetDate(y, mo, d); tim.GetTime(h, mi, s, t); return true; } override string toString() { int y,mo,d,h,mi,s,t; dat.GetDate(y, mo, d); tim.GetTime(h, mi, s, t); return zfill(std.string.toString(d),2)~"."~ zfill(std.string.toString(mo),2)~"."~ std.string.toString(y) ~" "~ zfill(std.string.toString(h),2)~":"~ zfill(std.string.toString(mi),2)~":" ~ zfill(std.string.toString(s),2)~"."~ zfill(std.string.toString(t),2); } bool SetStamp(ref int y, ref int mo, ref int d, ref int h, ref int mi, ref int s, ref int t = 0) { if(dat is null && tim is null) return false; dat.SetDate(y, mo, d); tim.SetTime(h, mi, s, t); return true; } Date GetDate() {return dat;} Time GetTime() {return tim;} void SetDate(int year, int month, int day) { dat.SetDate(year, month, day); } void SetDate(int dt) { dat.SetDate(dt); } void SetTime(int hour, int minute, int second, int tenthousandths = 0) { tim.SetTime(hour, minute, second, tenthousandths); } void SetTime(int tm) { tim.SetTime(tm); } bool opEquals(Timestamp *rv) { if((mDate == rv.dat.GetDate()) && (mTime == rv.tim.GetTime())) return true; else if((mDate != rv.dat.GetDate()) || (mTime != rv.tim.GetTime())) return false; return false; } /** bool opEquals(ref Timestamp rv) { * return(mDate != rv.dat.GetDate()) || (mTime != rv.tim.GetTime()); * } */ bool opCmp(Timestamp *rv) { if((mDate < rv.dat.GetDate()) || (mDate == rv.dat.GetDate() && mTime < rv.tim.GetTime())) return true; else if((mDate > rv.dat.GetDate()) || (mDate == rv.dat.GetDate() && mTime > rv.tim.GetTime())) return true; return false; } /** bool opCmp(Timestamp rv) { * return(mDate > rv.dat.GetDate()) || (mDate == rv.dat.GetDate() && mTime > rv.tim.GetTime()); * } */ void encodeTimestamp(ISC_TIMESTAMP *isc_ts, Timestamp *ts) { //encodeDate(isc_ts.timestamp_date, ts); isc_ts.timestamp_date = cast(ISC_DATE)(ts.dat.GetDate() + 15019); //encodeTime(isc_ts.timestamp_time, ts); isc_ts.timestamp_time = cast(ISC_TIME)ts.tim.GetTime(); } void decodeTimestamp(Timestamp *ts, ISC_TIMESTAMP *isc_ts) { //decodeDate(ts, isc_ts.timestamp_date); ts.dat.SetDate(cast(int)(isc_ts.timestamp_date - 15019)); //decodeTime(ts, isc_ts.timestamp_time); ts.tim.SetTime(cast(int)isc_ts.timestamp_time); } ~this() { delete dat; delete tim; } }