VirtualBox

Changeset 74148 in vbox for trunk/src/VBox/Runtime


Ignore:
Timestamp:
Sep 7, 2018 6:50:54 PM (6 years ago)
Author:
vboxsync
Message:

IPRT/time: Added RTTimeFromRfc2822 and RTTimeToStringEx (both untested). Fixed faction parsing in RTTimeFromString. bugref:9167

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/time/time.cpp

    r74125 r74148  
    800800
    801801/**
     802 * Converts a time spec to a ISO date string, extended version.
     803 *
     804 * @returns Output string length on success (positive), VERR_BUFFER_OVERFLOW
     805 *          (negative) or VERR_OUT_OF_RANGE (negative) on failure.
     806 * @param   pTime           The time. Caller should've normalized this.
     807 * @param   psz             Where to store the string.
     808 * @param   cb              The size of the buffer.
     809 * @param   cFractionDigits Number of digits in the fraction.  Max is 9.
     810 */
     811RTDECL(ssize_t) RTTimeToStringEx(PCRTTIME pTime, char *psz, size_t cb, unsigned cFractionDigits)
     812{
     813    size_t cch;
     814
     815    /* Format the fraction. */
     816    char szFraction[16];
     817    if (!cFractionDigits)
     818        szFraction[0] = '\0';
     819    else
     820    {
     821        AssertReturn(cFractionDigits <= 9, VERR_OUT_OF_RANGE);
     822        Assert(pTime->u32Nanosecond <= 999999999);
     823        RTStrPrintf(szFraction, sizeof(szFraction), ".%09RU32", pTime->u32Nanosecond);
     824        szFraction[cFractionDigits + 1] = '\0';
     825    }
     826
     827    /* (Default to UTC if not specified) */
     828    if (    (pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) == RTTIME_FLAGS_TYPE_LOCAL
     829        &&  pTime->offUTC)
     830    {
     831        int32_t  offUTC = pTime->offUTC;
     832        Assert(offUTC <= 840 && offUTC >= -840);
     833        char     chSign;
     834        if (offUTC >= 0)
     835            chSign = '+';
     836        else
     837        {
     838            chSign = '-';
     839            offUTC = -offUTC;
     840        }
     841        uint32_t offUTCHour   = (uint32_t)offUTC / 60;
     842        uint32_t offUTCMinute = (uint32_t)offUTC % 60;
     843
     844        /* Examples: 2018-09-07T16:12:00+02:00  2018-09-07T16:12:00.123456789+02:00 */
     845        cch = RTStrPrintf(psz, cb,
     846                          "%04RI32-%02u-%02uT%02u:%02u:%02u%s%c%02d%:02d",
     847                          pTime->i32Year, pTime->u8Month, pTime->u8MonthDay,
     848                          pTime->u8Hour, pTime->u8Minute, pTime->u8Second, szFraction,
     849                          chSign, offUTCHour, offUTCMinute);
     850        if (   cch >= 24
     851            && psz[cch - 6] == chSign)
     852            return cch;
     853    }
     854    else
     855    {
     856        /* Examples: 2018-09-07T16:12:00Z  2018-09-07T16:12:00.123456789Z */
     857        cch = RTStrPrintf(psz, cb, "%04RI32-%02u-%02uT%02u:%02u:%02u%sZ",
     858                          pTime->i32Year, pTime->u8Month, pTime->u8MonthDay,
     859                          pTime->u8Hour, pTime->u8Minute, pTime->u8Second, szFraction);
     860        if (   cch >= 19
     861            && psz[cch - 1] == 'Z')
     862            return cch;
     863    }
     864    return VERR_BUFFER_OVERFLOW;
     865}
     866RT_EXPORT_SYMBOL(RTTimeToStringEx);
     867
     868
     869/**
    802870 * Converts a time spec to a ISO date string.
    803871 *
     
    841909
    842910    /*
    843      * The day part.
     911     * The date part.
    844912     */
    845913
     
    912980        return NULL;
    913981
    914     /* Nanoseconds is optional and probably non-standard. */
     982    /* Just in case there is a fraction of seconds (should be!). */
    915983    if (*pszString == '.')
    916984    {
     985        const char * const pszStart = pszString;
    917986        rc = RTStrToUInt32Ex(pszString + 1, (char **)&pszString, 10, &pTime->u32Nanosecond);
    918987        if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS && rc != VWRN_TRAILING_SPACES)
    919988            return NULL;
     989        if (pTime->u32Nanosecond >= 1000000000)
     990            return NULL;
     991        switch (pszString - pszStart)
     992        {
     993            case 1: pTime->u32Nanosecond *= 100000000; break;
     994            case 2: pTime->u32Nanosecond *= 10000000; break;
     995            case 3: pTime->u32Nanosecond *= 1000000; break;
     996            case 4: pTime->u32Nanosecond *= 100000; break;
     997            case 5: pTime->u32Nanosecond *= 10000; break;
     998            case 6: pTime->u32Nanosecond *= 1000; break;
     999            case 7: pTime->u32Nanosecond *= 100; break;
     1000            case 8: pTime->u32Nanosecond *= 10; break;
     1001            case 9: break;
     1002            default:
     1003                return NULL;
     1004        }
    9201005        if (pTime->u32Nanosecond >= 1000000000)
    9211006            return NULL;
     
    10591144}
    10601145RT_EXPORT_SYMBOL(RTTimeToRfc2822);
     1146
     1147
     1148/**
     1149 * Attempts to convert an RFC-2822 date string to a time structure.
     1150 *
     1151 * We're a little forgiving with zero padding, unspecified parts, and leading
     1152 * and trailing spaces.
     1153 *
     1154 * @retval  pTime on success,
     1155 * @retval  NULL on failure.
     1156 * @param   pTime       Where to store the time on success.
     1157 * @param   pszString   The ISO date string to convert.
     1158 */
     1159RTDECL(PRTTIME) RTTimeFromRfc2822(PRTTIME pTime, const char *pszString)
     1160{
     1161    /*
     1162     * Fri, 31 Aug 2018 00:00:00 +0200
     1163     * Mon, 3 Sep 2018 00:00:00 GMT
     1164     * Mon, 3 Sep 2018 00:00:00 -0000
     1165     * 3 Sep 2018 00:00:00 -0000 (?)
     1166     * 3 Sep 2018 00:00:00 GMT   (?)
     1167     *
     1168     */
     1169
     1170    /* Ignore leading spaces. */
     1171    while (RT_C_IS_SPACE(*pszString))
     1172        pszString++;
     1173
     1174    /*
     1175     * Init non date & time parts.
     1176     */
     1177    pTime->fFlags = RTTIME_FLAGS_TYPE_LOCAL;
     1178    pTime->offUTC = 0;
     1179
     1180    /*
     1181     * The date part.
     1182     */
     1183
     1184    /* Optional day of week: */
     1185    if (RT_C_IS_ALPHA(pszString[0]))
     1186        pTime->u8WeekDay = UINT8_MAX;
     1187    else if (pszString[0] != '\0' && pszString[1] != '\0')
     1188    {
     1189        uint32_t uWeekDay = RT_MAKE_U32_FROM_U8(RT_C_TO_LOWER(pszString[0]), RT_C_TO_LOWER(pszString[0]),
     1190                                                RT_C_TO_LOWER(pszString[1]), 0);
     1191        if (     uWeekDay == RT_MAKE_U32_FROM_U8('m', 'o', 'n', 0))     pTime->u8WeekDay = 0;
     1192        else if (uWeekDay == RT_MAKE_U32_FROM_U8('t', 'u', 'e', 0))     pTime->u8WeekDay = 1;
     1193        else if (uWeekDay == RT_MAKE_U32_FROM_U8('w', 'e', 'd', 0))     pTime->u8WeekDay = 2;
     1194        else if (uWeekDay == RT_MAKE_U32_FROM_U8('t', 'h', 'u', 0))     pTime->u8WeekDay = 3;
     1195        else if (uWeekDay == RT_MAKE_U32_FROM_U8('f', 'r', 'i', 0))     pTime->u8WeekDay = 4;
     1196        else if (uWeekDay == RT_MAKE_U32_FROM_U8('s', 'a', 't', 0))     pTime->u8WeekDay = 5;
     1197        else if (uWeekDay == RT_MAKE_U32_FROM_U8('s', 'u', 'n', 0))     pTime->u8WeekDay = 6;
     1198        else
     1199            return NULL;
     1200        pszString += 3;
     1201        while (RT_C_IS_ALPHA(*pszString))
     1202            pszString++;
     1203        if (*pszString == ',')
     1204            pszString++;
     1205        while (RT_C_IS_SPACE(*pszString))
     1206            pszString++;
     1207        if (!RT_C_IS_DIGIT(pszString[0]))
     1208            return NULL;
     1209    }
     1210    else
     1211        return NULL;
     1212
     1213    /* Day of month.*/
     1214    int rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8MonthDay);
     1215    if (rc != VWRN_TRAILING_CHARS && rc != VINF_SUCCESS)
     1216        return NULL;
     1217    while (RT_C_IS_SPACE(*pszString))
     1218        pszString++;
     1219
     1220    /* Month of the year. */
     1221    if (pszString[0] == '\0' || pszString[1] == '\0' || pszString[2] == '\0')
     1222        return NULL;
     1223    uint32_t uMonth = RT_MAKE_U32_FROM_U8(RT_C_TO_LOWER(pszString[0]), RT_C_TO_LOWER(pszString[0]),
     1224                                          RT_C_TO_LOWER(pszString[1]), 0);
     1225    if (     uMonth == RT_MAKE_U32_FROM_U8('j', 'a', 'n', 0))     pTime->u8Month = 1;
     1226    else if (uMonth == RT_MAKE_U32_FROM_U8('f', 'e', 'b', 0))     pTime->u8Month = 2;
     1227    else if (uMonth == RT_MAKE_U32_FROM_U8('m', 'a', 'r', 0))     pTime->u8Month = 3;
     1228    else if (uMonth == RT_MAKE_U32_FROM_U8('a', 'p', 'r', 0))     pTime->u8Month = 4;
     1229    else if (uMonth == RT_MAKE_U32_FROM_U8('m', 'a', 'y', 0))     pTime->u8Month = 5;
     1230    else if (uMonth == RT_MAKE_U32_FROM_U8('j', 'u', 'n', 0))     pTime->u8Month = 6;
     1231    else if (uMonth == RT_MAKE_U32_FROM_U8('j', 'u', 'l', 0))     pTime->u8Month = 7;
     1232    else if (uMonth == RT_MAKE_U32_FROM_U8('a', 'u', 'g', 0))     pTime->u8Month = 8;
     1233    else if (uMonth == RT_MAKE_U32_FROM_U8('s', 'e', 'p', 0))     pTime->u8Month = 9;
     1234    else if (uMonth == RT_MAKE_U32_FROM_U8('o', 'c', 't', 0))     pTime->u8Month = 10;
     1235    else if (uMonth == RT_MAKE_U32_FROM_U8('n', 'o', 'v', 0))     pTime->u8Month = 11;
     1236    else if (uMonth == RT_MAKE_U32_FROM_U8('d', 'e', 'c', 0))     pTime->u8Month = 12;
     1237    else
     1238        return NULL;
     1239    pszString += 3;
     1240    while (RT_C_IS_ALPHA(*pszString))
     1241        pszString++;
     1242    while (RT_C_IS_SPACE(*pszString))
     1243        pszString++;
     1244
     1245    /* Year */
     1246    rc = RTStrToInt32Ex(pszString, (char **)&pszString, 10, &pTime->i32Year);
     1247    if (rc != VWRN_TRAILING_CHARS)
     1248        return NULL;
     1249
     1250    bool const fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
     1251    if (fLeapYear)
     1252        pTime->fFlags |= RTTIME_FLAGS_LEAP_YEAR;
     1253
     1254    while (RT_C_IS_SPACE(*pszString))
     1255        pszString++;
     1256
     1257
     1258    /* Calculate year day. */
     1259    unsigned const cDaysInMonth = fLeapYear
     1260                                ? g_acDaysInMonthsLeap[pTime->u8Month - 1]
     1261                                : g_acDaysInMonths[pTime->u8Month - 1];
     1262    if (pTime->u8MonthDay == 0 || pTime->u8MonthDay > cDaysInMonth)
     1263        return NULL;
     1264
     1265    pTime->u16YearDay = pTime->u8MonthDay - 1
     1266                      + (fLeapYear
     1267                         ? g_aiDayOfYearLeap[pTime->u8Month - 1]
     1268                         : g_aiDayOfYear[pTime->u8Month - 1]);
     1269
     1270    /*
     1271     * The time part.
     1272     */
     1273    /* Hour. */
     1274    rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Hour);
     1275    if (rc != VWRN_TRAILING_CHARS)
     1276        return NULL;
     1277    if (pTime->u8Hour > 23)
     1278        return NULL;
     1279    if (*pszString++ != ':')
     1280        return NULL;
     1281
     1282    /* Minute. */
     1283    rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Minute);
     1284    if (rc != VWRN_TRAILING_CHARS)
     1285        return NULL;
     1286    if (pTime->u8Minute > 59)
     1287        return NULL;
     1288    if (*pszString++ != ':')
     1289        return NULL;
     1290
     1291    /* Second. */
     1292    rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Second);
     1293    if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS && rc != VWRN_TRAILING_SPACES)
     1294        return NULL;
     1295    if (pTime->u8Second > 59)
     1296        return NULL;
     1297
     1298    /* Nanoseconds  and probably non-standard. */
     1299    if (*pszString == '.')
     1300    {
     1301        const char * const pszStart = pszString;
     1302        rc = RTStrToUInt32Ex(pszString + 1, (char **)&pszString, 10, &pTime->u32Nanosecond);
     1303        if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS && rc != VWRN_TRAILING_SPACES)
     1304            return NULL;
     1305        if (pTime->u32Nanosecond >= 1000000000)
     1306            return NULL;
     1307        switch (pszString - pszStart)
     1308        {
     1309            case 1: pTime->u32Nanosecond *= 100000000; break;
     1310            case 2: pTime->u32Nanosecond *= 10000000; break;
     1311            case 3: pTime->u32Nanosecond *= 1000000; break;
     1312            case 4: pTime->u32Nanosecond *= 100000; break;
     1313            case 5: pTime->u32Nanosecond *= 10000; break;
     1314            case 6: pTime->u32Nanosecond *= 1000; break;
     1315            case 7: pTime->u32Nanosecond *= 100; break;
     1316            case 8: pTime->u32Nanosecond *= 10; break;
     1317            case 9: break;
     1318            default:
     1319                return NULL;
     1320        }
     1321        if (pTime->u32Nanosecond >= 1000000000)
     1322            return NULL;
     1323    }
     1324    else
     1325        pTime->u32Nanosecond = 0;
     1326
     1327    /*
     1328     * Time zone.
     1329     */
     1330    if (   (pszString[0] == 'G' || pszString[0] == 'g')
     1331        && (pszString[1] == 'M' || pszString[1] == 'm')
     1332        && (pszString[2] == 'T' || pszString[2] == 't') )
     1333    {
     1334        pszString++;
     1335        pTime->fFlags &= ~RTTIME_FLAGS_TYPE_MASK;
     1336        pTime->fFlags |= ~RTTIME_FLAGS_TYPE_UTC;
     1337        pTime->offUTC = 0;
     1338    }
     1339    else if (   *pszString == '+'
     1340             || *pszString == '-')
     1341    {
     1342        if (   !RT_C_IS_DIGIT(pszString[1])
     1343            || !RT_C_IS_DIGIT(pszString[2]))
     1344            return NULL;
     1345        int8_t cUtcHours = (pszString[1] - '0') * 10 + (pszString[2] - '0');
     1346        if (*pszString == '-')
     1347            cUtcHours = -cUtcHours;
     1348        pszString += 3;
     1349
     1350        uint8_t cUtcMin = 0;
     1351        if (RT_C_IS_DIGIT(pszString[0]))
     1352        {
     1353            rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &cUtcMin);
     1354            if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
     1355                return NULL;
     1356        }
     1357        else if (*pszString && !RT_C_IS_BLANK(*pszString))
     1358            return NULL;
     1359        if (cUtcHours >= 0)
     1360            pTime->offUTC = cUtcHours * 60 + cUtcMin;
     1361        else
     1362            pTime->offUTC = cUtcHours * 60 - cUtcMin;
     1363        if (RT_ABS(pTime->offUTC) > 840)
     1364            return NULL;
     1365    }
     1366    /* else: No time zone given, local with offUTC = 0. */
     1367
     1368    /*
     1369     * The rest of the string should be blanks.
     1370     */
     1371    char ch;
     1372    while ((ch = *pszString++) != '\0')
     1373        if (!RT_C_IS_BLANK(ch))
     1374            return NULL;
     1375
     1376    return pTime;
     1377}
     1378RT_EXPORT_SYMBOL(RTTimeFromRfc2822);
    10611379
    10621380
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette