1 //------------------------------------------------------------------------------
2 // <copyright file="XmlBinaryWriter.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
7 
8 using System;
9 using System.Collections;
10 using System.IO;
11 using System.Text;
12 using System.Diagnostics;
13 using System.Globalization;
14 
15 namespace System.Xml {
16     // This is mostly just a copy of code in SqlTypes.SqlDecimal
17     internal struct BinXmlSqlDecimal {
18         internal byte m_bLen;
19         internal byte m_bPrec;
20         internal byte m_bScale;
21         internal byte m_bSign;
22         internal uint m_data1;
23         internal uint m_data2;
24         internal uint m_data3;
25         internal uint m_data4;
26 
27         public bool IsPositive {
28             get {
29                 return (m_bSign == 0);
30             }
31         }
32 
33         private static readonly byte NUMERIC_MAX_PRECISION = 38;            // Maximum precision of numeric
34         private static readonly byte MaxPrecision = NUMERIC_MAX_PRECISION;  // max SS precision
35         private static readonly byte MaxScale = NUMERIC_MAX_PRECISION;      // max SS scale
36 
37         private static readonly int x_cNumeMax = 4;
38         private static readonly long x_lInt32Base = ((long)1) << 32;      // 2**32
39         private static readonly ulong x_ulInt32Base = ((ulong)1) << 32;     // 2**32
40         private static readonly ulong x_ulInt32BaseForMod = x_ulInt32Base - 1;    // 2**32 - 1 (0xFFF...FF)
41         internal static readonly ulong x_llMax = Int64.MaxValue;   // Max of Int64
42         //private static readonly uint x_ulBase10 = 10;
43         private static readonly double DUINT_BASE = (double)x_lInt32Base;     // 2**32
44         private static readonly double DUINT_BASE2 = DUINT_BASE * DUINT_BASE;  // 2**64
45         private static readonly double DUINT_BASE3 = DUINT_BASE2 * DUINT_BASE; // 2**96
46         //private static readonly double DMAX_NUME = 1.0e+38;                  // Max value of numeric
47         //private static readonly uint DBL_DIG = 17;                       // Max decimal digits of double
48         //private static readonly byte x_cNumeDivScaleMin = 6;     // Minimum result scale of numeric division
49         // Array of multipliers for lAdjust and Ceiling/Floor.
50         private static readonly uint[] x_rgulShiftBase = new uint[9] {
51             10,
52             10 * 10,
53             10 * 10 * 10,
54             10 * 10 * 10 * 10,
55             10 * 10 * 10 * 10 * 10,
56             10 * 10 * 10 * 10 * 10 * 10,
57             10 * 10 * 10 * 10 * 10 * 10 * 10,
58             10 * 10 * 10 * 10 * 10 * 10 * 10 * 10,
59             10 * 10 * 10 * 10 * 10 * 10 * 10 * 10 * 10
60         };
61 
BinXmlSqlDecimalSystem.Xml.BinXmlSqlDecimal62         public BinXmlSqlDecimal (byte[] data, int offset, bool trim) {
63             byte b = data[offset];
64             switch (b) {
65                 case 7: m_bLen = 1; break;
66                 case 11: m_bLen = 2; break;
67                 case 15: m_bLen = 3; break;
68                 case 19: m_bLen = 4; break;
69                 default: throw new XmlException(Res.XmlBinary_InvalidSqlDecimal, (string[])null);
70             }
71             m_bPrec = data[offset+1];
72             m_bScale = data[offset+2];
73             m_bSign = 0 == data[offset+3] ? (byte)1 : (byte)0;
74             m_data1 = UIntFromByteArray(data, offset+4);
75             m_data2 = (m_bLen > 1) ? UIntFromByteArray(data, offset+8) : 0;
76             m_data3 = (m_bLen > 2) ? UIntFromByteArray(data, offset+12) : 0;
77             m_data4 = (m_bLen > 3) ? UIntFromByteArray(data, offset+16) : 0;
78             if (m_bLen == 4 && m_data4 == 0)
79                 m_bLen = 3;
80             if (m_bLen == 3 && m_data3 == 0)
81                 m_bLen = 2;
82             if (m_bLen == 2 && m_data2 == 0)
83                 m_bLen = 1;
84             AssertValid();
85             if (trim) {
86                 TrimTrailingZeros();
87                 AssertValid();
88             }
89         }
90 
WriteSystem.Xml.BinXmlSqlDecimal91         public void Write(Stream strm) {
92             strm.WriteByte((byte)(this.m_bLen * 4 + 3));
93             strm.WriteByte(this.m_bPrec);
94             strm.WriteByte(this.m_bScale);
95             strm.WriteByte(0 == this.m_bSign ? (byte)1 : (byte)0);
96             WriteUI4(this.m_data1, strm);
97             if (this.m_bLen > 1) {
98                 WriteUI4(this.m_data2, strm);
99                 if (this.m_bLen > 2) {
100                     WriteUI4(this.m_data3, strm);
101                     if (this.m_bLen > 3) {
102                         WriteUI4(this.m_data4, strm);
103                     }
104                 }
105             }
106         }
107 
WriteUI4System.Xml.BinXmlSqlDecimal108         private void WriteUI4(uint val, Stream strm) {
109             strm.WriteByte((byte)(val & 0xFF));
110             strm.WriteByte((byte)((val >> 8) & 0xFF));
111             strm.WriteByte((byte)((val >> 16) & 0xFF));
112             strm.WriteByte((byte)((val >> 24) & 0xFF));
113         }
114 
UIntFromByteArraySystem.Xml.BinXmlSqlDecimal115         private static uint UIntFromByteArray(byte[] data, int offset) {
116             int val = (data[offset]) << 0;
117             val |= (data[offset+1]) << 8;
118             val |= (data[offset+2]) << 16;
119             val |= (data[offset+3]) << 24;
120             return unchecked((uint)val);
121         }
122 
123         // check whether is zero
FZeroSystem.Xml.BinXmlSqlDecimal124         private bool FZero() {
125             return (m_data1 == 0) && (m_bLen <= 1);
126         }
127         // Store data back from rguiData[] to m_data*
StoreFromWorkingArraySystem.Xml.BinXmlSqlDecimal128         private void StoreFromWorkingArray(uint[] rguiData) {
129             Debug.Assert(rguiData.Length == 4);
130             m_data1 = rguiData[0];
131             m_data2 = rguiData[1];
132             m_data3 = rguiData[2];
133             m_data4 = rguiData[3];
134         }
135 
136         // Find the case where we overflowed 10**38, but not 2**128
FGt10_38System.Xml.BinXmlSqlDecimal137         private bool FGt10_38(uint[] rglData) {
138             //Debug.Assert(rglData.Length == 4, "rglData.Length == 4", "Wrong array length: " + rglData.Length.ToString(CultureInfo.InvariantCulture));
139             return rglData[3] >= 0x4b3b4ca8L && ((rglData[3] > 0x4b3b4ca8L) || (rglData[2] > 0x5a86c47aL) || (rglData[2] == 0x5a86c47aL) && (rglData[1] >= 0x098a2240L));
140         }
141 
142 
143         // Multi-precision one super-digit divide in place.
144         // U = U / D,
145         // R = U % D
146         // Length of U can decrease
MpDiv1System.Xml.BinXmlSqlDecimal147         private static void MpDiv1(uint[] rgulU,      // InOut| U
148                                    ref int ciulU,      // InOut| # of digits in U
149                                    uint iulD,       // In    | D
150                                    out uint iulR        // Out    | R
151                                    ) {
152             Debug.Assert(rgulU.Length == x_cNumeMax);
153 
154             uint ulCarry = 0;
155             ulong dwlAccum;
156             ulong ulD = (ulong)iulD;
157             int idU = ciulU;
158 
159             Debug.Assert(iulD != 0, "iulD != 0", "Divided by zero!");
160             Debug.Assert(iulD > 0, "iulD > 0", "Invalid data: less than zero");
161             Debug.Assert(ciulU > 0, "ciulU > 0", "No data in the array");
162             while (idU > 0) {
163                 idU--;
164                 dwlAccum = (((ulong)ulCarry) << 32) + (ulong)(rgulU[idU]);
165                 rgulU[idU] = (uint)(dwlAccum / ulD);
166                 ulCarry = (uint)(dwlAccum - (ulong)rgulU[idU] * ulD);  // (ULONG) (dwlAccum % iulD)
167             }
168 
169             iulR = ulCarry;
170             MpNormalize(rgulU, ref ciulU);
171         }
172         // Normalize multi-precision number - remove leading zeroes
MpNormalizeSystem.Xml.BinXmlSqlDecimal173         private static void MpNormalize(uint[] rgulU,      // In   | Number
174                                         ref int ciulU       // InOut| # of digits
175                                         ) {
176             while (ciulU > 1 && rgulU[ciulU - 1] == 0)
177                 ciulU--;
178         }
179 
180         //    AdjustScale()
181         //
182         //    Adjust number of digits to the right of the decimal point.
183         //    A positive adjustment increases the scale of the numeric value
184         //    while a negative adjustment decreases the scale.  When decreasing
185         //    the scale for the numeric value, the remainder is checked and
186         //    rounded accordingly.
187         //
AdjustScaleSystem.Xml.BinXmlSqlDecimal188         internal void AdjustScale(int digits, bool fRound) {
189             uint ulRem;                  //Remainder when downshifting
190             uint ulShiftBase;            //What to multiply by to effect scale adjust
191             bool fNeedRound = false;     //Do we really need to round?
192             byte bNewScale, bNewPrec;
193             int lAdjust = digits;
194 
195             //If downshifting causes truncation of data
196             if (lAdjust + m_bScale < 0)
197                 throw new XmlException(Res.SqlTypes_ArithTruncation, (string)null);
198 
199             //If uphifting causes scale overflow
200             if (lAdjust + m_bScale > NUMERIC_MAX_PRECISION)
201                 throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
202 
203             bNewScale = (byte)(lAdjust + m_bScale);
204             bNewPrec = (byte)(Math.Min(NUMERIC_MAX_PRECISION, Math.Max(1, lAdjust + m_bPrec)));
205             if (lAdjust > 0) {
206                 m_bScale = bNewScale;
207                 m_bPrec = bNewPrec;
208                 while (lAdjust > 0) {
209                     //if lAdjust>=9, downshift by 10^9 each time, otherwise by the full amount
210                     if (lAdjust >= 9) {
211                         ulShiftBase = x_rgulShiftBase[8];
212                         lAdjust -= 9;
213                     }
214                     else {
215                         ulShiftBase = x_rgulShiftBase[lAdjust - 1];
216                         lAdjust = 0;
217                     }
218 
219                     MultByULong(ulShiftBase);
220                 }
221             }
222             else if (lAdjust < 0) {
223                 do {
224                     if (lAdjust <= -9) {
225                         ulShiftBase = x_rgulShiftBase[8];
226                         lAdjust += 9;
227                     }
228                     else {
229                         ulShiftBase = x_rgulShiftBase[-lAdjust - 1];
230                         lAdjust = 0;
231                     }
232 
233                     ulRem = DivByULong(ulShiftBase);
234                 } while (lAdjust < 0);
235 
236                 // Do we really need to round?
237                 fNeedRound = (ulRem >= ulShiftBase / 2);
238                 m_bScale = bNewScale;
239                 m_bPrec = bNewPrec;
240             }
241 
242             AssertValid();
243 
244             // After adjusting, if the result is 0 and remainder is less than 5,
245             // set the sign to be positive and return.
246             if (fNeedRound && fRound) {
247                 // If remainder is 5 or above, increment/decrement by 1.
248                 AddULong(1);
249             }
250             else if (FZero())
251                 this.m_bSign = 0;
252         }
253         //    AddULong()
254         //
255         //    Add ulAdd to this numeric.  The result will be returned in *this.
256         //
257         //    Parameters:
258         //        this    - IN Operand1 & OUT Result
259         //        ulAdd    - IN operand2.
260         //
AddULongSystem.Xml.BinXmlSqlDecimal261         private void AddULong(uint ulAdd) {
262             ulong dwlAccum = (ulong)ulAdd;
263             int iData;                  // which UI4 in this we are on
264             int iDataMax = (int)m_bLen; // # of UI4s in this
265             uint[] rguiData = new uint[4] { m_data1, m_data2, m_data3, m_data4 };
266 
267             // Add, starting at the LS UI4 until out of UI4s or no carry
268             iData = 0;
269             do {
270                 dwlAccum += (ulong)rguiData[iData];
271                 rguiData[iData] = (uint)dwlAccum;       // equivalent to mod x_dwlBaseUI4
272                 dwlAccum >>= 32;                        // equivalent to dwlAccum /= x_dwlBaseUI4;
273                 if (0 == dwlAccum) {
274                     StoreFromWorkingArray(rguiData);
275                     return;
276                 }
277 
278                 iData++;
279             } while (iData < iDataMax);
280 
281             // There is carry at the end
282             Debug.Assert(dwlAccum < x_ulInt32Base, "dwlAccum < x_lInt32Base", "");
283 
284             // Either overflowed
285             if (iData == x_cNumeMax)
286                 throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
287 
288             // Or need to extend length by 1 UI4
289             rguiData[iData] = (uint)dwlAccum;
290             m_bLen++;
291             if (FGt10_38(rguiData))
292                 throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
293 
294             StoreFromWorkingArray(rguiData);
295         }
296         // multiply by a long integer
MultByULongSystem.Xml.BinXmlSqlDecimal297         private void MultByULong(uint uiMultiplier) {
298             int iDataMax = m_bLen; // How many UI4s currently in *this
299             ulong dwlAccum = 0;       // accumulated sum
300             ulong dwlNextAccum = 0;   // accumulation past dwlAccum
301             int iData;              // which UI4 in *This we are on.
302             uint[] rguiData = new uint[4] { m_data1, m_data2, m_data3, m_data4 };
303 
304             for (iData = 0; iData < iDataMax; iData++) {
305                 Debug.Assert(dwlAccum < x_ulInt32Base);
306 
307                 ulong ulTemp = (ulong)rguiData[iData];
308 
309                 dwlNextAccum = ulTemp * (ulong)uiMultiplier;
310                 dwlAccum += dwlNextAccum;
311                 if (dwlAccum < dwlNextAccum)        // Overflow of int64 add
312                     dwlNextAccum = x_ulInt32Base;   // how much to add to dwlAccum after div x_dwlBaseUI4
313                     else
314                     dwlNextAccum = 0;
315 
316                 rguiData[iData] = (uint)dwlAccum;           // equivalent to mod x_dwlBaseUI4
317                 dwlAccum = (dwlAccum >> 32) + dwlNextAccum; // equivalent to div x_dwlBaseUI4
318             }
319 
320             // If any carry,
321             if (dwlAccum != 0) {
322                 // Either overflowed
323                 Debug.Assert(dwlAccum < x_ulInt32Base, "dwlAccum < x_dwlBaseUI4", "Integer overflow");
324                 if (iDataMax == x_cNumeMax)
325                     throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
326 
327                 // Or extend length by one uint
328                 rguiData[iDataMax] = (uint)dwlAccum;
329                 m_bLen++;
330             }
331 
332             if (FGt10_38(rguiData))
333                 throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
334 
335             StoreFromWorkingArray(rguiData);
336         }
337         //    DivByULong()
338         //
339         //    Divide numeric value by a ULONG.  The result will be returned
340         //    in the dividend *this.
341         //
342         //    Parameters:
343         //        this        - IN Dividend & OUT Result
344         //        ulDivisor    - IN Divisor
345         //    Returns:        - OUT Remainder
346         //
DivByULongSystem.Xml.BinXmlSqlDecimal347         internal uint DivByULong(uint iDivisor) {
348             ulong dwlDivisor = (ulong)iDivisor;
349             ulong dwlAccum = 0;           //Accumulated sum
350             uint ulQuotientCur = 0;      // Value of the current UI4 of the quotient
351             bool fAllZero = true;    // All of the quotient (so far) has been 0
352             int iData;              //Which UI4 currently on
353 
354             // Check for zero divisor.
355             if (dwlDivisor == 0)
356                 throw new XmlException(Res.SqlTypes_DivideByZero, (string)null);
357 
358             // Copy into array, so that we can iterate through the data
359             uint[] rguiData = new uint[4] { m_data1, m_data2, m_data3, m_data4 };
360 
361             // Start from the MS UI4 of quotient, divide by divisor, placing result
362             //        in quotient and carrying the remainder.
363             //DEVNOTE DWORDLONG sufficient accumulator since:
364             //        Accum < Divisor <= 2^32 - 1    at start each loop
365             //                                    initially,and mod end previous loop
366             //        Accum*2^32 < 2^64 - 2^32
367             //                                    multiply both side by 2^32 (x_dwlBaseUI4)
368             //        Accum*2^32 + m_rgulData < 2^64
369             //                                    rglData < 2^32
370             for (iData = m_bLen; iData > 0; iData--) {
371                 Debug.Assert(dwlAccum < dwlDivisor);
372                 dwlAccum = (dwlAccum << 32) + (ulong)(rguiData[iData - 1]); // dwlA*x_dwlBaseUI4 + rglData
373                 Debug.Assert((dwlAccum / dwlDivisor) < x_ulInt32Base);
374 
375                 //Update dividend to the quotient.
376                 ulQuotientCur = (uint)(dwlAccum / dwlDivisor);
377                 rguiData[iData - 1] = ulQuotientCur;
378 
379                 //Remainder to be carried to the next lower significant byte.
380                 dwlAccum = dwlAccum % dwlDivisor;
381 
382                 // While current part of quotient still 0, reduce length
383                 fAllZero = fAllZero && (ulQuotientCur == 0);
384                 if (fAllZero)
385                     m_bLen--;
386             }
387 
388             StoreFromWorkingArray(rguiData);
389 
390             // If result is 0, preserve sign but set length to 5
391             if (fAllZero)
392                 m_bLen = 1;
393 
394             AssertValid();
395 
396             // return the remainder
397             Debug.Assert(dwlAccum < x_ulInt32Base);
398             return (uint)dwlAccum;
399         }
400 
401         //Determine the number of uints needed for a numeric given a precision
402         //Precision        Length
403         //    0            invalid
404         //    1-9            1
405         //    10-19        2
406         //    20-28        3
407         //    29-38        4
408         // The array in Shiloh. Listed here for comparison.
409         //private static readonly byte[] rgCLenFromPrec = new byte[] {5,5,5,5,5,5,5,5,5,9,9,9,9,9,
410         //    9,9,9,9,9,13,13,13,13,13,13,13,13,13,17,17,17,17,17,17,17,17,17,17};
411         private static readonly byte[] rgCLenFromPrec = new byte[] {
412             1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
413         };
CLenFromPrecSystem.Xml.BinXmlSqlDecimal414         private static byte CLenFromPrec(byte bPrec) {
415             Debug.Assert(bPrec <= MaxPrecision && bPrec > 0, "bPrec <= MaxPrecision && bPrec > 0", "Invalid numeric precision");
416             return rgCLenFromPrec[bPrec - 1];
417         }
418 
ChFromDigitSystem.Xml.BinXmlSqlDecimal419         private static char ChFromDigit(uint uiDigit) {
420             Debug.Assert(uiDigit < 10);
421             return (char)(uiDigit + '0');
422         }
423 
ToDecimalSystem.Xml.BinXmlSqlDecimal424         public Decimal ToDecimal() {
425             if ((int)m_data4 != 0 || m_bScale > 28)
426                 throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
427 
428             return new Decimal((int)m_data1, (int)m_data2, (int)m_data3, !IsPositive, m_bScale);
429         }
430 
TrimTrailingZerosSystem.Xml.BinXmlSqlDecimal431         void TrimTrailingZeros() {
432             uint[]  rgulNumeric = new uint[4] { m_data1, m_data2, m_data3, m_data4};
433             int     culLen = m_bLen;
434             uint    ulRem; //Remainder of a division by x_ulBase10, i.e.,least significant digit
435 
436             // special-case 0
437             if (culLen == 1 && rgulNumeric[0] == 0) {
438                 m_bScale = 0;
439                 return;
440             }
441 
442             while (m_bScale > 0 && (culLen > 1 || rgulNumeric[0] != 0)) {
443                 MpDiv1 (rgulNumeric, ref culLen, 10, out ulRem);
444                 if ( ulRem == 0 ) {
445                     m_data1 = rgulNumeric[0];
446                     m_data2 = rgulNumeric[1];
447                     m_data3 = rgulNumeric[2];
448                     m_data4 = rgulNumeric[3];
449                     m_bScale--;
450                 }
451                 else {
452                     break;
453                 }
454             }
455             if (m_bLen == 4 && m_data4 == 0)
456                 m_bLen = 3;
457             if (m_bLen == 3 && m_data3 == 0)
458                 m_bLen = 2;
459             if (m_bLen == 2 && m_data2 == 0)
460                 m_bLen = 1;
461         }
462 
ToStringSystem.Xml.BinXmlSqlDecimal463         public override String ToString() {
464             AssertValid();
465 
466             // Make local copy of data to avoid modifying input.
467             uint[]  rgulNumeric = new uint[4] { m_data1, m_data2, m_data3, m_data4};
468             int     culLen = m_bLen;
469             char[]  pszTmp = new char[NUMERIC_MAX_PRECISION + 1];   //Local Character buffer to hold
470                                                                     //the decimal digits, from the
471                                                                     //lowest significant to highest significant
472 
473             int     iDigits = 0;//Number of significant digits
474             uint    ulRem; //Remainder of a division by x_ulBase10, i.e.,least significant digit
475 
476             // Build the final numeric string by inserting the sign, reversing
477             // the order and inserting the decimal number at the correct position
478 
479             //Retrieve each digit from the lowest significant digit
480             while (culLen > 1 || rgulNumeric[0] != 0) {
481                 MpDiv1 (rgulNumeric, ref culLen, 10, out ulRem);
482                 //modulo x_ulBase10 is the lowest significant digit
483                 pszTmp[iDigits++] = ChFromDigit(ulRem);
484             }
485 
486             // if scale of the number has not been
487             // reached pad remaining number with zeros.
488             while (iDigits <= m_bScale) {
489                 pszTmp[iDigits++] = ChFromDigit(0);
490             }
491 
492             bool fPositive = IsPositive;
493 
494             // Increment the result length if negative (need to add '-')
495             int uiResultLen = fPositive ? iDigits : iDigits + 1;
496 
497             // Increment the result length if scale > 0 (need to add '.')
498             if (m_bScale > 0)
499                 uiResultLen++;
500 
501             char[] szResult = new char[uiResultLen];
502             int iCurChar = 0;
503 
504             if (!fPositive)
505                 szResult[iCurChar ++] = '-';
506 
507             while (iDigits > 0) {
508                 if (iDigits-- == m_bScale)
509                     szResult[iCurChar ++] = '.';
510                 szResult[iCurChar ++] = pszTmp[iDigits];
511             }
512 
513             AssertValid();
514 
515             return new String(szResult);
516         }
517 
518 
519         // Is this RE numeric valid?
520         [System.Diagnostics.Conditional("DEBUG")]
AssertValidSystem.Xml.BinXmlSqlDecimal521         private void AssertValid() {
522             // Scale,Prec in range
523             Debug.Assert(m_bScale <= NUMERIC_MAX_PRECISION, "m_bScale <= NUMERIC_MAX_PRECISION", "In AssertValid");
524             Debug.Assert(m_bScale <= m_bPrec, "m_bScale <= m_bPrec", "In AssertValid");
525             Debug.Assert(m_bScale >= 0, "m_bScale >= 0", "In AssertValid");
526             Debug.Assert(m_bPrec > 0, "m_bPrec > 0", "In AssertValid");
527             Debug.Assert(CLenFromPrec(m_bPrec) >= m_bLen, "CLenFromPrec(m_bPrec) >= m_bLen", "In AssertValid");
528             Debug.Assert(m_bLen <= x_cNumeMax, "m_bLen <= x_cNumeMax", "In AssertValid");
529 
530             uint[] rglData = new uint[4] { m_data1, m_data2, m_data3, m_data4 };
531 
532             // highest UI4 is non-0 unless value "zero"
533             if (rglData[m_bLen - 1] == 0) {
534                 Debug.Assert(m_bLen == 1, "m_bLen == 1", "In AssertValid");
535             }
536 
537             // All UI4s from length to end are 0
538             for (int iulData = m_bLen; iulData < x_cNumeMax; iulData++)
539                 Debug.Assert(rglData[iulData] == 0, "rglData[iulData] == 0", "In AssertValid");
540         }
541     }
542 
543     internal struct BinXmlSqlMoney {
544         long data;
545 
BinXmlSqlMoneySystem.Xml.BinXmlSqlMoney546         public BinXmlSqlMoney(int v) { this.data = v; }
BinXmlSqlMoneySystem.Xml.BinXmlSqlMoney547         public BinXmlSqlMoney(long v) { this.data = v; }
548 
ToDecimalSystem.Xml.BinXmlSqlMoney549         public Decimal ToDecimal() {
550             bool neg;
551             ulong v;
552             if (this.data < 0) {
553                 neg = true;
554                 v = (ulong)unchecked(-this.data);
555             }
556             else {
557                 neg = false;
558                 v = (ulong)this.data;
559             }
560             // SQL Server stores money8 as ticks of 1/10000.
561             const byte MoneyScale = 4;
562             return new Decimal(unchecked((int)v), unchecked((int)(v >> 32)), 0, neg, MoneyScale);
563         }
564 
ToStringSystem.Xml.BinXmlSqlMoney565         public override String ToString() {
566             Decimal money = ToDecimal();
567             // Formatting of SqlMoney: At least two digits after decimal point
568             return money.ToString("#0.00##", CultureInfo.InvariantCulture);
569         }
570     }
571 
572     internal abstract class BinXmlDateTime {
573 
574         const int MaxFractionDigits = 7;
575 
576         static internal int[] KatmaiTimeScaleMultiplicator = new int[8] {
577             10000000,
578             1000000,
579             100000,
580             10000,
581             1000,
582             100,
583             10,
584             1,
585         };
586 
Write2Dig( StringBuilder sb, int val )587         static void Write2Dig( StringBuilder sb, int val ) {
588             Debug.Assert(val >= 0 && val < 100);
589             sb.Append((char)('0' + (val/10)));
590             sb.Append((char)('0' + (val%10)));
591         }
Write4DigNeg(StringBuilder sb, int val)592         static void Write4DigNeg(StringBuilder sb, int val) {
593             Debug.Assert(val > -10000 && val < 10000);
594             if (val < 0) {
595                 val = -val;
596                 sb.Append('-');
597             }
598             Write2Dig(sb, val/100);
599             Write2Dig(sb, val%100);
600         }
601 
Write3Dec(StringBuilder sb, int val)602         static void Write3Dec(StringBuilder sb, int val) {
603             Debug.Assert(val >= 0 && val < 1000);
604             int c3 = val % 10;
605             val /= 10;
606             int c2 = val % 10;
607             val /= 10;
608             int c1 = val;
609             sb.Append('.');
610             sb.Append((char)('0'+c1));
611             sb.Append((char)('0'+c2));
612             sb.Append((char)('0'+c3));
613         }
614 
WriteDate(StringBuilder sb, int yr, int mnth, int day)615         static void WriteDate(StringBuilder sb, int yr, int mnth, int day) {
616             Write4DigNeg(sb, yr);
617             sb.Append('-');
618             Write2Dig(sb, mnth);
619             sb.Append('-');
620             Write2Dig(sb, day);
621         }
622 
WriteTime(StringBuilder sb, int hr, int min, int sec, int ms)623         static void WriteTime(StringBuilder sb, int hr, int min, int sec, int ms) {
624             Write2Dig(sb, hr);
625             sb.Append(':');
626             Write2Dig(sb, min);
627             sb.Append(':');
628             Write2Dig(sb, sec);
629             if (ms != 0) {
630                 Write3Dec(sb, ms);
631             }
632         }
633 
WriteTimeFullPrecision(StringBuilder sb, int hr, int min, int sec, int fraction)634         static void WriteTimeFullPrecision(StringBuilder sb, int hr, int min, int sec, int fraction) {
635             Write2Dig(sb, hr);
636             sb.Append(':');
637             Write2Dig(sb, min);
638             sb.Append(':');
639             Write2Dig(sb, sec);
640             if (fraction != 0) {
641                 int fractionDigits = MaxFractionDigits;
642                 while (fraction % 10 == 0) {
643                     fractionDigits --;
644                     fraction /= 10;
645                 }
646                 char[] charArray = new char[fractionDigits];
647                 while(fractionDigits > 0) {
648                     fractionDigits--;
649                     charArray[fractionDigits] = (char)(fraction % 10 + '0');
650                     fraction /= 10;
651                 }
652                 sb.Append('.');
653                 sb.Append(charArray);
654             }
655         }
656 
WriteTimeZone(StringBuilder sb, TimeSpan zone)657         static void WriteTimeZone(StringBuilder sb, TimeSpan zone) {
658             bool negTimeZone = true;
659             if (zone.Ticks < 0) {
660                 negTimeZone = false;
661                 zone = zone.Negate();
662             }
663             WriteTimeZone(sb, negTimeZone, zone.Hours, zone.Minutes);
664         }
665 
WriteTimeZone(StringBuilder sb, bool negTimeZone, int hr, int min)666         static void WriteTimeZone(StringBuilder sb, bool negTimeZone, int hr, int min) {
667             if (hr == 0 && min == 0) {
668                 sb.Append('Z');
669             }
670             else {
671                 sb.Append(negTimeZone ? '+' : '-');
672                 Write2Dig(sb, hr);
673                 sb.Append(':');
674                 Write2Dig(sb, min);
675             }
676         }
677 
BreakDownXsdDateTime(long val, out int yr, out int mnth, out int day, out int hr, out int min, out int sec, out int ms)678         static void BreakDownXsdDateTime(long val, out int yr, out int mnth, out int day, out int hr, out int min, out int sec, out int ms) {
679             if (val < 0)
680                 goto Error;
681             long date = val / 4; // trim indicator bits
682             ms = (int)(date % 1000);
683             date /= 1000;
684             sec = (int)(date % 60);
685             date /= 60;
686             min = (int)(date % 60);
687             date /= 60;
688             hr = (int)(date % 24);
689             date /= 24;
690             day = (int)(date % 31) + 1;
691             date /= 31;
692             mnth = (int)(date % 12) + 1;
693             date /= 12;
694             yr = (int)(date - 9999);
695             if (yr < -9999 || yr > 9999)
696                 goto Error;
697             return;
698         Error:
699             throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
700         }
701 
BreakDownXsdDate(long val, out int yr, out int mnth, out int day, out bool negTimeZone, out int hr, out int min)702         static void BreakDownXsdDate(long val, out int yr, out int mnth, out int day, out bool negTimeZone, out int hr, out int min) {
703             if (val < 0)
704                 goto Error;
705             val = val / 4; // trim indicator bits
706             int totalMin = (int)(val % (29*60)) - 60*14;
707             long totalDays = val / (29*60);
708 
709             if (negTimeZone = (totalMin < 0))
710                 totalMin = -totalMin;
711 
712             min = totalMin % 60;
713             hr = totalMin / 60;
714 
715             day = (int)(totalDays % 31) + 1;
716             totalDays /= 31;
717             mnth = (int)(totalDays % 12) + 1;
718             yr = (int)(totalDays / 12) - 9999;
719             if (yr < -9999 || yr > 9999)
720                 goto Error;
721             return;
722         Error:
723             throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
724         }
725 
BreakDownXsdTime(long val, out int hr, out int min, out int sec, out int ms)726         static void BreakDownXsdTime(long val, out int hr, out int min, out int sec, out int ms) {
727             if (val < 0)
728                 goto Error;
729             val = val / 4; // trim indicator bits
730             ms = (int)(val % 1000);
731             val /= 1000;
732             sec = (int)(val % 60);
733             val /= 60;
734             min = (int)(val % 60);
735             hr = (int)(val / 60);
736             if (0 > hr || hr > 23)
737                 goto Error;
738             return;
739         Error:
740             throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
741         }
742 
XsdDateTimeToString(long val)743         public static string XsdDateTimeToString(long val) {
744             int yr; int mnth; int day; int hr; int min; int sec; int ms;
745             BreakDownXsdDateTime(val, out yr, out mnth, out day, out hr, out min, out sec, out ms);
746             StringBuilder sb = new StringBuilder(20);
747             WriteDate(sb, yr, mnth, day);
748             sb.Append('T');
749             WriteTime(sb, hr, min, sec, ms);
750             sb.Append('Z');
751             return sb.ToString();
752         }
XsdDateTimeToDateTime(long val)753         public static DateTime XsdDateTimeToDateTime(long val) {
754             int yr; int mnth; int day; int hr; int min; int sec; int ms;
755             BreakDownXsdDateTime(val, out yr, out mnth, out day, out hr, out min, out sec, out ms);
756             return new DateTime(yr, mnth, day, hr, min, sec, ms, DateTimeKind.Utc);
757         }
758 
XsdDateToString(long val)759         public static string XsdDateToString(long val) {
760             int yr; int mnth; int day; int hr; int min; bool negTimeZ;
761             BreakDownXsdDate(val, out yr, out mnth, out day, out negTimeZ, out hr, out min);
762             StringBuilder sb = new StringBuilder(20);
763             WriteDate(sb, yr, mnth, day);
764             WriteTimeZone(sb, negTimeZ, hr, min);
765             return sb.ToString();
766         }
XsdDateToDateTime(long val)767         public static DateTime XsdDateToDateTime(long val) {
768             int yr; int mnth; int day; int hr; int min; bool negTimeZ;
769             BreakDownXsdDate(val, out yr, out mnth, out day, out negTimeZ, out hr, out min);
770             DateTime d = new DateTime(yr, mnth, day, 0, 0, 0, DateTimeKind.Utc);
771             // adjust for timezone
772             int adj = (negTimeZ ? -1 : 1) * ( (hr * 60) + min );
773             return TimeZone.CurrentTimeZone.ToLocalTime( d.AddMinutes(adj) );
774         }
775 
XsdTimeToString(long val)776         public static string XsdTimeToString(long val) {
777             int hr; int min; int sec; int ms;
778             BreakDownXsdTime(val, out hr, out min, out sec, out ms);
779             StringBuilder sb = new StringBuilder(16);
780             WriteTime(sb, hr, min, sec, ms);
781             sb.Append('Z');
782             return sb.ToString();
783         }
XsdTimeToDateTime(long val)784         public static DateTime XsdTimeToDateTime(long val) {
785             int hr; int min; int sec; int ms;
786             BreakDownXsdTime(val, out hr, out min, out sec, out ms);
787             return new DateTime(1, 1, 1, hr, min, sec, ms, DateTimeKind.Utc);
788         }
789 
SqlDateTimeToString(int dateticks, uint timeticks)790         public static string SqlDateTimeToString(int dateticks, uint timeticks) {
791             DateTime dateTime = SqlDateTimeToDateTime(dateticks, timeticks);
792             string format = (dateTime.Millisecond != 0) ? "yyyy/MM/dd\\THH:mm:ss.ffff" : "yyyy/MM/dd\\THH:mm:ss";
793             return dateTime.ToString(format, CultureInfo.InvariantCulture);
794         }
SqlDateTimeToDateTime(int dateticks, uint timeticks)795         public static DateTime SqlDateTimeToDateTime(int dateticks, uint timeticks) {
796             DateTime SQLBaseDate = new DateTime(1900, 1, 1);
797             //long millisecond = (long)(((ulong)timeticks * 20 + (ulong)3) / (ulong)6);
798             long millisecond = (long)(timeticks / SQLTicksPerMillisecond + 0.5);
799             return SQLBaseDate.Add( new TimeSpan( dateticks * TimeSpan.TicksPerDay +
800                                                   millisecond * TimeSpan.TicksPerMillisecond ) );
801         }
802 
803         // Number of (100ns) ticks per time unit
804         private static readonly double SQLTicksPerMillisecond = 0.3;
805         public static readonly int SQLTicksPerSecond = 300;
806         public static readonly int SQLTicksPerMinute = SQLTicksPerSecond * 60;
807         public static readonly int SQLTicksPerHour = SQLTicksPerMinute * 60;
808         private static readonly int SQLTicksPerDay = SQLTicksPerHour * 24;
809 
810 
SqlSmallDateTimeToString(short dateticks, ushort timeticks)811         public static string SqlSmallDateTimeToString(short dateticks, ushort timeticks) {
812             DateTime dateTime = SqlSmallDateTimeToDateTime(dateticks, timeticks);
813             return dateTime.ToString("yyyy/MM/dd\\THH:mm:ss", CultureInfo.InvariantCulture);
814         }
SqlSmallDateTimeToDateTime(short dateticks, ushort timeticks)815         public static DateTime SqlSmallDateTimeToDateTime(short dateticks, ushort timeticks) {
816             return SqlDateTimeToDateTime( (int)dateticks, (uint)(timeticks * SQLTicksPerMinute) );
817         }
818 
819         // Conversions of the Katmai date & time types to DateTime
XsdKatmaiDateToDateTime(byte[] data, int offset)820         public static DateTime XsdKatmaiDateToDateTime(byte[] data, int offset) {
821             // Katmai SQL type "DATE"
822             long dateTicks = GetKatmaiDateTicks(data, ref offset);
823             DateTime dt = new DateTime(dateTicks);
824             return dt;
825         }
826 
XsdKatmaiDateTimeToDateTime(byte[] data, int offset)827         public static DateTime XsdKatmaiDateTimeToDateTime(byte[] data, int offset) {
828             // Katmai SQL type "DATETIME2"
829             long timeTicks = GetKatmaiTimeTicks(data, ref offset);
830             long dateTicks = GetKatmaiDateTicks(data, ref offset);
831             DateTime dt = new DateTime(dateTicks + timeTicks);
832             return dt;
833         }
834 
XsdKatmaiTimeToDateTime(byte[] data, int offset)835         public static DateTime XsdKatmaiTimeToDateTime(byte[] data, int offset) {
836             // TIME without zone is stored as DATETIME2
837             return XsdKatmaiDateTimeToDateTime(data, offset);
838         }
839 
XsdKatmaiDateOffsetToDateTime( byte[] data, int offset )840         public static DateTime XsdKatmaiDateOffsetToDateTime( byte[] data, int offset ) {
841             // read the timezoned value into DateTimeOffset and then convert to local time
842             return XsdKatmaiDateOffsetToDateTimeOffset(data, offset).LocalDateTime;
843         }
844 
XsdKatmaiDateTimeOffsetToDateTime(byte[] data, int offset)845         public static DateTime XsdKatmaiDateTimeOffsetToDateTime(byte[] data, int offset) {
846             // read the timezoned value into DateTimeOffset and then convert to local time
847             return XsdKatmaiDateTimeOffsetToDateTimeOffset(data, offset).LocalDateTime;
848         }
849 
XsdKatmaiTimeOffsetToDateTime(byte[] data, int offset)850         public static DateTime XsdKatmaiTimeOffsetToDateTime(byte[] data, int offset) {
851             // read the timezoned value into DateTimeOffset and then convert to local time
852             return XsdKatmaiTimeOffsetToDateTimeOffset(data, offset).LocalDateTime;
853         }
854 
855         // Conversions of the Katmai date & time types to DateTimeOffset
XsdKatmaiDateToDateTimeOffset( byte[] data, int offset )856         public static DateTimeOffset XsdKatmaiDateToDateTimeOffset( byte[] data, int offset ) {
857             // read the value into DateTime and then convert it to DateTimeOffset, which adds local time zone
858             return (DateTimeOffset)XsdKatmaiDateToDateTime(data, offset);
859         }
860 
XsdKatmaiDateTimeToDateTimeOffset(byte[] data, int offset)861         public static DateTimeOffset XsdKatmaiDateTimeToDateTimeOffset(byte[] data, int offset) {
862             // read the value into DateTime and then convert it to DateTimeOffset, which adds local time zone
863             return (DateTimeOffset)XsdKatmaiDateTimeToDateTime(data, offset);
864         }
865 
XsdKatmaiTimeToDateTimeOffset(byte[] data, int offset)866         public static DateTimeOffset XsdKatmaiTimeToDateTimeOffset(byte[] data, int offset) {
867             // read the value into DateTime and then convert it to DateTimeOffset, which adds local time zone
868             return (DateTimeOffset)XsdKatmaiTimeToDateTime(data, offset);
869         }
870 
XsdKatmaiDateOffsetToDateTimeOffset(byte[] data, int offset)871         public static DateTimeOffset XsdKatmaiDateOffsetToDateTimeOffset(byte[] data, int offset) {
872             // DATE with zone is stored as DATETIMEOFFSET
873             return XsdKatmaiDateTimeOffsetToDateTimeOffset(data, offset);
874         }
875 
XsdKatmaiDateTimeOffsetToDateTimeOffset(byte[] data, int offset)876         public static DateTimeOffset XsdKatmaiDateTimeOffsetToDateTimeOffset(byte[] data, int offset) {
877             // Katmai SQL type "DATETIMEOFFSET"
878             long timeTicks = GetKatmaiTimeTicks(data, ref offset);
879             long dateTicks = GetKatmaiDateTicks(data, ref offset);
880             long zoneTicks = GetKatmaiTimeZoneTicks(data, offset);
881             // The DATETIMEOFFSET values are serialized in UTC, but DateTimeOffset takes adjusted time -> we need to add zoneTicks
882             DateTimeOffset dto = new DateTimeOffset(dateTicks + timeTicks + zoneTicks, new TimeSpan(zoneTicks));
883             return dto;
884         }
885 
XsdKatmaiTimeOffsetToDateTimeOffset(byte[] data, int offset)886         public static DateTimeOffset XsdKatmaiTimeOffsetToDateTimeOffset(byte[] data, int offset) {
887             // TIME with zone is stored as DATETIMEOFFSET
888             return XsdKatmaiDateTimeOffsetToDateTimeOffset(data, offset);
889         }
890 
891         // Conversions of the Katmai date & time types to string
XsdKatmaiDateToString(byte[] data, int offset)892         public static string XsdKatmaiDateToString(byte[] data, int offset) {
893             DateTime dt = XsdKatmaiDateToDateTime(data, offset);
894             StringBuilder sb = new StringBuilder(10);
895             WriteDate(sb, dt.Year, dt.Month, dt.Day);
896             return sb.ToString();
897         }
898 
XsdKatmaiDateTimeToString(byte[] data, int offset)899         public static string XsdKatmaiDateTimeToString(byte[] data, int offset) {
900             DateTime dt = XsdKatmaiDateTimeToDateTime(data, offset);
901             StringBuilder sb = new StringBuilder(33);
902             WriteDate(sb, dt.Year, dt.Month, dt.Day);
903             sb.Append('T');
904             WriteTimeFullPrecision(sb, dt.Hour, dt.Minute, dt.Second, GetFractions(dt));
905             return sb.ToString();
906         }
907 
XsdKatmaiTimeToString(byte[] data, int offset)908         public static string XsdKatmaiTimeToString(byte[] data, int offset) {
909             DateTime dt = XsdKatmaiTimeToDateTime(data, offset);
910             StringBuilder sb = new StringBuilder(16);
911             WriteTimeFullPrecision(sb, dt.Hour, dt.Minute, dt.Second, GetFractions(dt));
912             return sb.ToString();
913         }
914 
XsdKatmaiDateOffsetToString(byte[] data, int offset)915         public static string XsdKatmaiDateOffsetToString(byte[] data, int offset) {
916             DateTimeOffset dto = XsdKatmaiDateOffsetToDateTimeOffset(data, offset);
917             StringBuilder sb = new StringBuilder(16);
918             WriteDate(sb, dto.Year, dto.Month, dto.Day);
919             WriteTimeZone(sb, dto.Offset);
920             return sb.ToString();
921         }
922 
XsdKatmaiDateTimeOffsetToString(byte[] data, int offset)923         public static string XsdKatmaiDateTimeOffsetToString(byte[] data, int offset) {
924             DateTimeOffset dto = XsdKatmaiDateTimeOffsetToDateTimeOffset(data, offset);
925             StringBuilder sb = new StringBuilder(39);
926             WriteDate(sb, dto.Year, dto.Month, dto.Day);
927             sb.Append('T');
928             WriteTimeFullPrecision(sb, dto.Hour, dto.Minute, dto.Second, GetFractions(dto));
929             WriteTimeZone(sb, dto.Offset);
930             return sb.ToString();
931         }
932 
XsdKatmaiTimeOffsetToString(byte[] data, int offset)933         public static string XsdKatmaiTimeOffsetToString(byte[] data, int offset) {
934             DateTimeOffset dto = XsdKatmaiTimeOffsetToDateTimeOffset(data, offset);
935             StringBuilder sb = new StringBuilder(22);
936             WriteTimeFullPrecision(sb, dto.Hour, dto.Minute, dto.Second, GetFractions(dto));
937             WriteTimeZone(sb, dto.Offset);
938             return sb.ToString();
939         }
940 
941         // Helper methods for the Katmai date & time types
GetKatmaiDateTicks(byte[] data, ref int pos)942         static long GetKatmaiDateTicks(byte[] data, ref int pos) {
943             int p = pos;
944             pos = p + 3;
945             return (data[p] | data[p + 1] << 8 | data[p + 2] << 16) * TimeSpan.TicksPerDay;
946         }
947 
GetKatmaiTimeTicks(byte[] data, ref int pos)948         static long GetKatmaiTimeTicks(byte[] data, ref int pos) {
949             int p = pos;
950             byte scale = data[p];
951             long timeTicks;
952             p++;
953             if (scale <= 2) {
954                 timeTicks = data[p] | (data[p + 1] << 8) | (data[p + 2] << 16);
955                 pos = p + 3;
956             }
957             else if (scale <= 4) {
958                 timeTicks = data[p] | (data[p + 1] << 8) | (data[p + 2] << 16);
959                 timeTicks |= ((long)data[p + 3] << 24);
960                 pos = p + 4;
961             }
962             else if (scale <= 7) {
963                 timeTicks = data[p] | (data[p + 1] << 8) | (data[p + 2] << 16);
964                 timeTicks |= ((long)data[p + 3] << 24) | ((long)data[p + 4] << 32);
965                 pos = p + 5;
966             }
967             else {
968                 throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
969             }
970             return timeTicks * KatmaiTimeScaleMultiplicator[scale];
971         }
972 
GetKatmaiTimeZoneTicks(byte[] data, int pos)973         static long GetKatmaiTimeZoneTicks(byte[] data, int pos) {
974             return (short)(data[pos] | data[pos + 1] << 8) * TimeSpan.TicksPerMinute;
975         }
976 
GetFractions(DateTime dt)977         static int GetFractions(DateTime dt) {
978             return (int)(dt.Ticks - new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second).Ticks);
979         }
980 
GetFractions(DateTimeOffset dt)981         static int GetFractions(DateTimeOffset dt) {
982             return (int)(dt.Ticks - new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second).Ticks);
983         }
984 
985         /*
986         const long SqlDateTicks2Ticks = (long)10000 * 1000 * 60 * 60 * 24;
987         const long SqlBaseDate = 693595;
988 
989         public static void DateTime2SqlDateTime(DateTime datetime, out int dateticks, out uint timeticks) {
990             dateticks = (int)(datetime.Ticks / SqlDateTicks2Ticks) - 693595;
991             double time = (double)(datetime.Ticks % SqlDateTicks2Ticks);
992             time = time / 10000; // adjust to ms
993             time = time * 0.3 + .5;  // adjust to sqlticks (and round correctly)
994             timeticks = (uint)time;
995         }
996         public static void DateTime2SqlSmallDateTime(DateTime datetime, out short dateticks, out ushort timeticks) {
997             dateticks = (short)((int)(datetime.Ticks / SqlDateTicks2Ticks) - 693595);
998             int time = (int)(datetime.Ticks % SqlDateTicks2Ticks);
999             timeticks = (ushort)(time / (10000 * 1000 * 60)); // adjust to min
1000         }
1001         public static long DateTime2XsdTime(DateTime datetime) {
1002             // adjust to ms
1003             return (datetime.TimeOfDay.Ticks / 10000) * 4 + 0;
1004         }
1005         public static long DateTime2XsdDateTime(DateTime datetime) {
1006             long t = datetime.TimeOfDay.Ticks / 10000;
1007             t += (datetime.Day-1) * (long)1000*60*60*24;
1008             t += (datetime.Month-1) * (long)1000*60*60*24*31;
1009             int year = datetime.Year;
1010             if (year < -9999 || year > 9999)
1011                 throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
1012             t += (datetime.Year+9999) * (long)1000*60*60*24*31*12;
1013             return t*4 + 2;
1014         }
1015         public static long DateTime2XsdDate(DateTime datetime) {
1016             // compute local offset
1017             long tzOffset = -TimeZone.CurrentTimeZone.GetUtcOffset(datetime).Ticks  / TimeSpan.TicksPerMinute;
1018             tzOffset += 14*60;
1019             // adjust datetime to UTC
1020             datetime = TimeZone.CurrentTimeZone.ToUniversalTime(datetime);
1021 
1022             Debug.Assert( tzOffset >= 0 );
1023 
1024             int year = datetime.Year;
1025             if (year < -9999 || year > 9999)
1026                 throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
1027             long t = (datetime.Day - 1)
1028                  + 31*(datetime.Month - 1)
1029                  + 31*12*((long)(year+9999));
1030             t *= (29*60); // adjust in timezone
1031             t += tzOffset;
1032             return t*4+1;
1033         }
1034          * */
1035     }
1036 }
1037