1 //------------------------------------------------------------------------------
2 // <copyright file="SqlMoney.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">junfang</owner>
6 // <owner current="true" primary="false">[....]</owner>
7 // <owner current="true" primary="false">[....]</owner>
8 //------------------------------------------------------------------------------
10 //**************************************************************************
15 // Purpose: Implementation of SqlMoney which is equivalent to
16 // data type "money" in SQL Server
22 // 09/17/99 JunFang Created and implemented as first drop.
25 //**************************************************************************
28 using System.Data.Common;
29 using System.Diagnostics;
30 using System.Runtime.InteropServices;
31 using System.Globalization;
33 using System.Xml.Schema;
34 using System.Xml.Serialization;
36 namespace System.Data.SqlTypes {
40 /// Represents a currency value ranging from
41 /// -2<superscript term='63'/> (or -922,337,203,685,477.5808) to 2<superscript term='63'/> -1 (or
42 /// +922,337,203,685,477.5807) with an accuracy to
43 /// a ten-thousandth of currency unit to be stored in or retrieved from a
48 [StructLayout(LayoutKind.Sequential)]
49 [XmlSchemaProvider("GetXsdType")]
50 public struct SqlMoney : INullable, IComparable, IXmlSerializable {
51 private bool m_fNotNull; // false if null
54 // SQL Server stores money8 as ticks of 1/10000.
55 internal static readonly int x_iMoneyScale = 4;
56 private static readonly long x_lTickBase = 10000;
57 private static readonly double x_dTickBase = (double)x_lTickBase;
59 private static readonly long MinLong = unchecked((long)0x8000000000000000L) / x_lTickBase;
60 private static readonly long MaxLong = 0x7FFFFFFFFFFFFFFFL / x_lTickBase;
64 private SqlMoney(bool fNull) {
69 // Constructs from a long value without scaling. The ignored parameter exists
70 // only to distinguish this constructor from the constructor that takes a long.
71 // Used only internally.
72 internal SqlMoney(long value, int ignored) {
79 /// Initializes a new instance of the <see cref='System.Data.SqlTypes.SqlMoney'/> class with the value given.
82 public SqlMoney(int value) {
83 m_value = (long)value * x_lTickBase;
89 /// Initializes a new instance of the <see cref='System.Data.SqlTypes.SqlMoney'/> class with the value given.
92 public SqlMoney(long value) {
93 if (value < MinLong || value > MaxLong)
94 throw new OverflowException(SQLResource.ArithOverflowMessage);
95 m_value = value * x_lTickBase;
101 /// Initializes a new instance of the <see cref='System.Data.SqlTypes.SqlMoney'/> class with the value given.
104 public SqlMoney(Decimal value) {
105 // Since Decimal is a value type, operate directly on value, don't worry about changing it.
106 SqlDecimal snum = new SqlDecimal(value);
107 snum.AdjustScale(x_iMoneyScale - snum.Scale, true);
108 Debug.Assert(snum.Scale == x_iMoneyScale);
110 if (snum.m_data3 != 0 || snum.m_data4 != 0)
111 throw new OverflowException(SQLResource.ArithOverflowMessage);
113 bool fPositive = snum.IsPositive;
114 ulong ulValue = (ulong)snum.m_data1 + ( ((ulong)snum.m_data2) << 32 );
115 if (fPositive && ulValue > (ulong)(Int64.MaxValue) ||
116 !fPositive && ulValue > unchecked((ulong)(Int64.MinValue)))
117 throw new OverflowException(SQLResource.ArithOverflowMessage);
119 m_value = fPositive ? (long)ulValue : unchecked(- (long)ulValue);
125 /// Initializes a new instance of the <see cref='System.Data.SqlTypes.SqlMoney'/> class with the value given.
128 public SqlMoney(double value) : this(new Decimal(value)) {
135 /// Gets a value indicating whether the <see cref='System.Data.SqlTypes.SqlMoney.Value'/>
136 /// property is assigned to null.
140 get { return !m_fNotNull;}
146 /// Gets or sets the monetary value of an instance of the <see cref='System.Data.SqlTypes.SqlMoney'/>
150 public Decimal Value {
155 throw new SqlNullValueException();
160 /// <para>[To be supplied.]</para>
162 public Decimal ToDecimal() {
164 throw new SqlNullValueException();
166 bool fNegative = false;
167 long value = m_value;
173 return new Decimal(unchecked((int)value), unchecked((int)(value >> 32)), 0, fNegative, (byte)x_iMoneyScale);
177 /// <para>[To be supplied.]</para>
179 public long ToInt64() {
181 throw new SqlNullValueException();
183 long ret = m_value / (x_lTickBase / 10);
184 bool fPositive = (ret >= 0);
185 long remainder = ret % 10;
188 if (remainder >= 5) {
198 internal long ToSqlInternalRepresentation() {
200 throw new SqlNullValueException();
206 /// <para>[To be supplied.]</para>
208 public int ToInt32() {
209 return checked((int)(ToInt64()));
213 /// <para>[To be supplied.]</para>
215 public double ToDouble() {
216 return Decimal.ToDouble(ToDecimal());
219 // Implicit conversion from Decimal to SqlMoney
221 /// <para>[To be supplied.]</para>
223 public static implicit operator SqlMoney(Decimal x) {
224 return new SqlMoney(x);
227 // Explicit conversion from Double to SqlMoney
228 public static explicit operator SqlMoney(double x) {
229 return new SqlMoney(x);
232 // Implicit conversion from long to SqlMoney
233 public static implicit operator SqlMoney(long x) {
234 return new SqlMoney(new Decimal(x));
237 // Explicit conversion from SqlMoney to Decimal. Throw exception if x is Null.
239 /// <para>[To be supplied.]</para>
241 public static explicit operator Decimal(SqlMoney x) {
246 /// <para>[To be supplied.]</para>
248 public override String ToString() {
250 return SQLResource.NullString;
252 Decimal money = ToDecimal();
253 // Formatting of SqlMoney: At least two digits after decimal point
254 return money.ToString("#0.00##", (IFormatProvider)null);
258 /// <para>[To be supplied.]</para>
260 public static SqlMoney Parse(String s) {
261 // Try parsing the format of '#0.00##' generated by ToString() by using the
262 // culture invariant NumberFormatInfo as well as the current culture's format
267 const NumberStyles SqlNumberStyle =
268 NumberStyles.AllowCurrencySymbol |
269 NumberStyles.AllowDecimalPoint |
270 NumberStyles.AllowParentheses |
271 NumberStyles.AllowTrailingSign |
272 NumberStyles.AllowLeadingSign |
273 NumberStyles.AllowTrailingWhite |
274 NumberStyles.AllowLeadingWhite;
276 if ( s == SQLResource.NullString) {
277 money = SqlMoney.Null;
279 else if (Decimal.TryParse(s, SqlNumberStyle, NumberFormatInfo.InvariantInfo, out d)) {
280 money = new SqlMoney(d);
283 money = new SqlMoney(Decimal.Parse(s, NumberStyles.Currency, NumberFormatInfo.CurrentInfo));
291 /// <para>[To be supplied.]</para>
293 public static SqlMoney operator -(SqlMoney x) {
296 if (x.m_value == MinLong)
297 throw new OverflowException(SQLResource.ArithOverflowMessage);
298 return new SqlMoney(-x.m_value, 0);
304 // Arithmetic operators
306 /// <para>[To be supplied.]</para>
308 public static SqlMoney operator +(SqlMoney x, SqlMoney y) {
310 return(x.IsNull || y.IsNull) ? Null : new SqlMoney(checked(x.m_value + y.m_value), 0);
312 catch (OverflowException) {
313 throw new OverflowException(SQLResource.ArithOverflowMessage);
318 /// <para>[To be supplied.]</para>
320 public static SqlMoney operator -(SqlMoney x, SqlMoney y) {
322 return(x.IsNull || y.IsNull) ? Null : new SqlMoney(checked(x.m_value - y.m_value), 0);
324 catch (OverflowException) {
325 throw new OverflowException(SQLResource.ArithOverflowMessage);
330 /// <para>[To be supplied.]</para>
332 public static SqlMoney operator *(SqlMoney x, SqlMoney y) {
333 return (x.IsNull || y.IsNull) ? Null :
334 new SqlMoney(Decimal.Multiply(x.ToDecimal(), y.ToDecimal()));
338 /// <para>[To be supplied.]</para>
340 public static SqlMoney operator /(SqlMoney x, SqlMoney y) {
341 return (x.IsNull || y.IsNull) ? Null :
342 new SqlMoney(Decimal.Divide(x.ToDecimal(), y.ToDecimal()));
346 // Implicit conversions
348 // Implicit conversion from SqlBoolean to SqlMoney
350 /// <para>[To be supplied.]</para>
352 public static explicit operator SqlMoney(SqlBoolean x) {
353 return x.IsNull ? Null : new SqlMoney((int)x.ByteValue);
356 // Implicit conversion from SqlByte to SqlMoney
358 /// <para>[To be supplied.]</para>
360 public static implicit operator SqlMoney(SqlByte x) {
361 return x.IsNull ? Null : new SqlMoney((int)x.Value);
364 // Implicit conversion from SqlInt16 to SqlMoney
366 /// <para>[To be supplied.]</para>
368 public static implicit operator SqlMoney(SqlInt16 x) {
369 return x.IsNull ? Null : new SqlMoney((int)x.Value);
372 // Implicit conversion from SqlInt32 to SqlMoney
374 /// <para>[To be supplied.]</para>
376 public static implicit operator SqlMoney(SqlInt32 x) {
377 return x.IsNull ? Null : new SqlMoney(x.Value);
380 // Implicit conversion from SqlInt64 to SqlMoney
382 /// <para>[To be supplied.]</para>
384 public static implicit operator SqlMoney(SqlInt64 x) {
385 return x.IsNull ? Null : new SqlMoney(x.Value);
389 // Explicit conversions
391 // Explicit conversion from SqlSingle to SqlMoney
393 /// <para>[To be supplied.]</para>
395 public static explicit operator SqlMoney(SqlSingle x) {
396 return x.IsNull ? Null : new SqlMoney((double)x.Value);
399 // Explicit conversion from SqlDouble to SqlMoney
401 /// <para>[To be supplied.]</para>
403 public static explicit operator SqlMoney(SqlDouble x) {
404 return x.IsNull ? Null : new SqlMoney(x.Value);
407 // Explicit conversion from SqlDecimal to SqlMoney
409 /// <para>[To be supplied.]</para>
411 public static explicit operator SqlMoney(SqlDecimal x) {
412 return x.IsNull ? SqlMoney.Null : new SqlMoney(x.Value);
415 // Explicit conversion from SqlString to SqlMoney
416 // Throws FormatException or OverflowException if necessary.
418 /// <para>[To be supplied.]</para>
420 public static explicit operator SqlMoney(SqlString x) {
421 return x.IsNull ? Null : new SqlMoney(Decimal.Parse(x.Value,NumberStyles.Currency,null));
427 // Overloading comparison operators
429 /// <para>[To be supplied.]</para>
431 public static SqlBoolean operator==(SqlMoney x, SqlMoney y) {
432 return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.m_value == y.m_value);
436 /// <para>[To be supplied.]</para>
438 public static SqlBoolean operator!=(SqlMoney x, SqlMoney y) {
443 /// <para>[To be supplied.]</para>
445 public static SqlBoolean operator<(SqlMoney x, SqlMoney y) {
446 return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.m_value < y.m_value);
450 /// <para>[To be supplied.]</para>
452 public static SqlBoolean operator>(SqlMoney x, SqlMoney y) {
453 return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.m_value > y.m_value);
457 /// <para>[To be supplied.]</para>
459 public static SqlBoolean operator<=(SqlMoney x, SqlMoney y) {
460 return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.m_value <= y.m_value);
464 /// <para>[To be supplied.]</para>
466 public static SqlBoolean operator>=(SqlMoney x, SqlMoney y) {
467 return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.m_value >= y.m_value);
471 //--------------------------------------------------
472 // Alternative methods for overloaded operators
473 //--------------------------------------------------
475 // Alternative method for operator +
476 public static SqlMoney Add(SqlMoney x, SqlMoney y) {
479 // Alternative method for operator -
480 public static SqlMoney Subtract(SqlMoney x, SqlMoney y) {
484 // Alternative method for operator *
485 public static SqlMoney Multiply(SqlMoney x, SqlMoney y) {
489 // Alternative method for operator /
490 public static SqlMoney Divide(SqlMoney x, SqlMoney y) {
494 // Alternative method for operator ==
495 public static SqlBoolean Equals(SqlMoney x, SqlMoney y) {
499 // Alternative method for operator !=
500 public static SqlBoolean NotEquals(SqlMoney x, SqlMoney y) {
504 // Alternative method for operator <
505 public static SqlBoolean LessThan(SqlMoney x, SqlMoney y) {
509 // Alternative method for operator >
510 public static SqlBoolean GreaterThan(SqlMoney x, SqlMoney y) {
514 // Alternative method for operator <=
515 public static SqlBoolean LessThanOrEqual(SqlMoney x, SqlMoney y) {
519 // Alternative method for operator >=
520 public static SqlBoolean GreaterThanOrEqual(SqlMoney x, SqlMoney y) {
524 // Alternative method for conversions.
526 public SqlBoolean ToSqlBoolean() {
527 return (SqlBoolean)this;
530 public SqlByte ToSqlByte() {
531 return (SqlByte)this;
534 public SqlDouble ToSqlDouble() {
535 return (SqlDouble)this;
538 public SqlInt16 ToSqlInt16() {
539 return (SqlInt16)this;
542 public SqlInt32 ToSqlInt32() {
543 return (SqlInt32)this;
546 public SqlInt64 ToSqlInt64() {
547 return (SqlInt64)this;
550 public SqlDecimal ToSqlDecimal() {
551 return (SqlDecimal)this;
554 public SqlSingle ToSqlSingle() {
555 return (SqlSingle)this;
558 public SqlString ToSqlString() {
559 return (SqlString)this;
564 // Compares this object to another object, returning an integer that
565 // indicates the relationship.
566 // Returns a value less than zero if this < object, zero if this = object,
567 // or a value greater than zero if this > object.
568 // null is considered to be less than any instance.
569 // If object is not of same type, this method throws an ArgumentException.
571 /// <para>[To be supplied.]</para>
573 public int CompareTo(Object value) {
574 if (value is SqlMoney) {
575 SqlMoney i = (SqlMoney)value;
579 throw ADP.WrongType(value.GetType(), typeof(SqlMoney));
582 public int CompareTo(SqlMoney value) {
583 // If both Null, consider them equal.
584 // Otherwise, Null is less than anything.
586 return value.IsNull ? 0 : -1;
587 else if (value.IsNull)
590 if (this < value) return -1;
591 if (this > value) return 1;
595 // Compares this instance with a specified object
597 /// <para>[To be supplied.]</para>
599 public override bool Equals(Object value) {
600 if (!(value is SqlMoney)) {
604 SqlMoney i = (SqlMoney)value;
606 if (i.IsNull || IsNull)
607 return (i.IsNull && IsNull);
609 return (this == i).Value;
612 // For hashing purpose
614 /// <para>[To be supplied.]</para>
616 public override int GetHashCode() {
617 // Don't use Value property, because Value will convert to Decimal, which is not necessary.
618 return IsNull ? 0 : m_value.GetHashCode();
622 /// <para>[To be supplied.]</para>
624 XmlSchema IXmlSerializable.GetSchema() { return null; }
627 /// <para>[To be supplied.]</para>
629 void IXmlSerializable.ReadXml(XmlReader reader) {
630 string isNull = reader.GetAttribute("nil", XmlSchema.InstanceNamespace);
631 if (isNull != null && XmlConvert.ToBoolean(isNull)) {
632 // VSTFDevDiv# 479603 - SqlTypes read null value infinitely and never read the next value. Fix - Read the next value.
633 reader.ReadElementString();
634 this.m_fNotNull = false;
637 SqlMoney money = new SqlMoney(XmlConvert.ToDecimal(reader.ReadElementString()));
638 this.m_fNotNull = money.m_fNotNull;
639 this.m_value = money.m_value;
644 /// <para>[To be supplied.]</para>
646 void IXmlSerializable.WriteXml(XmlWriter writer) {
648 writer.WriteAttributeString("xsi", "nil", XmlSchema.InstanceNamespace, "true");
651 writer.WriteString( XmlConvert.ToString(ToDecimal()) );
656 /// <para>[To be supplied.]</para>
658 public static XmlQualifiedName GetXsdType(XmlSchemaSet schemaSet) {
659 return new XmlQualifiedName("decimal", XmlSchema.Namespace);
664 /// Represents a null value that can be assigned to
665 /// the <see cref='System.Data.SqlTypes.SqlMoney.Value'/> property of an instance of
666 /// the <see cref='System.Data.SqlTypes.SqlMoney'/>class.
669 public static readonly SqlMoney Null = new SqlMoney(true);
673 /// Represents the zero value that can be assigned to the <see cref='System.Data.SqlTypes.SqlMoney.Value'/> property of an instance of
674 /// the <see cref='System.Data.SqlTypes.SqlMoney'/> class.
677 public static readonly SqlMoney Zero = new SqlMoney(0);
681 /// Represents the minimum value that can be assigned
682 /// to <see cref='System.Data.SqlTypes.SqlMoney.Value'/> property of an instance of
683 /// the <see cref='System.Data.SqlTypes.SqlMoney'/>
687 public static readonly SqlMoney MinValue = new SqlMoney(unchecked((long)0x8000000000000000L), 0);
691 /// Represents the maximum value that can be assigned to
692 /// the <see cref='System.Data.SqlTypes.SqlMoney.Value'/> property of an instance of
693 /// the <see cref='System.Data.SqlTypes.SqlMoney'/>
697 public static readonly SqlMoney MaxValue = new SqlMoney(0x7FFFFFFFFFFFFFFFL, 0);
701 } // namespace System.Data.SqlTypes