You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
646 lines
17 KiB
646 lines
17 KiB
// Copyright (c) 2004-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
|
|
//
|
|
// 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;
|
|
using System.Globalization;
|
|
|
|
namespace MySql.Data.Types
|
|
{
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public struct MySqlDateTime : IMySqlValue, IConvertible, IComparable
|
|
{
|
|
private bool isNull;
|
|
private MySqlDbType type;
|
|
private int year, month, day, hour, minute, second;
|
|
private int millisecond;
|
|
|
|
/// <summary>
|
|
/// Constructs a new <b>MySqlDateTime</b> object by setting the individual time properties to
|
|
/// the given values.
|
|
/// </summary>
|
|
/// <param name="year">The year to use.</param>
|
|
/// <param name="month">The month to use.</param>
|
|
/// <param name="day">The day to use.</param>
|
|
/// <param name="hour">The hour to use.</param>
|
|
/// <param name="minute">The minute to use.</param>
|
|
/// <param name="second">The second to use.</param>
|
|
public MySqlDateTime(int year, int month, int day, int hour, int minute, int second)
|
|
: this(MySqlDbType.DateTime, year, month, day, hour, minute, second)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs a new <b>MySqlDateTime</b> object by using values from the given <see cref="DateTime"/> object.
|
|
/// </summary>
|
|
/// <param name="dt">The <see cref="DateTime"/> object to copy.</param>
|
|
public MySqlDateTime(DateTime dt)
|
|
: this(MySqlDbType.DateTime, dt)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs a new <b>MySqlDateTime</b> object by copying the current value of the given object.
|
|
/// </summary>
|
|
/// <param name="mdt">The <b>MySqlDateTime</b> object to copy.</param>
|
|
public MySqlDateTime(MySqlDateTime mdt)
|
|
{
|
|
year = mdt.Year;
|
|
month = mdt.Month;
|
|
day = mdt.Day;
|
|
hour = mdt.Hour;
|
|
minute = mdt.Minute;
|
|
second = mdt.Second;
|
|
millisecond = 0;
|
|
type = MySqlDbType.DateTime;
|
|
isNull = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enables the contruction of a <b>MySqlDateTime</b> object by parsing a string.
|
|
/// </summary>
|
|
public MySqlDateTime(string dateTime)
|
|
: this(MySqlDateTime.Parse(dateTime))
|
|
{
|
|
}
|
|
|
|
internal MySqlDateTime(MySqlDbType type, int year, int month, int day, int hour, int minute,
|
|
int second)
|
|
{
|
|
this.isNull = false;
|
|
this.type = type;
|
|
this.year = year;
|
|
this.month = month;
|
|
this.day = day;
|
|
this.hour = hour;
|
|
this.minute = minute;
|
|
this.second = second;
|
|
this.millisecond = 0;
|
|
}
|
|
|
|
internal MySqlDateTime(MySqlDbType type, bool isNull)
|
|
: this(type, 0, 0, 0, 0, 0, 0)
|
|
{
|
|
this.isNull = isNull;
|
|
}
|
|
|
|
internal MySqlDateTime(MySqlDbType type, DateTime val)
|
|
: this(type, 0, 0, 0, 0, 0, 0)
|
|
{
|
|
this.isNull = false;
|
|
year = val.Year;
|
|
month = val.Month;
|
|
day = val.Day;
|
|
hour = val.Hour;
|
|
minute = val.Minute;
|
|
second = val.Second;
|
|
millisecond = val.Millisecond;
|
|
}
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Indicates if this object contains a value that can be represented as a DateTime
|
|
/// </summary>
|
|
public bool IsValidDateTime
|
|
{
|
|
get
|
|
{
|
|
return year != 0 && month != 0 && day != 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>Returns the year portion of this datetime</summary>
|
|
public int Year
|
|
{
|
|
get { return year; }
|
|
set { year = value; }
|
|
}
|
|
|
|
/// <summary>Returns the month portion of this datetime</summary>
|
|
public int Month
|
|
{
|
|
get { return month; }
|
|
set { month = value; }
|
|
}
|
|
|
|
/// <summary>Returns the day portion of this datetime</summary>
|
|
public int Day
|
|
{
|
|
get { return day; }
|
|
set { day = value; }
|
|
}
|
|
|
|
/// <summary>Returns the hour portion of this datetime</summary>
|
|
public int Hour
|
|
{
|
|
get { return hour; }
|
|
set { hour = value; }
|
|
}
|
|
|
|
/// <summary>Returns the minute portion of this datetime</summary>
|
|
public int Minute
|
|
{
|
|
get { return minute; }
|
|
set { minute = value; }
|
|
}
|
|
|
|
/// <summary>Returns the second portion of this datetime</summary>
|
|
public int Second
|
|
{
|
|
get { return second; }
|
|
set { second = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the millisecond value of this object.
|
|
/// </summary>
|
|
public int Millisecond
|
|
{
|
|
get { return millisecond; }
|
|
set { millisecond = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IMySqlValue Members
|
|
|
|
/// <summary>
|
|
/// Returns true if this datetime object has a null value
|
|
/// </summary>
|
|
public bool IsNull
|
|
{
|
|
get { return isNull; }
|
|
}
|
|
|
|
MySqlDbType IMySqlValue.MySqlDbType
|
|
{
|
|
get { return type; }
|
|
}
|
|
|
|
DbType IMySqlValue.DbType
|
|
{
|
|
get
|
|
{
|
|
if (type == MySqlDbType.Date || type == MySqlDbType.Newdate)
|
|
return DbType.Date;
|
|
return DbType.DateTime;
|
|
}
|
|
}
|
|
|
|
object IMySqlValue.Value
|
|
{
|
|
get { return GetDateTime(); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the value of this <see cref="MySqlDateTime"/> as a DateTime object.
|
|
/// </summary>
|
|
public DateTime Value
|
|
{
|
|
get { return GetDateTime(); }
|
|
}
|
|
|
|
Type IMySqlValue.SystemType
|
|
{
|
|
get { return typeof(DateTime); }
|
|
}
|
|
|
|
string IMySqlValue.MySqlTypeName
|
|
{
|
|
get
|
|
{
|
|
switch (type)
|
|
{
|
|
case MySqlDbType.Date: return "DATE";
|
|
case MySqlDbType.Newdate: return "NEWDATE";
|
|
case MySqlDbType.Timestamp: return "TIMESTAMP";
|
|
}
|
|
return "DATETIME";
|
|
}
|
|
}
|
|
|
|
|
|
private void SerializeText(MySqlPacket packet, MySqlDateTime value)
|
|
{
|
|
string val = String.Empty;
|
|
|
|
if (type == MySqlDbType.Timestamp && !packet.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}",
|
|
value.Year, value.Month, value.Day);
|
|
if (type != MySqlDbType.Date)
|
|
val = String.Format("{0} {1:00}:{2:00}:{3:00}", val,
|
|
value.Hour, value.Minute, value.Second);
|
|
}
|
|
packet.WriteStringNoNull("'" + val + "'");
|
|
}
|
|
|
|
void IMySqlValue.WriteValue(MySqlPacket packet, bool binary, object value, int length)
|
|
{
|
|
MySqlDateTime dtValue;
|
|
|
|
string valueAsString = value as string;
|
|
|
|
if (value is DateTime)
|
|
dtValue = new MySqlDateTime(type, (DateTime)value);
|
|
else if (valueAsString != null)
|
|
dtValue = new MySqlDateTime(type, DateTime.Parse(valueAsString, CultureInfo.CurrentCulture));
|
|
else if (value is MySqlDateTime)
|
|
dtValue = (MySqlDateTime)value;
|
|
else
|
|
throw new MySqlException("Unable to serialize date/time value.");
|
|
|
|
if (!binary)
|
|
{
|
|
SerializeText(packet, dtValue);
|
|
return;
|
|
}
|
|
|
|
if (type == MySqlDbType.Timestamp)
|
|
packet.WriteByte(11);
|
|
else
|
|
packet.WriteByte(7);
|
|
|
|
packet.WriteInteger(dtValue.Year, 2);
|
|
packet.WriteByte((byte)dtValue.Month);
|
|
packet.WriteByte((byte)dtValue.Day);
|
|
if (type == MySqlDbType.Date)
|
|
{
|
|
packet.WriteByte(0);
|
|
packet.WriteByte(0);
|
|
packet.WriteByte(0);
|
|
}
|
|
else
|
|
{
|
|
packet.WriteByte((byte)dtValue.Hour);
|
|
packet.WriteByte((byte)dtValue.Minute);
|
|
packet.WriteByte((byte)dtValue.Second);
|
|
}
|
|
|
|
if (type == MySqlDbType.Timestamp)
|
|
packet.WriteInteger(dtValue.Millisecond, 4);
|
|
}
|
|
|
|
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(type, year, month, day, hour,
|
|
minute, second);
|
|
}
|
|
|
|
static internal MySqlDateTime Parse(string s)
|
|
{
|
|
MySqlDateTime dt = new MySqlDateTime();
|
|
return dt.ParseMySql(s, true);
|
|
}
|
|
|
|
static internal MySqlDateTime Parse(string s, Common.DBVersion version)
|
|
{
|
|
MySqlDateTime dt = new MySqlDateTime();
|
|
return dt.ParseMySql(s, version.isAtLeast(4, 1, 0));
|
|
}
|
|
|
|
private MySqlDateTime ParseMySql(string s, bool is41)
|
|
{
|
|
if (type == 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(type, year, month, day, hour, minute, second);
|
|
}
|
|
|
|
IMySqlValue IMySqlValue.ReadValue(MySqlPacket packet, long length, bool nullVal)
|
|
{
|
|
if (nullVal) return new MySqlDateTime(type, true);
|
|
|
|
if (length >= 0)
|
|
{
|
|
string value = packet.ReadString(length);
|
|
return ParseMySql(value, packet.Version.isAtLeast(4, 1, 0));
|
|
}
|
|
|
|
long bufLength = packet.ReadByte();
|
|
int year = 0, month = 0, day = 0;
|
|
int hour = 0, minute = 0, second = 0;
|
|
|
|
if (bufLength >= 4)
|
|
{
|
|
year = packet.ReadInteger(2);
|
|
month = packet.ReadByte();
|
|
day = packet.ReadByte();
|
|
}
|
|
|
|
if (bufLength > 4)
|
|
{
|
|
hour = packet.ReadByte();
|
|
minute = packet.ReadByte();
|
|
second = packet.ReadByte();
|
|
}
|
|
|
|
if (bufLength > 7)
|
|
packet.ReadInteger(4);
|
|
|
|
return new MySqlDateTime(type, year, month, day, hour, minute, second);
|
|
}
|
|
|
|
void IMySqlValue.SkipValue(MySqlPacket packet)
|
|
{
|
|
int len = packet.ReadByte();
|
|
packet.Position += len;
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>Returns this value as a DateTime</summary>
|
|
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 static string FormatDateCustom(string format, int monthVal, int dayVal, int yearVal)
|
|
{
|
|
format = format.Replace("MM", "{0:00}");
|
|
format = format.Replace("M", "{0}");
|
|
format = format.Replace("dd", "{1:00}");
|
|
format = format.Replace("d", "{1}");
|
|
format = format.Replace("yyyy", "{2:0000}");
|
|
format = format.Replace("yy", "{3:00}");
|
|
format = format.Replace("y", "{4:0}");
|
|
|
|
int year2digit = yearVal - ((yearVal / 1000) * 1000);
|
|
year2digit -= ((year2digit / 100) * 100);
|
|
int year1digit = year2digit - ((year2digit / 10) * 10);
|
|
|
|
return String.Format(format, monthVal, dayVal, yearVal, year2digit, year1digit);
|
|
}
|
|
|
|
/// <summary>Returns a MySQL specific string representation of this value</summary>
|
|
public override string ToString()
|
|
{
|
|
if (this.IsValidDateTime)
|
|
{
|
|
DateTime d = new DateTime(year, month, day, hour, minute, second);
|
|
return (type == MySqlDbType.Date) ? d.ToString("d") : d.ToString();
|
|
}
|
|
|
|
string dateString = FormatDateCustom(
|
|
CultureInfo.CurrentUICulture.DateTimeFormat.ShortDatePattern, month, day, year);
|
|
if (type == MySqlDbType.Date)
|
|
return dateString;
|
|
|
|
DateTime dt = new DateTime(1, 2, 3, hour, minute, second);
|
|
dateString = String.Format("{0} {1}", dateString, dt.ToLongTimeString());
|
|
return dateString;
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <param name="val"></param>
|
|
/// <returns></returns>
|
|
public static explicit operator DateTime(MySqlDateTime val)
|
|
{
|
|
if (!val.IsValidDateTime) return DateTime.MinValue;
|
|
return val.GetDateTime();
|
|
}
|
|
|
|
internal static void SetDSInfo(DataTable dsTable)
|
|
{
|
|
string[] types = new string[] { "DATE", "DATETIME", "TIMESTAMP" };
|
|
MySqlDbType[] dbtype = new MySqlDbType[] { MySqlDbType.Date,
|
|
MySqlDbType.DateTime, MySqlDbType.Timestamp };
|
|
|
|
// we use name indexing because this method will only be called
|
|
// when GetSchema is called for the DataSourceInformation
|
|
// collection and then it wil be cached.
|
|
for (int x = 0; x < types.Length; x++)
|
|
{
|
|
DataRow row = dsTable.NewRow();
|
|
row["TypeName"] = types[x];
|
|
row["ProviderDbType"] = dbtype[x];
|
|
row["ColumnSize"] = 0;
|
|
row["CreateFormat"] = types[x];
|
|
row["CreateParameters"] = null;
|
|
row["DataType"] = "System.DateTime";
|
|
row["IsAutoincrementable"] = false;
|
|
row["IsBestMatch"] = true;
|
|
row["IsCaseSensitive"] = false;
|
|
row["IsFixedLength"] = true;
|
|
row["IsFixedPrecisionScale"] = true;
|
|
row["IsLong"] = false;
|
|
row["IsNullable"] = true;
|
|
row["IsSearchable"] = true;
|
|
row["IsSearchableWithLike"] = false;
|
|
row["IsUnsigned"] = false;
|
|
row["MaximumScale"] = 0;
|
|
row["MinimumScale"] = 0;
|
|
row["IsConcurrencyType"] = DBNull.Value;
|
|
row["IsLiteralSupported"] = false;
|
|
row["LiteralPrefix"] = null;
|
|
row["LiteralSuffix"] = null;
|
|
row["NativeDataType"] = null;
|
|
dsTable.Rows.Add(row);
|
|
}
|
|
}
|
|
|
|
#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;
|
|
|
|
if (Millisecond < otherDate.Millisecond) return -1;
|
|
else if (Millisecond > otherDate.Millisecond) return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|
|
|