/*  Copyright (c) MediaArea.net SARL. All Rights Reserved.
 *
 *  Use of this source code is governed by a zlib-style license that can
 *  be found in the License.txt file in the root of the source tree.
 */

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// More methods for std::(w)string
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//---------------------------------------------------------------------------
#include "ZenLib/PreComp.h"
#ifdef __BORLANDC__
    #pragma hdrstop
#endif
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#include "ZenLib/Conf_Internal.h"
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#ifdef ZENLIB_USEWX
    #include <wx/strconv.h>
    #include <wx/datetime.h>
#else //ZENLIB_USEWX
    #ifdef ZENLIB_STANDARD
        #undef WINDOWS
    #endif
    #ifdef WINDOWS
        #undef __TEXT
        #include <windows.h>
        #ifndef WINDOWS_GAMES
            #include <tchar.h>
        #endif
    #endif
#endif //ZENLIB_USEWX
#ifdef __MINGW32__
    #include <windows.h>
#endif //__MINGW32__
#include <algorithm>
#include <iomanip>
#include <cmath>
#include <ctime>
#include "ZenLib/OS_Utils.h"
#include "ZenLib/File.h"
using namespace std;
//---------------------------------------------------------------------------

//MSVC2005 : "deprecated" warning (replacement functions are not in MinGW32 or Borland!)
#if defined(_MSC_VER) && _MSC_VER >= 1400
    #pragma warning(disable : 4996)
#endif

//---------------------------------------------------------------------------

namespace ZenLib
{

int16u Ztring_ISO_8859_2[96]=
{
    0x00A0,
    0x0104,
    0x02D8,
    0x0141,
    0x00A4,
    0x013D,
    0x015A,
    0x00A7,
    0x00A8,
    0x0160,
    0x015E,
    0x0164,
    0x0179,
    0x00AD,
    0x017D,
    0x017B,
    0x00B0,
    0x0105,
    0x02DB,
    0x0142,
    0x00B4,
    0x013E,
    0x015B,
    0x02C7,
    0x00B8,
    0x0161,
    0x015F,
    0x0165,
    0x017A,
    0x02DD,
    0x017E,
    0x017C,
    0x0154,
    0x00C1,
    0x00C2,
    0x0102,
    0x00C4,
    0x0139,
    0x0106,
    0x00C7,
    0x010C,
    0x00C9,
    0x0118,
    0x00CB,
    0x011A,
    0x00CD,
    0x00CE,
    0x010E,
    0x0110,
    0x0143,
    0x0147,
    0x00D3,
    0x00D4,
    0x0150,
    0x00D6,
    0x00D7,
    0x0158,
    0x016E,
    0x00DA,
    0x0170,
    0x00DC,
    0x00DD,
    0x0162,
    0x00DF,
    0x0155,
    0x00E1,
    0x00E2,
    0x0103,
    0x00E4,
    0x013A,
    0x0107,
    0x00E7,
    0x010D,
    0x00E9,
    0x0119,
    0x00EB,
    0x011B,
    0x00ED,
    0x00EE,
    0x010F,
    0x0111,
    0x0144,
    0x0148,
    0x00F3,
    0x00F4,
    0x0151,
    0x00F6,
    0x00F7,
    0x0159,
    0x016F,
    0x00FA,
    0x0171,
    0x00FC,
    0x00FD,
    0x0163,
    0x02D9,
};

//---------------------------------------------------------------------------
Ztring EmptyZtring;
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#if defined(STREAM_MISSING)
    #if defined (_UNICODE)
        #if defined (MACOS) || defined (MACOSX)
            #define _tnprintf swprintf
        #else
            #define _tnprintf snwprintf
        #endif
    #else
        #define _tnprintf snprintf
    #endif
#else
    typedef basic_stringstream<Char>  tStringStream;
    typedef basic_istringstream<Char> tiStringStream;
    typedef basic_ostringstream<Char> toStringStream;
#endif
//---------------------------------------------------------------------------

//***************************************************************************
// Operators
//***************************************************************************

Char &Ztring::operator() (size_type Pos)
{
    if (Pos>size())
        resize(Pos);
    return operator[] (Pos);
}

//***************************************************************************
// Assign
//***************************************************************************

bool Ztring::Assign_FromFile (const Ztring &FileName)
{
    File F;
    if (!F.Open(FileName))
        return false;
    int64u F_Size=F.Size_Get();
    if (F_Size>((size_t)-1)-1)
        return false;

    //Creating buffer
    int8u* Buffer=new int8u[(size_t)F_Size+1];
    size_t Buffer_Offset=0;

    //Reading the file
    while(Buffer_Offset<F_Size)
    {
        size_t BytesRead=F.Read(Buffer+Buffer_Offset, (size_t)F_Size-Buffer_Offset);
        if (BytesRead==0)
            break; //Read is finished
        Buffer_Offset+=BytesRead;
    }
    if (Buffer_Offset<F_Size)
    {
        delete[] Buffer;
        return false;
    }
    Buffer[Buffer_Offset]='\0';

    //Filling
    assign((const Char*)Buffer);
    delete[] Buffer;

    return true;
}

//***************************************************************************
// Conversions
//***************************************************************************

Ztring& Ztring::From_Unicode (const wchar_t S)
{
    #ifdef _UNICODE
        append(1, S);
    #else
        From_Unicode(&S, 1);
    #endif
    return *this;
}

Ztring& Ztring::From_Unicode (const wchar_t* S)
{
    if (S==NULL)
        return *this;

    #ifdef _UNICODE
        assign(S);
    #else
        #ifdef ZENLIB_USEWX
            size_type OK=wxConvCurrent->WC2MB(NULL, S, 0);
            if (OK!=0 && OK!=Error)
                assign(wxConvCurrent->cWC2MB(S));
        #else //ZENLIB_USEWX
            #ifdef WINDOWS
                int Size=WideCharToMultiByte(CP_UTF8, 0, S, -1, NULL, 0, NULL, NULL);
                if (Size!=0)
                {
                    char* AnsiString=new char[Size+1];
                    WideCharToMultiByte(CP_UTF8, 0, S, -1, AnsiString, Size, NULL, NULL);
                    AnsiString[Size]='\0';
                    assign (AnsiString);
                    delete[] AnsiString;
                }
                else
                    clear();
            #else //WINDOWS
                size_t Size=wcstombs(NULL, S, 0);
                if (Size!=0 && Size!=(size_t)-1)
                {
                    char* AnsiString=new char[Size+1];
                    Size=wcstombs(AnsiString, S, Size+1);
                    AnsiString[Size]='\0';
                    assign (AnsiString);
                    delete[] AnsiString;
                }
                else
                    clear();
            #endif
        #endif //ZENLIB_USEWX
    #endif
    return *this;
}

Ztring& Ztring::From_Unicode (const wchar_t *S, size_type Start, size_type Length)
{
    if (S==NULL)
        return *this;

    if (Length==Error)
        Length=wcslen(S+Start);
    wchar_t* Temp=new wchar_t[Length+1];
    wcsncpy (Temp, S+Start, Length);
    Temp[Length]=__T('\0');

    From_Unicode(Temp);
    delete[] Temp; //Temp=NULL;
    return *this;
}

Ztring& Ztring::From_UTF8 (const char* S)
{
    if (S==NULL)
        return *this;

    #ifdef ZENLIB_USEWX
        size_type OK=wxConvUTF8.MB2WC(NULL, S, 0);
        if (OK!=0 && OK!=Error)
            #ifdef _UNICODE
                assign(wxConvUTF8.cMB2WC(S).data());
            #else
                assign(wxConvCurrent->cWC2MB(wxConvUTF8.cMB2WC(S)));
            #endif
    #else //ZENLIB_USEWX
        #ifdef _UNICODE
            // Don't use MultiByteToWideChar(), some characters are not well decoded
            clear();
            const int8u* Z=(const int8u*)S;
            while (*Z) //0 is end
            {
                //1 byte
                if (*Z<0x80)
                {
                    operator += ((wchar_t)(*Z));
                    Z++;
                }
                //2 bytes
                else if ((*Z&0xE0)==0xC0)
                {
                    if ((*(Z+1)&0xC0)==0x80)
                    {
                        operator += ((((wchar_t)(*Z&0x1F))<<6)|(*(Z+1)&0x3F));
                        Z+=2;
                    }
                    else
                    {
                        clear();
                        return *this; //Bad character
                    }
                }
                //3 bytes
                else if ((*Z&0xF0)==0xE0)
                {
                    if ((*(Z+1)&0xC0)==0x80 && (*(Z+2)&0xC0)==0x80)
                    {
                        operator += ((((wchar_t)(*Z&0x0F))<<12)|((*(Z+1)&0x3F)<<6)|(*(Z+2)&0x3F));
                        Z+=3;
                    }
                    else
                    {
                        clear();
                        return *this; //Bad character
                    }
                }
                //4 bytes
                else if ((*Z&0xF8)==0xF0)
                {
                    if ((*(Z+1)&0xC0)==0x80 && (*(Z+2)&0xC0)==0x80 && (*(Z+3)&0xC0)==0x80)
                    {
                        #if defined(_MSC_VER)
                            #pragma warning(push)
                            #pragma warning(disable:4127)
                        #endif //defined(_MSC_VER)
                        if (sizeof(wchar_t) == 2)
                        #if defined(_MSC_VER)
                            #pragma warning(pop)
                        #endif //defined(_MSC_VER)
                        {
                            int32u Value = ((((int32u)(*Z&0x0F))<<18)|((*(Z+1)&0x3F)<<12)|((*(Z+2)&0x3F)<<6)|(*(Z+3)&0x3F));
                            operator += (0xD800|((Value>>10)-0x40));
                            operator += (0xDC00| (Value&0x3FF));
                        }
                        else
                            operator += ((((wchar_t)(*Z&0x0F))<<18)|((*(Z+1)&0x3F)<<12)|((*(Z+2)&0x3F)<<6)|(*(Z+3)&0x3F));
                        Z+=4;
                    }
                    else
                    {
                        clear();
                        return *this; //Bad character
                    }
                }
                else
                {
                    clear();
                    return *this; //Bad character
                }
            }
        #else
            assign(S); //Not implemented
        #endif
    #endif //ZENLIB_USEWX
    return *this;
}

Ztring& Ztring::From_UTF8 (const char* S, size_type Start, size_type Length)
{
    if (S==NULL)
        return *this;

    if (Length==Error)
        Length=strlen(S+Start);
    char* Temp=new char[Length+1];
    strncpy (Temp, S+Start, Length);
    Temp[Length]='\0';

    From_UTF8(Temp);
    delete[] Temp; //Temp=NULL;
    return *this;
}

Ztring& Ztring::From_UTF16 (const char* S)
{
    if (S==NULL)
        return *this;

         if ((unsigned char)S[0]==(unsigned char)0xFF && (unsigned char)S[1]==(unsigned char)0xFE)
        return From_UTF16LE(S+2);
    else if ((unsigned char)S[0]==(unsigned char)0xFE && (unsigned char)S[1]==(unsigned char)0xFF)
        return From_UTF16BE(S+2);
    else if ((unsigned char)S[0]==(unsigned char)0x00 && (unsigned char)S[1]==(unsigned char)0x00)
    {
        clear(); //No begin!
        return *this;
    }
    else
        return From_UTF16LE(S); //Not sure, default
}

Ztring& Ztring::From_UTF16 (const char* S, size_type Start, size_type Length)
{
    if (S==NULL)
        return *this;

    if (Length<2)
        return *this;

         if ((unsigned char)S[0]==(unsigned char)0xFF && (unsigned char)S[1]==(unsigned char)0xFE)
        return From_UTF16LE(S+2, Start, Length-2);
    else if ((unsigned char)S[0]==(unsigned char)0xFE && (unsigned char)S[1]==(unsigned char)0xFF)
        return From_UTF16BE(S+2, Start, Length-2);
    else if ((unsigned char)S[0]==(unsigned char)0x00 && (unsigned char)S[1]==(unsigned char)0x00)
    {
        clear(); //No begin!
        return *this;
    }
    else
        return From_UTF16LE(S, Start, Length); //Not sure, default
}

Ztring& Ztring::From_UTF16BE (const char* S)
{
    if (S==NULL)
        return *this;

    #ifdef ZENLIB_USEWX
        //clear(); return *this;
        wxMBConvUTF16BE wxConvUTF16BE;
        size_type OK=wxConvUTF16BE.MB2WC(NULL, S, 0);
        if (OK!=0 && OK!=Error)
            #ifdef _UNICODE
                assign(wxConvUTF16BE.cMB2WC(S).data());
            #else
                assign(wxConvCurrent->cWC2MB(wxConvUTF16BE.cMB2WC(S)));
            #endif
    #else //ZENLIB_USEWX
        #ifdef WINDOWS
            clear();
            const wchar_t* SW=(const wchar_t*)S;
            size_t Pos=0;
            while (SW[Pos]!=__T('\0'))
            {
                Char Temp=(Char)(((SW[Pos]&0xFF00)>>8)+((SW[Pos]&0x00FF)<<8));
                append(1, Temp);
                Pos++;
            }
        #else //WINDOWS
            clear();
            while (S[0]!=0 || S[1]!=0)
            {
                append(1, (Char)BigEndian2int16u(S));
                S+=2;
            }
        #endif
    #endif //ZENLIB_USEWX
    return *this;
}

Ztring& Ztring::From_UTF16BE (const char* S, size_type Start, size_type Length)
{
    if (S==NULL)
        return *this;

    if (Length==Error)
    {
        Length=0;
        while(S[Length]!=0x0000)
            Length++;
    }
    else
        Length&=(size_t)-2; //odd number

    char* Temp=new char[Length+2];
    memcpy (Temp, S+Start, Length);
    Temp[Length+0]=0x00;
    Temp[Length+1]=0x00;
    reserve(Length);
    From_UTF16BE(Temp);
    delete[] Temp; //Temp=NULL;
    return *this;
}

Ztring& Ztring::From_UTF16LE (const char* S)
{
    if (S==NULL)
        return *this;

    #ifdef ZENLIB_USEWX
        //clear(); return *this;
        wxMBConvUTF16LE wxConvUTF16LE;
        size_type OK=wxConvUTF16LE.MB2WC(NULL, S, 0);
        if (OK!=0 && OK!=Error)
            #ifdef _UNICODE
                assign(wxConvUTF16LE.cMB2WC(S).data());
            #else
                assign(wxConvCurrent->cWC2MB(wxConvUTF16LE.cMB2WC(S)));
            #endif
    #else //ZENLIB_USEWX
        #ifdef WINDOWS
            #ifdef UNICODE
                const wchar_t* SW=(const wchar_t*)S;
                assign(SW);
            #else
                clear(); //Not implemented
            #endif
        #else //WINDOWS
            clear();
            while (S[0]!=0 || S[1]!=0)
            {
                append(1, (Char)LittleEndian2int16u(S));
                S+=2;
            }
        #endif
    #endif //ZENLIB_USEWX
    return *this;
}

Ztring& Ztring::From_UTF16LE (const char* S, size_type Start, size_type Length)
{
    if (S==NULL)
        return *this;

    if (Length==Error)
    {
        Length=0;
        while(S[Length]!=0x0000)
            Length+=2;
    }
    else
        Length&=(size_t)-2; //odd number

    char* Temp=new char[Length+2];
    memcpy (Temp, S+Start, Length);
    Temp[Length+0]=0x00;
    Temp[Length+1]=0x00;
    From_UTF16LE(Temp);
    delete[] Temp; //Temp=NULL;
    return *this;
}

Ztring& Ztring::From_Local (const char* S)
{
    if (S==NULL)
        return *this;

    #ifdef _UNICODE
        #ifdef ZENLIB_USEWX
            size_type OK=wxConvCurrent->MB2WC(NULL, S, 0);
            if (OK!=0 && OK!=Error)
                assign(wxConvCurrent->cMB2WC(S).data());
        #else //ZENLIB_USEWX
            #ifdef WINDOWS
                int Size=MultiByteToWideChar(CP_ACP, 0, S, -1, NULL, 0);
                if (Size!=0)
                {
                    wchar_t* WideString=new wchar_t[Size+1];
                    MultiByteToWideChar(CP_ACP, 0, S, -1, WideString, Size);
                    WideString[Size]=L'\0';
                    assign (WideString);
                    delete[] WideString; //WideString=NULL;
                }
                else
                    clear();
            #else //WINDOWS
                size_t Size=mbsrtowcs(NULL, &S, 0, NULL);
                if (Size!=0 && Size!=(size_t)-1)
                {
                    wchar_t* WideString=new wchar_t[Size+1];
                    Size=mbsrtowcs(WideString, &S, Size, NULL);
                    WideString[Size]=L'\0';
                    assign (WideString);
                    delete[] WideString; //WideString=NULL;
                }
                else
                    clear();
            #endif
        #endif //ZENLIB_USEWX
    #else
        assign(S);
    #endif
    return *this;
}

Ztring& Ztring::From_Local (const char* S, size_type Start, size_type Length)
{
    if (S==NULL)
        return *this;

    if (Length==Error)
        Length=strlen(S+Start);
    #ifdef _UNICODE
        char* Temp=new char[Length+1];
        strncpy (Temp, S+Start, Length);
        Temp[Length]='\0';
        From_Local(Temp);
        delete[] Temp; //Temp=NULL;
    #else
        assign(S+Start, Length);
        if (find(__T('\0'))!=std::string::npos)
            resize(find(__T('\0')));
    #endif
    return *this;
}

Ztring& Ztring::From_ISO_8859_1(const char* S)
{
    size_t Length = strlen(S);
    wchar_t* Temp = new wchar_t[Length +1];

    for (size_t Pos=0; Pos<Length+1; Pos++)
        Temp[Pos]=(wchar_t)((int8u)S[Pos]);

    From_Unicode(Temp);
    delete[] Temp;
    return *this;
}

Ztring& Ztring::From_ISO_8859_1(const char* S, size_type Start, size_type Length)
{
    if (S==NULL)
        return *this;

    if (Length==Error)
        Length=strlen(S+Start);

    char* Temp = new char[Length+1];
    strncpy(Temp, S +Start, Length);
    Temp[Length] = '\0';
    From_ISO_8859_1(Temp);
    delete[] Temp;

    return *this;
}

Ztring& Ztring::From_ISO_8859_2(const char* S)
{
    size_t Length = strlen(S);
    wchar_t* Temp = new wchar_t[Length +1];

    for (size_t Pos=0; Pos<Length+1; Pos++)
    {
        if ((int8u)S[Pos]>=0xA0)
            Temp[Pos]=(wchar_t)Ztring_ISO_8859_2[((int8u)S[Pos])-0xA0];
        else
            Temp[Pos]=(wchar_t)((int8u)S[Pos]);
    }

    From_Unicode(Temp);
    delete[] Temp;
    return *this;
}

Ztring& Ztring::From_ISO_8859_2(const char* S, size_type Start, size_type Length)
{
    if (S==NULL)
        return *this;

    if (Length==Error)
        Length=strlen(S+Start);

    char* Temp = new char[Length+1];
    strncpy(Temp, S +Start, Length);
    Temp[Length] = '\0';
    From_ISO_8859_2(Temp);
    delete[] Temp;

    return *this;
}

Ztring& Ztring::From_GUID (const int128u S)
{
    Ztring S1;
    S1.From_CC1((int8u) ((S.hi&0x000000FF00000000LL)>>32)); append(S1);
    S1.From_CC1((int8u) ((S.hi&0x0000FF0000000000LL)>>40)); append(S1);
    S1.From_CC1((int8u) ((S.hi&0x00FF000000000000LL)>>48)); append(S1);
    S1.From_CC1((int8u) ((S.hi&0xFF00000000000000LL)>>56)); append(S1); append(__T("-"));
    S1.From_CC1((int8u) ((S.hi&0x0000000000FF0000LL)>>16)); append(S1);
    S1.From_CC1((int8u) ((S.hi&0x00000000FF000000LL)>>24)); append(S1); append(__T("-"));
    S1.From_CC1((int8u) ( S.hi&0x00000000000000FFLL     )); append(S1);
    S1.From_CC1((int8u) ((S.hi&0x000000000000FF00LL)>> 8)); append(S1); append(__T("-"));
    S1.From_CC2((int16u)((S.lo&0xFFFF000000000000LL)>>48)); append(S1); append(__T("-"));
    S1.From_CC2((int16u)((S.lo&0x0000FFFF00000000LL)>>32)); append(S1);
    S1.From_CC2((int16u)((S.lo&0x00000000FFFF0000LL)>>16)); append(S1);
    S1.From_CC2((int16u)( S.lo&0x000000000000FFFFLL     )); append(S1);

    return *this;
}

Ztring& Ztring::From_UUID (const int128u S)
{
    Ztring S1;
    S1.From_CC2((int16u)((S.hi&0xFFFF000000000000LL)>>48)); assign(S1);
    S1.From_CC2((int16u)((S.hi&0x0000FFFF00000000LL)>>32)); append(S1); append(__T("-"));
    S1.From_CC2((int16u)((S.hi&0x00000000FFFF0000LL)>>16)); append(S1); append(__T("-"));
    S1.From_CC2((int16u)( S.hi&0x000000000000FFFFLL     )); append(S1); append(__T("-"));
    S1.From_CC2((int16u)((S.lo&0xFFFF000000000000LL)>>48)); append(S1); append(__T("-"));
    S1.From_CC2((int16u)((S.lo&0x0000FFFF00000000LL)>>32)); append(S1);
    S1.From_CC2((int16u)((S.lo&0x00000000FFFF0000LL)>>16)); append(S1);
    S1.From_CC2((int16u)( S.lo&0x000000000000FFFFLL     )); append(S1);

    return *this;
}

Ztring& Ztring::From_CC4 (const int32u S)
{
    clear();
    for (int8s i=(4-1)*8; i>=0; i-=8)
    {
        int32u Value=(S&(0xFF<<i))>>i;
        if (Value<0x20)
        {
            if (!i || (i!=24  && !(S&(0xFFFFFFFF>>(32-i)))))
                return *this; // Trailing 0 are fine

            // Not valid, using 0x as fallback
            clear();
            append(__T("0x"));
            append(Ztring().From_CC1((int8u)((S&0xFF000000)>>24)));
            append(Ztring().From_CC1((int8u)((S&0x00FF0000)>>16)));
            append(Ztring().From_CC1((int8u)((S&0x0000FF00)>> 8)));
            append(Ztring().From_CC1((int8u)((S&0x000000FF)    )));
            return *this;
        }
        append(1, (Char)(Value));
    }
    return *this;
}

Ztring& Ztring::From_CC3 (const int32u S)
{
    clear();
    for (int8s i=(3-1)*8; i>=0; i-=8)
    {
        int32u Value=(S&(0xFF<<i))>>i;
        if (Value<0x20)
        {
            if (!i || (i!=16  && !(S&(0xFFFFFF>>(24-i)))))
                return *this; // Trailing 0 are fine

            // Not valid, using 0x as fallback
            clear();
            append(__T("0x"));
            append(Ztring().From_CC1((int8u)((S&0x00FF0000)>>16)));
            append(Ztring().From_CC1((int8u)((S&0x0000FF00)>> 8)));
            append(Ztring().From_CC1((int8u)((S&0x000000FF)    )));
            return *this;
        }
        append(1, (Char)(Value));
    }
    return *this;
}

Ztring& Ztring::From_CC2 (const int16u S)
{
    clear();
    Ztring Pos1; Pos1.From_Number(S, 16);
    resize(4-Pos1.size(), __T('0'));
    append(Pos1);
    MakeUpperCase();

    return *this;
}

Ztring& Ztring::From_CC1 (const int8u S)
{
    clear();
    Ztring Pos1; Pos1.From_Number(S, 16);
    resize(2-Pos1.size(), __T('0'));
    append(Pos1);
    MakeUpperCase();

    return *this;
}

Ztring& Ztring::From_Number (const int8s I, int8u Radix)
{
    #if defined(STREAM_MISSING)
        if (Radix==0)
        {
            clear();
            return *this;
        }
        Char* C1=new Char[33];
        #ifdef __MINGW32__
            _itot (I, C1, Radix);
        #else
            _tnprintf(C1, 32, Radix==10?__T("%d"):(Radix==16?__T("%x"):(Radix==8?__T("%o"):__T(""))), I);
        #endif
        assign (C1);
        delete[] C1; //C1=NULL;
    #else
        toStringStream Stream;
        #ifdef UNICODE
            Stream << setbase(Radix) << I;
        #else //UNICODE
            Stream << setbase(Radix) << (size_t)I; //On linux (at least), (un)signed char is detected as a char
        #endif //UNICODE
        assign(Stream.str());
    #endif
    MakeUpperCase();
    return *this;
}

Ztring& Ztring::From_Number (const int8u I, int8u Radix)
{
    #if defined(STREAM_MISSING)
        if (Radix==0)
        {
            clear();
            return *this;
        }
        Char* C1=new Char[33];
        #ifdef __MINGW32__
            _ultot (I, C1, Radix);
        #else
            _tnprintf(C1, 32, Radix==10?__T("%d"):(Radix==16?__T("%x"):(Radix==8?__T("%o"):__T(""))), I);
        #endif
        assign (C1);
        delete[] C1; //C1=NULL;
    #else
        if (Radix==2)
        {
            clear();
            for (int8u Pos=0; Pos<8; Pos++)
            {
                if (I<(((int8u)1)<<Pos))
                    break;
                insert(0, 1, (I&(((int8u)1)<<Pos))?__T('1'):__T('0'));
            }
        }
        else
        {
            toStringStream Stream;
            #ifdef UNICODE
                Stream << setbase(Radix) << I;
            #else //UNICODE
                Stream << setbase(Radix) << (size_t)I; //On linux (at least), (un)signed char is detected as a char
            #endif //UNICODE
            assign(Stream.str());
        }
    #endif
    MakeUpperCase();
    return *this;
}

Ztring& Ztring::From_Number (const int16s I, int8u Radix)
{
    #if defined(STREAM_MISSING)
        if (Radix==0)
        {
            clear();
            return *this;
        }
        Char* C1=new Char[33];
        #ifdef __MINGW32__
            _itot (I, C1, Radix);
        #else
            _tnprintf(C1, 32, Radix==10?__T("%d"):(Radix==16?__T("%x"):(Radix==8?__T("%o"):__T(""))), I);
        #endif
        assign (C1);
        delete[] C1; //C1=NULL;
    #else
        toStringStream Stream;
        Stream << setbase(Radix) << I;
        assign(Stream.str());
    #endif
    MakeUpperCase();
    return *this;
}

Ztring& Ztring::From_Number (const int16u I, int8u Radix)
{
    #if defined(STREAM_MISSING)
        if (Radix==0)
        {
            clear();
            return *this;
        }
        Char* C1=new Char[33];
        #ifdef __MINGW32__
            _ultot (I, C1, Radix);
        #else
            _tnprintf(C1, 32, Radix==10?__T("%u"):(Radix==16?__T("%x"):(Radix==8?__T("%o"):__T(""))), I);
        #endif
        assign (C1);
        delete[] C1; //C1=NULL;
    #else
        if (Radix==2)
        {
            clear();
            for (int8u Pos=0; Pos<16; Pos++)
            {
                if (I<(((int16u)1)<<Pos))
                    break;
                insert(0, 1, (I&(((int16u)1)<<Pos))?__T('1'):__T('0'));
            }
        }
        else
        {
            toStringStream Stream;
            Stream << setbase(Radix) << I;
            assign(Stream.str());
        }
    #endif
    MakeUpperCase();
    return *this;
}

Ztring& Ztring::From_Number (const int32s I, int8u Radix)
{
    #if defined(STREAM_MISSING)
        if (Radix==0)
        {
            clear();
            return *this;
        }
        Char* C1=new Char[33];
        #ifdef __MINGW32__
            _itot (I, C1, Radix);
        #else
            _tnprintf(C1, 32, Radix==10?__T("%ld"):(Radix==16?__T("%lx"):(Radix==8?__T("%lo"):__T(""))), I);
        #endif
        assign (C1);
        delete[] C1; //C1=NULL;
    #else
        toStringStream Stream;
        Stream << setbase(Radix) << I;
        assign(Stream.str());
    #endif
    MakeUpperCase();
    return *this;
}

Ztring& Ztring::From_Number (const int32u I, int8u Radix)
{
    #if defined(STREAM_MISSING)
        if (Radix==0)
        {
            clear();
            return *this;
        }
        Char* C1=new Char[33];
        #ifdef __MINGW32__
            _ultot (I, C1, Radix);
        #else
            _tnprintf(C1, 32, Radix==10?__T("%lu"):(Radix==16?__T("%lx"):(Radix==8?__T("%lo"):__T(""))), I);
        #endif
        assign (C1);
        delete[] C1; //C1=NULL;
    #else
        if (Radix==2)
        {
            clear();
            for (int8u Pos=0; Pos<32; Pos++)
            {
                if (I<(((int32u)1)<<Pos))
                    break;
                insert(0, 1, (I&(((int32u)1)<<Pos))?__T('1'):__T('0'));
            }
        }
        else
        {
            toStringStream Stream;
            Stream << setbase(Radix) << I;
            assign(Stream.str());
        }
    #endif
    MakeUpperCase();
    return *this;
}

Ztring& Ztring::From_Number (const int64s I, int8u Radix)
{
    #if defined(STREAM_MISSING)
        if (Radix==0)
        {
            clear();
            return *this;
        }
        Char* C1=new Char[65];
        #ifdef __MINGW32__
            _i64tot (I, C1, Radix);
        #else
            _tnprintf(C1, 64, Radix==10?__T("%lld"):(Radix==16?__T("%llx"):(Radix==8?__T("%llo"):__T(""))), I);
        #endif
        assign (C1);
        delete[] C1; //C1=NULL;
    #else
        toStringStream Stream;
        Stream << setbase(Radix) << I;
        assign(Stream.str());
    #endif
    MakeUpperCase();
    return *this;
}

Ztring& Ztring::From_Number (const int64u I, int8u Radix)
{
    #if defined(STREAM_MISSING)
        if (Radix==0)
        {
            clear();
            return *this;
        }
        Char* C1=new Char[65]; C1[0] = 0;
        #ifdef __MINGW32__
            _ui64tot (I, C1, Radix);
        #else
            _tnprintf(C1, 64, Radix==10?__T("%llu"):(Radix==16?__T("%llx"):(Radix==8?__T("%llo"):__T(""))), I);
        #endif
        assign (C1);
        delete[] C1; //C1=NULL;
    #else
        if (Radix==2)
        {
            clear();
            for (int8u Pos=0; Pos<32; Pos++)
            {
                if (I<(((int64u)1)<<Pos))
                    break;
                insert(0, 1, (I&(((int64u)1)<<Pos))?__T('1'):__T('0'));
            }
        }
        else
        {
            toStringStream Stream;
            Stream << setbase(Radix) << I;
            assign(Stream.str());
        }
    #endif
    MakeUpperCase();
    return *this;
}

Ztring& Ztring::From_Number (const int128u I, int8u Radix)
{
    From_Local(I.toString(Radix));

    return *this;
}

Ztring& Ztring::From_Number (const float32 F, int8u Precision, ztring_t Options)
{
    #if defined(STREAM_MISSING)
        Char C1[100];
        _tnprintf (C1, 99, (Ztring(__T("%."))+Ztring::ToZtring(Precision)+__T("f")).c_str(), F);
        assign(C1);
    #else
        toStringStream Stream;
        Stream << setprecision(Precision) << fixed << F;
        assign(Stream.str());
        #if defined(__BORLANDC__)
            FindAndReplace(__T(","), __T(".")); //Borland C++ Builder 2010+Windows Seven put a comma for istringstream, but does not support comma for ostringstream
        #endif
    #endif

    if ((Options & Ztring_NoZero && size()>0) && find(__T('.'))!=string::npos)
    {
        while (size()>0 && ((*this)[size()-1]==__T('0')))
            resize(size()-1);
        if (size()>0 && (*this)[size()-1]==__T('.'))
            resize(size()-1);
    }

    return *this;
}

Ztring& Ztring::From_Number (const float64 F, int8u Precision, ztring_t Options)
{
    #if defined(STREAM_MISSING)
        Char C1[100];
        _tnprintf (C1, 99, (Ztring(__T("%."))+Ztring::ToZtring(Precision)+__T("f")).c_str(), F);
        assign(C1);
    #else
        toStringStream Stream;
        Stream << setprecision(Precision) << fixed << F;
        assign(Stream.str());
        #if defined(__BORLANDC__)
            FindAndReplace(__T(","), __T(".")); //Borland C++ Builder 2010+Windows Seven put a comma for istringstream, but does not support comma for ostringstream
        #endif
    #endif

    if ((Options & Ztring_NoZero && size()>0) && find(__T('.'))!=string::npos)
    {
        while (size()>0 && ((*this)[size()-1]==__T('0')))
            resize(size()-1);
        if (size()>0 && (*this)[size()-1]==__T('.'))
            resize(size()-1);
    }

    return *this;
}

Ztring& Ztring::From_Number (const float80 F, int8u Precision, ztring_t Options)
{
    #if defined(STREAM_MISSING)
        Char C1[100];
        _tnprintf (C1, 99, (Ztring(__T("%."))+Ztring::ToZtring(Precision)+__T("f")).c_str(), F);
        assign(C1);
    #else
        toStringStream Stream;
        Stream << setprecision(Precision) << fixed << F;
        assign(Stream.str());
        #if defined(__BORLANDC__)
            FindAndReplace(__T(","), __T(".")); //Borland C++ Builder 2010+Windows Seven put a comma for istringstream, but does not support comma for ostringstream
        #endif
    #endif

    if ((Options & Ztring_NoZero && size()>0) && find(__T('.'))!=string::npos)
    {
        while (size()>0 && ((*this)[size()-1]==__T('0')))
            resize(size()-1);
        if (size()>0 && (*this)[size()-1]==__T('.'))
            resize(size()-1);
    }

    return *this;
}

#ifdef SIZE_T_IS_LONG
Ztring& Ztring::From_Number (const size_t I, int8u Radix)
{
    #if defined(STREAM_MISSING)
        Char C1[100];
        _tnprintf(C1, 64, Radix==10?__T("%zu"):(Radix==16?__T("%zx"):(Radix==8?__T("%zo"):__T(""))), I);
        assign(C1);
    #else
        toStringStream Stream;
        Stream << setbase(Radix) << I;
        assign(Stream.str());
        #if defined(__BORLANDC__)
            FindAndReplace(__T(","), __T(".")); //Borland C++ Builder 2010+Windows Seven put a comma for istringstream, but does not support comma for ostringstream
        #endif
    #endif
    MakeUpperCase();
    return *this;
}
#endif //SIZE_T_IS_LONG

Ztring& Ztring::From_BCD     (const int8u I)
{
    #if defined(STREAM_MISSING)
        clear();
        append(1, __T('0')+I/0x10);
        append(1, __T('0')+I%0x10);
    #else
        toStringStream Stream;
        Stream << I/0x10;
        Stream << I%0x10;
        assign(Stream.str());
    #endif
    return *this;
}

//---------------------------------------------------------------------------
Ztring& Ztring::Duration_From_Milliseconds (const int64s Value_)
{
    int64s Value=Value_;
    bool Negative=false;
    if (Value<0)
    {
        Value=-Value;
        Negative=true;
    }

    int64u HH=(int8u)(Value/1000/60/60);
    int64u MM=Value/1000/60   -((HH*60));
    int64u Stream=Value/1000      -((HH*60+MM)*60);
    int64u MS=Value           -((HH*60+MM)*60+Stream)*1000;
    Ztring DateT;
    Ztring Date;
    DateT.From_Number(HH); if (DateT.size()<2){DateT=Ztring(__T("0"))+DateT;}
    Date+=DateT;
    Date+=__T(":");
    DateT.From_Number(MM); if (DateT.size()<2){DateT=Ztring(__T("0"))+DateT;}
    Date+=DateT;
    Date+=__T(":");
    DateT.From_Number(Stream); if (DateT.size()<2){DateT=Ztring(__T("0"))+DateT;}
    Date+=DateT;
    Date+=__T(".");
    DateT.From_Number(MS); if (DateT.size()<2){DateT=Ztring(__T("00"))+DateT;} else if (DateT.size()<3){DateT=Ztring(__T("0"))+DateT;}
    Date+=DateT;
    if (Negative)
    {
        assign(__T("-"));
        append(Date);
    }
    else
        assign (Date.c_str());
    return *this;
}

//---------------------------------------------------------------------------
Ztring& Ztring::Duration_From_Milliseconds (const int64u Value)
{
    int64u HH=(int8u)(Value/1000/60/60);
    int64u MM=Value/1000/60   -((HH*60));
    int64u Stream=Value/1000      -((HH*60+MM)*60);
    int64u MS=Value           -((HH*60+MM)*60+Stream)*1000;
    Ztring DateT;
    Ztring Date;
    DateT.From_Number(HH); if (DateT.size()<2){DateT=Ztring(__T("0"))+DateT;}
    Date+=DateT;
    Date+=__T(":");
    DateT.From_Number(MM); if (DateT.size()<2){DateT=Ztring(__T("0"))+DateT;}
    Date+=DateT;
    Date+=__T(":");
    DateT.From_Number(Stream); if (DateT.size()<2){DateT=Ztring(__T("0"))+DateT;}
    Date+=DateT;
    Date+=__T(".");
    DateT.From_Number(MS); if (DateT.size()<2){DateT=Ztring(__T("00"))+DateT;} else if (DateT.size()<3){DateT=Ztring(__T("0"))+DateT;}
    Date+=DateT;
    assign (Date.c_str());
    return *this;
}

Ztring& Ztring::Date_From_Milliseconds_1601 (const int64u Value)
{
    if (Value>=11644473600000LL) //Values <1970 are not supported
    {
        Date_From_Seconds_1970((int32u)((Value-11644473600000LL)/1000));
        append(__T("."));
        Ztring Milliseconds; Milliseconds.From_Number(Value%1000);
        while (Milliseconds.size()<3)
            Milliseconds+=__T('0');
        append(Milliseconds);
    }
    else
        clear(); //Not supported

    return *this;
}

Ztring& Ztring::Date_From_Seconds_1601 (const int64u Value)
{
    return Date_From_Seconds_1970(((int64s)Value)-11644473600LL);
}

Ztring& Ztring::Date_From_Seconds_1900 (const int32u Value)
{
    if (Value>2208988800)
        return Date_From_Seconds_1970(((int64s)Value)-2208988800);
    else
        return Date_From_Seconds_1970(((int64s)Value)+0x100000000LL-2208988800); //Value is considering to loop e.g. NTP value
}

Ztring& Ztring::Date_From_Seconds_1900 (const int64s Value)
{
    return Date_From_Seconds_1970(Value-2208988800);
}

Ztring& Ztring::Date_From_Seconds_1904 (const int32u Value)
{
    return Date_From_Seconds_1970(((int64s)Value)-2082844800);
}

Ztring& Ztring::Date_From_Seconds_1904 (const int64u Value)
{
    return Date_From_Seconds_1970(((int64s)Value)-2082844800);
}

Ztring& Ztring::Date_From_Seconds_1904 (const int64s Value)
{
    return Date_From_Seconds_1970(Value-2082844800);
}

Ztring& Ztring::Date_From_Seconds_1970 (const int32u Value)
{
    return Date_From_Seconds_1970((int64s)Value);
}

Ztring& Ztring::Date_From_Seconds_1970 (const int32s Value)
{
    return Date_From_Seconds_1970((int64s)Value);
}

Ztring& Ztring::Date_From_Seconds_1970 (const int64s Value)
{
    time_t Time=(time_t)Value;
    #if defined(_WIN32)
        #if _CRT_USE_CONFORMING_ANNEX_K_TIME || (__STDC_WANT_LIB_EXT1__ && defined(__STDC_LIB_EXT1__))
        // C11 standard version on MSVC or other compilers
        tm Gmt_Temp;
        tm* Gmt = gmtime_s(&Time, &Gmt_Temp);
        #else
        // MSVC and MinGW-w64 argument order and return value differs from C11 standard
        tm Gmt_Temp;
        errno_t gmtime_s_Result = gmtime_s(&Gmt_Temp, &Time);
        tm* Gmt = gmtime_s_Result ? nullptr : &Gmt_Temp;
        #endif
    #elif defined(HAVE_GMTIME_R)
        // POSIX or C23
        tm Gmt_Temp;
        tm *Gmt = gmtime_r(&Time, &Gmt_Temp);
    #else
        // Fallback: not thread-safe, but prevents compile errors
        #ifdef __GNUC__
        #warning "This version of ZenLib is not thread safe"
        #endif
        tm *Gmt = gmtime(&Time);
    #endif
    if (!Gmt)
    {
        clear();
        return *this;
    }
    Ztring DateT;
    Ztring Date=__T("UTC ");
    Date+=Ztring::ToZtring((Gmt->tm_year+1900));
    Date+=__T("-");
    DateT.From_Number(Gmt->tm_mon+1); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Gmt->tm_mon+1);}
    Date+=DateT;
    Date+=__T("-");
    DateT.From_Number(Gmt->tm_mday); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Gmt->tm_mday);}
    Date+=DateT;
    Date+=__T(" ");
    DateT.From_Number(Gmt->tm_hour); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Gmt->tm_hour);}
    Date+=DateT;
    Date+=__T(":");
    DateT=Ztring::ToZtring(Gmt->tm_min); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Gmt->tm_min);}
    Date+=DateT;
    Date+=__T(":");
    DateT.From_Number(Gmt->tm_sec); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Gmt->tm_sec);}
    Date+=DateT;
    assign (Date.c_str());
    return *this;
}

Ztring& Ztring::Date_From_Seconds_1970_Local (const int32u Value)
{
    time_t Time=(time_t)Value;
    #if defined(_WIN32)
        #if _CRT_USE_CONFORMING_ANNEX_K_TIME || (__STDC_WANT_LIB_EXT1__ && defined(__STDC_LIB_EXT1__))
        // C11 standard version on MSVC or other compilers
        tm Gmt_Temp;
        tm* Gmt = localtime_s(&Time, &Gmt_Temp);
        #else
        // MSVC and MinGW-w64 argument order and return value differs from C11 standard
        tm Gmt_Temp;
        errno_t localtime_s_Result = localtime_s(&Gmt_Temp, &Time);
        tm* Gmt = localtime_s_Result ? nullptr : &Gmt_Temp;
        #endif
    #elif defined(HAVE_GMTIME_R)
        // POSIX or C23
        tm Gmt_Temp;
        tm *Gmt = localtime_r(&Time, &Gmt_Temp);
    #else
        // Fallback: not thread-safe, but prevents compile errors
        #ifdef __GNUC__
        #warning "This version of ZenLib is not thread safe"
        #endif
        tm *Gmt = localtime(&Time);
    #endif
    Ztring DateT;
    Ztring Date;
    if (Gmt)
    {
    Date+=Ztring::ToZtring((Gmt->tm_year+1900));
    Date+=__T("-");
    DateT.From_Number(Gmt->tm_mon+1); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Gmt->tm_mon+1);}
    Date+=DateT;
    Date+=__T("-");
    DateT.From_Number(Gmt->tm_mday); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Gmt->tm_mday);}
    Date+=DateT;
    Date+=__T(" ");
    DateT.From_Number(Gmt->tm_hour); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Gmt->tm_hour);}
    Date+=DateT;
    Date+=__T(":");
    DateT=Ztring::ToZtring(Gmt->tm_min); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Gmt->tm_min);}
    Date+=DateT;
    Date+=__T(":");
    DateT.From_Number(Gmt->tm_sec); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Gmt->tm_sec);}
    Date+=DateT;
    assign (Date.c_str());
    }
    return *this;
}

Ztring& Ztring::Date_From_String (const char* Value, size_t Value_Size)
{
    //Only the year
    if (Value_Size<10)
    {
        From_UTF8(Value, 0, Value_Size);
        return *this;
    }

    #ifdef ZENLIB_USEWX
        Ztring ToReturn=__T("UTC ");
        wxDateTime Date;
        Ztring DateS;
        DateS.From_Local(Value, Value_Size).c_str();
        if (!DateS.empty() && DateS[DateS.size()-1]==__T('\n'))
            DateS.resize(DateS.size()-1);

        //Some strange formating : exactly 24 bytes (or 25 with 0x0A at the end) and Year is at the end
        if (DateS.size()==24 && DateS[23]>=__T('0') && DateS[23]<=__T('9') && DateS[21]>=__T('0') && DateS[21]<=__T('9') && DateS[19]==__T(' '))
            Date.ParseFormat(DateS.c_str(), __T("%a %b %d %H:%M:%S %Y"));
        //ISO date
        else if (DateS.size()==10 && (DateS[4]<__T('0') || DateS[4]>__T('9')) && (DateS[7]<__T('0') || DateS[7]>__T('9')))
        {
            DateS[4]=__T('-');
            DateS[7]=__T('-');
            ToReturn+=DateS;
        }
        //Default
        else
            Date.ParseDateTime(DateS.c_str());

        if (ToReturn.size()<5)
        {
            ToReturn+=Date.FormatISODate();
            ToReturn+=__T(" ");
            ToReturn+=Date.FormatISOTime();
        }
        else
            ToReturn+=DateS;

        assign (ToReturn.c_str());
    #else //ZENLIB_USEWX
        Ztring DateS; DateS.From_UTF8(Value, 0, Value_Size);
        //Unix style formating : exactly 24 bytes (or 25 with 0x0A at the end) and Year is at the end
        if ((DateS.size()==24 || (DateS.size()==25 && DateS[24]==__T('\n'))) && DateS[23]>=__T('0') && DateS[23]<=__T('9') && DateS[21]>=__T('0') && DateS[21]<=__T('9') && DateS[19]==__T(' '))
        {
            clear();
            append(1, DateS[20]);
            append(1, DateS[21]);
            append(1, DateS[22]);
            append(1, DateS[23]);
            append(1, __T('-'));
                 if (DateS[4]==__T('J') && DateS[5]==__T('a') && DateS[6]==__T('n') && DateS[7]==__T(' '))
            {
                append(1, __T('0'));
                append(1, __T('1'));
            }
            else if (DateS[4]==__T('F') && DateS[5]==__T('e') && DateS[6]==__T('b') && DateS[7]==__T(' '))
            {
                append(1, __T('0'));
                append(1, __T('2'));
            }
            else if (DateS[4]==__T('M') && DateS[5]==__T('a') && DateS[6]==__T('r') && DateS[7]==__T(' '))
            {
                append(1, __T('0'));
                append(1, __T('3'));
            }
            else if (DateS[4]==__T('A') && DateS[5]==__T('p') && DateS[6]==__T('r') && DateS[7]==__T(' '))
            {
                append(1, __T('0'));
                append(1, __T('4'));
            }
            else if (DateS[4]==__T('M') && DateS[5]==__T('a') && DateS[6]==__T('y') && DateS[7]==__T(' '))
            {
                append(1, __T('0'));
                append(1, __T('5'));
            }
            else if (DateS[4]==__T('J') && DateS[5]==__T('u') && DateS[6]==__T('n') && DateS[7]==__T(' '))
            {
                append(1, __T('0'));
                append(1, __T('6'));
            }
            else if (DateS[4]==__T('J') && DateS[5]==__T('u') && DateS[6]==__T('l') && DateS[7]==__T(' '))
            {
                append(1, __T('0'));
                append(1, __T('7'));
            }
            else if (DateS[4]==__T('A') && DateS[5]==__T('u') && DateS[6]==__T('g') && DateS[7]==__T(' '))
            {
                append(1, __T('0'));
                append(1, __T('8'));
            }
            else if (DateS[4]==__T('S') && DateS[5]==__T('e') && DateS[6]==__T('p') && DateS[7]==__T(' '))
            {
                append(1, __T('0'));
                append(1, __T('9'));
            }
            else if (DateS[4]==__T('O') && DateS[5]==__T('c') && DateS[6]==__T('t') && DateS[7]==__T(' '))
            {
                append(1, __T('1'));
                append(1, __T('0'));
            }
            else if (DateS[4]==__T('N') && DateS[5]==__T('o') && DateS[6]==__T('v') && DateS[7]==__T(' '))
            {
                append(1, __T('1'));
                append(1, __T('1'));
            }
            else if (DateS[4]==__T('D') && DateS[5]==__T('e') && DateS[6]==__T('c') && DateS[7]==__T(' '))
            {
                append(1, __T('1'));
                append(1, __T('2'));
            }
            else
            {
                assign(DateS);
                return *this;
            }
            append(1, __T('-'));
            append(1, DateS[8]);
            append(1, DateS[9]);
            append(1, __T(' '));
            append(1, DateS[11]);
            append(1, DateS[12]);
            append(1, __T(':'));
            append(1, DateS[14]);
            append(1, DateS[15]);
            append(1, __T(':'));
            append(1, DateS[17]);
            append(1, DateS[18]);
        }
        else if (DateS.size()==20 && DateS[4]==__T('-') && DateS[7]==__T('-') && DateS[10]==__T('T') && DateS[13]==__T(':') && DateS[16]==__T(':') && DateS[19]==__T('Z'))
        {
            DateS.resize(19);
            DateS[10]=__T(' ');
            assign(__T("UTC "));
            append(DateS);
        }
        else if (DateS.size()==23 && DateS[4]==__T('-') && DateS[7]==__T('-') && DateS[10]==__T(' ') && DateS[14]==__T(' ') && DateS[17]==__T(':') && DateS[20]==__T(':'))
        {
            DateS.erase(10, 4);
            //assign(__T("UTC ")); //Is not UTC
            append(DateS);
        }
        else
            From_UTF8(Value, 0, Value_Size); //Not implemented
    #endif //ZENLIB_USEWX
    return *this;
}

Ztring& Ztring::Date_From_Numbers (const int8u Year, const int8u Month, const int8u Day, const int8u Hour, const int8u Minute, const int8u Second)
{
    Ztring DateT;
    Ztring Date=__T("UTC ");
    DateT.From_Number(Year); if (DateT.size()<2){DateT=Ztring(__T("200"))+Ztring::ToZtring(Year);}; if (DateT.size()<3){DateT=Ztring(__T("20"))+Ztring::ToZtring(Year);}
    Date+=DateT;
    Date+=__T("-");
    DateT.From_Number(Month); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Month);}
    Date+=DateT;
    Date+=__T("-");
    DateT.From_Number(Day); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Day);}
    Date+=DateT;
    Date+=__T(" ");
    DateT.From_Number(Hour); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Hour);}
    Date+=DateT;
    Date+=__T(":");
    DateT=Ztring::ToZtring(Minute); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Minute);}
    Date+=DateT;
    Date+=__T(":");
    DateT.From_Number(Second); if (DateT.size()<2){DateT=Ztring(__T("0"))+Ztring::ToZtring(Second);}
    Date+=DateT;
    assign (Date.c_str());
    return *this;
}

#ifndef WSTRING_MISSING
//---------------------------------------------------------------------------
std::wstring Ztring::To_Unicode () const
{
    #ifdef _UNICODE
        return c_str();
    #else //_UNICODE
        #ifdef ZENLIB_USEWX
            return wxConvCurrent->cMB2WC(c_str()).data();
        #else //ZENLIB_USEWX
            return std::wstring(); //Not implemented
        #endif //ZENLIB_USEWX
    #endif //_UNICODE
}
#endif //WSTRING_MISSING

std::string Ztring::To_UTF8 () const
{
    #ifdef _UNICODE
        //Correction thanks to Andrew Jang
        // Don't use WideCharToMultiByte(), some characters are not well converted
        std::string ToReturn;
        ToReturn.reserve(size()); // more efficient

        const wchar_t* Z=c_str();

        while (*Z)
        {
            if (*Z < 0x80)
            {
                ToReturn += (char)(*(Z++));
                continue;
            }
            
            int32u wc; // must be unsigned.

            #if defined(_MSC_VER)
                #pragma warning(push)
                #pragma warning(disable:4127)
            #endif //defined(_MSC_VER)
            if (sizeof(wchar_t) == 2)
            #if defined(_MSC_VER)
                #pragma warning(pop)
            #endif //defined(_MSC_VER)
            {
                if (((*Z) & 0xFC00) == 0xD800)
                {
                    //UTF-16
                    wc =( (((int16u) *Z) & 0x3FF) + 0x40) << 10;
                    Z++;
                    wc |= (((int16u) *Z) & 0x3FF);
                }
                else
                wc = (int16u) *Z; // avoid a cast problem if wchar_t is signed.
            }
            else
                wc = *Z;

            int count;

            // refer to http://en.wikipedia.org/wiki/UTF-8#Description

            if (wc < 0x80)
                count = 1;
            else if (wc < 0x800)
                count = 2;
            else if (wc < 0x10000)
                count = 3;
            else if (wc < 0x200000)
                count = 4;
            else if (wc < 0x4000000)
                count = 5;
            else if (wc <= 0x7fffffff)
                count = 6;
            else
                break;  // bad character

            int64u utfbuf = 0; // 8 bytes
            char* utf8chars = (char*) &utfbuf;

            switch (count)
            {
            case 6:
                utf8chars[5] = 0x80 | (wc & 0x3f);
                wc = (wc >> 6) | 0x4000000;
                [[fallthrough]];
            case 5:
                utf8chars[4] = 0x80 | (wc & 0x3f);
                wc = (wc >> 6) | 0x200000;
                [[fallthrough]];
            case 4:
                utf8chars[3] = 0x80 | (wc & 0x3f);
                wc = (wc >> 6) | 0x10000;
                [[fallthrough]];
            case 3:
                utf8chars[2] = 0x80 | (wc & 0x3f);
                wc = (wc >> 6) | 0x800;
                [[fallthrough]];
            case 2:
                utf8chars[1] = 0x80 | (wc & 0x3f);
                wc = (wc >> 6) | 0xc0;
                [[fallthrough]];
            case 1:
                utf8chars[0] = (char) wc;
            }

            ToReturn += utf8chars;

            ++Z;
        }

        return ToReturn;
    #else
        #ifdef ZENLIB_USEWX
            return wxConvUTF8.cWC2MB(wxConvCurrent->cMB2WC(c_str())).data();
        #else //ZENLIB_USEWX
            return c_str(); //Not implemented
        #endif //ZENLIB_USEWX
    #endif
}

std::string Ztring::To_Local () const
{
    #ifdef _UNICODE
        #ifdef ZENLIB_USEWX
            wxCharBuffer C=wxConvCurrent->cWC2MB(c_str());
            if (C.data())
                return C.data();
            else
                return std::string();
        #else //ZENLIB_USEWX
            #ifdef WINDOWS
                int Size=WideCharToMultiByte(CP_ACP, 0, c_str(), -1, NULL, 0, NULL, NULL);
                if (Size!=0)
                {
                    char* AnsiString=new char[Size+1];
                    WideCharToMultiByte(CP_ACP, 0, c_str(), -1, AnsiString, Size, NULL, NULL);
                    AnsiString[Size]='\0';
                    std::string ToReturn(AnsiString);
                    delete[] AnsiString; //AnsiString=NULL;
                    return ToReturn;
                }
                else
                    return std::string();
            #else //WINDOWS
                if (empty())
                    return std::string();

                size_t Size=wcstombs(NULL, c_str(), 0);
                if (Size!=0 && Size!=(size_t)-1)
                {
                    char* AnsiString=new char[Size+1];
                    Size=wcstombs(AnsiString, c_str(), Size);
                    if (Size!=0 && Size!=(size_t)-1)
                    {
                        AnsiString[Size]='\0';
                        std::string ToReturn(AnsiString);
                        delete[] AnsiString; //AnsiString=NULL;
                        return ToReturn;
                    }

                    //Failed
                    delete[] AnsiString; //AnsiString=NULL;
                }

                //Trying with bad chars
                char* Result=new char[MB_CUR_MAX];
                std::string AnsiString;
                for (size_t Pos=0; Pos<size(); Pos++)
                {
                    size_t Result_Size=wcrtomb(Result, operator[](Pos), 0);
                    if (Result_Size && Result_Size!=(size_t)-1)
                        AnsiString.append(Result, Result_Size);
                    else
                        AnsiString+='?';
                }
                delete[] Result; //Result=NULL;
                return AnsiString;
            #endif
        #endif //ZENLIB_USEWX
    #else
        return c_str();
    #endif
}

//---------------------------------------------------------------------------
int128u Ztring::To_UUID () const
{
    if (size()!=36)
        return 0;

    Ztring Temp=*this;

    for (size_t Pos=0; Pos<36; Pos++)
    {
        if ((Temp[Pos]< __T('0') || Temp[Pos]> __T('9'))
         && (Temp[Pos]< __T('A') || Temp[Pos]> __T('F'))
         && (Temp[Pos]< __T('a') || Temp[Pos]> __T('f')))
            return 0;
        if (Temp[Pos]>=__T('A') && Temp[Pos]<=__T('F'))
        {
            Temp[Pos]-=__T('A');
            Temp[Pos]+=__T('9')+1;
        }
        if (Temp[Pos]>=__T('a') && Temp[Pos]<=__T('f'))
        {
            Temp[Pos]-=__T('a');
            Temp[Pos]+=__T('9')+1;
        }

        switch(Pos)
        {
            case  7 :
            case 12 :
            case 17 :
            case 22 :
                        if (at(Pos+1)!=__T('-'))
                            return 0;
                        Pos++; //Skipping dash in the test
        }
    }

    int128u I;
    I.hi=((int64u)((int8u)(Temp[ 0]-'0'))<<60)
       | ((int64u)((int8u)(Temp[ 1]-'0'))<<56)
       | ((int64u)((int8u)(Temp[ 2]-'0'))<<52)
       | ((int64u)((int8u)(Temp[ 3]-'0'))<<48)
       | ((int64u)((int8u)(Temp[ 4]-'0'))<<44)
       | ((int64u)((int8u)(Temp[ 5]-'0'))<<40)
       | ((int64u)((int8u)(Temp[ 6]-'0'))<<36)
       | ((int64u)((int8u)(Temp[ 7]-'0'))<<32)
       | ((int64u)((int8u)(Temp[ 9]-'0'))<<28)
       | ((int64u)((int8u)(Temp[10]-'0'))<<24)
       | ((int64u)((int8u)(Temp[11]-'0'))<<20)
       | ((int64u)((int8u)(Temp[12]-'0'))<<16)
       | ((int64u)((int8u)(Temp[14]-'0'))<<12)
       | ((int64u)((int8u)(Temp[15]-'0'))<< 8)
       | ((int64u)((int8u)(Temp[16]-'0'))<< 4)
       | ((int64u)((int8u)(Temp[17]-'0'))    );
    I.lo=((int64u)((int8u)(Temp[19]-'0'))<<60)
       | ((int64u)((int8u)(Temp[20]-'0'))<<56)
       | ((int64u)((int8u)(Temp[21]-'0'))<<52)
       | ((int64u)((int8u)(Temp[22]-'0'))<<48)
       | ((int64u)((int8u)(Temp[24]-'0'))<<44)
       | ((int64u)((int8u)(Temp[25]-'0'))<<40)
       | ((int64u)((int8u)(Temp[26]-'0'))<<36)
       | ((int64u)((int8u)(Temp[27]-'0'))<<32)
       | ((int64u)((int8u)(Temp[28]-'0'))<<28)
       | ((int64u)((int8u)(Temp[29]-'0'))<<24)
       | ((int64u)((int8u)(Temp[30]-'0'))<<20)
       | ((int64u)((int8u)(Temp[31]-'0'))<<16)
       | ((int64u)((int8u)(Temp[32]-'0'))<<12)
       | ((int64u)((int8u)(Temp[33]-'0'))<< 8)
       | ((int64u)((int8u)(Temp[34]-'0'))<< 4)
       | ((int64u)((int8u)(Temp[35]-'0'))    );

    return I;
}

//---------------------------------------------------------------------------
int32u Ztring::To_CC4 () const
{
    int32u I;
    I =((int32u)((int8u)at(0))<<24)
     | ((int32u)((int8u)at(1))<<16)
     | ((int32u)((int8u)at(2))<< 8)
     | ((int32u)((int8u)at(3))    );

    return I;
}

//---------------------------------------------------------------------------
//Operateur ToInt
int8s Ztring::To_int8s (int8u Radix, ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    //Conversion
    int I;
    #if defined(STREAM_MISSING)
        #ifdef __MINGW32__
            I=_ttoi(c_str());
        #elif defined(UNICODE)
            std::string S=To_UTF8();
            I=atoi(S.c_str());
        #else //UNICODE
            I=atoi(c_str());
        #endif //UNICODE
    #else
        tStringStream Stream(*this);
        Stream >> setbase(Radix) >> I;
        if (Stream.fail())
            return 0;
    #endif

    //Rounded
    if (Options&Ztring_Rounded && find(__T('.'))!=Error)
    {
        float80 F=To_float80();
        F-=I;
        if (F>=0.5f)
            return (int8s)I+1;
    }

    return (int8s)I;
}

//---------------------------------------------------------------------------
//Operateur ToInt
int8u Ztring::To_int8u (int8u Radix, ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    //Conversion
    unsigned int I;
    #if defined(STREAM_MISSING)
        #ifdef __MINGW32__
            I=_ttoi64(c_str()); //TODO : I>0x7FFFFFFF - Replaced by i64 version to support, but not good
        #elif defined(UNICODE)
            std::string S=To_UTF8();
            I=atoi(S.c_str());
        #else //defined(UNICODE)
            I=atoi(c_str());
        #endif //defined(UNICODE)
    #else
        tStringStream Stream(*this);
        Stream >> setbase(Radix) >> I;
        if (Stream.fail())
            return 0;
    #endif

    //Rounded
    if (Options&Ztring_Rounded && find(__T('.'))!=std::string::npos)
    {
        float32 F=To_float32();
        F-=I;
        if (F>=0.5f)
            return (int8u)I+1;
    }

    return (int8u)I;
}

//---------------------------------------------------------------------------
//Operateur ToInt
int16s Ztring::To_int16s (int8u Radix, ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    //Conversion
    int I;
    #if defined(STREAM_MISSING)
        #ifdef __MINGW32__
            I=_ttoi(c_str());
        #elif defined(UNICODE)
            std::string S=To_UTF8();
            I=atoi(S.c_str());
        #else //defined(UNICODE)
            I=atoi(c_str());
        #endif //defined(UNICODE)
    #else
        tStringStream Stream(*this);
        Stream >> setbase(Radix) >> I;
        if (Stream.fail())
            return 0;
    #endif

    //Rounded
    if (Options&Ztring_Rounded && find(__T('.'))!=Error)
    {
        float80 F=To_float80();
        F-=I;
        if (F>=0.5f)
            return (int16s)I+1;
    }

    return (int16s)I;
}

//---------------------------------------------------------------------------
//Operateur ToInt
int16u Ztring::To_int16u (int8u Radix, ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    //Conversion
    unsigned int I;
    #if defined(STREAM_MISSING)
        #ifdef __MINGW32__
            I=_ttoi64(c_str()); //TODO : I>0x7FFFFFFF - Replaced by i64 version to support, but not good
        #elif defined(UNICODE)
            std::string S=To_UTF8();
            I=atoi(S.c_str());
        #else //defined(UNICODE)
            I=atoi(c_str());
        #endif //defined(UNICODE)
    #else
        tStringStream Stream(*this);
        Stream >> setbase(Radix) >> I;
        if (Stream.fail())
            return 0;
    #endif

    //Rounded
    if (Options&Ztring_Rounded && find(__T('.'))!=std::string::npos)
    {
        float32 F=To_float32();
        F-=I;
        if (F>=0.5f)
            return (int16u)I+1;
    }

    return (int16u)I;
}

//---------------------------------------------------------------------------
//Operateur ToInt
int32s Ztring::To_int32s (int8u Radix, ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    //Conversion
    int32s I;
    #if defined(STREAM_MISSING)
        #ifdef __MINGW32__
            I=_ttoi(c_str());
        #elif defined(UNICODE)
            std::string S=To_UTF8();
            I=atol(S.c_str());
        #else //defined(UNICODE)
            I=atol(c_str());
        #endif //defined(UNICODE)
    #else
        tStringStream Stream(*this);
        Stream >> setbase(Radix) >> I;
        if (Stream.fail())
            return 0;
    #endif

    //Rounded
    if (Options&Ztring_Rounded && find(__T('.'))!=Error)
    {
        float80 F=To_float80();
        F-=I;
        if (F>=0.5f)
            return I+1;
    }

    return I;
}

//---------------------------------------------------------------------------
//Operateur ToInt
int32u Ztring::To_int32u (int8u Radix, ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    //Conversion
    int32u I;
    #if defined(STREAM_MISSING)
        #ifdef __MINGW32__
            I=_ttoi64(c_str()); //TODO : I>0x7FFFFFFF - Replaced by i64 version to support, but not good
        #elif defined(UNICODE)
            std::string S=To_UTF8();
            I=atol(S.c_str());
        #else //defined(UNICODE)
            I=atol(c_str());
        #endif //defined(UNICODE)
    #else
        tStringStream Stream(*this);
        Stream >> setbase(Radix) >> I;
        if (Stream.fail())
            return 0;
    #endif

    //Rounded
    if (Options&Ztring_Rounded && find(__T('.'))!=std::string::npos)
    {
        float32 F=To_float32();
        F-=I;
        if (F>=0.5f)
            return I+1;
    }

    return I;
}

//---------------------------------------------------------------------------
//Operateur ToInt
int64s Ztring::To_int64s (int8u Radix, ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    //Conversion
    int64s I;
    #if defined(STREAM_MISSING)
        #ifdef __MINGW32__
            I=_ttoi64(c_str());
        #elif defined(UNICODE)
            std::string S=To_UTF8();
            I=atoll(S.c_str());
        #else //defined(UNICODE)
            I=atoll(c_str());
        #endif //defined(UNICODE)
    #else
        tStringStream Stream(*this);
        Stream >> setbase(Radix) >> I;
        if (Stream.fail())
            return 0;
    #endif

    //Rounded
    if (Options&Ztring_Rounded && find(__T('.'))!=std::string::npos)
    {
        float32 F=To_float32();
        F-=I;
        if (F>0.5f)
            return I+1;
    }

    return I;
}

//---------------------------------------------------------------------------
//Operateur ToInt
int64u Ztring::To_int64u (int8u Radix, ztring_t Options) const
{
    //Integrity
    if (empty())
        return 0;

    //Conversion
    int64u I;
    #if defined(STREAM_MISSING)
        #ifdef __MINGW32__
            I=_ttoi64(c_str()); //TODO : I>0x7FFFFFFFFFFFFFFF
        #elif defined(UNICODE)
            std::string S=To_UTF8();
            I=atoll(S.c_str());
        #else //defined(UNICODE)
            I=atoll(c_str());
        #endif //defined(UNICODE)
    #else
        tStringStream Stream(*this);
        Stream >> setbase(Radix) >> I;
        if (Stream.fail())
            return 0;
    #endif

    //Rounded
    if (Options&Ztring_Rounded && find(__T('.'))!=std::string::npos)
    {
        float32 F=To_float32();
        F-=I;
        if (F>=0.5f)
            return I+1;
    }

    return I;
}

//---------------------------------------------------------------------------
int128u Ztring::To_int128u (int8u, ztring_t) const
{
    if (size()!=32)
        return 0;

    Ztring Temp=*this;

    for (size_t Pos=0; Pos<32; Pos++)
    {
        if ((Temp[Pos]< __T('0') || Temp[Pos]> __T('9'))
         && (Temp[Pos]< __T('A') || Temp[Pos]> __T('F'))
         && (Temp[Pos]< __T('a') || Temp[Pos]> __T('f')))
            return 0;
        if (Temp[Pos]>=__T('A') && Temp[Pos]<=__T('F'))
        {
            Temp[Pos]-=__T('A');
            Temp[Pos]+=__T('9')+1;
        }
        if (Temp[Pos]>=__T('a') && Temp[Pos]<=__T('f'))
        {
            Temp[Pos]-=__T('a');
            Temp[Pos]+=__T('9')+1;
        }
    }

    int128u I;
    I.hi=((int64u)((int8u)(Temp[ 0]-'0'))<<60)
       | ((int64u)((int8u)(Temp[ 1]-'0'))<<56)
       | ((int64u)((int8u)(Temp[ 2]-'0'))<<52)
       | ((int64u)((int8u)(Temp[ 3]-'0'))<<48)
       | ((int64u)((int8u)(Temp[ 4]-'0'))<<44)
       | ((int64u)((int8u)(Temp[ 5]-'0'))<<40)
       | ((int64u)((int8u)(Temp[ 6]-'0'))<<36)
       | ((int64u)((int8u)(Temp[ 7]-'0'))<<32)
       | ((int64u)((int8u)(Temp[ 8]-'0'))<<28)
       | ((int64u)((int8u)(Temp[ 9]-'0'))<<24)
       | ((int64u)((int8u)(Temp[10]-'0'))<<20)
       | ((int64u)((int8u)(Temp[11]-'0'))<<16)
       | ((int64u)((int8u)(Temp[12]-'0'))<<12)
       | ((int64u)((int8u)(Temp[13]-'0'))<< 8)
       | ((int64u)((int8u)(Temp[14]-'0'))<< 4)
       | ((int64u)((int8u)(Temp[15]-'0'))    );
    I.lo=((int64u)((int8u)(Temp[16]-'0'))<<60)
       | ((int64u)((int8u)(Temp[17]-'0'))<<56)
       | ((int64u)((int8u)(Temp[18]-'0'))<<52)
       | ((int64u)((int8u)(Temp[19]-'0'))<<48)
       | ((int64u)((int8u)(Temp[20]-'0'))<<44)
       | ((int64u)((int8u)(Temp[21]-'0'))<<40)
       | ((int64u)((int8u)(Temp[22]-'0'))<<36)
       | ((int64u)((int8u)(Temp[23]-'0'))<<32)
       | ((int64u)((int8u)(Temp[24]-'0'))<<28)
       | ((int64u)((int8u)(Temp[25]-'0'))<<24)
       | ((int64u)((int8u)(Temp[26]-'0'))<<20)
       | ((int64u)((int8u)(Temp[27]-'0'))<<16)
       | ((int64u)((int8u)(Temp[28]-'0'))<<12)
       | ((int64u)((int8u)(Temp[29]-'0'))<< 8)
       | ((int64u)((int8u)(Temp[30]-'0'))<< 4)
       | ((int64u)((int8u)(Temp[31]-'0'))    );

    return I;
}

//---------------------------------------------------------------------------
//Operateur ToFloat
float32 Ztring::To_float32(ztring_t) const
{
    //Integrity
    if (empty())
        return 0;

    //Conversion
    #if defined(STREAM_MISSING)
        #ifdef UNICODE
            return (wcstod(c_str(),NULL));
        #else
            return (strtod(c_str(),NULL));
        #endif
    #else
        float32 F;
        tStringStream Stream(*this);
        Stream >> F;
        if (Stream.fail())
            return 0;

        return F;
    #endif
}

//---------------------------------------------------------------------------
//Operateur ToFloat
float64 Ztring::To_float64(ztring_t) const
{
    //Integrity
    if (empty())
        return 0;

    //Conversion
    #if defined(STREAM_MISSING)
        #ifdef UNICODE
            return (wcstod(c_str(),NULL)); //TODO verify no wcstold
        #else
            return (strtod(c_str(),NULL)); //TODO verify no strtold
        #endif
    #else
        float64 F;
        tStringStream Stream(*this);
        Stream >> F;
        if (Stream.fail())
            return 0;

        return F;
    #endif
}

//---------------------------------------------------------------------------
//Operateur ToFloat
float80 Ztring::To_float80(ztring_t) const
{
    //Integrity
    if (empty())
        return 0;

    //Conversion
    #if defined(STREAM_MISSING)
        #ifdef UNICODE
            return (wcstod(c_str(),NULL)); //TODO verify no wcstold
        #else
            return (strtod(c_str(),NULL)); //TODO verify no strtold
        #endif
    #else
        float80 F;
        tStringStream Stream(*this);
        Stream >> F;
        if (Stream.fail())
            return 0;

        return F;
    #endif
}

//***************************************************************************
// Edition
//***************************************************************************

//---------------------------------------------------------------------------
// Retourne une partie de la chaine
Ztring Ztring::SubString (const tstring &Begin, const tstring &End, size_type Pos, ztring_t Options) const
{
    //Recherche Debut
    size_type I_Debut=find(Begin, Pos);
    if (I_Debut==Error)
        return Ztring();
    I_Debut+=Begin.size();

    //gestion fin NULL
    if (End.empty())
        return substr(I_Debut);

    //Recherche Fin
    size_type I_Fin=find(End, I_Debut);
    if (I_Fin==Error)
    {
        if (Options & Ztring_AddLastItem)
            return substr(I_Debut);
        else
            return Ztring();
    }

    return substr(I_Debut, I_Fin-I_Debut);
}

//---------------------------------------------------------------------------
//FindAndReplace
Ztring::size_type Ztring::FindAndReplace (const ZenLib::tstring &ToFind, const ZenLib::tstring &ReplaceBy, size_type Pos, ZenLib::ztring_t Options)
{
    if (ToFind.empty())
        return 0;

    size_type Count=0;
    size_type Middle=Pos;
    while (!(Count==1 && !(Options&Ztring_Recursive)) && (Middle=find(ToFind, Middle))!=npos)
    {
        replace(Middle, ToFind.length(), ReplaceBy);
        Middle += ReplaceBy.length();
        Count++;
    }

    return Count;
}

//---------------------------------------------------------------------------
//test if it is a number
bool Ztring::IsNumber() const
{
    if (empty())
        return false;

    bool OK=true;
    size_t Size=size();
    for (size_t Pos=0; Pos<Size; Pos++)
        if (operator[](Pos)<__T('0') || operator[](Pos)>__T('9'))
        {
            OK=false;
            break;
        }
    return OK;
}

//---------------------------------------------------------------------------
//Mise en minuscules
Ztring &Ztring::MakeLowerCase()
{
#if defined(__UNICODE__)
    transform(begin(), end(), begin(), [](wchar_t c) { return towlower(c); });
#else // defined(__UNICODE__)
    transform(begin(), end(), begin(), [](unsigned char c) { return tolower(c); });
#endif // defined(__UNICODE__)
    return *this;
}

//---------------------------------------------------------------------------
// Mise en majuscules
Ztring &Ztring::MakeUpperCase()
{
#if defined(__UNICODE__)
    transform(begin(), end(), begin(), [](wchar_t c) { return towupper(c); });
#else // defined(__UNICODE__)
    transform(begin(), end(), begin(), [](unsigned char c) { return toupper(c); });
#endif // defined(__UNICODE__)
    return *this;
}

//---------------------------------------------------------------------------
// Remove leading whitespaces from a string
Ztring &Ztring::TrimLeft(Char ToTrim)
{
    size_type First=0;
    while (First<size() && operator[](First)==ToTrim)
        First++;
    assign (c_str()+First);
    return *this;
}

//---------------------------------------------------------------------------
// Remove trailing whitespaces from a string
Ztring &Ztring::TrimRight(Char ToTrim)
{
    if (size()==0)
        return *this;

    size_type Last=size()-1;
    while (Last!=(size_type)-1 && operator[](Last)==ToTrim)
        Last--;
    assign (c_str(), Last+1);
    return *this;
}

//---------------------------------------------------------------------------
// Remove leading and trailing whitespaces from a string
Ztring &Ztring::Trim(Char ToTrim)
{
    TrimLeft(ToTrim);
    TrimRight(ToTrim);
    return *this;
}

//---------------------------------------------------------------------------
// Quotes a string
Ztring &Ztring::Quote(Char ToTrim)
{
    assign(tstring(1, ToTrim)+c_str()+ToTrim);
    return *this;
}

//***************************************************************************
// Information
//***************************************************************************

//---------------------------------------------------------------------------
//Count
Ztring::size_type Ztring::Count (const Ztring &ToCount, ztring_t) const
{
    size_type Count=0;
    for (size_type Pos=0; Pos<=size(); Pos++)
        if (find(ToCount, Pos)!=npos)
        {
            Count++;
            Pos+=ToCount.size()-1; //-1 because the loop will add 1
        }
    return Count;
}

//---------------------------------------------------------------------------
//Compare
bool Ztring::Compare (const Ztring &ToCompare, const Ztring &Comparator, ztring_t Options) const
{
    //Integers management
    if (IsNumber() && ToCompare.IsNumber())
    {
        int64s Left=To_int64s();
        int64s Right=ToCompare.To_int64s();
        if (Comparator==__T("==")) return (Left==Right);
        if (Comparator==__T("<"))  return (Left< Right);
        if (Comparator==__T("<=")) return (Left<=Right);
        if (Comparator==__T(">=")) return (Left>=Right);
        if (Comparator==__T(">"))  return (Left> Right);
        if (Comparator==__T("!=")) return (Left!=Right);
        if (Comparator==__T("<>")) return (Left!=Right);
        return false;
    }

    //Case sensitive option
    if (!(Options & Ztring_CaseSensitive))
    {
        //Need to copy strings and make it lowercase
        Ztring Left (c_str());
        Ztring Right (ToCompare.c_str());
        Left.MakeLowerCase();
        Right.MakeLowerCase();

        //string comparasion
        if (Comparator==__T("==")) return (Left==Right);
        if (Comparator==__T("IN")) {if (Left.find(Right)!=string::npos) return true; else return false;}
        if (Comparator==__T("<"))  return (Left< Right);
        if (Comparator==__T("<=")) return (Left<=Right);
        if (Comparator==__T(">=")) return (Left>=Right);
        if (Comparator==__T(">"))  return (Left> Right);
        if (Comparator==__T("!=")) return (Left!=Right);
        if (Comparator==__T("<>")) return (Left!=Right);
        return false;
    }
    else
    {
        //string comparasion
        if (Comparator==__T("==")) return (*this==ToCompare);
        if (Comparator==__T("IN")) {if (this->find(ToCompare)!=string::npos) return true; else return false;}
        if (Comparator==__T("<"))  return (*this< ToCompare);
        if (Comparator==__T("<=")) return (*this<=ToCompare);
        if (Comparator==__T(">=")) return (*this>=ToCompare);
        if (Comparator==__T(">"))  return (*this> ToCompare);
        if (Comparator==__T("!=")) return (*this!=ToCompare);
        if (Comparator==__T("<>")) return (*this!=ToCompare);
        return false;
    }
}

} //namespace
