// Copyright (C) 2004-2005 MySQL AB
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as published by
// the Free Software Foundation
//
// There are special exceptions to the terms and conditions of the GPL
// as it is applied to this software. View the full text of the
// exception in file EXCEPTIONS in the directory of this software
// distribution.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.Data;
using System.IO;
using MySql.Data.MySqlClient;
namespace MySql.Data.Types
{
///
/// Summary description for MySqlDateTime.
///
public class MySqlDateTime : MySqlValue, IConvertible, IComparable
{
private int year, month, day, hour, minute, second;
private static string fullPattern;
private static string shortPattern;
internal MySqlDateTime( int year, int month, int day, int hour, int minute,
int second, MySqlDbType type ) : this(type)
{
this.year = year;
this.month = month;
this.day = day;
this.hour = hour;
this.minute = minute;
this.second = second;
// we construct a date that is guaranteed not have zeros in the date part
// we do this for comparison
// DateTime d = DateTime.MinValue;
// d = d.AddYears(year+1).AddMonths(month+1).AddDays(day+1).AddHours(hour);
// d = d.AddMinutes(minute).AddSeconds(second);
// comparingDate = d;
if (fullPattern == null)
ComposePatterns();
}
internal MySqlDateTime( MySqlDbType type )
{
mySqlDbType = type;
objectValue = this;
}
///
/// Instantiate a new value and sets it to 0001-01-01 00:00:00
///
/// Sets the value to 0001-01-01 00:00:00
public MySqlDateTime()
{
// Set the value to the lowest value that a DateTime value can have
year = 1;
month = 1;
day = 1;
}
///
/// Instantiate a new value and sets it to the value of argument supplied
///
/// DateTime value to set as defautlt value for this object
public MySqlDateTime(DateTime val)
{
year = val.Year;
month = val.Month;
day = val.Day;
hour = val.Hour;
minute = val.Minute;
second = val.Second;
}
internal MySqlDateTime(DateTime val, MySqlDbType type) : this(type)
{
year = val.Year;
month = val.Month;
day = val.Day;
hour = val.Hour;
minute = val.Minute;
second = val.Second;
}
#region Properties
///
/// Indicates if this object contains a value that can be represented as a DateTime
///
public bool IsValidDateTime
{
get
{
return year != 0 && month != 0 && day != 0;
}
}
///
/// Indicates if this object contains a value that can be represented as a DateTime
///
public bool IsStrictlyValidDateTime
{
get
{
// In case this represents a zero date, return FALSE
if (year == 0 || month == 0 || day == 0)
{
return false;
}
// Check http://msdn2.microsoft.com/en-us/library/xcfzdy4x(d=ide).aspx
// Here I try assign the value of this object to a DateTime object.
// In case the date is illegal, an Exception will be thrown
// In case the date is legal, the value will be accepted
try
{
DateTime dt = new DateTime(year, month, day);
return true;
} catch (ArgumentOutOfRangeException)
{
//year is less than 1 or greater than 9999.
//-or-
//month is less than 1 or greater than 12.
//-or-
//day is less than 1 or greater than the number of days in month
return false;
} catch (ArgumentException)
{
// The specified parameters evaluate to less than MinValue or more than MaxValue
return false;
}
}
}
/// Returns the year portion of this datetime
public int Year
{
get { return year; }
set { year = value; }
}
/// Returns the month portion of this datetime
public int Month
{
get { return month; }
set { month = value; }
}
/// Returns the day portion of this datetime
public int Day
{
get { return day; }
set { day = value; }
}
/// Returns the hour portion of this datetime
public int Hour
{
get { return hour; }
set { hour = value; }
}
/// Returns the minute portion of this datetime
public int Minute
{
get { return minute; }
set { minute = value; }
}
/// Returns the second portion of this datetime
public int Second
{
get { return second; }
set { second = value; }
}
#endregion
private void SerializeText( PacketWriter writer, DateTime value )
{
string val = String.Empty;
if (mySqlDbType == MySqlDbType.Timestamp && !writer.Version.isAtLeast(4,1,0))
val = String.Format("{0:0000}{1:00}{2:00}{3:00}{4:00}{5:00}",
value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second );
else
{
val = String.Format("{0:0000}-{1:00}-{2:00} {3:00}:{4:00}:{5:00}", value.Year, value.Month,
value.Day, value.Hour, value.Minute, value.Second );
}
writer.WriteStringNoNull( "'" + val + "'" );
}
internal override void Serialize(PacketWriter writer, bool binary, object value, int length)
{
if (value is MySqlDateTime)
value = (value as MySqlDateTime).GetDateTime();
if (value is string)
value = DateTime.Parse((string)value,
System.Globalization.CultureInfo.CurrentCulture);
if (! (value is DateTime))
throw new MySqlException( "Only DateTime objects can be serialized by MySqlDateTime" );
DateTime dtValue = (DateTime)value;
if (! binary)
{
SerializeText( writer, dtValue );
return;
}
if (mySqlDbType == MySqlDbType.Timestamp)
writer.WriteByte( 11 );
else
writer.WriteByte( 7 );
writer.WriteInteger( dtValue.Year, 2 );
writer.WriteByte( (byte)dtValue.Month );
writer.WriteByte( (byte)dtValue.Day );
if (mySqlDbType == MySqlDbType.Date)
{
writer.WriteByte( 0 );
writer.WriteByte( 0 );
writer.WriteByte( 0 );
}
else
{
writer.WriteByte( (byte)dtValue.Hour );
writer.WriteByte( (byte)dtValue.Minute );
writer.WriteByte( (byte)dtValue.Second );
}
if (mySqlDbType == MySqlDbType.Timestamp)
writer.WriteInteger( dtValue.Millisecond, 4 );
}
internal override DbType DbType
{
get
{
switch (mySqlDbType)
{
case MySqlDbType.Date:
case MySqlDbType.Newdate:
return DbType.Date;
}
return DbType.DateTime;
}
}
internal override string GetMySqlTypeName()
{
switch (mySqlDbType)
{
case MySqlDbType.Date: return "DATE";
case MySqlDbType.Newdate: return "NEWDATE";
case MySqlDbType.Timestamp: return "TIMESTAMP";
}
return "DATETIME";
}
/// Returns this value as a DateTime
public DateTime GetDateTime()
{
if (! IsValidDateTime)
throw new MySqlConversionException("Unable to convert MySQL date/time value to System.DateTime");
return new DateTime( year, month, day, hour, minute, second );
}
private MySqlDateTime Parse40Timestamp( string s )
{
int pos = 0;
year = month = day = 1;
hour = minute = second = 0;
if (s.Length == 14 || s.Length == 8)
{
year = int.Parse(s.Substring(pos, 4));
pos += 4;
}
else
{
year = int.Parse(s.Substring(pos, 2));
pos += 2;
if (year >= 70)
year += 1900;
else
year += 2000;
}
if (s.Length > 2)
{
month = int.Parse(s.Substring(pos, 2));
pos += 2;
}
if (s.Length > 4)
{
day = int.Parse(s.Substring(pos, 2));
pos += 2;
}
if (s.Length > 8)
{
hour = int.Parse(s.Substring(pos, 2));
minute = int.Parse(s.Substring(pos+2, 2));
pos += 4;
}
if (s.Length > 10)
second = int.Parse(s.Substring(pos, 2));
return new MySqlDateTime(year, month, day, hour, minute,
second, mySqlDbType );
}
internal MySqlDateTime ParseMySql( string s, bool is41 )
{
if (mySqlDbType == MySqlDbType.Timestamp && ! is41)
return Parse40Timestamp(s);
string[] parts = s.Split( '-', ' ', ':' );
int year = int.Parse( parts[0] );
int month = int.Parse( parts[1] );
int day = int.Parse( parts[2] );
int hour = 0, minute = 0, second = 0;
if (parts.Length > 3)
{
hour = int.Parse( parts[3] );
minute = int.Parse( parts[4] );
second = int.Parse( parts[5] );
}
return new MySqlDateTime( year, month, day, hour, minute, second, mySqlDbType );
}
internal override Type SystemType
{
get { return typeof(MySqlDateTime); }
}
internal override MySqlValue ReadValue(PacketReader reader, long length)
{
if (length >= 0)
{
string value = reader.ReadString( length );
return ParseMySql( value, reader.Version.isAtLeast(4,1,0) );
}
long bufLength = reader.ReadByte();
int year = reader.ReadInteger(2);
int month = reader.ReadByte();
int day = reader.ReadByte();
int hour = 0, minute = 0, second = 0;
if (bufLength > 4)
{
hour = reader.ReadByte();
minute = reader.ReadByte();
second = reader.ReadByte();
}
if (bufLength > 7)
reader.ReadInteger(4);
return new MySqlDateTime( year, month, day, hour, minute, second, mySqlDbType );
}
internal override void Skip(PacketReader reader)
{
long len = reader.ReadByte();
reader.Skip( len );
}
/// Returns a MySQL specific string representation of this value
public override string ToString()
{
if (this.IsValidDateTime)
{
DateTime d = new DateTime( year, month, day, hour, minute, second );
return (mySqlDbType == MySqlDbType.Date) ? d.ToString("d") : d.ToString();
}
if (mySqlDbType == MySqlDbType.Date)
return String.Format( shortPattern, year, month, day);
if (hour >= 12)
fullPattern = fullPattern.Replace("A", "P");
return String.Format( fullPattern, year, month, day, hour, minute, second);
}
private void ComposePatterns()
{
DateTime tempDT = new DateTime(1, 2, 3, 4, 5, 6);
fullPattern = tempDT.ToString();
fullPattern = fullPattern.Replace( "0001", "{0:0000}" );
if (fullPattern.IndexOf("02") != -1)
fullPattern = fullPattern.Replace( "02", "{1:00}" );
else
fullPattern = fullPattern.Replace("2", "{1}" );
if (fullPattern.IndexOf("03") != -1)
fullPattern = fullPattern.Replace( "03", "{2:00}" );
else
fullPattern = fullPattern.Replace("3", "{2}" );
if (fullPattern.IndexOf("04") != -1)
fullPattern = fullPattern.Replace( "04", "{3:00}" );
else
fullPattern = fullPattern.Replace("4", "{3}" );
if (fullPattern.IndexOf("05") != -1)
fullPattern = fullPattern.Replace( "05", "{4:00}" );
else
fullPattern = fullPattern.Replace("5", "{4}" );
if (fullPattern.IndexOf("06") != -1)
fullPattern = fullPattern.Replace( "06", "{5:00}" );
else
fullPattern = fullPattern.Replace("6", "{5}" );
shortPattern = tempDT.ToString("d");
shortPattern = shortPattern.Replace( "0001", "{0:0000}" );
if (shortPattern.IndexOf("02") != -1)
shortPattern = shortPattern.Replace( "02", "{1:00}" );
else
shortPattern = shortPattern.Replace("2", "{1}" );
if (shortPattern.IndexOf("03") != -1)
shortPattern = shortPattern.Replace( "03", "{2:00}" );
else
shortPattern = shortPattern.Replace("3", "{2}" );
}
///
///
///
public static explicit operator DateTime( MySqlDateTime val )
{
if (! val.IsValidDateTime) return DateTime.MinValue;
return val.GetDateTime();
}
#region IConvertible Members
ulong IConvertible.ToUInt64 (IFormatProvider provider)
{
return 0;
}
sbyte IConvertible.ToSByte(IFormatProvider provider)
{
// TODO: Add MySqlDateTime.ToSByte implementation
return 0;
}
double IConvertible.ToDouble(IFormatProvider provider)
{
return 0;
}
DateTime IConvertible.ToDateTime(IFormatProvider provider)
{
return this.GetDateTime();
}
float IConvertible.ToSingle(IFormatProvider provider)
{
return 0;
}
bool IConvertible.ToBoolean(IFormatProvider provider)
{
return false;
}
int IConvertible.ToInt32(IFormatProvider provider)
{
return 0;
}
ushort IConvertible.ToUInt16(IFormatProvider provider)
{
return 0;
}
short IConvertible.ToInt16(IFormatProvider provider)
{
return 0;
}
string System.IConvertible.ToString(IFormatProvider provider)
{
return null;
}
byte IConvertible.ToByte(IFormatProvider provider)
{
return 0;
}
char IConvertible.ToChar(IFormatProvider provider)
{
return '\0';
}
long IConvertible.ToInt64(IFormatProvider provider)
{
return 0;
}
System.TypeCode IConvertible.GetTypeCode()
{
return new System.TypeCode ();
}
decimal IConvertible.ToDecimal(IFormatProvider provider)
{
return 0;
}
object IConvertible.ToType(Type conversionType, IFormatProvider provider)
{
return null;
}
uint IConvertible.ToUInt32(IFormatProvider provider)
{
return 0;
}
#endregion
#region IComparable Members
int IComparable.CompareTo(object obj)
{
MySqlDateTime otherDate = (MySqlDateTime)obj;
if (Year < otherDate.Year) return -1;
else if (Year > otherDate.Year) return 1;
if (Month < otherDate.Month) return -1;
else if (Month > otherDate.Month) return 1;
if (Day < otherDate.Day) return -1;
else if (Day > otherDate.Day) return 1;
if (Hour < otherDate.Hour) return -1;
else if (Hour > otherDate.Hour) return 1;
if (Minute < otherDate.Minute) return -1;
else if (Minute > otherDate.Minute) return 1;
if (Second < otherDate.Second) return -1;
else if (Second > otherDate.Second) return 1;
return 0;
}
#endregion
}
}