mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
741 lines
23 KiB
C++
741 lines
23 KiB
C++
//*********************************************************
|
|
//
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
|
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
|
//
|
|
//*********************************************************
|
|
|
|
// Functions to convert dates back and forth to the ISO8601 format
|
|
// http://www.iso.org/iso/en/prods-services/popstds/datesandtime.html
|
|
|
|
#include "pch.h"
|
|
#include "ISO8601.h"
|
|
|
|
#define ISO8601_MAX_USED_CCH 26 // Max amount of characters when generating ISO 8601 strings: YYYY-MM-DDThh:mm:ss.ssssZ + terminating zero
|
|
|
|
|
|
// This table defines different "types" of characters for use as the columns
|
|
// of the state table:
|
|
// 0 - invalid character
|
|
// 1 - number
|
|
// 2 - '-'
|
|
// 3 - date-time separator ('T', 't' and ' ')
|
|
// 4 - ':'
|
|
// 5 - UTC zone ('Z' and 'z')
|
|
// 6 - '+'
|
|
// 7 - second-fraction separator ('.' and ',')
|
|
|
|
static const unsigned char iso8601chartable[256] =
|
|
{
|
|
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 (00 - 0f)
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 (10 - 1f)
|
|
3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 2, 7, 0, // 2 (20 - 2f)
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0, 0, 0, 0, 0, // 3 (30 - 3f)
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 (40 - 4f)
|
|
0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, // 5 (50 - 5f)
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 (60 - 6f)
|
|
0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, // 7 (70 - 7f)
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 (80 - 8f)
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 (90 - 9f)
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a (a0 - af)
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // b (b0 - bf)
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c (c0 - cf)
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // d (d0 - df)
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e (e0 - ef)
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // f (f0 - ff)
|
|
};
|
|
|
|
// Parsing state table is made up of WORD values with this meaning:
|
|
//
|
|
// | action | | next state |
|
|
// |_|_|_|_|_|_|_|_| |_|_|_|_|_|_|_|_|
|
|
// HIBYTE LOBYTE
|
|
|
|
#define STENTRY WORD
|
|
#define S(a,b) MAKEWORD(b,a)
|
|
#define ACTION(a) HIBYTE(a)
|
|
#define STATE(a) LOBYTE(a)
|
|
|
|
// Actions
|
|
#define _OK_ 0x00 // valid character
|
|
#define _NXT 0x01 // end of a segment, move to next
|
|
#define _TZM 0x02 // starting '-' time zone offset
|
|
#define _TZP 0x03 // starting '+' time zone offset
|
|
#define _MSC 0x04 // millisecond digit
|
|
#define _TZU 0x05 // time zone UTC
|
|
#define _ERR 0x80 // error, invalid character
|
|
#define ___ERROR____ MAKEWORD(0x00, _ERR)
|
|
|
|
// State table
|
|
#define STATE_TABLE_DIM 8
|
|
static const STENTRY iso8601StateTable[][STATE_TABLE_DIM] =
|
|
{
|
|
// unknown number '-' 'T' ':' 'Z' '+' '.'
|
|
___ERROR____, S(_OK_,0x01), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x00 year
|
|
___ERROR____, S(_OK_,0x02), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x01
|
|
___ERROR____, S(_OK_,0x03), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x02
|
|
___ERROR____, S(_NXT,0x04), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x03
|
|
___ERROR____, S(_OK_,0x06), S(_OK_,0x05), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x04 month
|
|
___ERROR____, S(_OK_,0x06), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x05
|
|
___ERROR____, S(_NXT,0x07), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x06
|
|
___ERROR____, S(_OK_,0x09), S(_OK_,0x08), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x07 day
|
|
___ERROR____, S(_OK_,0x09), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x08
|
|
___ERROR____, S(_NXT,0x0a), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x09
|
|
___ERROR____, S(_OK_,0x0c), ___ERROR____, S(_OK_,0x0b), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0a hour
|
|
___ERROR____, S(_OK_,0x0c), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0b
|
|
___ERROR____, S(_NXT,0x0d), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0c
|
|
___ERROR____, S(_OK_,0x0f), S(_TZM,0x15), ___ERROR____, S(_OK_,0x0e), S(_TZU,0x1b), S(_TZP,0x15), ___ERROR____, //0x0d min
|
|
___ERROR____, S(_OK_,0x0f), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0e
|
|
___ERROR____, S(_NXT,0x10), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0f
|
|
___ERROR____, S(_OK_,0x12), S(_TZM,0x15), ___ERROR____, S(_OK_,0x11), S(_TZU,0x1b), S(_TZP,0x15), ___ERROR____, //0x10 sec
|
|
___ERROR____, S(_OK_,0x12), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x11
|
|
___ERROR____, S(_NXT,0x13), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x12
|
|
___ERROR____, ___ERROR____, S(_TZM,0x15), ___ERROR____, ___ERROR____, S(_TZU,0x1b), S(_TZP,0x15), S(_OK_,0x14), //0x13 '.' or 'Z' or '+/-'
|
|
___ERROR____, S(_MSC,0x14), S(_TZM,0x15), ___ERROR____, ___ERROR____, S(_TZU,0x1b), S(_TZP,0x15), ___ERROR____, //0x14 fragment of a second
|
|
___ERROR____, S(_OK_,0x16), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x15 TZ offset - hour
|
|
___ERROR____, S(_NXT,0x17), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x16
|
|
___ERROR____, S(_OK_,0x19), ___ERROR____, ___ERROR____, S(_OK_,0x18), ___ERROR____, ___ERROR____, ___ERROR____, //0x17 TZ offset - minute
|
|
___ERROR____, S(_OK_,0x19), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x18
|
|
___ERROR____, S(_NXT,0x1a), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x19
|
|
___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x1a offset done - parsing done
|
|
___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x1b UTC zone done - parsing done
|
|
};
|
|
|
|
|
|
#define IsLeapYear(YEARS) ( \
|
|
(((YEARS) % 400 == 0) || \
|
|
((YEARS) % 100 != 0) && ((YEARS) % 4 == 0)) ? \
|
|
TRUE \
|
|
: \
|
|
FALSE \
|
|
)
|
|
|
|
static HRESULT _GetNumDaysForYearMonth(
|
|
WORD wYear,
|
|
WORD wMonth,
|
|
__out WORD *pwDays)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL == pwDays ||
|
|
wMonth < 1 || wMonth > 12 ||
|
|
wYear < 1601 || wYear > 9999)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
static const WORD s_wNumDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
*pwDays = s_wNumDays[wMonth - 1];
|
|
// Check for leap year
|
|
if ((wMonth == 2) && IsLeapYear(wYear))
|
|
{
|
|
(*pwDays)++;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
#define ONE_SECOND 10000000ui64 // number of 100-nanosecond intervals in a second
|
|
#define ONE_MINUTE (ONE_SECOND * 60) // number of 100-nanosecond intervals in a minute
|
|
#define ONE_HOUR (ONE_MINUTE * 60) // number of 100-nanosecond intervals in an hour
|
|
|
|
typedef union tagTU
|
|
{
|
|
FILETIME ft;
|
|
ULARGE_INTEGER ui;
|
|
} TU;
|
|
|
|
static HRESULT _AddTZOffset(
|
|
Iso8601ParsingStage ips,
|
|
WORD wValue,
|
|
int iTZDirection,
|
|
__inout SYSTEMTIME *pSysTime)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
_ASSERTE((ips == IPS_TZHOUR && wValue < 24) || (ips == IPS_TZMINUTE && wValue < 60));
|
|
_ASSERTE(iTZDirection == 1 || iTZDirection == -1);
|
|
|
|
// Convert SYSTEMTIME to FILETIME to perform date-time arithmetic
|
|
TU ftTime = {0};
|
|
if (SystemTimeToFileTime(pSysTime, &ftTime.ft))
|
|
{
|
|
ULARGE_INTEGER ulOffset;
|
|
ulOffset.QuadPart = (ips == IPS_TZHOUR) ? ONE_HOUR * wValue : ONE_MINUTE * wValue;
|
|
if (iTZDirection > 0)
|
|
{
|
|
ftTime.ui.QuadPart += ulOffset.QuadPart;
|
|
}
|
|
else
|
|
{
|
|
ftTime.ui.QuadPart -= ulOffset.QuadPart;
|
|
}
|
|
if (!FileTimeToSystemTime(&ftTime.ft, pSysTime))
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT _CheckValueAndAddToSysTime(
|
|
Iso8601ParsingStage ips,
|
|
WORD wValue,
|
|
WORD wMSDigits,
|
|
int iTZDirection,
|
|
__inout SYSTEMTIME *pSysTime)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WORD wHiLimit = 0;
|
|
WORD wLoLimit = 0;
|
|
WORD *pDateWord = NULL;
|
|
|
|
_ASSERTE((iTZDirection == 0 && ips != IPS_TZHOUR && ips != IPS_TZMINUTE) ||
|
|
(iTZDirection != 0 && (ips == IPS_TZHOUR || ips == IPS_TZMINUTE)));
|
|
_ASSERTE((wMSDigits == 0 && ips != IPS_MILLISECOND) ||
|
|
(wMSDigits > 0 && wMSDigits < 4 && ips == IPS_MILLISECOND));
|
|
|
|
switch (ips)
|
|
{
|
|
case IPS_YEAR:
|
|
wLoLimit = 1601;
|
|
wHiLimit = 9999;
|
|
pDateWord = (WORD *) &(pSysTime->wYear);
|
|
break;
|
|
case IPS_MONTH:
|
|
wLoLimit = 1;
|
|
wHiLimit = 12;
|
|
pDateWord = (WORD *) &(pSysTime->wMonth);
|
|
break;
|
|
case IPS_DAY:
|
|
wLoLimit = 1;
|
|
hr = _GetNumDaysForYearMonth(pSysTime->wYear, pSysTime->wMonth, &wHiLimit);
|
|
_ASSERTE(SUCCEEDED(hr)); // internal call, should never fail
|
|
pDateWord = (WORD *) &(pSysTime->wDay);
|
|
break;
|
|
case IPS_HOUR:
|
|
wLoLimit = 0;
|
|
wHiLimit = 23;
|
|
pDateWord = (WORD *) &(pSysTime->wHour);
|
|
break;
|
|
case IPS_MINUTE:
|
|
wLoLimit = 0;
|
|
wHiLimit = 59;
|
|
pDateWord = (WORD *) &(pSysTime->wMinute);
|
|
break;
|
|
case IPS_SECOND:
|
|
wLoLimit = 0;
|
|
wHiLimit = 59;
|
|
pDateWord = (WORD *) &(pSysTime->wSecond);
|
|
break;
|
|
case IPS_MILLISECOND:
|
|
wLoLimit = 0;
|
|
wHiLimit = 999;
|
|
while (wMSDigits++ < 3)
|
|
{
|
|
wValue *= 10;
|
|
}
|
|
pDateWord = (WORD *) &(pSysTime->wMilliseconds);
|
|
break;
|
|
case IPS_TZHOUR:
|
|
wLoLimit = 0;
|
|
wHiLimit = 23; // valid offsets appear to be -12 to +14, but we'll allow any valid hour value
|
|
pDateWord = (WORD *) &(pSysTime->wHour);
|
|
break;
|
|
case IPS_TZMINUTE:
|
|
wLoLimit = 0;
|
|
wHiLimit = 59;
|
|
pDateWord = (WORD *) &(pSysTime->wMinute);
|
|
break;
|
|
default:
|
|
hr = E_UNEXPECTED;
|
|
break;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_ASSERTE(NULL != pDateWord);
|
|
if (wValue < wLoLimit || wValue > wHiLimit)
|
|
{
|
|
hr = E_ABORT;
|
|
}
|
|
else
|
|
{
|
|
_ASSERTE(*pDateWord == 0 || ips >= IPS_MILLISECOND);
|
|
if (ips != IPS_TZHOUR && ips != IPS_TZMINUTE)
|
|
{
|
|
*pDateWord = wValue;
|
|
}
|
|
else
|
|
{
|
|
_ASSERTE(iTZDirection == 1 || iTZDirection == -1);
|
|
// handle offset rollover - can affect entire date
|
|
hr = _AddTZOffset(ips, wValue, iTZDirection, pSysTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT _iso8601ToSysTime(
|
|
const char *pszisoDate,
|
|
__out SYSTEMTIME *pSysTime,
|
|
__out Iso8601ParsingStage *pips)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL == pszisoDate ||
|
|
NULL == pSysTime ||
|
|
NULL == pips ||
|
|
*pszisoDate == '\0')
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ZeroMemory(pSysTime, sizeof(SYSTEMTIME));
|
|
*pips = IPS_INVALID;
|
|
|
|
BYTE action = 0;
|
|
BYTE state = 0;
|
|
WORD DateWord = 0;
|
|
Iso8601ParsingStage ips = IPS_YEAR;
|
|
int iTZDirection = 0;
|
|
WORD wMSDigits = 0;
|
|
|
|
// Main state machine loop
|
|
while(*pszisoDate && SUCCEEDED(hr))
|
|
{
|
|
unsigned char code = iso8601chartable[*pszisoDate];
|
|
|
|
// Prevent overflows - sanity check
|
|
if ((code >= STATE_TABLE_DIM) ||
|
|
(state >= ARRAYSIZE(iso8601StateTable)))
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
break;
|
|
}
|
|
|
|
STENTRY stValue = iso8601StateTable[state][code];
|
|
state = STATE(stValue);
|
|
action = ACTION(stValue);
|
|
|
|
switch(action)
|
|
{
|
|
case _OK_: // input OK, valid character
|
|
case _NXT: // finish piece and advance to next stage, valid character
|
|
{
|
|
if (code == 1)
|
|
{
|
|
DateWord = (DateWord * 10) + (*pszisoDate - '0');
|
|
}
|
|
|
|
if (action == _NXT)
|
|
{
|
|
hr = _CheckValueAndAddToSysTime(ips, DateWord, 0 /*wMSDigits*/, iTZDirection, pSysTime);
|
|
DateWord = 0;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*pips = ips;
|
|
ips = Iso8601ParsingStage(int(ips) + 1);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case _TZM: // start '-' timezone offset
|
|
iTZDirection = 1;
|
|
DateWord = 0;
|
|
*pips = ips;
|
|
ips = IPS_TZHOUR;
|
|
break;
|
|
case _TZP: // start '+' timezone offset
|
|
iTZDirection = -1;
|
|
DateWord = 0;
|
|
*pips = ips;
|
|
ips = IPS_TZHOUR;
|
|
break;
|
|
case _MSC: // process millisecond digit
|
|
_ASSERTE(code == 1 && ips == IPS_MILLISECOND);
|
|
wMSDigits++;
|
|
if (wMSDigits < 4)
|
|
{
|
|
DateWord = (DateWord * 10) + (*pszisoDate - '0');
|
|
|
|
hr = _CheckValueAndAddToSysTime(ips, DateWord, wMSDigits, iTZDirection, pSysTime);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*pips = IPS_MILLISECOND;
|
|
}
|
|
}
|
|
break;
|
|
case _TZU: // 'Z' for UTC zone
|
|
*pips = IPS_TZUTC;
|
|
ips = IPS_TZUTC;
|
|
break;
|
|
case _ERR: // error, invalid character
|
|
_ASSERTE(state == 0x00);
|
|
hr = E_ABORT;
|
|
break;
|
|
default:
|
|
hr = E_UNEXPECTED;
|
|
break;
|
|
}
|
|
|
|
pszisoDate++;
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && *pszisoDate == '\0')
|
|
{
|
|
if (action != _NXT && action != _MSC && action != _TZU)
|
|
{
|
|
hr = E_ABORT;
|
|
}
|
|
}
|
|
|
|
if (hr != S_OK && hr != E_ABORT)
|
|
{
|
|
*pips = IPS_INVALID;
|
|
}
|
|
}
|
|
|
|
_ASSERTE(hr != E_UNEXPECTED);
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT _iso8601ToFileTime(
|
|
const char *pszisoDate,
|
|
__out FILETIME *pftTime,
|
|
__out Iso8601ParsingStage *pips)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
SYSTEMTIME stTime = {0};
|
|
|
|
hr = _iso8601ToSysTime(pszisoDate, &stTime, pips);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!SystemTimeToFileTime(&stTime, pftTime))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT _SysTimeToiso8601(
|
|
__in SYSTEMTIME *pstTime,
|
|
__in BOOL fGeneratePartial,
|
|
__out_ecount(cch) char *pszBuf,
|
|
__in ULONG cch,
|
|
__in BOOL fUseShortTimeFormat = FALSE
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL == pstTime ||
|
|
NULL == pszBuf ||
|
|
cch < ISO8601_MAX_USED_CCH)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (pstTime->wYear < 1601 || // FILETIME cannot handle less
|
|
pstTime->wYear > 9999 || // ISO8601 has four digits for a year
|
|
pstTime->wMonth > 12 ||
|
|
pstTime->wDay > 31 ||
|
|
pstTime->wHour > 24 ||
|
|
pstTime->wMinute > 59 ||
|
|
pstTime->wSecond > 59 ||
|
|
pstTime->wMilliseconds > 999)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WORD wDays = 0;
|
|
hr = _GetNumDaysForYearMonth(pstTime->wYear, pstTime->wMonth, &wDays);
|
|
if (SUCCEEDED(hr) && pstTime->wDay > wDays)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pszBuf[0] = static_cast<char>((pstTime->wYear / 1000) + '0');
|
|
pszBuf[1] = static_cast<char>(((pstTime->wYear / 100) % 10) + '0');
|
|
pszBuf[2] = static_cast<char>(((pstTime->wYear / 10) % 10) + '0');
|
|
pszBuf[3] = static_cast<char>(((pstTime->wYear) % 10) + '0');
|
|
if (pstTime->wMonth > 0 ||
|
|
!fGeneratePartial)
|
|
{
|
|
pszBuf[4] = '-';
|
|
pszBuf[5] = static_cast<char>((pstTime->wMonth / 10) + '0');
|
|
pszBuf[6] = static_cast<char>((pstTime->wMonth % 10) + '0');
|
|
if (pstTime->wDay > 0 ||
|
|
!fGeneratePartial)
|
|
{
|
|
pszBuf[7] = '-';
|
|
pszBuf[8] = static_cast<char>((pstTime->wDay / 10) + '0');
|
|
pszBuf[9] = static_cast<char>((pstTime->wDay % 10) + '0');
|
|
if (pstTime->wHour != 0 ||
|
|
pstTime->wMinute != 0 ||
|
|
pstTime->wSecond != 0 ||
|
|
pstTime->wMilliseconds != 0 ||
|
|
!fGeneratePartial)
|
|
{
|
|
pszBuf[10] = 'T';
|
|
pszBuf[11] = static_cast<char>(pstTime->wHour / 10 + '0');
|
|
pszBuf[12] = static_cast<char>((pstTime->wHour % 10) + '0');
|
|
pszBuf[13] = ':';
|
|
pszBuf[14] = static_cast<char>(pstTime->wMinute / 10 + '0');
|
|
pszBuf[15] = static_cast<char>((pstTime->wMinute % 10) + '0');
|
|
pszBuf[16] = ':';
|
|
pszBuf[17] = static_cast<char>(pstTime->wSecond / 10 + '0');
|
|
pszBuf[18] = static_cast<char>((pstTime->wSecond % 10) + '0');
|
|
if ( !fUseShortTimeFormat && ( pstTime->wMilliseconds != 0 ) )
|
|
{
|
|
// YYYY-MM-DDThh:mm:ss.ssssZ
|
|
pszBuf[19] = '.';
|
|
pszBuf[20] = static_cast<char>(pstTime->wMilliseconds / 100 + '0');
|
|
pszBuf[21] = static_cast<char>(((pstTime->wMilliseconds / 10) % 10) + '0');
|
|
pszBuf[22] = static_cast<char>((pstTime->wMilliseconds % 10) + '0');
|
|
|
|
// pad the last digit of millisecond with 0
|
|
pszBuf[23] = '0';
|
|
pszBuf[24] = 'Z';
|
|
pszBuf[25] = 0;
|
|
}
|
|
else
|
|
{
|
|
// YYYY-MM-DDThh:mm:ssZ
|
|
pszBuf[19] = 'Z';
|
|
pszBuf[20] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// YYYY-MM-DD
|
|
pszBuf[10] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// YYYY-MM
|
|
pszBuf[7] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// YYYY
|
|
pszBuf[4] = 0;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT _FileTimeToiso8601(
|
|
const FILETIME *pftTime,
|
|
BOOL fGeneratePartial,
|
|
__out_ecount(cch) char *pszBuf,
|
|
ULONG cch,
|
|
BOOL fUseShortTimeFormat
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
SYSTEMTIME stTime = {0};
|
|
|
|
if (NULL == pftTime)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (FileTimeToSystemTime( pftTime, &stTime))
|
|
{
|
|
hr = _SysTimeToiso8601( &stTime, fGeneratePartial, pszBuf, cch, fUseShortTimeFormat );
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT FILETIMEToISO8601W(
|
|
__in const FILETIME* pft,
|
|
__in BOOL fGeneratePartial,
|
|
__out_ecount(cchISO8601) PWSTR pszISO8601,
|
|
__in size_t cchISO8601,
|
|
__in BOOL fUseShortTimeFormat
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CHAR aszISO8601[ISO8601_MAX_USED_CCH];
|
|
hr = _FileTimeToiso8601( pft, fGeneratePartial, aszISO8601, ARRAYSIZE(aszISO8601), fUseShortTimeFormat );
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
int cchSHAnsiToUnicode;
|
|
hr = SizeTToInt(cchISO8601, &cchSHAnsiToUnicode);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ( ISO8601_MAX_USED_CCH >= ::MultiByteToWideChar(
|
|
CP_ACP,
|
|
0,
|
|
aszISO8601,
|
|
-1,
|
|
pszISO8601, cchSHAnsiToUnicode) ) ? S_OK : E_UNEXPECTED;
|
|
_ASSERTE(SUCCEEDED(hr));
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT ISO8601ToFILETIMEW(
|
|
__in PCWSTR pszISO8601,
|
|
__out FILETIME* pft,
|
|
__out Iso8601ParsingStage *pips)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CHAR aszISO8601[ISO8601_MAX_CCH];
|
|
|
|
if( !::WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
pszISO8601,
|
|
-1,
|
|
aszISO8601,
|
|
ISO8601_MAX_CCH,
|
|
NULL,
|
|
NULL ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _iso8601ToFileTime(aszISO8601, pft, pips);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT SYSTEMTIMEToISO8601ExW(
|
|
__in const SYSTEMTIME* pst,
|
|
__in BOOL fGeneratePartial,
|
|
__out_ecount(cchISO8601) PWSTR pszISO8601,
|
|
__in size_t cchISO8601)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
SYSTEMTIME st = *pst;
|
|
CHAR aszISO8601[ISO8601_MAX_USED_CCH];
|
|
hr = _SysTimeToiso8601(&st, fGeneratePartial, aszISO8601, ARRAYSIZE(aszISO8601));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
int cchSHAnsiToUnicode;
|
|
hr = SizeTToInt(cchISO8601, &cchSHAnsiToUnicode);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ( ISO8601_MAX_USED_CCH >= ::MultiByteToWideChar(
|
|
CP_ACP,
|
|
0,
|
|
aszISO8601,
|
|
-1,
|
|
pszISO8601,
|
|
cchSHAnsiToUnicode ) ) ? S_OK : E_UNEXPECTED;
|
|
_ASSERTE(SUCCEEDED(hr));
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT SYSTEMTIMEToISO8601W(
|
|
__in const SYSTEMTIME* pst,
|
|
__out_ecount(cchISO8601) PWSTR pszISO8601,
|
|
__in size_t cchISO8601)
|
|
{
|
|
return SYSTEMTIMEToISO8601ExW(pst, FALSE /*fGeneratePartial*/, pszISO8601, cchISO8601);
|
|
}
|
|
|
|
HRESULT ISO8601ToSYSTEMTIMEExW(
|
|
__in PCWSTR pszISO8601,
|
|
__out SYSTEMTIME* pst,
|
|
__out Iso8601ParsingStage *pips)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CHAR aszISO8601[ISO8601_MAX_CCH];
|
|
|
|
if( !::WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
pszISO8601,
|
|
-1,
|
|
aszISO8601,
|
|
ISO8601_MAX_CCH,
|
|
NULL,
|
|
NULL ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _iso8601ToSysTime(aszISO8601, pst, pips);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT ISO8601ToSYSTEMTIMEW(
|
|
__in PCWSTR pszISO8601,
|
|
__out SYSTEMTIME* pst)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
Iso8601ParsingStage ips = IPS_INVALID;
|
|
|
|
hr = ISO8601ToSYSTEMTIMEExW(pszISO8601, pst, &ips);
|
|
|
|
// Fix up less-than-full-date
|
|
if (SUCCEEDED(hr) && ips < IPS_DAY)
|
|
{
|
|
if (ips < IPS_DAY)
|
|
{
|
|
pst->wDay = 1;
|
|
}
|
|
if (ips < IPS_MONTH)
|
|
{
|
|
pst->wMonth = 1;
|
|
}
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|