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