2016-08-03 10:59:49 +00:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
// <copyright file="XmlBinaryWriter.cs" company="Microsoft">
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
// </copyright>
|
2017-08-21 15:34:15 +00:00
|
|
|
// <owner current="true" primary="true">Microsoft</owner>
|
2016-08-03 10:59:49 +00:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Collections;
|
|
|
|
using System.IO;
|
|
|
|
using System.Text;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
|
|
namespace System.Xml {
|
|
|
|
// This is mostly just a copy of code in SqlTypes.SqlDecimal
|
|
|
|
internal struct BinXmlSqlDecimal {
|
|
|
|
internal byte m_bLen;
|
|
|
|
internal byte m_bPrec;
|
|
|
|
internal byte m_bScale;
|
|
|
|
internal byte m_bSign;
|
|
|
|
internal uint m_data1;
|
|
|
|
internal uint m_data2;
|
|
|
|
internal uint m_data3;
|
|
|
|
internal uint m_data4;
|
|
|
|
|
|
|
|
public bool IsPositive {
|
|
|
|
get {
|
|
|
|
return (m_bSign == 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static readonly byte NUMERIC_MAX_PRECISION = 38; // Maximum precision of numeric
|
|
|
|
private static readonly byte MaxPrecision = NUMERIC_MAX_PRECISION; // max SS precision
|
|
|
|
private static readonly byte MaxScale = NUMERIC_MAX_PRECISION; // max SS scale
|
|
|
|
|
|
|
|
private static readonly int x_cNumeMax = 4;
|
|
|
|
private static readonly long x_lInt32Base = ((long)1) << 32; // 2**32
|
|
|
|
private static readonly ulong x_ulInt32Base = ((ulong)1) << 32; // 2**32
|
|
|
|
private static readonly ulong x_ulInt32BaseForMod = x_ulInt32Base - 1; // 2**32 - 1 (0xFFF...FF)
|
|
|
|
internal static readonly ulong x_llMax = Int64.MaxValue; // Max of Int64
|
|
|
|
//private static readonly uint x_ulBase10 = 10;
|
|
|
|
private static readonly double DUINT_BASE = (double)x_lInt32Base; // 2**32
|
|
|
|
private static readonly double DUINT_BASE2 = DUINT_BASE * DUINT_BASE; // 2**64
|
|
|
|
private static readonly double DUINT_BASE3 = DUINT_BASE2 * DUINT_BASE; // 2**96
|
|
|
|
//private static readonly double DMAX_NUME = 1.0e+38; // Max value of numeric
|
|
|
|
//private static readonly uint DBL_DIG = 17; // Max decimal digits of double
|
|
|
|
//private static readonly byte x_cNumeDivScaleMin = 6; // Minimum result scale of numeric division
|
|
|
|
// Array of multipliers for lAdjust and Ceiling/Floor.
|
|
|
|
private static readonly uint[] x_rgulShiftBase = new uint[9] {
|
|
|
|
10,
|
|
|
|
10 * 10,
|
|
|
|
10 * 10 * 10,
|
|
|
|
10 * 10 * 10 * 10,
|
|
|
|
10 * 10 * 10 * 10 * 10,
|
|
|
|
10 * 10 * 10 * 10 * 10 * 10,
|
|
|
|
10 * 10 * 10 * 10 * 10 * 10 * 10,
|
|
|
|
10 * 10 * 10 * 10 * 10 * 10 * 10 * 10,
|
|
|
|
10 * 10 * 10 * 10 * 10 * 10 * 10 * 10 * 10
|
|
|
|
};
|
|
|
|
|
|
|
|
public BinXmlSqlDecimal (byte[] data, int offset, bool trim) {
|
|
|
|
byte b = data[offset];
|
|
|
|
switch (b) {
|
|
|
|
case 7: m_bLen = 1; break;
|
|
|
|
case 11: m_bLen = 2; break;
|
|
|
|
case 15: m_bLen = 3; break;
|
|
|
|
case 19: m_bLen = 4; break;
|
|
|
|
default: throw new XmlException(Res.XmlBinary_InvalidSqlDecimal, (string[])null);
|
|
|
|
}
|
|
|
|
m_bPrec = data[offset+1];
|
|
|
|
m_bScale = data[offset+2];
|
|
|
|
m_bSign = 0 == data[offset+3] ? (byte)1 : (byte)0;
|
|
|
|
m_data1 = UIntFromByteArray(data, offset+4);
|
|
|
|
m_data2 = (m_bLen > 1) ? UIntFromByteArray(data, offset+8) : 0;
|
|
|
|
m_data3 = (m_bLen > 2) ? UIntFromByteArray(data, offset+12) : 0;
|
|
|
|
m_data4 = (m_bLen > 3) ? UIntFromByteArray(data, offset+16) : 0;
|
|
|
|
if (m_bLen == 4 && m_data4 == 0)
|
|
|
|
m_bLen = 3;
|
|
|
|
if (m_bLen == 3 && m_data3 == 0)
|
|
|
|
m_bLen = 2;
|
|
|
|
if (m_bLen == 2 && m_data2 == 0)
|
|
|
|
m_bLen = 1;
|
|
|
|
AssertValid();
|
|
|
|
if (trim) {
|
|
|
|
TrimTrailingZeros();
|
|
|
|
AssertValid();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Write(Stream strm) {
|
|
|
|
strm.WriteByte((byte)(this.m_bLen * 4 + 3));
|
|
|
|
strm.WriteByte(this.m_bPrec);
|
|
|
|
strm.WriteByte(this.m_bScale);
|
|
|
|
strm.WriteByte(0 == this.m_bSign ? (byte)1 : (byte)0);
|
|
|
|
WriteUI4(this.m_data1, strm);
|
|
|
|
if (this.m_bLen > 1) {
|
|
|
|
WriteUI4(this.m_data2, strm);
|
|
|
|
if (this.m_bLen > 2) {
|
|
|
|
WriteUI4(this.m_data3, strm);
|
|
|
|
if (this.m_bLen > 3) {
|
|
|
|
WriteUI4(this.m_data4, strm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void WriteUI4(uint val, Stream strm) {
|
|
|
|
strm.WriteByte((byte)(val & 0xFF));
|
|
|
|
strm.WriteByte((byte)((val >> 8) & 0xFF));
|
|
|
|
strm.WriteByte((byte)((val >> 16) & 0xFF));
|
|
|
|
strm.WriteByte((byte)((val >> 24) & 0xFF));
|
|
|
|
}
|
|
|
|
|
|
|
|
private static uint UIntFromByteArray(byte[] data, int offset) {
|
|
|
|
int val = (data[offset]) << 0;
|
|
|
|
val |= (data[offset+1]) << 8;
|
|
|
|
val |= (data[offset+2]) << 16;
|
|
|
|
val |= (data[offset+3]) << 24;
|
|
|
|
return unchecked((uint)val);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check whether is zero
|
|
|
|
private bool FZero() {
|
|
|
|
return (m_data1 == 0) && (m_bLen <= 1);
|
|
|
|
}
|
|
|
|
// Store data back from rguiData[] to m_data*
|
|
|
|
private void StoreFromWorkingArray(uint[] rguiData) {
|
|
|
|
Debug.Assert(rguiData.Length == 4);
|
|
|
|
m_data1 = rguiData[0];
|
|
|
|
m_data2 = rguiData[1];
|
|
|
|
m_data3 = rguiData[2];
|
|
|
|
m_data4 = rguiData[3];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the case where we overflowed 10**38, but not 2**128
|
|
|
|
private bool FGt10_38(uint[] rglData) {
|
|
|
|
//Debug.Assert(rglData.Length == 4, "rglData.Length == 4", "Wrong array length: " + rglData.Length.ToString(CultureInfo.InvariantCulture));
|
|
|
|
return rglData[3] >= 0x4b3b4ca8L && ((rglData[3] > 0x4b3b4ca8L) || (rglData[2] > 0x5a86c47aL) || (rglData[2] == 0x5a86c47aL) && (rglData[1] >= 0x098a2240L));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Multi-precision one super-digit divide in place.
|
|
|
|
// U = U / D,
|
|
|
|
// R = U % D
|
|
|
|
// Length of U can decrease
|
|
|
|
private static void MpDiv1(uint[] rgulU, // InOut| U
|
|
|
|
ref int ciulU, // InOut| # of digits in U
|
|
|
|
uint iulD, // In | D
|
|
|
|
out uint iulR // Out | R
|
|
|
|
) {
|
|
|
|
Debug.Assert(rgulU.Length == x_cNumeMax);
|
|
|
|
|
|
|
|
uint ulCarry = 0;
|
|
|
|
ulong dwlAccum;
|
|
|
|
ulong ulD = (ulong)iulD;
|
|
|
|
int idU = ciulU;
|
|
|
|
|
|
|
|
Debug.Assert(iulD != 0, "iulD != 0", "Divided by zero!");
|
|
|
|
Debug.Assert(iulD > 0, "iulD > 0", "Invalid data: less than zero");
|
|
|
|
Debug.Assert(ciulU > 0, "ciulU > 0", "No data in the array");
|
|
|
|
while (idU > 0) {
|
|
|
|
idU--;
|
|
|
|
dwlAccum = (((ulong)ulCarry) << 32) + (ulong)(rgulU[idU]);
|
|
|
|
rgulU[idU] = (uint)(dwlAccum / ulD);
|
|
|
|
ulCarry = (uint)(dwlAccum - (ulong)rgulU[idU] * ulD); // (ULONG) (dwlAccum % iulD)
|
|
|
|
}
|
|
|
|
|
|
|
|
iulR = ulCarry;
|
|
|
|
MpNormalize(rgulU, ref ciulU);
|
|
|
|
}
|
|
|
|
// Normalize multi-precision number - remove leading zeroes
|
|
|
|
private static void MpNormalize(uint[] rgulU, // In | Number
|
|
|
|
ref int ciulU // InOut| # of digits
|
|
|
|
) {
|
|
|
|
while (ciulU > 1 && rgulU[ciulU - 1] == 0)
|
|
|
|
ciulU--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// AdjustScale()
|
|
|
|
//
|
|
|
|
// Adjust number of digits to the right of the decimal point.
|
|
|
|
// A positive adjustment increases the scale of the numeric value
|
|
|
|
// while a negative adjustment decreases the scale. When decreasing
|
|
|
|
// the scale for the numeric value, the remainder is checked and
|
|
|
|
// rounded accordingly.
|
|
|
|
//
|
|
|
|
internal void AdjustScale(int digits, bool fRound) {
|
|
|
|
uint ulRem; //Remainder when downshifting
|
|
|
|
uint ulShiftBase; //What to multiply by to effect scale adjust
|
|
|
|
bool fNeedRound = false; //Do we really need to round?
|
|
|
|
byte bNewScale, bNewPrec;
|
|
|
|
int lAdjust = digits;
|
|
|
|
|
|
|
|
//If downshifting causes truncation of data
|
|
|
|
if (lAdjust + m_bScale < 0)
|
|
|
|
throw new XmlException(Res.SqlTypes_ArithTruncation, (string)null);
|
|
|
|
|
|
|
|
//If uphifting causes scale overflow
|
|
|
|
if (lAdjust + m_bScale > NUMERIC_MAX_PRECISION)
|
|
|
|
throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
|
|
|
|
|
|
|
|
bNewScale = (byte)(lAdjust + m_bScale);
|
|
|
|
bNewPrec = (byte)(Math.Min(NUMERIC_MAX_PRECISION, Math.Max(1, lAdjust + m_bPrec)));
|
|
|
|
if (lAdjust > 0) {
|
|
|
|
m_bScale = bNewScale;
|
|
|
|
m_bPrec = bNewPrec;
|
|
|
|
while (lAdjust > 0) {
|
|
|
|
//if lAdjust>=9, downshift by 10^9 each time, otherwise by the full amount
|
|
|
|
if (lAdjust >= 9) {
|
|
|
|
ulShiftBase = x_rgulShiftBase[8];
|
|
|
|
lAdjust -= 9;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ulShiftBase = x_rgulShiftBase[lAdjust - 1];
|
|
|
|
lAdjust = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
MultByULong(ulShiftBase);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (lAdjust < 0) {
|
|
|
|
do {
|
|
|
|
if (lAdjust <= -9) {
|
|
|
|
ulShiftBase = x_rgulShiftBase[8];
|
|
|
|
lAdjust += 9;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ulShiftBase = x_rgulShiftBase[-lAdjust - 1];
|
|
|
|
lAdjust = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ulRem = DivByULong(ulShiftBase);
|
|
|
|
} while (lAdjust < 0);
|
|
|
|
|
|
|
|
// Do we really need to round?
|
|
|
|
fNeedRound = (ulRem >= ulShiftBase / 2);
|
|
|
|
m_bScale = bNewScale;
|
|
|
|
m_bPrec = bNewPrec;
|
|
|
|
}
|
|
|
|
|
|
|
|
AssertValid();
|
|
|
|
|
|
|
|
// After adjusting, if the result is 0 and remainder is less than 5,
|
|
|
|
// set the sign to be positive and return.
|
|
|
|
if (fNeedRound && fRound) {
|
|
|
|
// If remainder is 5 or above, increment/decrement by 1.
|
|
|
|
AddULong(1);
|
|
|
|
}
|
|
|
|
else if (FZero())
|
|
|
|
this.m_bSign = 0;
|
|
|
|
}
|
|
|
|
// AddULong()
|
|
|
|
//
|
|
|
|
// Add ulAdd to this numeric. The result will be returned in *this.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// this - IN Operand1 & OUT Result
|
|
|
|
// ulAdd - IN operand2.
|
|
|
|
//
|
|
|
|
private void AddULong(uint ulAdd) {
|
|
|
|
ulong dwlAccum = (ulong)ulAdd;
|
|
|
|
int iData; // which UI4 in this we are on
|
|
|
|
int iDataMax = (int)m_bLen; // # of UI4s in this
|
|
|
|
uint[] rguiData = new uint[4] { m_data1, m_data2, m_data3, m_data4 };
|
|
|
|
|
|
|
|
// Add, starting at the LS UI4 until out of UI4s or no carry
|
|
|
|
iData = 0;
|
|
|
|
do {
|
|
|
|
dwlAccum += (ulong)rguiData[iData];
|
|
|
|
rguiData[iData] = (uint)dwlAccum; // equivalent to mod x_dwlBaseUI4
|
|
|
|
dwlAccum >>= 32; // equivalent to dwlAccum /= x_dwlBaseUI4;
|
|
|
|
if (0 == dwlAccum) {
|
|
|
|
StoreFromWorkingArray(rguiData);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
iData++;
|
|
|
|
} while (iData < iDataMax);
|
|
|
|
|
|
|
|
// There is carry at the end
|
|
|
|
Debug.Assert(dwlAccum < x_ulInt32Base, "dwlAccum < x_lInt32Base", "");
|
|
|
|
|
|
|
|
// Either overflowed
|
|
|
|
if (iData == x_cNumeMax)
|
|
|
|
throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
|
|
|
|
|
|
|
|
// Or need to extend length by 1 UI4
|
|
|
|
rguiData[iData] = (uint)dwlAccum;
|
|
|
|
m_bLen++;
|
|
|
|
if (FGt10_38(rguiData))
|
|
|
|
throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
|
|
|
|
|
|
|
|
StoreFromWorkingArray(rguiData);
|
|
|
|
}
|
|
|
|
// multiply by a long integer
|
|
|
|
private void MultByULong(uint uiMultiplier) {
|
|
|
|
int iDataMax = m_bLen; // How many UI4s currently in *this
|
|
|
|
ulong dwlAccum = 0; // accumulated sum
|
|
|
|
ulong dwlNextAccum = 0; // accumulation past dwlAccum
|
|
|
|
int iData; // which UI4 in *This we are on.
|
|
|
|
uint[] rguiData = new uint[4] { m_data1, m_data2, m_data3, m_data4 };
|
|
|
|
|
|
|
|
for (iData = 0; iData < iDataMax; iData++) {
|
|
|
|
Debug.Assert(dwlAccum < x_ulInt32Base);
|
|
|
|
|
|
|
|
ulong ulTemp = (ulong)rguiData[iData];
|
|
|
|
|
|
|
|
dwlNextAccum = ulTemp * (ulong)uiMultiplier;
|
|
|
|
dwlAccum += dwlNextAccum;
|
|
|
|
if (dwlAccum < dwlNextAccum) // Overflow of int64 add
|
|
|
|
dwlNextAccum = x_ulInt32Base; // how much to add to dwlAccum after div x_dwlBaseUI4
|
|
|
|
else
|
|
|
|
dwlNextAccum = 0;
|
|
|
|
|
|
|
|
rguiData[iData] = (uint)dwlAccum; // equivalent to mod x_dwlBaseUI4
|
|
|
|
dwlAccum = (dwlAccum >> 32) + dwlNextAccum; // equivalent to div x_dwlBaseUI4
|
|
|
|
}
|
|
|
|
|
|
|
|
// If any carry,
|
|
|
|
if (dwlAccum != 0) {
|
|
|
|
// Either overflowed
|
|
|
|
Debug.Assert(dwlAccum < x_ulInt32Base, "dwlAccum < x_dwlBaseUI4", "Integer overflow");
|
|
|
|
if (iDataMax == x_cNumeMax)
|
|
|
|
throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
|
|
|
|
|
|
|
|
// Or extend length by one uint
|
|
|
|
rguiData[iDataMax] = (uint)dwlAccum;
|
|
|
|
m_bLen++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FGt10_38(rguiData))
|
|
|
|
throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
|
|
|
|
|
|
|
|
StoreFromWorkingArray(rguiData);
|
|
|
|
}
|
|
|
|
// DivByULong()
|
|
|
|
//
|
|
|
|
// Divide numeric value by a ULONG. The result will be returned
|
|
|
|
// in the dividend *this.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// this - IN Dividend & OUT Result
|
|
|
|
// ulDivisor - IN Divisor
|
|
|
|
// Returns: - OUT Remainder
|
|
|
|
//
|
|
|
|
internal uint DivByULong(uint iDivisor) {
|
|
|
|
ulong dwlDivisor = (ulong)iDivisor;
|
|
|
|
ulong dwlAccum = 0; //Accumulated sum
|
|
|
|
uint ulQuotientCur = 0; // Value of the current UI4 of the quotient
|
|
|
|
bool fAllZero = true; // All of the quotient (so far) has been 0
|
|
|
|
int iData; //Which UI4 currently on
|
|
|
|
|
|
|
|
// Check for zero divisor.
|
|
|
|
if (dwlDivisor == 0)
|
|
|
|
throw new XmlException(Res.SqlTypes_DivideByZero, (string)null);
|
|
|
|
|
|
|
|
// Copy into array, so that we can iterate through the data
|
|
|
|
uint[] rguiData = new uint[4] { m_data1, m_data2, m_data3, m_data4 };
|
|
|
|
|
|
|
|
// Start from the MS UI4 of quotient, divide by divisor, placing result
|
|
|
|
// in quotient and carrying the remainder.
|
|
|
|
//DEVNOTE DWORDLONG sufficient accumulator since:
|
|
|
|
// Accum < Divisor <= 2^32 - 1 at start each loop
|
|
|
|
// initially,and mod end previous loop
|
|
|
|
// Accum*2^32 < 2^64 - 2^32
|
|
|
|
// multiply both side by 2^32 (x_dwlBaseUI4)
|
|
|
|
// Accum*2^32 + m_rgulData < 2^64
|
|
|
|
// rglData < 2^32
|
|
|
|
for (iData = m_bLen; iData > 0; iData--) {
|
|
|
|
Debug.Assert(dwlAccum < dwlDivisor);
|
|
|
|
dwlAccum = (dwlAccum << 32) + (ulong)(rguiData[iData - 1]); // dwlA*x_dwlBaseUI4 + rglData
|
|
|
|
Debug.Assert((dwlAccum / dwlDivisor) < x_ulInt32Base);
|
|
|
|
|
|
|
|
//Update dividend to the quotient.
|
|
|
|
ulQuotientCur = (uint)(dwlAccum / dwlDivisor);
|
|
|
|
rguiData[iData - 1] = ulQuotientCur;
|
|
|
|
|
|
|
|
//Remainder to be carried to the next lower significant byte.
|
|
|
|
dwlAccum = dwlAccum % dwlDivisor;
|
|
|
|
|
|
|
|
// While current part of quotient still 0, reduce length
|
|
|
|
fAllZero = fAllZero && (ulQuotientCur == 0);
|
|
|
|
if (fAllZero)
|
|
|
|
m_bLen--;
|
|
|
|
}
|
|
|
|
|
|
|
|
StoreFromWorkingArray(rguiData);
|
|
|
|
|
|
|
|
// If result is 0, preserve sign but set length to 5
|
|
|
|
if (fAllZero)
|
|
|
|
m_bLen = 1;
|
|
|
|
|
|
|
|
AssertValid();
|
|
|
|
|
|
|
|
// return the remainder
|
|
|
|
Debug.Assert(dwlAccum < x_ulInt32Base);
|
|
|
|
return (uint)dwlAccum;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Determine the number of uints needed for a numeric given a precision
|
|
|
|
//Precision Length
|
|
|
|
// 0 invalid
|
|
|
|
// 1-9 1
|
|
|
|
// 10-19 2
|
|
|
|
// 20-28 3
|
|
|
|
// 29-38 4
|
|
|
|
// The array in Shiloh. Listed here for comparison.
|
|
|
|
//private static readonly byte[] rgCLenFromPrec = new byte[] {5,5,5,5,5,5,5,5,5,9,9,9,9,9,
|
|
|
|
// 9,9,9,9,9,13,13,13,13,13,13,13,13,13,17,17,17,17,17,17,17,17,17,17};
|
|
|
|
private static readonly byte[] rgCLenFromPrec = new byte[] {
|
|
|
|
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
|
|
|
|
};
|
|
|
|
private static byte CLenFromPrec(byte bPrec) {
|
|
|
|
Debug.Assert(bPrec <= MaxPrecision && bPrec > 0, "bPrec <= MaxPrecision && bPrec > 0", "Invalid numeric precision");
|
|
|
|
return rgCLenFromPrec[bPrec - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
private static char ChFromDigit(uint uiDigit) {
|
|
|
|
Debug.Assert(uiDigit < 10);
|
|
|
|
return (char)(uiDigit + '0');
|
|
|
|
}
|
|
|
|
|
|
|
|
public Decimal ToDecimal() {
|
|
|
|
if ((int)m_data4 != 0 || m_bScale > 28)
|
|
|
|
throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
|
|
|
|
|
|
|
|
return new Decimal((int)m_data1, (int)m_data2, (int)m_data3, !IsPositive, m_bScale);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrimTrailingZeros() {
|
|
|
|
uint[] rgulNumeric = new uint[4] { m_data1, m_data2, m_data3, m_data4};
|
|
|
|
int culLen = m_bLen;
|
|
|
|
uint ulRem; //Remainder of a division by x_ulBase10, i.e.,least significant digit
|
|
|
|
|
|
|
|
// special-case 0
|
|
|
|
if (culLen == 1 && rgulNumeric[0] == 0) {
|
|
|
|
m_bScale = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (m_bScale > 0 && (culLen > 1 || rgulNumeric[0] != 0)) {
|
|
|
|
MpDiv1 (rgulNumeric, ref culLen, 10, out ulRem);
|
|
|
|
if ( ulRem == 0 ) {
|
|
|
|
m_data1 = rgulNumeric[0];
|
|
|
|
m_data2 = rgulNumeric[1];
|
|
|
|
m_data3 = rgulNumeric[2];
|
|
|
|
m_data4 = rgulNumeric[3];
|
|
|
|
m_bScale--;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_bLen == 4 && m_data4 == 0)
|
|
|
|
m_bLen = 3;
|
|
|
|
if (m_bLen == 3 && m_data3 == 0)
|
|
|
|
m_bLen = 2;
|
|
|
|
if (m_bLen == 2 && m_data2 == 0)
|
|
|
|
m_bLen = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override String ToString() {
|
|
|
|
AssertValid();
|
|
|
|
|
|
|
|
// Make local copy of data to avoid modifying input.
|
|
|
|
uint[] rgulNumeric = new uint[4] { m_data1, m_data2, m_data3, m_data4};
|
|
|
|
int culLen = m_bLen;
|
|
|
|
char[] pszTmp = new char[NUMERIC_MAX_PRECISION + 1]; //Local Character buffer to hold
|
|
|
|
//the decimal digits, from the
|
|
|
|
//lowest significant to highest significant
|
|
|
|
|
|
|
|
int iDigits = 0;//Number of significant digits
|
|
|
|
uint ulRem; //Remainder of a division by x_ulBase10, i.e.,least significant digit
|
|
|
|
|
|
|
|
// Build the final numeric string by inserting the sign, reversing
|
|
|
|
// the order and inserting the decimal number at the correct position
|
|
|
|
|
|
|
|
//Retrieve each digit from the lowest significant digit
|
|
|
|
while (culLen > 1 || rgulNumeric[0] != 0) {
|
|
|
|
MpDiv1 (rgulNumeric, ref culLen, 10, out ulRem);
|
|
|
|
//modulo x_ulBase10 is the lowest significant digit
|
|
|
|
pszTmp[iDigits++] = ChFromDigit(ulRem);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if scale of the number has not been
|
|
|
|
// reached pad remaining number with zeros.
|
|
|
|
while (iDigits <= m_bScale) {
|
|
|
|
pszTmp[iDigits++] = ChFromDigit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool fPositive = IsPositive;
|
|
|
|
|
|
|
|
// Increment the result length if negative (need to add '-')
|
|
|
|
int uiResultLen = fPositive ? iDigits : iDigits + 1;
|
|
|
|
|
|
|
|
// Increment the result length if scale > 0 (need to add '.')
|
|
|
|
if (m_bScale > 0)
|
|
|
|
uiResultLen++;
|
|
|
|
|
|
|
|
char[] szResult = new char[uiResultLen];
|
|
|
|
int iCurChar = 0;
|
|
|
|
|
|
|
|
if (!fPositive)
|
|
|
|
szResult[iCurChar ++] = '-';
|
|
|
|
|
|
|
|
while (iDigits > 0) {
|
|
|
|
if (iDigits-- == m_bScale)
|
|
|
|
szResult[iCurChar ++] = '.';
|
|
|
|
szResult[iCurChar ++] = pszTmp[iDigits];
|
|
|
|
}
|
|
|
|
|
|
|
|
AssertValid();
|
|
|
|
|
|
|
|
return new String(szResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Is this RE numeric valid?
|
|
|
|
[System.Diagnostics.Conditional("DEBUG")]
|
|
|
|
private void AssertValid() {
|
|
|
|
// Scale,Prec in range
|
|
|
|
Debug.Assert(m_bScale <= NUMERIC_MAX_PRECISION, "m_bScale <= NUMERIC_MAX_PRECISION", "In AssertValid");
|
|
|
|
Debug.Assert(m_bScale <= m_bPrec, "m_bScale <= m_bPrec", "In AssertValid");
|
|
|
|
Debug.Assert(m_bScale >= 0, "m_bScale >= 0", "In AssertValid");
|
|
|
|
Debug.Assert(m_bPrec > 0, "m_bPrec > 0", "In AssertValid");
|
|
|
|
Debug.Assert(CLenFromPrec(m_bPrec) >= m_bLen, "CLenFromPrec(m_bPrec) >= m_bLen", "In AssertValid");
|
|
|
|
Debug.Assert(m_bLen <= x_cNumeMax, "m_bLen <= x_cNumeMax", "In AssertValid");
|
|
|
|
|
|
|
|
uint[] rglData = new uint[4] { m_data1, m_data2, m_data3, m_data4 };
|
|
|
|
|
|
|
|
// highest UI4 is non-0 unless value "zero"
|
|
|
|
if (rglData[m_bLen - 1] == 0) {
|
|
|
|
Debug.Assert(m_bLen == 1, "m_bLen == 1", "In AssertValid");
|
|
|
|
}
|
|
|
|
|
|
|
|
// All UI4s from length to end are 0
|
|
|
|
for (int iulData = m_bLen; iulData < x_cNumeMax; iulData++)
|
|
|
|
Debug.Assert(rglData[iulData] == 0, "rglData[iulData] == 0", "In AssertValid");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal struct BinXmlSqlMoney {
|
|
|
|
long data;
|
|
|
|
|
|
|
|
public BinXmlSqlMoney(int v) { this.data = v; }
|
|
|
|
public BinXmlSqlMoney(long v) { this.data = v; }
|
|
|
|
|
|
|
|
public Decimal ToDecimal() {
|
|
|
|
bool neg;
|
|
|
|
ulong v;
|
|
|
|
if (this.data < 0) {
|
|
|
|
neg = true;
|
|
|
|
v = (ulong)unchecked(-this.data);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
neg = false;
|
|
|
|
v = (ulong)this.data;
|
|
|
|
}
|
|
|
|
// SQL Server stores money8 as ticks of 1/10000.
|
|
|
|
const byte MoneyScale = 4;
|
|
|
|
return new Decimal(unchecked((int)v), unchecked((int)(v >> 32)), 0, neg, MoneyScale);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override String ToString() {
|
|
|
|
Decimal money = ToDecimal();
|
|
|
|
// Formatting of SqlMoney: At least two digits after decimal point
|
|
|
|
return money.ToString("#0.00##", CultureInfo.InvariantCulture);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal abstract class BinXmlDateTime {
|
|
|
|
|
|
|
|
const int MaxFractionDigits = 7;
|
|
|
|
|
|
|
|
static internal int[] KatmaiTimeScaleMultiplicator = new int[8] {
|
|
|
|
10000000,
|
|
|
|
1000000,
|
|
|
|
100000,
|
|
|
|
10000,
|
|
|
|
1000,
|
|
|
|
100,
|
|
|
|
10,
|
|
|
|
1,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void Write2Dig( StringBuilder sb, int val ) {
|
|
|
|
Debug.Assert(val >= 0 && val < 100);
|
|
|
|
sb.Append((char)('0' + (val/10)));
|
|
|
|
sb.Append((char)('0' + (val%10)));
|
|
|
|
}
|
|
|
|
static void Write4DigNeg(StringBuilder sb, int val) {
|
|
|
|
Debug.Assert(val > -10000 && val < 10000);
|
|
|
|
if (val < 0) {
|
|
|
|
val = -val;
|
|
|
|
sb.Append('-');
|
|
|
|
}
|
|
|
|
Write2Dig(sb, val/100);
|
|
|
|
Write2Dig(sb, val%100);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Write3Dec(StringBuilder sb, int val) {
|
|
|
|
Debug.Assert(val >= 0 && val < 1000);
|
|
|
|
int c3 = val % 10;
|
|
|
|
val /= 10;
|
|
|
|
int c2 = val % 10;
|
|
|
|
val /= 10;
|
|
|
|
int c1 = val;
|
|
|
|
sb.Append('.');
|
|
|
|
sb.Append((char)('0'+c1));
|
|
|
|
sb.Append((char)('0'+c2));
|
|
|
|
sb.Append((char)('0'+c3));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void WriteDate(StringBuilder sb, int yr, int mnth, int day) {
|
|
|
|
Write4DigNeg(sb, yr);
|
|
|
|
sb.Append('-');
|
|
|
|
Write2Dig(sb, mnth);
|
|
|
|
sb.Append('-');
|
|
|
|
Write2Dig(sb, day);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void WriteTime(StringBuilder sb, int hr, int min, int sec, int ms) {
|
|
|
|
Write2Dig(sb, hr);
|
|
|
|
sb.Append(':');
|
|
|
|
Write2Dig(sb, min);
|
|
|
|
sb.Append(':');
|
|
|
|
Write2Dig(sb, sec);
|
|
|
|
if (ms != 0) {
|
|
|
|
Write3Dec(sb, ms);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void WriteTimeFullPrecision(StringBuilder sb, int hr, int min, int sec, int fraction) {
|
|
|
|
Write2Dig(sb, hr);
|
|
|
|
sb.Append(':');
|
|
|
|
Write2Dig(sb, min);
|
|
|
|
sb.Append(':');
|
|
|
|
Write2Dig(sb, sec);
|
|
|
|
if (fraction != 0) {
|
|
|
|
int fractionDigits = MaxFractionDigits;
|
|
|
|
while (fraction % 10 == 0) {
|
|
|
|
fractionDigits --;
|
|
|
|
fraction /= 10;
|
|
|
|
}
|
|
|
|
char[] charArray = new char[fractionDigits];
|
|
|
|
while(fractionDigits > 0) {
|
|
|
|
fractionDigits--;
|
|
|
|
charArray[fractionDigits] = (char)(fraction % 10 + '0');
|
|
|
|
fraction /= 10;
|
|
|
|
}
|
|
|
|
sb.Append('.');
|
|
|
|
sb.Append(charArray);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void WriteTimeZone(StringBuilder sb, TimeSpan zone) {
|
|
|
|
bool negTimeZone = true;
|
|
|
|
if (zone.Ticks < 0) {
|
|
|
|
negTimeZone = false;
|
|
|
|
zone = zone.Negate();
|
|
|
|
}
|
|
|
|
WriteTimeZone(sb, negTimeZone, zone.Hours, zone.Minutes);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void WriteTimeZone(StringBuilder sb, bool negTimeZone, int hr, int min) {
|
|
|
|
if (hr == 0 && min == 0) {
|
|
|
|
sb.Append('Z');
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sb.Append(negTimeZone ? '+' : '-');
|
|
|
|
Write2Dig(sb, hr);
|
|
|
|
sb.Append(':');
|
|
|
|
Write2Dig(sb, min);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
if (val < 0)
|
|
|
|
goto Error;
|
|
|
|
long date = val / 4; // trim indicator bits
|
|
|
|
ms = (int)(date % 1000);
|
|
|
|
date /= 1000;
|
|
|
|
sec = (int)(date % 60);
|
|
|
|
date /= 60;
|
|
|
|
min = (int)(date % 60);
|
|
|
|
date /= 60;
|
|
|
|
hr = (int)(date % 24);
|
|
|
|
date /= 24;
|
|
|
|
day = (int)(date % 31) + 1;
|
|
|
|
date /= 31;
|
|
|
|
mnth = (int)(date % 12) + 1;
|
|
|
|
date /= 12;
|
|
|
|
yr = (int)(date - 9999);
|
|
|
|
if (yr < -9999 || yr > 9999)
|
|
|
|
goto Error;
|
|
|
|
return;
|
|
|
|
Error:
|
|
|
|
throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void BreakDownXsdDate(long val, out int yr, out int mnth, out int day, out bool negTimeZone, out int hr, out int min) {
|
|
|
|
if (val < 0)
|
|
|
|
goto Error;
|
|
|
|
val = val / 4; // trim indicator bits
|
|
|
|
int totalMin = (int)(val % (29*60)) - 60*14;
|
|
|
|
long totalDays = val / (29*60);
|
|
|
|
|
|
|
|
if (negTimeZone = (totalMin < 0))
|
|
|
|
totalMin = -totalMin;
|
|
|
|
|
|
|
|
min = totalMin % 60;
|
|
|
|
hr = totalMin / 60;
|
|
|
|
|
|
|
|
day = (int)(totalDays % 31) + 1;
|
|
|
|
totalDays /= 31;
|
|
|
|
mnth = (int)(totalDays % 12) + 1;
|
|
|
|
yr = (int)(totalDays / 12) - 9999;
|
|
|
|
if (yr < -9999 || yr > 9999)
|
|
|
|
goto Error;
|
|
|
|
return;
|
|
|
|
Error:
|
|
|
|
throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void BreakDownXsdTime(long val, out int hr, out int min, out int sec, out int ms) {
|
|
|
|
if (val < 0)
|
|
|
|
goto Error;
|
|
|
|
val = val / 4; // trim indicator bits
|
|
|
|
ms = (int)(val % 1000);
|
|
|
|
val /= 1000;
|
|
|
|
sec = (int)(val % 60);
|
|
|
|
val /= 60;
|
|
|
|
min = (int)(val % 60);
|
|
|
|
hr = (int)(val / 60);
|
|
|
|
if (0 > hr || hr > 23)
|
|
|
|
goto Error;
|
|
|
|
return;
|
|
|
|
Error:
|
|
|
|
throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static string XsdDateTimeToString(long val) {
|
|
|
|
int yr; int mnth; int day; int hr; int min; int sec; int ms;
|
|
|
|
BreakDownXsdDateTime(val, out yr, out mnth, out day, out hr, out min, out sec, out ms);
|
|
|
|
StringBuilder sb = new StringBuilder(20);
|
|
|
|
WriteDate(sb, yr, mnth, day);
|
|
|
|
sb.Append('T');
|
|
|
|
WriteTime(sb, hr, min, sec, ms);
|
|
|
|
sb.Append('Z');
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
|
|
|
public static DateTime XsdDateTimeToDateTime(long val) {
|
|
|
|
int yr; int mnth; int day; int hr; int min; int sec; int ms;
|
|
|
|
BreakDownXsdDateTime(val, out yr, out mnth, out day, out hr, out min, out sec, out ms);
|
|
|
|
return new DateTime(yr, mnth, day, hr, min, sec, ms, DateTimeKind.Utc);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static string XsdDateToString(long val) {
|
|
|
|
int yr; int mnth; int day; int hr; int min; bool negTimeZ;
|
|
|
|
BreakDownXsdDate(val, out yr, out mnth, out day, out negTimeZ, out hr, out min);
|
|
|
|
StringBuilder sb = new StringBuilder(20);
|
|
|
|
WriteDate(sb, yr, mnth, day);
|
|
|
|
WriteTimeZone(sb, negTimeZ, hr, min);
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
|
|
|
public static DateTime XsdDateToDateTime(long val) {
|
|
|
|
int yr; int mnth; int day; int hr; int min; bool negTimeZ;
|
|
|
|
BreakDownXsdDate(val, out yr, out mnth, out day, out negTimeZ, out hr, out min);
|
|
|
|
DateTime d = new DateTime(yr, mnth, day, 0, 0, 0, DateTimeKind.Utc);
|
|
|
|
// adjust for timezone
|
|
|
|
int adj = (negTimeZ ? -1 : 1) * ( (hr * 60) + min );
|
|
|
|
return TimeZone.CurrentTimeZone.ToLocalTime( d.AddMinutes(adj) );
|
|
|
|
}
|
|
|
|
|
|
|
|
public static string XsdTimeToString(long val) {
|
|
|
|
int hr; int min; int sec; int ms;
|
|
|
|
BreakDownXsdTime(val, out hr, out min, out sec, out ms);
|
|
|
|
StringBuilder sb = new StringBuilder(16);
|
|
|
|
WriteTime(sb, hr, min, sec, ms);
|
|
|
|
sb.Append('Z');
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
|
|
|
public static DateTime XsdTimeToDateTime(long val) {
|
|
|
|
int hr; int min; int sec; int ms;
|
|
|
|
BreakDownXsdTime(val, out hr, out min, out sec, out ms);
|
|
|
|
return new DateTime(1, 1, 1, hr, min, sec, ms, DateTimeKind.Utc);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static string SqlDateTimeToString(int dateticks, uint timeticks) {
|
|
|
|
DateTime dateTime = SqlDateTimeToDateTime(dateticks, timeticks);
|
|
|
|
string format = (dateTime.Millisecond != 0) ? "yyyy/MM/dd\\THH:mm:ss.ffff" : "yyyy/MM/dd\\THH:mm:ss";
|
|
|
|
return dateTime.ToString(format, CultureInfo.InvariantCulture);
|
|
|
|
}
|
|
|
|
public static DateTime SqlDateTimeToDateTime(int dateticks, uint timeticks) {
|
|
|
|
DateTime SQLBaseDate = new DateTime(1900, 1, 1);
|
|
|
|
//long millisecond = (long)(((ulong)timeticks * 20 + (ulong)3) / (ulong)6);
|
|
|
|
long millisecond = (long)(timeticks / SQLTicksPerMillisecond + 0.5);
|
|
|
|
return SQLBaseDate.Add( new TimeSpan( dateticks * TimeSpan.TicksPerDay +
|
|
|
|
millisecond * TimeSpan.TicksPerMillisecond ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Number of (100ns) ticks per time unit
|
|
|
|
private static readonly double SQLTicksPerMillisecond = 0.3;
|
|
|
|
public static readonly int SQLTicksPerSecond = 300;
|
|
|
|
public static readonly int SQLTicksPerMinute = SQLTicksPerSecond * 60;
|
|
|
|
public static readonly int SQLTicksPerHour = SQLTicksPerMinute * 60;
|
|
|
|
private static readonly int SQLTicksPerDay = SQLTicksPerHour * 24;
|
|
|
|
|
|
|
|
|
|
|
|
public static string SqlSmallDateTimeToString(short dateticks, ushort timeticks) {
|
|
|
|
DateTime dateTime = SqlSmallDateTimeToDateTime(dateticks, timeticks);
|
|
|
|
return dateTime.ToString("yyyy/MM/dd\\THH:mm:ss", CultureInfo.InvariantCulture);
|
|
|
|
}
|
|
|
|
public static DateTime SqlSmallDateTimeToDateTime(short dateticks, ushort timeticks) {
|
|
|
|
return SqlDateTimeToDateTime( (int)dateticks, (uint)(timeticks * SQLTicksPerMinute) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Conversions of the Katmai date & time types to DateTime
|
|
|
|
public static DateTime XsdKatmaiDateToDateTime(byte[] data, int offset) {
|
|
|
|
// Katmai SQL type "DATE"
|
|
|
|
long dateTicks = GetKatmaiDateTicks(data, ref offset);
|
|
|
|
DateTime dt = new DateTime(dateTicks);
|
|
|
|
return dt;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static DateTime XsdKatmaiDateTimeToDateTime(byte[] data, int offset) {
|
|
|
|
// Katmai SQL type "DATETIME2"
|
|
|
|
long timeTicks = GetKatmaiTimeTicks(data, ref offset);
|
|
|
|
long dateTicks = GetKatmaiDateTicks(data, ref offset);
|
|
|
|
DateTime dt = new DateTime(dateTicks + timeTicks);
|
|
|
|
return dt;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static DateTime XsdKatmaiTimeToDateTime(byte[] data, int offset) {
|
|
|
|
// TIME without zone is stored as DATETIME2
|
|
|
|
return XsdKatmaiDateTimeToDateTime(data, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static DateTime XsdKatmaiDateOffsetToDateTime( byte[] data, int offset ) {
|
|
|
|
// read the timezoned value into DateTimeOffset and then convert to local time
|
|
|
|
return XsdKatmaiDateOffsetToDateTimeOffset(data, offset).LocalDateTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static DateTime XsdKatmaiDateTimeOffsetToDateTime(byte[] data, int offset) {
|
|
|
|
// read the timezoned value into DateTimeOffset and then convert to local time
|
|
|
|
return XsdKatmaiDateTimeOffsetToDateTimeOffset(data, offset).LocalDateTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static DateTime XsdKatmaiTimeOffsetToDateTime(byte[] data, int offset) {
|
|
|
|
// read the timezoned value into DateTimeOffset and then convert to local time
|
|
|
|
return XsdKatmaiTimeOffsetToDateTimeOffset(data, offset).LocalDateTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Conversions of the Katmai date & time types to DateTimeOffset
|
|
|
|
public static DateTimeOffset XsdKatmaiDateToDateTimeOffset( byte[] data, int offset ) {
|
|
|
|
// read the value into DateTime and then convert it to DateTimeOffset, which adds local time zone
|
|
|
|
return (DateTimeOffset)XsdKatmaiDateToDateTime(data, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static DateTimeOffset XsdKatmaiDateTimeToDateTimeOffset(byte[] data, int offset) {
|
|
|
|
// read the value into DateTime and then convert it to DateTimeOffset, which adds local time zone
|
|
|
|
return (DateTimeOffset)XsdKatmaiDateTimeToDateTime(data, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static DateTimeOffset XsdKatmaiTimeToDateTimeOffset(byte[] data, int offset) {
|
|
|
|
// read the value into DateTime and then convert it to DateTimeOffset, which adds local time zone
|
|
|
|
return (DateTimeOffset)XsdKatmaiTimeToDateTime(data, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static DateTimeOffset XsdKatmaiDateOffsetToDateTimeOffset(byte[] data, int offset) {
|
|
|
|
// DATE with zone is stored as DATETIMEOFFSET
|
|
|
|
return XsdKatmaiDateTimeOffsetToDateTimeOffset(data, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static DateTimeOffset XsdKatmaiDateTimeOffsetToDateTimeOffset(byte[] data, int offset) {
|
|
|
|
// Katmai SQL type "DATETIMEOFFSET"
|
|
|
|
long timeTicks = GetKatmaiTimeTicks(data, ref offset);
|
|
|
|
long dateTicks = GetKatmaiDateTicks(data, ref offset);
|
|
|
|
long zoneTicks = GetKatmaiTimeZoneTicks(data, offset);
|
|
|
|
// The DATETIMEOFFSET values are serialized in UTC, but DateTimeOffset takes adjusted time -> we need to add zoneTicks
|
|
|
|
DateTimeOffset dto = new DateTimeOffset(dateTicks + timeTicks + zoneTicks, new TimeSpan(zoneTicks));
|
|
|
|
return dto;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static DateTimeOffset XsdKatmaiTimeOffsetToDateTimeOffset(byte[] data, int offset) {
|
|
|
|
// TIME with zone is stored as DATETIMEOFFSET
|
|
|
|
return XsdKatmaiDateTimeOffsetToDateTimeOffset(data, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Conversions of the Katmai date & time types to string
|
|
|
|
public static string XsdKatmaiDateToString(byte[] data, int offset) {
|
|
|
|
DateTime dt = XsdKatmaiDateToDateTime(data, offset);
|
|
|
|
StringBuilder sb = new StringBuilder(10);
|
|
|
|
WriteDate(sb, dt.Year, dt.Month, dt.Day);
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static string XsdKatmaiDateTimeToString(byte[] data, int offset) {
|
|
|
|
DateTime dt = XsdKatmaiDateTimeToDateTime(data, offset);
|
|
|
|
StringBuilder sb = new StringBuilder(33);
|
|
|
|
WriteDate(sb, dt.Year, dt.Month, dt.Day);
|
|
|
|
sb.Append('T');
|
|
|
|
WriteTimeFullPrecision(sb, dt.Hour, dt.Minute, dt.Second, GetFractions(dt));
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static string XsdKatmaiTimeToString(byte[] data, int offset) {
|
|
|
|
DateTime dt = XsdKatmaiTimeToDateTime(data, offset);
|
|
|
|
StringBuilder sb = new StringBuilder(16);
|
|
|
|
WriteTimeFullPrecision(sb, dt.Hour, dt.Minute, dt.Second, GetFractions(dt));
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static string XsdKatmaiDateOffsetToString(byte[] data, int offset) {
|
|
|
|
DateTimeOffset dto = XsdKatmaiDateOffsetToDateTimeOffset(data, offset);
|
|
|
|
StringBuilder sb = new StringBuilder(16);
|
|
|
|
WriteDate(sb, dto.Year, dto.Month, dto.Day);
|
|
|
|
WriteTimeZone(sb, dto.Offset);
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static string XsdKatmaiDateTimeOffsetToString(byte[] data, int offset) {
|
|
|
|
DateTimeOffset dto = XsdKatmaiDateTimeOffsetToDateTimeOffset(data, offset);
|
|
|
|
StringBuilder sb = new StringBuilder(39);
|
|
|
|
WriteDate(sb, dto.Year, dto.Month, dto.Day);
|
|
|
|
sb.Append('T');
|
|
|
|
WriteTimeFullPrecision(sb, dto.Hour, dto.Minute, dto.Second, GetFractions(dto));
|
|
|
|
WriteTimeZone(sb, dto.Offset);
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static string XsdKatmaiTimeOffsetToString(byte[] data, int offset) {
|
|
|
|
DateTimeOffset dto = XsdKatmaiTimeOffsetToDateTimeOffset(data, offset);
|
|
|
|
StringBuilder sb = new StringBuilder(22);
|
|
|
|
WriteTimeFullPrecision(sb, dto.Hour, dto.Minute, dto.Second, GetFractions(dto));
|
|
|
|
WriteTimeZone(sb, dto.Offset);
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper methods for the Katmai date & time types
|
|
|
|
static long GetKatmaiDateTicks(byte[] data, ref int pos) {
|
|
|
|
int p = pos;
|
|
|
|
pos = p + 3;
|
|
|
|
return (data[p] | data[p + 1] << 8 | data[p + 2] << 16) * TimeSpan.TicksPerDay;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long GetKatmaiTimeTicks(byte[] data, ref int pos) {
|
|
|
|
int p = pos;
|
|
|
|
byte scale = data[p];
|
|
|
|
long timeTicks;
|
|
|
|
p++;
|
|
|
|
if (scale <= 2) {
|
|
|
|
timeTicks = data[p] | (data[p + 1] << 8) | (data[p + 2] << 16);
|
|
|
|
pos = p + 3;
|
|
|
|
}
|
|
|
|
else if (scale <= 4) {
|
|
|
|
timeTicks = data[p] | (data[p + 1] << 8) | (data[p + 2] << 16);
|
|
|
|
timeTicks |= ((long)data[p + 3] << 24);
|
|
|
|
pos = p + 4;
|
|
|
|
}
|
|
|
|
else if (scale <= 7) {
|
|
|
|
timeTicks = data[p] | (data[p + 1] << 8) | (data[p + 2] << 16);
|
|
|
|
timeTicks |= ((long)data[p + 3] << 24) | ((long)data[p + 4] << 32);
|
|
|
|
pos = p + 5;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
|
|
|
|
}
|
|
|
|
return timeTicks * KatmaiTimeScaleMultiplicator[scale];
|
|
|
|
}
|
|
|
|
|
|
|
|
static long GetKatmaiTimeZoneTicks(byte[] data, int pos) {
|
|
|
|
return (short)(data[pos] | data[pos + 1] << 8) * TimeSpan.TicksPerMinute;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int GetFractions(DateTime dt) {
|
|
|
|
return (int)(dt.Ticks - new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second).Ticks);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int GetFractions(DateTimeOffset dt) {
|
|
|
|
return (int)(dt.Ticks - new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second).Ticks);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
const long SqlDateTicks2Ticks = (long)10000 * 1000 * 60 * 60 * 24;
|
|
|
|
const long SqlBaseDate = 693595;
|
|
|
|
|
|
|
|
public static void DateTime2SqlDateTime(DateTime datetime, out int dateticks, out uint timeticks) {
|
|
|
|
dateticks = (int)(datetime.Ticks / SqlDateTicks2Ticks) - 693595;
|
|
|
|
double time = (double)(datetime.Ticks % SqlDateTicks2Ticks);
|
|
|
|
time = time / 10000; // adjust to ms
|
|
|
|
time = time * 0.3 + .5; // adjust to sqlticks (and round correctly)
|
|
|
|
timeticks = (uint)time;
|
|
|
|
}
|
|
|
|
public static void DateTime2SqlSmallDateTime(DateTime datetime, out short dateticks, out ushort timeticks) {
|
|
|
|
dateticks = (short)((int)(datetime.Ticks / SqlDateTicks2Ticks) - 693595);
|
|
|
|
int time = (int)(datetime.Ticks % SqlDateTicks2Ticks);
|
|
|
|
timeticks = (ushort)(time / (10000 * 1000 * 60)); // adjust to min
|
|
|
|
}
|
|
|
|
public static long DateTime2XsdTime(DateTime datetime) {
|
|
|
|
// adjust to ms
|
|
|
|
return (datetime.TimeOfDay.Ticks / 10000) * 4 + 0;
|
|
|
|
}
|
|
|
|
public static long DateTime2XsdDateTime(DateTime datetime) {
|
|
|
|
long t = datetime.TimeOfDay.Ticks / 10000;
|
|
|
|
t += (datetime.Day-1) * (long)1000*60*60*24;
|
|
|
|
t += (datetime.Month-1) * (long)1000*60*60*24*31;
|
|
|
|
int year = datetime.Year;
|
|
|
|
if (year < -9999 || year > 9999)
|
|
|
|
throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
|
|
|
|
t += (datetime.Year+9999) * (long)1000*60*60*24*31*12;
|
|
|
|
return t*4 + 2;
|
|
|
|
}
|
|
|
|
public static long DateTime2XsdDate(DateTime datetime) {
|
|
|
|
// compute local offset
|
|
|
|
long tzOffset = -TimeZone.CurrentTimeZone.GetUtcOffset(datetime).Ticks / TimeSpan.TicksPerMinute;
|
|
|
|
tzOffset += 14*60;
|
|
|
|
// adjust datetime to UTC
|
|
|
|
datetime = TimeZone.CurrentTimeZone.ToUniversalTime(datetime);
|
|
|
|
|
|
|
|
Debug.Assert( tzOffset >= 0 );
|
|
|
|
|
|
|
|
int year = datetime.Year;
|
|
|
|
if (year < -9999 || year > 9999)
|
|
|
|
throw new XmlException(Res.SqlTypes_ArithOverflow, (string)null);
|
|
|
|
long t = (datetime.Day - 1)
|
|
|
|
+ 31*(datetime.Month - 1)
|
|
|
|
+ 31*12*((long)(year+9999));
|
|
|
|
t *= (29*60); // adjust in timezone
|
|
|
|
t += tzOffset;
|
|
|
|
return t*4+1;
|
|
|
|
}
|
|
|
|
* */
|
|
|
|
}
|
|
|
|
}
|