summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/time
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/time')
-rw-r--r--src/VBox/Runtime/common/time/Makefile.kup0
-rw-r--r--src/VBox/Runtime/common/time/RTTimeFormatDurationEx.cpp267
-rw-r--r--src/VBox/Runtime/common/time/time.cpp1492
-rw-r--r--src/VBox/Runtime/common/time/timeprog.cpp107
-rw-r--r--src/VBox/Runtime/common/time/timesup.cpp467
-rw-r--r--src/VBox/Runtime/common/time/timesupA.asm161
-rw-r--r--src/VBox/Runtime/common/time/timesupA.mac895
-rw-r--r--src/VBox/Runtime/common/time/timesupref.cpp318
-rw-r--r--src/VBox/Runtime/common/time/timesupref.h408
-rw-r--r--src/VBox/Runtime/common/time/timesysalias.cpp67
-rwxr-xr-xsrc/VBox/Runtime/common/time/timezoneinfo-gen.py470
-rw-r--r--src/VBox/Runtime/common/time/timezoneinfo.cpp1171
12 files changed, 5823 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/time/Makefile.kup b/src/VBox/Runtime/common/time/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/common/time/Makefile.kup
diff --git a/src/VBox/Runtime/common/time/RTTimeFormatDurationEx.cpp b/src/VBox/Runtime/common/time/RTTimeFormatDurationEx.cpp
new file mode 100644
index 00000000..1b017816
--- /dev/null
+++ b/src/VBox/Runtime/common/time/RTTimeFormatDurationEx.cpp
@@ -0,0 +1,267 @@
+/* $Id: RTTimeFormatDurationEx.cpp $ */
+/** @file
+ * IPRT - RTTimeFormatInterval.
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/time.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+static size_t rtTimeFormatDurationNumberEx(char *psz, uint32_t uValue, size_t cchValue)
+{
+ switch (cchValue)
+ {
+ case 10:
+ *psz++ = (uint8_t)(uValue / 1000000000) + '0';
+ uValue %= 1000000000;
+ RT_FALL_THROUGH();
+ case 9:
+ *psz++ = (uint8_t)(uValue / 100000000) + '0';
+ uValue %= 100000000;
+ RT_FALL_THROUGH();
+ case 8:
+ *psz++ = (uint8_t)(uValue / 10000000) + '0';
+ uValue %= 10000000;
+ RT_FALL_THROUGH();
+ case 7:
+ *psz++ = (uint8_t)(uValue / 1000000) + '0';
+ uValue %= 1000000;
+ RT_FALL_THROUGH();
+ case 6:
+ *psz++ = (uint8_t)(uValue / 100000) + '0';
+ uValue %= 100000;
+ RT_FALL_THROUGH();
+ case 5:
+ *psz++ = (uint8_t)(uValue / 10000) + '0';
+ uValue %= 10000;
+ RT_FALL_THROUGH();
+ case 4:
+ *psz++ = (uint8_t)(uValue / 1000) + '0';
+ uValue %= 1000;
+ RT_FALL_THROUGH();
+ case 3:
+ *psz++ = (uint8_t)(uValue / 100) + '0';
+ uValue %= 100;
+ RT_FALL_THROUGH();
+ case 2:
+ *psz++ = (uint8_t)(uValue / 10) + '0';
+ uValue %= 10;
+ RT_FALL_THROUGH();
+ case 1:
+ *psz++ = (uint8_t)uValue + '0';
+ break;
+ }
+ return cchValue;
+}
+
+
+static size_t rtTimeFormatDurationNumber(char *psz, uint32_t uValue)
+{
+ size_t cchValue;
+ if (uValue < 10)
+ cchValue = 1;
+ else if (uValue < 100)
+ cchValue = 2;
+ else if (uValue < 1000)
+ cchValue = 3;
+ else if (uValue < 10000)
+ cchValue = 4;
+ else if (uValue < 100000)
+ cchValue = 5;
+ else if (uValue < 1000000)
+ cchValue = 6;
+ else if (uValue < 10000000)
+ cchValue = 7;
+ else if (uValue < 100000000)
+ cchValue = 8;
+ else if (uValue < 1000000000)
+ cchValue = 9;
+ else
+ cchValue = 10;
+ return rtTimeFormatDurationNumberEx(psz, uValue, cchValue);
+}
+
+
+static ssize_t rtTimeFormatDurationCopyOutResult(char *pszDst, size_t cbDst, const char *pszValue, size_t cchValue)
+{
+ if (cbDst > cchValue)
+ {
+ memcpy(pszDst, pszValue, cchValue);
+ pszDst[cchValue] = '\0';
+ return cchValue;
+ }
+ if (cbDst)
+ {
+ memcpy(pszDst, pszValue, cbDst);
+ pszDst[cbDst - 1] = '\0';
+ }
+ return VERR_BUFFER_OVERFLOW;
+}
+
+
+/**
+ * Formats duration as best we can according to ISO-8601.
+ *
+ * The returned value is on the form "[-]PnnnnnWnDTnnHnnMnn.fffffffffS", where a
+ * sequence of 'n' can be between 1 and the given lenght, and all but the
+ * "nn.fffffffffS" part is optional and will only be outputted when the duration
+ * is sufficiently large. The code currently does not omit any inbetween
+ * elements other than the day count (D), so an exactly 7 day duration is
+ * formatted as "P1WT0H0M0.000000000S" when @a cFractionDigits is 9.
+ *
+ * @returns Number of characters in the output on success. VERR_BUFFER_OVEFLOW
+ * on failure.
+ * @retval VERR_OUT_OF_RANGE if @a cFractionDigits is too large.
+ * @param pszDst Pointer to the output buffer. In case of overflow,
+ * the max number of characters will be written and
+ * zero terminated, provided @a cbDst isn't zero.
+ * @param cbDst The size of the output buffer.
+ * @param pDuration The duration to format.
+ * @param cFractionDigits Number of digits in the second fraction part. Zero
+ * for whole no fraction. Max is 9 (nano seconds).
+ */
+RTDECL(ssize_t) RTTimeFormatDurationEx(char *pszDst, size_t cbDst, PCRTTIMESPEC pDuration, uint32_t cFractionDigits)
+{
+ AssertReturn(cFractionDigits <= 9, VERR_OUT_OF_RANGE);
+ AssertReturn(cbDst != 0, VERR_BUFFER_OVERFLOW);
+
+ /*
+ * Get the seconds and .
+ */
+ int64_t cNanoSecsSigned = RTTimeSpecGetNano(pDuration);
+
+ /* Special case: zero interval */
+ if (cNanoSecsSigned == 0)
+ return rtTimeFormatDurationCopyOutResult(pszDst, cbDst, RT_STR_TUPLE("PT0S"));
+
+ char szTmp[64];
+ size_t offTmp = 0;
+
+ /* Negative intervals aren't really allowed by the standard, but we slap a
+ minus in from of the 'P' and get on with it. */
+ if (cNanoSecsSigned < 0)
+ {
+ szTmp[offTmp++] = '-';
+ cNanoSecsSigned = -cNanoSecsSigned;
+ }
+ uint64_t cNanoSecs = (uint64_t)cNanoSecsSigned;
+
+ /* Emit the duration indicator: */
+ szTmp[offTmp++] = 'P';
+ size_t const offPostP = offTmp;
+
+ /* Any full weeks? */
+ if (cNanoSecs >= RT_NS_1WEEK)
+ {
+ uint64_t const cWeeks = cNanoSecs / RT_NS_1WEEK; /* (the max value here is 15250) */
+ cNanoSecs %= RT_NS_1WEEK;
+ offTmp += rtTimeFormatDurationNumber(&szTmp[offTmp], (uint32_t)cWeeks);
+ szTmp[offTmp++] = 'W';
+ }
+
+ /* Any full days?*/
+ if (cNanoSecs >= RT_NS_1DAY)
+ {
+ uint8_t const cDays = (uint8_t)(cNanoSecs / RT_NS_1DAY);
+ cNanoSecs %= RT_NS_1DAY;
+ szTmp[offTmp++] = '0' + cDays;
+ szTmp[offTmp++] = 'D';
+ }
+
+ szTmp[offTmp++] = 'T';
+
+ /* Hours: */
+ if (cNanoSecs >= RT_NS_1HOUR || offTmp > offPostP + 1)
+ {
+ uint8_t const cHours = (uint8_t)(cNanoSecs / RT_NS_1HOUR);
+ cNanoSecs %= RT_NS_1HOUR;
+ offTmp += rtTimeFormatDurationNumber(&szTmp[offTmp], cHours);
+ szTmp[offTmp++] = 'H';
+ }
+
+ /* Minutes: */
+ if (cNanoSecs >= RT_NS_1MIN || offTmp > offPostP + 1)
+ {
+ uint8_t const cMins = (uint8_t)(cNanoSecs / RT_NS_1MIN);
+ cNanoSecs %= RT_NS_1MIN;
+ offTmp += rtTimeFormatDurationNumber(&szTmp[offTmp], cMins);
+ szTmp[offTmp++] = 'M';
+ }
+
+ /* Seconds: */
+ uint8_t const cSecs = (uint8_t)(cNanoSecs / RT_NS_1SEC);
+ cNanoSecs %= RT_NS_1SEC;
+ offTmp += rtTimeFormatDurationNumber(&szTmp[offTmp], cSecs);
+ if (cFractionDigits > 0)
+ {
+ szTmp[offTmp++] = '.';
+ static uint32_t const s_auFactors[9] = { 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1 };
+ offTmp += rtTimeFormatDurationNumberEx(&szTmp[offTmp], (uint32_t)(cNanoSecs / s_auFactors[cFractionDigits - 1]),
+ cFractionDigits);
+ }
+ szTmp[offTmp++] = 'S';
+ szTmp[offTmp] = '\0';
+
+ return rtTimeFormatDurationCopyOutResult(pszDst, cbDst, szTmp, offTmp);
+}
+RT_EXPORT_SYMBOL(RTTimeFormatDurationEx);
+
+
+/**
+ * Formats duration as best we can according to ISO-8601, with no fraction.
+ *
+ * See RTTimeFormatDurationEx for details.
+ *
+ * @returns Number of characters in the output on success. VERR_BUFFER_OVEFLOW
+ * on failure.
+ * @param pszDst Pointer to the output buffer. In case of overflow,
+ * the max number of characters will be written and
+ * zero terminated, provided @a cbDst isn't zero.
+ * @param cbDst The size of the output buffer.
+ * @param pDuration The duration to format.
+ */
+RTDECL(int) RTTimeFormatDuration(char *pszDst, size_t cbDst, PCRTTIMESPEC pDuration)
+{
+ return RTTimeFormatDurationEx(pszDst, cbDst, pDuration, 0);
+}
+RT_EXPORT_SYMBOL(RTTimeFormatDuration);
+
diff --git a/src/VBox/Runtime/common/time/time.cpp b/src/VBox/Runtime/common/time/time.cpp
new file mode 100644
index 00000000..193bbd29
--- /dev/null
+++ b/src/VBox/Runtime/common/time/time.cpp
@@ -0,0 +1,1492 @@
+/* $Id: time.cpp $ */
+/** @file
+ * IPRT - Time.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include "internal/time.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The max year we possibly could implode. */
+#define RTTIME_MAX_YEAR (292 + 1970)
+/** The min year we possibly could implode. */
+#define RTTIME_MIN_YEAR (-293 + 1970)
+
+/** The max day supported by our time representation. (2262-04-11T23-47-16.854775807) */
+#define RTTIME_MAX_DAY (365*292+71 + 101-1)
+/** The min day supported by our time representation. (1677-09-21T00-12-43.145224192) */
+#define RTTIME_MIN_DAY (365*-293-70 + 264-1)
+
+/** The max nano second into the max day. (2262-04-11T23-47-16.854775807) */
+#define RTTIME_MAX_DAY_NANO ( INT64_C(1000000000) * (23*3600 + 47*60 + 16) + 854775807 )
+/** The min nano second into the min day. (1677-09-21T00-12-43.145224192) */
+#define RTTIME_MIN_DAY_NANO ( INT64_C(1000000000) * (00*3600 + 12*60 + 43) + 145224192 )
+
+/**
+ * Asserts that a_pTime is normalized.
+ */
+#define RTTIME_ASSERT_NORMALIZED(a_pTime) \
+ do \
+ { \
+ Assert(RT_ABS((a_pTime)->offUTC) <= 840); \
+ Assert((a_pTime)->u32Nanosecond < 1000000000); \
+ Assert((a_pTime)->u8Second < 60); \
+ Assert((a_pTime)->u8Minute < 60); \
+ Assert((a_pTime)->u8Hour < 24); \
+ Assert((a_pTime)->u8Month >= 1 && (a_pTime)->u8Month <= 12); \
+ Assert((a_pTime)->u8WeekDay < 7); \
+ Assert((a_pTime)->u16YearDay >= 1); \
+ Assert((a_pTime)->u16YearDay <= (rtTimeIsLeapYear((a_pTime)->i32Year) ? 366 : 365)); \
+ Assert((a_pTime)->u8MonthDay >= 1 && (a_pTime)->u8MonthDay <= 31); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Days per month in a common year.
+ */
+static const uint8_t g_acDaysInMonths[12] =
+{
+ /*Jan Feb Mar Arp May Jun Jul Aug Sep Oct Nov Dec */
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/**
+ * Days per month in a leap year.
+ */
+static const uint8_t g_acDaysInMonthsLeap[12] =
+{
+ /*Jan Feb Mar Arp May Jun Jul Aug Sep Oct Nov Dec */
+ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/**
+ * The day of year for each month in a common year.
+ */
+static const uint16_t g_aiDayOfYear[12 + 1] =
+{
+ 1, /* Jan */
+ 1+31, /* Feb */
+ 1+31+28, /* Mar */
+ 1+31+28+31, /* Apr */
+ 1+31+28+31+30, /* May */
+ 1+31+28+31+30+31, /* Jun */
+ 1+31+28+31+30+31+30, /* Jul */
+ 1+31+28+31+30+31+30+31, /* Aug */
+ 1+31+28+31+30+31+30+31+31, /* Sep */
+ 1+31+28+31+30+31+30+31+31+30, /* Oct */
+ 1+31+28+31+30+31+30+31+31+30+31, /* Nov */
+ 1+31+28+31+30+31+30+31+31+30+31+30, /* Dec */
+ 1+31+28+31+30+31+30+31+31+30+31+30+31
+};
+
+/**
+ * The day of year for each month in a leap year.
+ */
+static const uint16_t g_aiDayOfYearLeap[12 + 1] =
+{
+ 1, /* Jan */
+ 1+31, /* Feb */
+ 1+31+29, /* Mar */
+ 1+31+29+31, /* Apr */
+ 1+31+29+31+30, /* May */
+ 1+31+29+31+30+31, /* Jun */
+ 1+31+29+31+30+31+30, /* Jul */
+ 1+31+29+31+30+31+30+31, /* Aug */
+ 1+31+29+31+30+31+30+31+31, /* Sep */
+ 1+31+29+31+30+31+30+31+31+30, /* Oct */
+ 1+31+29+31+30+31+30+31+31+30+31, /* Nov */
+ 1+31+29+31+30+31+30+31+31+30+31+30, /* Dec */
+ 1+31+29+31+30+31+30+31+31+30+31+30+31
+};
+
+/** The index of 1970 in g_aoffYear */
+#define OFF_YEAR_IDX_EPOCH 300
+/** The year of the first index. */
+#define OFF_YEAR_IDX_0_YEAR 1670
+
+/**
+ * The number of days the 1st of January a year is offseted from 1970-01-01.
+ */
+static const int32_t g_aoffYear[] =
+{
+/*1670:*/ 365*-300+-72, 365*-299+-72, 365*-298+-72, 365*-297+-71, 365*-296+-71, 365*-295+-71, 365*-294+-71, 365*-293+-70, 365*-292+-70, 365*-291+-70,
+/*1680:*/ 365*-290+-70, 365*-289+-69, 365*-288+-69, 365*-287+-69, 365*-286+-69, 365*-285+-68, 365*-284+-68, 365*-283+-68, 365*-282+-68, 365*-281+-67,
+/*1690:*/ 365*-280+-67, 365*-279+-67, 365*-278+-67, 365*-277+-66, 365*-276+-66, 365*-275+-66, 365*-274+-66, 365*-273+-65, 365*-272+-65, 365*-271+-65,
+/*1700:*/ 365*-270+-65, 365*-269+-65, 365*-268+-65, 365*-267+-65, 365*-266+-65, 365*-265+-64, 365*-264+-64, 365*-263+-64, 365*-262+-64, 365*-261+-63,
+/*1710:*/ 365*-260+-63, 365*-259+-63, 365*-258+-63, 365*-257+-62, 365*-256+-62, 365*-255+-62, 365*-254+-62, 365*-253+-61, 365*-252+-61, 365*-251+-61,
+/*1720:*/ 365*-250+-61, 365*-249+-60, 365*-248+-60, 365*-247+-60, 365*-246+-60, 365*-245+-59, 365*-244+-59, 365*-243+-59, 365*-242+-59, 365*-241+-58,
+/*1730:*/ 365*-240+-58, 365*-239+-58, 365*-238+-58, 365*-237+-57, 365*-236+-57, 365*-235+-57, 365*-234+-57, 365*-233+-56, 365*-232+-56, 365*-231+-56,
+/*1740:*/ 365*-230+-56, 365*-229+-55, 365*-228+-55, 365*-227+-55, 365*-226+-55, 365*-225+-54, 365*-224+-54, 365*-223+-54, 365*-222+-54, 365*-221+-53,
+/*1750:*/ 365*-220+-53, 365*-219+-53, 365*-218+-53, 365*-217+-52, 365*-216+-52, 365*-215+-52, 365*-214+-52, 365*-213+-51, 365*-212+-51, 365*-211+-51,
+/*1760:*/ 365*-210+-51, 365*-209+-50, 365*-208+-50, 365*-207+-50, 365*-206+-50, 365*-205+-49, 365*-204+-49, 365*-203+-49, 365*-202+-49, 365*-201+-48,
+/*1770:*/ 365*-200+-48, 365*-199+-48, 365*-198+-48, 365*-197+-47, 365*-196+-47, 365*-195+-47, 365*-194+-47, 365*-193+-46, 365*-192+-46, 365*-191+-46,
+/*1780:*/ 365*-190+-46, 365*-189+-45, 365*-188+-45, 365*-187+-45, 365*-186+-45, 365*-185+-44, 365*-184+-44, 365*-183+-44, 365*-182+-44, 365*-181+-43,
+/*1790:*/ 365*-180+-43, 365*-179+-43, 365*-178+-43, 365*-177+-42, 365*-176+-42, 365*-175+-42, 365*-174+-42, 365*-173+-41, 365*-172+-41, 365*-171+-41,
+/*1800:*/ 365*-170+-41, 365*-169+-41, 365*-168+-41, 365*-167+-41, 365*-166+-41, 365*-165+-40, 365*-164+-40, 365*-163+-40, 365*-162+-40, 365*-161+-39,
+/*1810:*/ 365*-160+-39, 365*-159+-39, 365*-158+-39, 365*-157+-38, 365*-156+-38, 365*-155+-38, 365*-154+-38, 365*-153+-37, 365*-152+-37, 365*-151+-37,
+/*1820:*/ 365*-150+-37, 365*-149+-36, 365*-148+-36, 365*-147+-36, 365*-146+-36, 365*-145+-35, 365*-144+-35, 365*-143+-35, 365*-142+-35, 365*-141+-34,
+/*1830:*/ 365*-140+-34, 365*-139+-34, 365*-138+-34, 365*-137+-33, 365*-136+-33, 365*-135+-33, 365*-134+-33, 365*-133+-32, 365*-132+-32, 365*-131+-32,
+/*1840:*/ 365*-130+-32, 365*-129+-31, 365*-128+-31, 365*-127+-31, 365*-126+-31, 365*-125+-30, 365*-124+-30, 365*-123+-30, 365*-122+-30, 365*-121+-29,
+/*1850:*/ 365*-120+-29, 365*-119+-29, 365*-118+-29, 365*-117+-28, 365*-116+-28, 365*-115+-28, 365*-114+-28, 365*-113+-27, 365*-112+-27, 365*-111+-27,
+/*1860:*/ 365*-110+-27, 365*-109+-26, 365*-108+-26, 365*-107+-26, 365*-106+-26, 365*-105+-25, 365*-104+-25, 365*-103+-25, 365*-102+-25, 365*-101+-24,
+/*1870:*/ 365*-100+-24, 365* -99+-24, 365* -98+-24, 365* -97+-23, 365* -96+-23, 365* -95+-23, 365* -94+-23, 365* -93+-22, 365* -92+-22, 365* -91+-22,
+/*1880:*/ 365* -90+-22, 365* -89+-21, 365* -88+-21, 365* -87+-21, 365* -86+-21, 365* -85+-20, 365* -84+-20, 365* -83+-20, 365* -82+-20, 365* -81+-19,
+/*1890:*/ 365* -80+-19, 365* -79+-19, 365* -78+-19, 365* -77+-18, 365* -76+-18, 365* -75+-18, 365* -74+-18, 365* -73+-17, 365* -72+-17, 365* -71+-17,
+/*1900:*/ 365* -70+-17, 365* -69+-17, 365* -68+-17, 365* -67+-17, 365* -66+-17, 365* -65+-16, 365* -64+-16, 365* -63+-16, 365* -62+-16, 365* -61+-15,
+/*1910:*/ 365* -60+-15, 365* -59+-15, 365* -58+-15, 365* -57+-14, 365* -56+-14, 365* -55+-14, 365* -54+-14, 365* -53+-13, 365* -52+-13, 365* -51+-13,
+/*1920:*/ 365* -50+-13, 365* -49+-12, 365* -48+-12, 365* -47+-12, 365* -46+-12, 365* -45+-11, 365* -44+-11, 365* -43+-11, 365* -42+-11, 365* -41+-10,
+/*1930:*/ 365* -40+-10, 365* -39+-10, 365* -38+-10, 365* -37+-9 , 365* -36+-9 , 365* -35+-9 , 365* -34+-9 , 365* -33+-8 , 365* -32+-8 , 365* -31+-8 ,
+/*1940:*/ 365* -30+-8 , 365* -29+-7 , 365* -28+-7 , 365* -27+-7 , 365* -26+-7 , 365* -25+-6 , 365* -24+-6 , 365* -23+-6 , 365* -22+-6 , 365* -21+-5 ,
+/*1950:*/ 365* -20+-5 , 365* -19+-5 , 365* -18+-5 , 365* -17+-4 , 365* -16+-4 , 365* -15+-4 , 365* -14+-4 , 365* -13+-3 , 365* -12+-3 , 365* -11+-3 ,
+/*1960:*/ 365* -10+-3 , 365* -9+-2 , 365* -8+-2 , 365* -7+-2 , 365* -6+-2 , 365* -5+-1 , 365* -4+-1 , 365* -3+-1 , 365* -2+-1 , 365* -1+0 ,
+/*1970:*/ 365* 0+0 , 365* 1+0 , 365* 2+0 , 365* 3+1 , 365* 4+1 , 365* 5+1 , 365* 6+1 , 365* 7+2 , 365* 8+2 , 365* 9+2 ,
+/*1980:*/ 365* 10+2 , 365* 11+3 , 365* 12+3 , 365* 13+3 , 365* 14+3 , 365* 15+4 , 365* 16+4 , 365* 17+4 , 365* 18+4 , 365* 19+5 ,
+/*1990:*/ 365* 20+5 , 365* 21+5 , 365* 22+5 , 365* 23+6 , 365* 24+6 , 365* 25+6 , 365* 26+6 , 365* 27+7 , 365* 28+7 , 365* 29+7 ,
+/*2000:*/ 365* 30+7 , 365* 31+8 , 365* 32+8 , 365* 33+8 , 365* 34+8 , 365* 35+9 , 365* 36+9 , 365* 37+9 , 365* 38+9 , 365* 39+10 ,
+/*2010:*/ 365* 40+10 , 365* 41+10 , 365* 42+10 , 365* 43+11 , 365* 44+11 , 365* 45+11 , 365* 46+11 , 365* 47+12 , 365* 48+12 , 365* 49+12 ,
+/*2020:*/ 365* 50+12 , 365* 51+13 , 365* 52+13 , 365* 53+13 , 365* 54+13 , 365* 55+14 , 365* 56+14 , 365* 57+14 , 365* 58+14 , 365* 59+15 ,
+/*2030:*/ 365* 60+15 , 365* 61+15 , 365* 62+15 , 365* 63+16 , 365* 64+16 , 365* 65+16 , 365* 66+16 , 365* 67+17 , 365* 68+17 , 365* 69+17 ,
+/*2040:*/ 365* 70+17 , 365* 71+18 , 365* 72+18 , 365* 73+18 , 365* 74+18 , 365* 75+19 , 365* 76+19 , 365* 77+19 , 365* 78+19 , 365* 79+20 ,
+/*2050:*/ 365* 80+20 , 365* 81+20 , 365* 82+20 , 365* 83+21 , 365* 84+21 , 365* 85+21 , 365* 86+21 , 365* 87+22 , 365* 88+22 , 365* 89+22 ,
+/*2060:*/ 365* 90+22 , 365* 91+23 , 365* 92+23 , 365* 93+23 , 365* 94+23 , 365* 95+24 , 365* 96+24 , 365* 97+24 , 365* 98+24 , 365* 99+25 ,
+/*2070:*/ 365* 100+25 , 365* 101+25 , 365* 102+25 , 365* 103+26 , 365* 104+26 , 365* 105+26 , 365* 106+26 , 365* 107+27 , 365* 108+27 , 365* 109+27 ,
+/*2080:*/ 365* 110+27 , 365* 111+28 , 365* 112+28 , 365* 113+28 , 365* 114+28 , 365* 115+29 , 365* 116+29 , 365* 117+29 , 365* 118+29 , 365* 119+30 ,
+/*2090:*/ 365* 120+30 , 365* 121+30 , 365* 122+30 , 365* 123+31 , 365* 124+31 , 365* 125+31 , 365* 126+31 , 365* 127+32 , 365* 128+32 , 365* 129+32 ,
+/*2100:*/ 365* 130+32 , 365* 131+32 , 365* 132+32 , 365* 133+32 , 365* 134+32 , 365* 135+33 , 365* 136+33 , 365* 137+33 , 365* 138+33 , 365* 139+34 ,
+/*2110:*/ 365* 140+34 , 365* 141+34 , 365* 142+34 , 365* 143+35 , 365* 144+35 , 365* 145+35 , 365* 146+35 , 365* 147+36 , 365* 148+36 , 365* 149+36 ,
+/*2120:*/ 365* 150+36 , 365* 151+37 , 365* 152+37 , 365* 153+37 , 365* 154+37 , 365* 155+38 , 365* 156+38 , 365* 157+38 , 365* 158+38 , 365* 159+39 ,
+/*2130:*/ 365* 160+39 , 365* 161+39 , 365* 162+39 , 365* 163+40 , 365* 164+40 , 365* 165+40 , 365* 166+40 , 365* 167+41 , 365* 168+41 , 365* 169+41 ,
+/*2140:*/ 365* 170+41 , 365* 171+42 , 365* 172+42 , 365* 173+42 , 365* 174+42 , 365* 175+43 , 365* 176+43 , 365* 177+43 , 365* 178+43 , 365* 179+44 ,
+/*2150:*/ 365* 180+44 , 365* 181+44 , 365* 182+44 , 365* 183+45 , 365* 184+45 , 365* 185+45 , 365* 186+45 , 365* 187+46 , 365* 188+46 , 365* 189+46 ,
+/*2160:*/ 365* 190+46 , 365* 191+47 , 365* 192+47 , 365* 193+47 , 365* 194+47 , 365* 195+48 , 365* 196+48 , 365* 197+48 , 365* 198+48 , 365* 199+49 ,
+/*2170:*/ 365* 200+49 , 365* 201+49 , 365* 202+49 , 365* 203+50 , 365* 204+50 , 365* 205+50 , 365* 206+50 , 365* 207+51 , 365* 208+51 , 365* 209+51 ,
+/*2180:*/ 365* 210+51 , 365* 211+52 , 365* 212+52 , 365* 213+52 , 365* 214+52 , 365* 215+53 , 365* 216+53 , 365* 217+53 , 365* 218+53 , 365* 219+54 ,
+/*2190:*/ 365* 220+54 , 365* 221+54 , 365* 222+54 , 365* 223+55 , 365* 224+55 , 365* 225+55 , 365* 226+55 , 365* 227+56 , 365* 228+56 , 365* 229+56 ,
+/*2200:*/ 365* 230+56 , 365* 231+56 , 365* 232+56 , 365* 233+56 , 365* 234+56 , 365* 235+57 , 365* 236+57 , 365* 237+57 , 365* 238+57 , 365* 239+58 ,
+/*2210:*/ 365* 240+58 , 365* 241+58 , 365* 242+58 , 365* 243+59 , 365* 244+59 , 365* 245+59 , 365* 246+59 , 365* 247+60 , 365* 248+60 , 365* 249+60 ,
+/*2220:*/ 365* 250+60 , 365* 251+61 , 365* 252+61 , 365* 253+61 , 365* 254+61 , 365* 255+62 , 365* 256+62 , 365* 257+62 , 365* 258+62 , 365* 259+63 ,
+/*2230:*/ 365* 260+63 , 365* 261+63 , 365* 262+63 , 365* 263+64 , 365* 264+64 , 365* 265+64 , 365* 266+64 , 365* 267+65 , 365* 268+65 , 365* 269+65 ,
+/*2240:*/ 365* 270+65 , 365* 271+66 , 365* 272+66 , 365* 273+66 , 365* 274+66 , 365* 275+67 , 365* 276+67 , 365* 277+67 , 365* 278+67 , 365* 279+68 ,
+/*2250:*/ 365* 280+68 , 365* 281+68 , 365* 282+68 , 365* 283+69 , 365* 284+69 , 365* 285+69 , 365* 286+69 , 365* 287+70 , 365* 288+70 , 365* 289+70 ,
+/*2260:*/ 365* 290+70 , 365* 291+71 , 365* 292+71 , 365* 293+71 , 365* 294+71 , 365* 295+72 , 365* 296+72 , 365* 297+72 , 365* 298+72 , 365* 299+73
+};
+
+/* generator code:
+#include <stdio.h>
+bool isLeapYear(int iYear)
+{
+ return iYear % 4 == 0 && (iYear % 100 != 0 || iYear % 400 == 0);
+}
+void printYear(int iYear, int iLeap)
+{
+ if (!(iYear % 10))
+ printf("\n/" "*%d:*" "/", iYear + 1970);
+ printf(" 365*%4d+%-3d,", iYear, iLeap);
+}
+int main()
+{
+ int iYear = 0;
+ int iLeap = 0;
+ while (iYear > -300)
+ iLeap -= isLeapYear(1970 + --iYear);
+ while (iYear < 300)
+ {
+ printYear(iYear, iLeap);
+ iLeap += isLeapYear(1970 + iYear++);
+ }
+ printf("\n");
+ return 0;
+}
+*/
+
+/** RFC-1123 week day names. */
+static const char * const g_apszWeekDays[7] =
+{
+ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
+};
+/** RFC-1123 month of the year names. */
+static const char * const g_apszMonths[1+12] =
+{
+ "000", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+
+/**
+ * Checks if a year is a leap year or not.
+ *
+ * @returns true if it's a leap year.
+ * @returns false if it's a common year.
+ * @param i32Year The year in question.
+ */
+DECLINLINE(bool) rtTimeIsLeapYear(int32_t i32Year)
+{
+ return i32Year % 4 == 0
+ && ( i32Year % 100 != 0
+ || i32Year % 400 == 0);
+}
+
+
+RTDECL(bool) RTTimeIsLeapYear(int32_t i32Year)
+{
+ return rtTimeIsLeapYear(i32Year);
+}
+RT_EXPORT_SYMBOL(RTTimeIsLeapYear);
+
+
+RTDECL(PRTTIME) RTTimeExplode(PRTTIME pTime, PCRTTIMESPEC pTimeSpec)
+{
+ int64_t i64Div;
+ int32_t i32Div;
+ int32_t i32Rem;
+ unsigned iYear;
+ const uint16_t *paiDayOfYear;
+ int iMonth;
+
+ AssertPtr(pTime);
+ AssertPtr(pTimeSpec);
+
+ /*
+ * The simple stuff first.
+ */
+ pTime->fFlags = RTTIME_FLAGS_TYPE_UTC;
+ i64Div = pTimeSpec->i64NanosecondsRelativeToUnixEpoch;
+ i32Rem = (int32_t)(i64Div % 1000000000);
+ i64Div /= 1000000000;
+ if (i32Rem < 0)
+ {
+ i32Rem += 1000000000;
+ i64Div--;
+ }
+ pTime->u32Nanosecond = i32Rem;
+
+ /* second */
+ i32Rem = (int32_t)(i64Div % 60);
+ i64Div /= 60;
+ if (i32Rem < 0)
+ {
+ i32Rem += 60;
+ i64Div--;
+ }
+ pTime->u8Second = i32Rem;
+
+ /* minute */
+ i32Div = (int32_t)i64Div; /* 60,000,000,000 > 33bit, so 31bit suffices. */
+ i32Rem = i32Div % 60;
+ i32Div /= 60;
+ if (i32Rem < 0)
+ {
+ i32Rem += 60;
+ i32Div--;
+ }
+ pTime->u8Minute = i32Rem;
+
+ /* hour */
+ i32Rem = i32Div % 24;
+ i32Div /= 24; /* days relative to 1970-01-01 */
+ if (i32Rem < 0)
+ {
+ i32Rem += 24;
+ i32Div--;
+ }
+ pTime->u8Hour = i32Rem;
+
+ /* weekday - 1970-01-01 was a Thursday (3) */
+ pTime->u8WeekDay = ((int)(i32Div % 7) + 3 + 7) % 7;
+
+ /*
+ * We've now got a number of days relative to 1970-01-01.
+ * To get the correct year number we have to mess with leap years. Fortunately,
+ * the representation we've got only supports a few hundred years, so we can
+ * generate a table and perform a simple two way search from the modulus 365 derived.
+ */
+ iYear = OFF_YEAR_IDX_EPOCH + i32Div / 365;
+ while (g_aoffYear[iYear + 1] <= i32Div)
+ iYear++;
+ while (g_aoffYear[iYear] > i32Div)
+ iYear--;
+ pTime->i32Year = iYear + OFF_YEAR_IDX_0_YEAR;
+ i32Div -= g_aoffYear[iYear];
+ pTime->u16YearDay = i32Div + 1;
+
+ /*
+ * Figuring out the month is done in a manner similar to the year, only here we
+ * ensure that the index is matching or too small.
+ */
+ if (rtTimeIsLeapYear(pTime->i32Year))
+ {
+ pTime->fFlags |= RTTIME_FLAGS_LEAP_YEAR;
+ paiDayOfYear = &g_aiDayOfYearLeap[0];
+ }
+ else
+ {
+ pTime->fFlags |= RTTIME_FLAGS_COMMON_YEAR;
+ paiDayOfYear = &g_aiDayOfYear[0];
+ }
+ iMonth = i32Div / 32;
+ i32Div++;
+ while (paiDayOfYear[iMonth + 1] <= i32Div)
+ iMonth++;
+ pTime->u8Month = iMonth + 1;
+ i32Div -= paiDayOfYear[iMonth];
+ pTime->u8MonthDay = i32Div + 1;
+
+ /* This is for UTC timespecs, so, no offset. */
+ pTime->offUTC = 0;
+
+ return pTime;
+}
+RT_EXPORT_SYMBOL(RTTimeExplode);
+
+
+RTDECL(PRTTIMESPEC) RTTimeImplode(PRTTIMESPEC pTimeSpec, PCRTTIME pTime)
+{
+ int32_t i32Days;
+ uint32_t u32Secs;
+ int64_t i64Nanos;
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pTimeSpec, NULL);
+ AssertPtrReturn(pTime, NULL);
+ AssertReturn(pTime->u32Nanosecond < 1000000000, NULL);
+ AssertReturn(pTime->u8Second < 60, NULL);
+ AssertReturn(pTime->u8Minute < 60, NULL);
+ AssertReturn(pTime->u8Hour < 24, NULL);
+ AssertReturn(pTime->u16YearDay >= 1, NULL);
+ AssertReturn(pTime->u16YearDay <= (rtTimeIsLeapYear(pTime->i32Year) ? 366 : 365), NULL);
+ AssertMsgReturn(pTime->i32Year <= RTTIME_MAX_YEAR && pTime->i32Year >= RTTIME_MIN_YEAR, ("%RI32\n", pTime->i32Year), NULL);
+ Assert(pTime->offUTC >= -840 && pTime->offUTC <= 840);
+
+ /*
+ * Do the conversion to nanoseconds.
+ */
+ i32Days = g_aoffYear[pTime->i32Year - OFF_YEAR_IDX_0_YEAR]
+ + pTime->u16YearDay - 1;
+ AssertMsgReturn(i32Days <= RTTIME_MAX_DAY && i32Days >= RTTIME_MIN_DAY, ("%RI32\n", i32Days), NULL);
+
+ u32Secs = pTime->u8Second
+ + pTime->u8Minute * 60
+ + pTime->u8Hour * 3600;
+ i64Nanos = (uint64_t)pTime->u32Nanosecond
+ + u32Secs * UINT64_C(1000000000);
+ AssertMsgReturn(i32Days != RTTIME_MAX_DAY || i64Nanos <= RTTIME_MAX_DAY_NANO, ("%RI64\n", i64Nanos), NULL);
+ AssertMsgReturn(i32Days != RTTIME_MIN_DAY || i64Nanos >= RTTIME_MIN_DAY_NANO, ("%RI64\n", i64Nanos), NULL);
+
+ i64Nanos += i32Days * UINT64_C(86400000000000);
+ if ((pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) == RTTIME_FLAGS_TYPE_LOCAL)
+ i64Nanos -= pTime->offUTC * RT_NS_1MIN;
+
+ pTimeSpec->i64NanosecondsRelativeToUnixEpoch = i64Nanos;
+ return pTimeSpec;
+}
+RT_EXPORT_SYMBOL(RTTimeImplode);
+
+
+/**
+ * Internal worker for RTTimeNormalize and RTTimeLocalNormalize.
+ */
+static PRTTIME rtTimeNormalizeInternal(PRTTIME pTime)
+{
+ unsigned uSecond;
+ unsigned uMinute;
+ unsigned uHour;
+ bool fLeapYear;
+
+ /*
+ * Fix the YearDay and Month/MonthDay.
+ */
+ fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
+ if (!pTime->u16YearDay)
+ {
+ /*
+ * The Month+MonthDay must present, overflow adjust them and calc the year day.
+ */
+ AssertMsgReturn( pTime->u8Month
+ && pTime->u8MonthDay,
+ ("date=%d-%d-%d\n", pTime->i32Year, pTime->u8Month, pTime->u8MonthDay),
+ NULL);
+ while (pTime->u8Month > 12)
+ {
+ pTime->u8Month -= 12;
+ pTime->i32Year++;
+ fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
+ pTime->fFlags &= ~(RTTIME_FLAGS_COMMON_YEAR | RTTIME_FLAGS_LEAP_YEAR);
+ }
+
+ for (;;)
+ {
+ unsigned cDaysInMonth = fLeapYear
+ ? g_acDaysInMonthsLeap[pTime->u8Month - 1]
+ : g_acDaysInMonths[pTime->u8Month - 1];
+ if (pTime->u8MonthDay <= cDaysInMonth)
+ break;
+ pTime->u8MonthDay -= cDaysInMonth;
+ if (pTime->u8Month != 12)
+ pTime->u8Month++;
+ else
+ {
+ pTime->u8Month = 1;
+ pTime->i32Year++;
+ fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
+ pTime->fFlags &= ~(RTTIME_FLAGS_COMMON_YEAR | RTTIME_FLAGS_LEAP_YEAR);
+ }
+ }
+
+ pTime->u16YearDay = pTime->u8MonthDay - 1
+ + (fLeapYear
+ ? g_aiDayOfYearLeap[pTime->u8Month - 1]
+ : g_aiDayOfYear[pTime->u8Month - 1]);
+ }
+ else
+ {
+ /*
+ * Are both YearDay and Month/MonthDay valid?
+ * Check that they don't overflow and match, if not use YearDay (simpler).
+ */
+ bool fRecalc = true;
+ if ( pTime->u8Month
+ && pTime->u8MonthDay)
+ {
+ do
+ {
+ uint16_t u16YearDay;
+
+ /* If you change one, zero the other to make clear what you mean. */
+ AssertBreak(pTime->u8Month <= 12);
+ AssertBreak(pTime->u8MonthDay <= (fLeapYear
+ ? g_acDaysInMonthsLeap[pTime->u8Month - 1]
+ : g_acDaysInMonths[pTime->u8Month - 1]));
+ u16YearDay = pTime->u8MonthDay - 1
+ + (fLeapYear
+ ? g_aiDayOfYearLeap[pTime->u8Month - 1]
+ : g_aiDayOfYear[pTime->u8Month - 1]);
+ AssertBreak(u16YearDay == pTime->u16YearDay);
+ fRecalc = false;
+ } while (0);
+ }
+ if (fRecalc)
+ {
+ const uint16_t *paiDayOfYear;
+
+ /* overflow adjust YearDay */
+ while (pTime->u16YearDay > (fLeapYear ? 366 : 365))
+ {
+ pTime->u16YearDay -= fLeapYear ? 366 : 365;
+ pTime->i32Year++;
+ fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
+ pTime->fFlags &= ~(RTTIME_FLAGS_COMMON_YEAR | RTTIME_FLAGS_LEAP_YEAR);
+ }
+
+ /* calc Month and MonthDay */
+ paiDayOfYear = fLeapYear
+ ? &g_aiDayOfYearLeap[0]
+ : &g_aiDayOfYear[0];
+ pTime->u8Month = 1;
+ while (pTime->u16YearDay >= paiDayOfYear[pTime->u8Month])
+ pTime->u8Month++;
+ Assert(pTime->u8Month >= 1 && pTime->u8Month <= 12);
+ pTime->u8MonthDay = pTime->u16YearDay - paiDayOfYear[pTime->u8Month - 1] + 1;
+ }
+ }
+
+ /*
+ * Fixup time overflows.
+ * Use unsigned int values internally to avoid overflows.
+ */
+ uSecond = pTime->u8Second;
+ uMinute = pTime->u8Minute;
+ uHour = pTime->u8Hour;
+
+ while (pTime->u32Nanosecond >= 1000000000)
+ {
+ pTime->u32Nanosecond -= 1000000000;
+ uSecond++;
+ }
+
+ while (uSecond >= 60)
+ {
+ uSecond -= 60;
+ uMinute++;
+ }
+
+ while (uMinute >= 60)
+ {
+ uMinute -= 60;
+ uHour++;
+ }
+
+ while (uHour >= 24)
+ {
+ uHour -= 24;
+
+ /* This is really a RTTimeIncDay kind of thing... */
+ if (pTime->u16YearDay + 1 != (fLeapYear ? g_aiDayOfYearLeap[pTime->u8Month] : g_aiDayOfYear[pTime->u8Month]))
+ {
+ pTime->u16YearDay++;
+ pTime->u8MonthDay++;
+ }
+ else if (pTime->u8Month != 12)
+ {
+ pTime->u16YearDay++;
+ pTime->u8Month++;
+ pTime->u8MonthDay = 1;
+ }
+ else
+ {
+ pTime->i32Year++;
+ fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
+ pTime->fFlags &= ~(RTTIME_FLAGS_COMMON_YEAR | RTTIME_FLAGS_LEAP_YEAR);
+ pTime->u16YearDay = 1;
+ pTime->u8Month = 1;
+ pTime->u8MonthDay = 1;
+ }
+ }
+
+ pTime->u8Second = uSecond;
+ pTime->u8Minute = uMinute;
+ pTime->u8Hour = uHour;
+
+ /*
+ * Correct the leap year flag.
+ * Assert if it's wrong, but ignore if unset.
+ */
+ if (fLeapYear)
+ {
+ Assert(!(pTime->fFlags & RTTIME_FLAGS_COMMON_YEAR));
+ pTime->fFlags &= ~RTTIME_FLAGS_COMMON_YEAR;
+ pTime->fFlags |= RTTIME_FLAGS_LEAP_YEAR;
+ }
+ else
+ {
+ Assert(!(pTime->fFlags & RTTIME_FLAGS_LEAP_YEAR));
+ pTime->fFlags &= ~RTTIME_FLAGS_LEAP_YEAR;
+ pTime->fFlags |= RTTIME_FLAGS_COMMON_YEAR;
+ }
+
+
+ /*
+ * Calc week day.
+ *
+ * 1970-01-01 was a Thursday (3), so find the number of days relative to
+ * that point. We use the table when possible and a slow+stupid+brute-force
+ * algorithm for points outside it. Feel free to optimize the latter by
+ * using some clever formula.
+ */
+ if ( pTime->i32Year >= OFF_YEAR_IDX_0_YEAR
+ && pTime->i32Year < OFF_YEAR_IDX_0_YEAR + (int32_t)RT_ELEMENTS(g_aoffYear))
+ {
+ int32_t offDays = g_aoffYear[pTime->i32Year - OFF_YEAR_IDX_0_YEAR]
+ + pTime->u16YearDay -1;
+ pTime->u8WeekDay = ((offDays % 7) + 3 + 7) % 7;
+ }
+ else
+ {
+ int32_t i32Year = pTime->i32Year;
+ if (i32Year >= 1970)
+ {
+ uint64_t offDays = pTime->u16YearDay - 1;
+ while (--i32Year >= 1970)
+ offDays += rtTimeIsLeapYear(i32Year) ? 366 : 365;
+ pTime->u8WeekDay = (uint8_t)((offDays + 3) % 7);
+ }
+ else
+ {
+ int64_t offDays = (fLeapYear ? -366 - 1 : -365 - 1) + pTime->u16YearDay;
+ while (++i32Year < 1970)
+ offDays -= rtTimeIsLeapYear(i32Year) ? 366 : 365;
+ pTime->u8WeekDay = ((int)(offDays % 7) + 3 + 7) % 7;
+ }
+ }
+ return pTime;
+}
+
+
+RTDECL(PRTTIME) RTTimeNormalize(PRTTIME pTime)
+{
+ /*
+ * Validate that we've got the minimum of stuff handy.
+ */
+ AssertPtrReturn(pTime, NULL);
+ AssertMsgReturn(!(pTime->fFlags & ~RTTIME_FLAGS_MASK), ("%#x\n", pTime->fFlags), NULL);
+ AssertMsgReturn((pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) != RTTIME_FLAGS_TYPE_LOCAL, ("Use RTTimeLocalNormalize!\n"), NULL);
+ AssertMsgReturn(pTime->offUTC == 0, ("%d; Use RTTimeLocalNormalize!\n", pTime->offUTC), NULL);
+
+ pTime = rtTimeNormalizeInternal(pTime);
+ if (pTime)
+ pTime->fFlags |= RTTIME_FLAGS_TYPE_UTC;
+ return pTime;
+}
+RT_EXPORT_SYMBOL(RTTimeNormalize);
+
+
+RTDECL(PRTTIME) RTTimeLocalNormalize(PRTTIME pTime)
+{
+ /*
+ * Validate that we've got the minimum of stuff handy.
+ */
+ AssertPtrReturn(pTime, NULL);
+ AssertMsgReturn(!(pTime->fFlags & ~RTTIME_FLAGS_MASK), ("%#x\n", pTime->fFlags), NULL);
+ AssertMsgReturn((pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) != RTTIME_FLAGS_TYPE_UTC, ("Use RTTimeNormalize!\n"), NULL);
+
+ pTime = rtTimeNormalizeInternal(pTime);
+ if (pTime)
+ pTime->fFlags |= RTTIME_FLAGS_TYPE_LOCAL;
+ return pTime;
+}
+RT_EXPORT_SYMBOL(RTTimeLocalNormalize);
+
+
+RTDECL(char *) RTTimeToString(PCRTTIME pTime, char *psz, size_t cb)
+{
+ size_t cch;
+
+ /* (Default to UTC if not specified) */
+ if ( (pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) == RTTIME_FLAGS_TYPE_LOCAL
+ && pTime->offUTC)
+ {
+ int32_t offUTC = pTime->offUTC;
+ Assert(offUTC <= 840 && offUTC >= -840);
+ char chSign;
+ if (offUTC >= 0)
+ chSign = '+';
+ else
+ {
+ chSign = '-';
+ offUTC = -offUTC;
+ }
+ uint32_t offUTCHour = (uint32_t)offUTC / 60;
+ uint32_t offUTCMinute = (uint32_t)offUTC % 60;
+ cch = RTStrPrintf(psz, cb,
+ "%RI32-%02u-%02uT%02u:%02u:%02u.%09RU32%c%02d%:02d",
+ pTime->i32Year, pTime->u8Month, pTime->u8MonthDay,
+ pTime->u8Hour, pTime->u8Minute, pTime->u8Second, pTime->u32Nanosecond,
+ chSign, offUTCHour, offUTCMinute);
+ if ( cch <= 15
+ || psz[cch - 6] != chSign)
+ return NULL;
+ }
+ else
+ {
+ cch = RTStrPrintf(psz, cb, "%RI32-%02u-%02uT%02u:%02u:%02u.%09RU32Z",
+ pTime->i32Year, pTime->u8Month, pTime->u8MonthDay,
+ pTime->u8Hour, pTime->u8Minute, pTime->u8Second, pTime->u32Nanosecond);
+ if ( cch <= 15
+ || psz[cch - 1] != 'Z')
+ return NULL;
+ }
+ return psz;
+}
+RT_EXPORT_SYMBOL(RTTimeToString);
+
+
+RTDECL(ssize_t) RTTimeToStringEx(PCRTTIME pTime, char *psz, size_t cb, unsigned cFractionDigits)
+{
+ size_t cch;
+
+ /* Format the fraction. */
+ char szFraction[16];
+ if (!cFractionDigits)
+ szFraction[0] = '\0';
+ else
+ {
+ AssertReturn(cFractionDigits <= 9, VERR_OUT_OF_RANGE);
+ Assert(pTime->u32Nanosecond <= 999999999);
+ RTStrPrintf(szFraction, sizeof(szFraction), ".%09RU32", pTime->u32Nanosecond);
+ szFraction[cFractionDigits + 1] = '\0';
+ }
+
+ /* (Default to UTC if not specified) */
+ if ( (pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) == RTTIME_FLAGS_TYPE_LOCAL
+ && pTime->offUTC)
+ {
+ int32_t offUTC = pTime->offUTC;
+ Assert(offUTC <= 840 && offUTC >= -840);
+ char chSign;
+ if (offUTC >= 0)
+ chSign = '+';
+ else
+ {
+ chSign = '-';
+ offUTC = -offUTC;
+ }
+ uint32_t offUTCHour = (uint32_t)offUTC / 60;
+ uint32_t offUTCMinute = (uint32_t)offUTC % 60;
+
+ /* Examples: 2018-09-07T16:12:00+02:00 2018-09-07T16:12:00.123456789+02:00 */
+ cch = RTStrPrintf(psz, cb,
+ "%04RI32-%02u-%02uT%02u:%02u:%02u%s%c%02d%:02d",
+ pTime->i32Year, pTime->u8Month, pTime->u8MonthDay,
+ pTime->u8Hour, pTime->u8Minute, pTime->u8Second, szFraction,
+ chSign, offUTCHour, offUTCMinute);
+ if ( cch >= 24
+ && psz[cch - 6] == chSign)
+ return cch;
+ }
+ else
+ {
+ /* Examples: 2018-09-07T16:12:00Z 2018-09-07T16:12:00.123456789Z */
+ cch = RTStrPrintf(psz, cb, "%04RI32-%02u-%02uT%02u:%02u:%02u%sZ",
+ pTime->i32Year, pTime->u8Month, pTime->u8MonthDay,
+ pTime->u8Hour, pTime->u8Minute, pTime->u8Second, szFraction);
+ if ( cch >= 19
+ && psz[cch - 1] == 'Z')
+ return cch;
+ }
+ return VERR_BUFFER_OVERFLOW;
+}
+RT_EXPORT_SYMBOL(RTTimeToStringEx);
+
+
+RTDECL(char *) RTTimeSpecToString(PCRTTIMESPEC pTime, char *psz, size_t cb)
+{
+ RTTIME Time;
+ return RTTimeToString(RTTimeExplode(&Time, pTime), psz, cb);
+}
+RT_EXPORT_SYMBOL(RTTimeSpecToString);
+
+
+
+RTDECL(PRTTIME) RTTimeFromString(PRTTIME pTime, const char *pszString)
+{
+ /* Ignore leading spaces. */
+ while (RT_C_IS_SPACE(*pszString))
+ pszString++;
+
+ /*
+ * Init non date & time parts.
+ */
+ pTime->fFlags = RTTIME_FLAGS_TYPE_LOCAL;
+ pTime->offUTC = 0;
+
+ /*
+ * The date part.
+ */
+
+ /* Year */
+ int rc = RTStrToInt32Ex(pszString, (char **)&pszString, 10, &pTime->i32Year);
+ if (rc != VWRN_TRAILING_CHARS)
+ return NULL;
+
+ bool const fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
+ if (fLeapYear)
+ pTime->fFlags |= RTTIME_FLAGS_LEAP_YEAR;
+
+ if (*pszString++ != '-')
+ return NULL;
+
+ /* Month of the year. */
+ rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Month);
+ if (rc != VWRN_TRAILING_CHARS)
+ return NULL;
+ if (pTime->u8Month == 0 || pTime->u8Month > 12)
+ return NULL;
+ if (*pszString++ != '-')
+ return NULL;
+
+ /* Day of month.*/
+ rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8MonthDay);
+ if (rc != VWRN_TRAILING_CHARS && rc != VINF_SUCCESS)
+ return NULL;
+ unsigned const cDaysInMonth = fLeapYear
+ ? g_acDaysInMonthsLeap[pTime->u8Month - 1]
+ : g_acDaysInMonths[pTime->u8Month - 1];
+ if (pTime->u8MonthDay == 0 || pTime->u8MonthDay > cDaysInMonth)
+ return NULL;
+
+ /* Calculate year day. */
+ pTime->u16YearDay = pTime->u8MonthDay - 1
+ + (fLeapYear
+ ? g_aiDayOfYearLeap[pTime->u8Month - 1]
+ : g_aiDayOfYear[pTime->u8Month - 1]);
+
+ pTime->u8WeekDay = UINT8_MAX; /* later */
+
+ /*
+ * The time part.
+ */
+ if (*pszString++ != 'T')
+ return NULL;
+
+ /* Hour. */
+ rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Hour);
+ if (rc != VWRN_TRAILING_CHARS)
+ return NULL;
+ if (pTime->u8Hour > 23)
+ return NULL;
+ if (*pszString++ != ':')
+ return NULL;
+
+ /* Minute. */
+ rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Minute);
+ if (rc != VWRN_TRAILING_CHARS)
+ return NULL;
+ if (pTime->u8Minute > 59)
+ return NULL;
+ if (*pszString++ != ':')
+ return NULL;
+
+ /* Second. */
+ rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Second);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS && rc != VWRN_TRAILING_SPACES)
+ return NULL;
+ if (pTime->u8Second > 59)
+ return NULL;
+
+ /* We generally put a 9 digit fraction here, but it's entirely optional. */
+ if (*pszString == '.')
+ {
+ const char * const pszStart = ++pszString;
+ rc = RTStrToUInt32Ex(pszString, (char **)&pszString, 10, &pTime->u32Nanosecond);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS && rc != VWRN_TRAILING_SPACES)
+ return NULL;
+ if (pTime->u32Nanosecond >= 1000000000)
+ return NULL;
+ switch (pszString - pszStart)
+ {
+ case 1: pTime->u32Nanosecond *= 100000000; break;
+ case 2: pTime->u32Nanosecond *= 10000000; break;
+ case 3: pTime->u32Nanosecond *= 1000000; break;
+ case 4: pTime->u32Nanosecond *= 100000; break;
+ case 5: pTime->u32Nanosecond *= 10000; break;
+ case 6: pTime->u32Nanosecond *= 1000; break;
+ case 7: pTime->u32Nanosecond *= 100; break;
+ case 8: pTime->u32Nanosecond *= 10; break;
+ case 9: break;
+ default:
+ return NULL;
+ }
+ if (pTime->u32Nanosecond >= 1000000000)
+ return NULL;
+ }
+ else
+ pTime->u32Nanosecond = 0;
+
+ /*
+ * Time zone.
+ */
+ if (*pszString == 'Z')
+ {
+ pszString++;
+ pTime->fFlags &= ~RTTIME_FLAGS_TYPE_MASK;
+ pTime->fFlags |= RTTIME_FLAGS_TYPE_UTC;
+ pTime->offUTC = 0;
+ }
+ else if ( *pszString == '+'
+ || *pszString == '-')
+ {
+ int8_t cUtcHours = 0;
+ rc = RTStrToInt8Ex(pszString, (char **)&pszString, 10, &cUtcHours);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS && rc != VWRN_TRAILING_SPACES)
+ return NULL;
+ uint8_t cUtcMin = 0;
+ if (*pszString == ':')
+ {
+ rc = RTStrToUInt8Ex(pszString + 1, (char **)&pszString, 10, &cUtcMin);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
+ return NULL;
+ }
+ else if (*pszString && !RT_C_IS_BLANK(*pszString))
+ return NULL;
+ if (cUtcHours >= 0)
+ pTime->offUTC = cUtcHours * 60 + cUtcMin;
+ else
+ pTime->offUTC = cUtcHours * 60 - cUtcMin;
+ if (RT_ABS(pTime->offUTC) > 840)
+ return NULL;
+ }
+ /* else: No time zone given, local with offUTC = 0. */
+
+ /*
+ * The rest of the string should be blanks.
+ */
+ char ch;
+ while ((ch = *pszString++) != '\0')
+ if (!RT_C_IS_BLANK(ch))
+ return NULL;
+
+ /* Calc week day. */
+ rtTimeNormalizeInternal(pTime);
+ return pTime;
+}
+RT_EXPORT_SYMBOL(RTTimeFromString);
+
+
+RTDECL(PRTTIMESPEC) RTTimeSpecFromString(PRTTIMESPEC pTime, const char *pszString)
+{
+ RTTIME Time;
+ if (RTTimeFromString(&Time, pszString))
+ return RTTimeImplode(pTime, &Time);
+ return NULL;
+}
+RT_EXPORT_SYMBOL(RTTimeSpecFromString);
+
+
+RTDECL(ssize_t) RTTimeToRfc2822(PRTTIME pTime, char *psz, size_t cb, uint32_t fFlags)
+{
+ Assert(pTime->u8Month > 0 && pTime->u8Month <= 12);
+ Assert(pTime->u8WeekDay < 7);
+ Assert(!(fFlags & ~RTTIME_RFC2822_F_GMT));
+
+ /* (Default to UTC if not specified) */
+ if ( (pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) == RTTIME_FLAGS_TYPE_LOCAL
+ && pTime->offUTC)
+ {
+ Assert(!(fFlags & RTTIME_RFC2822_F_GMT) /* don't call with local time. duh! */ );
+
+ /* Calc the UTC offset part. */
+ int32_t offUtc = pTime->offUTC;
+ Assert(offUtc <= 840 && offUtc >= -840);
+ char chSign;
+ if (offUtc >= 0)
+ chSign = '+';
+ else
+ {
+ chSign = '-';
+ offUtc = -offUtc;
+ }
+ uint32_t offUtcHour = (uint32_t)offUtc / 60;
+ uint32_t offUtcMinute = (uint32_t)offUtc % 60;
+
+ /* Example: "Mon, 31 Aug 2018 00:00:00 +0200" */
+ size_t cch = RTStrPrintf(psz, cb, "%s, %u %s %04RI32 %02u:%02u:%02u %c%02u%02u", g_apszWeekDays[pTime->u8WeekDay],
+ pTime->u8MonthDay, g_apszMonths[pTime->u8Month], pTime->i32Year,
+ pTime->u8Hour, pTime->u8Minute, pTime->u8Second, chSign, offUtcHour, offUtcMinute);
+ if ( cch >= 27
+ && psz[cch - 5] == chSign)
+ return cch;
+ }
+ else if (fFlags & RTTIME_RFC2822_F_GMT)
+ {
+ /* Example: "Mon, 1 Jan 1971 23:55:59 GMT" */
+ size_t cch = RTStrPrintf(psz, cb, "%s, %u %s %04RI32 %02u:%02u:%02u GMT", g_apszWeekDays[pTime->u8WeekDay],
+ pTime->u8MonthDay, g_apszMonths[pTime->u8Month], pTime->i32Year,
+ pTime->u8Hour, pTime->u8Minute, pTime->u8Second);
+ if ( cch >= 27
+ && psz[cch - 1] == 'T')
+ return cch;
+ }
+ else
+ {
+ /* Example: "Mon, 1 Jan 1971 00:00:00 -0000" */
+ size_t cch = RTStrPrintf(psz, cb, "%s, %u %s %04RI32 %02u:%02u:%02u -0000", g_apszWeekDays[pTime->u8WeekDay],
+ pTime->u8MonthDay, g_apszMonths[pTime->u8Month], pTime->i32Year,
+ pTime->u8Hour, pTime->u8Minute, pTime->u8Second);
+ if ( cch >= 27
+ && psz[cch - 5] == '-')
+ return cch;
+ }
+ return VERR_BUFFER_OVERFLOW;
+}
+RT_EXPORT_SYMBOL(RTTimeToRfc2822);
+
+
+RTDECL(PRTTIME) RTTimeFromRfc2822(PRTTIME pTime, const char *pszString)
+{
+ /*
+ * Fri, 31 Aug 2018 00:00:00 +0200
+ * Mon, 3 Sep 2018 00:00:00 GMT
+ * Mon, 3 Sep 2018 00:00:00 -0000
+ * 3 Sep 2018 00:00:00 -0000 (?)
+ * 3 Sep 2018 00:00:00 GMT (?)
+ *
+ */
+
+ /* Ignore leading spaces. */
+ while (RT_C_IS_SPACE(*pszString))
+ pszString++;
+
+ /*
+ * Init non date & time parts.
+ */
+ pTime->fFlags = RTTIME_FLAGS_TYPE_LOCAL;
+ pTime->offUTC = 0;
+
+ /*
+ * The date part.
+ */
+
+ /* Optional day of week: */
+ if (RT_C_IS_ALPHA(pszString[0]) && pszString[1] != '\0')
+ {
+ uint32_t uWeekDay = RT_MAKE_U32_FROM_U8(RT_C_TO_LOWER(pszString[0]), RT_C_TO_LOWER(pszString[1]),
+ RT_C_TO_LOWER(pszString[2]), 0);
+ if ( uWeekDay == RT_MAKE_U32_FROM_U8('m', 'o', 'n', 0)) pTime->u8WeekDay = 0;
+ else if (uWeekDay == RT_MAKE_U32_FROM_U8('t', 'u', 'e', 0)) pTime->u8WeekDay = 1;
+ else if (uWeekDay == RT_MAKE_U32_FROM_U8('w', 'e', 'd', 0)) pTime->u8WeekDay = 2;
+ else if (uWeekDay == RT_MAKE_U32_FROM_U8('t', 'h', 'u', 0)) pTime->u8WeekDay = 3;
+ else if (uWeekDay == RT_MAKE_U32_FROM_U8('f', 'r', 'i', 0)) pTime->u8WeekDay = 4;
+ else if (uWeekDay == RT_MAKE_U32_FROM_U8('s', 'a', 't', 0)) pTime->u8WeekDay = 5;
+ else if (uWeekDay == RT_MAKE_U32_FROM_U8('s', 'u', 'n', 0)) pTime->u8WeekDay = 6;
+ else
+ return NULL;
+ pszString += 3;
+ while (RT_C_IS_ALPHA(*pszString))
+ pszString++;
+ if (*pszString == ',')
+ pszString++;
+ while (RT_C_IS_SPACE(*pszString))
+ pszString++;
+ if (!RT_C_IS_DIGIT(pszString[0]))
+ return NULL;
+ }
+ else if (RT_C_IS_DIGIT(pszString[0]))
+ pTime->u8WeekDay = UINT8_MAX;
+ else
+ return NULL;
+
+ /* Day of month.*/
+ int rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8MonthDay);
+ if (rc != VWRN_TRAILING_CHARS && rc != VINF_SUCCESS)
+ return NULL;
+ while (RT_C_IS_SPACE(*pszString))
+ pszString++;
+
+ /* Month of the year. */
+ if (pszString[0] == '\0' || pszString[1] == '\0' || pszString[2] == '\0')
+ return NULL;
+ uint32_t uMonth = RT_MAKE_U32_FROM_U8(RT_C_TO_LOWER(pszString[0]), RT_C_TO_LOWER(pszString[1]),
+ RT_C_TO_LOWER(pszString[2]), 0);
+ if ( uMonth == RT_MAKE_U32_FROM_U8('j', 'a', 'n', 0)) pTime->u8Month = 1;
+ else if (uMonth == RT_MAKE_U32_FROM_U8('f', 'e', 'b', 0)) pTime->u8Month = 2;
+ else if (uMonth == RT_MAKE_U32_FROM_U8('m', 'a', 'r', 0)) pTime->u8Month = 3;
+ else if (uMonth == RT_MAKE_U32_FROM_U8('a', 'p', 'r', 0)) pTime->u8Month = 4;
+ else if (uMonth == RT_MAKE_U32_FROM_U8('m', 'a', 'y', 0)) pTime->u8Month = 5;
+ else if (uMonth == RT_MAKE_U32_FROM_U8('j', 'u', 'n', 0)) pTime->u8Month = 6;
+ else if (uMonth == RT_MAKE_U32_FROM_U8('j', 'u', 'l', 0)) pTime->u8Month = 7;
+ else if (uMonth == RT_MAKE_U32_FROM_U8('a', 'u', 'g', 0)) pTime->u8Month = 8;
+ else if (uMonth == RT_MAKE_U32_FROM_U8('s', 'e', 'p', 0)) pTime->u8Month = 9;
+ else if (uMonth == RT_MAKE_U32_FROM_U8('o', 'c', 't', 0)) pTime->u8Month = 10;
+ else if (uMonth == RT_MAKE_U32_FROM_U8('n', 'o', 'v', 0)) pTime->u8Month = 11;
+ else if (uMonth == RT_MAKE_U32_FROM_U8('d', 'e', 'c', 0)) pTime->u8Month = 12;
+ else
+ return NULL;
+ pszString += 3;
+ while (RT_C_IS_ALPHA(*pszString))
+ pszString++;
+ while (RT_C_IS_SPACE(*pszString))
+ pszString++;
+
+ /* Year */
+ const char * const pszStartYear = pszString;
+ rc = RTStrToInt32Ex(pszString, (char **)&pszString, 10, &pTime->i32Year);
+ if (rc != VWRN_TRAILING_CHARS)
+ return NULL;
+ if (pszString - pszStartYear >= 4 )
+ { /* likely */ }
+ else if (pszString - pszStartYear == 3)
+ pTime->i32Year += 1900;
+ else if (pszString - pszStartYear == 2)
+ pTime->i32Year += pTime->i32Year >= 50 ? 1900 : 2000;
+ else
+ return NULL;
+
+ bool const fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
+ if (fLeapYear)
+ pTime->fFlags |= RTTIME_FLAGS_LEAP_YEAR;
+
+ while (RT_C_IS_SPACE(*pszString))
+ pszString++;
+
+
+ /* Calculate year day. */
+ unsigned const cDaysInMonth = fLeapYear
+ ? g_acDaysInMonthsLeap[pTime->u8Month - 1]
+ : g_acDaysInMonths[pTime->u8Month - 1];
+ if (pTime->u8MonthDay == 0 || pTime->u8MonthDay > cDaysInMonth)
+ return NULL;
+
+ pTime->u16YearDay = pTime->u8MonthDay - 1
+ + (fLeapYear
+ ? g_aiDayOfYearLeap[pTime->u8Month - 1]
+ : g_aiDayOfYear[pTime->u8Month - 1]);
+
+ /*
+ * The time part.
+ */
+ /* Hour. */
+ rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Hour);
+ if (rc != VWRN_TRAILING_CHARS)
+ return NULL;
+ if (pTime->u8Hour > 23)
+ return NULL;
+ if (*pszString++ != ':')
+ return NULL;
+
+ /* Minute. */
+ rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Minute);
+ if (rc != VWRN_TRAILING_CHARS)
+ return NULL;
+ if (pTime->u8Minute > 59)
+ return NULL;
+ if (*pszString++ != ':')
+ return NULL;
+
+ /* Second. */
+ rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Second);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS && rc != VWRN_TRAILING_SPACES)
+ return NULL;
+ if (pTime->u8Second > 59)
+ return NULL;
+
+ /* Non-standard fraction. Handy for testing, though. */
+ if (*pszString == '.')
+ {
+ const char * const pszStart = ++pszString;
+ rc = RTStrToUInt32Ex(pszString, (char **)&pszString, 10, &pTime->u32Nanosecond);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS && rc != VWRN_TRAILING_SPACES)
+ return NULL;
+ if (pTime->u32Nanosecond >= 1000000000)
+ return NULL;
+ switch (pszString - pszStart)
+ {
+ case 1: pTime->u32Nanosecond *= 100000000; break;
+ case 2: pTime->u32Nanosecond *= 10000000; break;
+ case 3: pTime->u32Nanosecond *= 1000000; break;
+ case 4: pTime->u32Nanosecond *= 100000; break;
+ case 5: pTime->u32Nanosecond *= 10000; break;
+ case 6: pTime->u32Nanosecond *= 1000; break;
+ case 7: pTime->u32Nanosecond *= 100; break;
+ case 8: pTime->u32Nanosecond *= 10; break;
+ case 9: break;
+ default:
+ return NULL;
+ }
+ if (pTime->u32Nanosecond >= 1000000000)
+ return NULL;
+ }
+ else
+ pTime->u32Nanosecond = 0;
+ while (RT_C_IS_SPACE(*pszString))
+ pszString++;
+
+ /*
+ * Time zone.
+ */
+ if ( *pszString == '+'
+ || *pszString == '-')
+ {
+ if ( !RT_C_IS_DIGIT(pszString[1])
+ || !RT_C_IS_DIGIT(pszString[2]))
+ return NULL;
+ int8_t cUtcHours = (pszString[1] - '0') * 10 + (pszString[2] - '0');
+ char chSign = *pszString;
+ if (chSign == '-')
+ cUtcHours = -cUtcHours;
+ pszString += 3;
+
+ uint8_t cUtcMin = 0;
+ if (RT_C_IS_DIGIT(pszString[0]))
+ {
+ rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &cUtcMin);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
+ return NULL;
+ }
+ else if (*pszString && !RT_C_IS_BLANK(*pszString))
+ return NULL;
+ if (cUtcHours >= 0)
+ pTime->offUTC = cUtcHours * 60 + cUtcMin;
+ else
+ pTime->offUTC = cUtcHours * 60 - cUtcMin;
+ if (RT_ABS(pTime->offUTC) > 840)
+ return NULL;
+
+ /* -0000: GMT isn't necessarily the local time zone, so change flags from local to UTC. */
+ if (pTime->offUTC == 0 && chSign == '-')
+ {
+ pTime->fFlags &= ~RTTIME_FLAGS_TYPE_MASK;
+ pTime->fFlags |= RTTIME_FLAGS_TYPE_UTC;
+ }
+ }
+ else if (RT_C_IS_ALPHA(*pszString))
+ {
+ uint32_t uTimeZone = RT_MAKE_U32_FROM_U8(RT_C_TO_LOWER(pszString[0]), RT_C_TO_LOWER(pszString[1]),
+ RT_C_TO_LOWER(pszString[2]), 0);
+ if (uTimeZone == RT_MAKE_U32_FROM_U8('g', 'm', 't', 0))
+ {
+ pTime->fFlags &= ~RTTIME_FLAGS_TYPE_MASK;
+ pTime->fFlags |= RTTIME_FLAGS_TYPE_UTC;
+ pTime->offUTC = 0;
+ pszString += 3;
+ }
+ else if ((uint16_t)uTimeZone == RT_MAKE_U16('u', 't'))
+ {
+ pTime->fFlags &= ~RTTIME_FLAGS_TYPE_MASK;
+ pTime->fFlags |= RTTIME_FLAGS_TYPE_UTC;
+ pTime->offUTC = 0;
+ pszString += 2;
+ }
+ else
+ {
+ static const struct { uint32_t uTimeZone; int32_t offUtc; } s_aLegacyTimeZones[] =
+ {
+ { RT_MAKE_U32_FROM_U8('e', 'd', 't', 0), -4*60 },
+ { RT_MAKE_U32_FROM_U8('e', 's', 't', 0), -5*60 },
+ { RT_MAKE_U32_FROM_U8('c', 'd', 't', 0), -5*60 },
+ { RT_MAKE_U32_FROM_U8('c', 's', 't', 0), -6*60 },
+ { RT_MAKE_U32_FROM_U8('m', 'd', 't', 0), -6*60 },
+ { RT_MAKE_U32_FROM_U8('m', 's', 't', 0), -7*60 },
+ { RT_MAKE_U32_FROM_U8('p', 'd', 't', 0), -7*60 },
+ { RT_MAKE_U32_FROM_U8('p', 's', 't', 0), -8*60 },
+ };
+ size_t i = RT_ELEMENTS(s_aLegacyTimeZones);
+ while (i-- > 0)
+ if (s_aLegacyTimeZones[i].uTimeZone == uTimeZone)
+ {
+ pTime->fFlags &= ~RTTIME_FLAGS_TYPE_MASK;
+ pTime->fFlags |= RTTIME_FLAGS_TYPE_LOCAL;
+ pTime->offUTC = s_aLegacyTimeZones[i].offUtc;
+ pszString += 3;
+ break;
+ }
+ }
+
+ }
+ /* else: No time zone given, local with offUTC = 0. */
+
+ /*
+ * The rest of the string should be blanks.
+ */
+ char ch;
+ while ((ch = *pszString++) != '\0')
+ if (!RT_C_IS_BLANK(ch))
+ return NULL;
+
+ rtTimeNormalizeInternal(pTime);
+ return pTime;
+}
+RT_EXPORT_SYMBOL(RTTimeFromRfc2822);
+
+
+/**
+ * Adds one day to @a pTime.
+ *
+ * ASSUMES it is zulu time so DST can be ignored.
+ */
+static PRTTIME rtTimeAdd1Day(PRTTIME pTime)
+{
+ Assert(!pTime->offUTC);
+ rtTimeNormalizeInternal(pTime);
+ pTime->u8MonthDay += 1;
+ pTime->u16YearDay = 0;
+ return rtTimeNormalizeInternal(pTime);
+}
+
+
+/**
+ * Subtracts one day from @a pTime.
+ *
+ * ASSUMES it is zulu time so DST can be ignored.
+ */
+static PRTTIME rtTimeSub1Day(PRTTIME pTime)
+{
+ Assert(!pTime->offUTC);
+ rtTimeNormalizeInternal(pTime);
+ if (pTime->u16YearDay > 1)
+ {
+ pTime->u16YearDay -= 1;
+ pTime->u8Month = 0;
+ pTime->u8MonthDay = 0;
+ }
+ else
+ {
+ pTime->i32Year -= 1;
+ pTime->u16YearDay = rtTimeIsLeapYear(pTime->i32Year) ? 366 : 365;
+ pTime->u8MonthDay = 31;
+ pTime->u8Month = 12;
+ pTime->fFlags &= ~(RTTIME_FLAGS_COMMON_YEAR | RTTIME_FLAGS_LEAP_YEAR);
+ }
+ return rtTimeNormalizeInternal(pTime);
+}
+
+
+/**
+ * Adds a signed number of minutes to @a pTime.
+ *
+ * ASSUMES it is zulu time so DST can be ignored.
+ *
+ * @param pTime The time structure to work on.
+ * @param cAddend Number of minutes to add.
+ * ASSUMES the value isn't all that high!
+ */
+static PRTTIME rtTimeAddMinutes(PRTTIME pTime, int32_t cAddend)
+{
+ Assert(RT_ABS(cAddend) < 31 * 24 * 60);
+
+ /*
+ * Work on minutes of the day.
+ */
+ int32_t const cMinutesInDay = 24 * 60;
+ int32_t iDayMinute = (unsigned)pTime->u8Hour * 60 + pTime->u8Minute;
+ iDayMinute += cAddend;
+
+ while (iDayMinute >= cMinutesInDay)
+ {
+ rtTimeAdd1Day(pTime);
+ iDayMinute -= cMinutesInDay;
+ }
+
+ while (iDayMinute < 0)
+ {
+ rtTimeSub1Day(pTime);
+ iDayMinute += cMinutesInDay;
+ }
+
+ pTime->u8Hour = iDayMinute / 60;
+ pTime->u8Minute = iDayMinute % 60;
+
+ return pTime;
+}
+
+
+/**
+ * Converts @a pTime to zulu time (UTC) if needed.
+ *
+ * @returns pTime.
+ * @param pTime What to convert (in/out).
+ */
+static PRTTIME rtTimeConvertToZulu(PRTTIME pTime)
+{
+ RTTIME_ASSERT_NORMALIZED(pTime);
+ if ((pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) != RTTIME_FLAGS_TYPE_UTC)
+ {
+ int32_t offUTC = pTime->offUTC;
+ pTime->offUTC = 0;
+ pTime->fFlags &= ~RTTIME_FLAGS_TYPE_MASK;
+ pTime->fFlags |= RTTIME_FLAGS_TYPE_UTC;
+ if (offUTC != 0)
+ rtTimeAddMinutes(pTime, -offUTC);
+ }
+ return pTime;
+}
+
+
+RTDECL(PRTTIME) RTTimeConvertToZulu(PRTTIME pTime)
+{
+ /*
+ * Validate that we've got the minimum of stuff handy.
+ */
+ AssertPtrReturn(pTime, NULL);
+ AssertMsgReturn(!(pTime->fFlags & ~RTTIME_FLAGS_MASK), ("%#x\n", pTime->fFlags), NULL);
+
+ return rtTimeConvertToZulu(rtTimeNormalizeInternal(pTime));
+}
+RT_EXPORT_SYMBOL(RTTimeConvertToZulu);
+
+
+RTDECL(int) RTTimeCompare(PCRTTIME pLeft, PCRTTIME pRight)
+{
+#ifdef RT_STRICT
+ if (pLeft)
+ RTTIME_ASSERT_NORMALIZED(pLeft);
+ if (pRight)
+ RTTIME_ASSERT_NORMALIZED(pRight);
+#endif
+
+ int iRet;
+ if (pLeft)
+ {
+ if (pRight)
+ {
+ /*
+ * Only work with normalized zulu time.
+ */
+ RTTIME TmpLeft;
+ if ( pLeft->offUTC != 0
+ || pLeft->u16YearDay == 0
+ || pLeft->u16YearDay > 366
+ || pLeft->u8Hour >= 60
+ || pLeft->u8Minute >= 60
+ || pLeft->u8Second >= 60)
+ {
+ TmpLeft = *pLeft;
+ pLeft = rtTimeConvertToZulu(rtTimeNormalizeInternal(&TmpLeft));
+ }
+
+ RTTIME TmpRight;
+ if ( pRight->offUTC != 0
+ || pRight->u16YearDay == 0
+ || pRight->u16YearDay > 366
+ || pRight->u8Hour >= 60
+ || pRight->u8Minute >= 60
+ || pRight->u8Second >= 60)
+ {
+ TmpRight = *pRight;
+ pRight = rtTimeConvertToZulu(rtTimeNormalizeInternal(&TmpRight));
+ }
+
+ /*
+ * Do the comparison.
+ */
+ if ( pLeft->i32Year != pRight->i32Year)
+ iRet = pLeft->i32Year < pRight->i32Year ? -1 : 1;
+ else if ( pLeft->u16YearDay != pRight->u16YearDay)
+ iRet = pLeft->u16YearDay < pRight->u16YearDay ? -1 : 1;
+ else if ( pLeft->u8Hour != pRight->u8Hour)
+ iRet = pLeft->u8Hour < pRight->u8Hour ? -1 : 1;
+ else if ( pLeft->u8Minute != pRight->u8Minute)
+ iRet = pLeft->u8Minute < pRight->u8Minute ? -1 : 1;
+ else if ( pLeft->u8Second != pRight->u8Second)
+ iRet = pLeft->u8Second < pRight->u8Second ? -1 : 1;
+ else if ( pLeft->u32Nanosecond != pRight->u32Nanosecond)
+ iRet = pLeft->u32Nanosecond < pRight->u32Nanosecond ? -1 : 1;
+ else
+ iRet = 0;
+ }
+ else
+ iRet = 1;
+ }
+ else
+ iRet = pRight ? -1 : 0;
+ return iRet;
+}
+RT_EXPORT_SYMBOL(RTTimeCompare);
+
diff --git a/src/VBox/Runtime/common/time/timeprog.cpp b/src/VBox/Runtime/common/time/timeprog.cpp
new file mode 100644
index 00000000..0f0bf030
--- /dev/null
+++ b/src/VBox/Runtime/common/time/timeprog.cpp
@@ -0,0 +1,107 @@
+/* $Id: timeprog.cpp $ */
+/** @file
+ * IPRT - Time Relative to Program Start.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include "internal/time.h"
+
+
+
+/**
+ * Get the nanosecond timestamp relative to program startup.
+ *
+ * @returns Timestamp relative to program startup.
+ */
+RTDECL(uint64_t) RTTimeProgramNanoTS(void)
+{
+ return RTTimeNanoTS() - g_u64ProgramStartNanoTS;
+}
+RT_EXPORT_SYMBOL(RTTimeProgramNanoTS);
+
+
+/**
+ * Get the microsecond timestamp relative to program startup.
+ *
+ * @returns Timestamp relative to program startup.
+ */
+RTDECL(uint64_t) RTTimeProgramMicroTS(void)
+{
+ return RTTimeProgramNanoTS() / RT_NS_1US;
+}
+RT_EXPORT_SYMBOL(RTTimeProgramMicroTS);
+
+
+/**
+ * Get the millisecond timestamp relative to program startup.
+ *
+ * @returns Timestamp relative to program startup.
+ */
+RTDECL(uint64_t) RTTimeProgramMilliTS(void)
+{
+ return RTTimeProgramNanoTS() / RT_NS_1MS;
+}
+RT_EXPORT_SYMBOL(RTTimeProgramMilliTS);
+
+
+/**
+ * Get the second timestamp relative to program startup.
+ *
+ * @returns Timestamp relative to program startup.
+ */
+RTDECL(uint32_t) RTTimeProgramSecTS(void)
+{
+ return (uint32_t)(RTTimeProgramNanoTS() / RT_NS_1SEC);
+}
+RT_EXPORT_SYMBOL(RTTimeProgramSecTS);
+
+
+/**
+ * Get the RTTimeNanoTS() of when the program started.
+ *
+ * @returns Program startup timestamp.
+ */
+RTDECL(uint64_t) RTTimeProgramStartNanoTS(void)
+{
+ return g_u64ProgramStartNanoTS;
+}
+RT_EXPORT_SYMBOL(RTTimeProgramStartNanoTS);
+
diff --git a/src/VBox/Runtime/common/time/timesup.cpp b/src/VBox/Runtime/common/time/timesup.cpp
new file mode 100644
index 00000000..a6c86a6b
--- /dev/null
+++ b/src/VBox/Runtime/common/time/timesup.cpp
@@ -0,0 +1,467 @@
+/* $Id: timesup.cpp $ */
+/** @file
+ * IPRT - Time using SUPLib.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#if !defined(IN_GUEST) && !defined(RT_NO_GIP)
+# include <iprt/asm.h>
+# include <iprt/asm-amd64-x86.h>
+# include <iprt/x86.h>
+# include <VBox/sup.h>
+#endif
+#include "internal/time.h"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#if !defined(IN_GUEST) && !defined(RT_NO_GIP)
+static DECLCALLBACK(void) rtTimeNanoTSInternalBitch(PRTTIMENANOTSDATA pData, uint64_t u64NanoTS, uint64_t u64DeltaPrev, uint64_t u64PrevNanoTS);
+static DECLCALLBACK(uint64_t) rtTimeNanoTSInternalFallback(PRTTIMENANOTSDATA pData, PRTITMENANOTSEXTRA pExtra);
+static DECLCALLBACK(uint64_t) rtTimeNanoTSInternalRediscover(PRTTIMENANOTSDATA pData, PRTITMENANOTSEXTRA pExtra);
+static DECLCALLBACK(uint64_t) rtTimeNanoTSInternalBadCpuIndex(PRTTIMENANOTSDATA pData, PRTITMENANOTSEXTRA pExtra,
+ uint16_t idApic, uint16_t iCpuSet, uint16_t iGipCpu);
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#if !defined(IN_GUEST) && !defined(RT_NO_GIP)
+/** The previous timestamp value returned by RTTimeNanoTS. */
+static uint64_t g_TimeNanoTSPrev = 0;
+
+/** The RTTimeNanoTS data structure that's passed down to the worker functions. */
+static RTTIMENANOTSDATA g_TimeNanoTSData =
+{
+ /* .pu64Prev = */ &g_TimeNanoTSPrev,
+ /* .pfnBad = */ rtTimeNanoTSInternalBitch,
+ /* .pfnRediscover = */ rtTimeNanoTSInternalRediscover,
+ /* .pfnBadCpuIndex = */ rtTimeNanoTSInternalBadCpuIndex,
+ /* .c1nsSteps = */ 0,
+ /* .cExpired = */ 0,
+ /* .cBadPrev = */ 0,
+ /* .cUpdateRaces = */ 0
+};
+
+# ifdef IN_RC
+/** Array of rtTimeNanoTSInternal worker functions.
+ * This array is indexed by g_iWorker. */
+static const PFNTIMENANOTSINTERNAL g_apfnWorkers[] =
+{
+# define RTTIMENANO_WORKER_DETECT 0
+ rtTimeNanoTSInternalRediscover,
+
+# define RTTIMENANO_WORKER_LEGACY_SYNC_INVAR_NO_DELTA 1
+ RTTimeNanoTSLegacySyncInvarNoDelta,
+# define RTTIMENANO_WORKER_LEGACY_SYNC_INVAR_WITH_DELTA 2
+ RTTimeNanoTSLegacySyncInvarWithDelta,
+# define RTTIMENANO_WORKER_LEGACY_ASYNC 3
+ RTTimeNanoTSLegacyAsync,
+
+# define RTTIMENANO_WORKER_LFENCE_SYNC_INVAR_NO_DELTA 4
+ RTTimeNanoTSLFenceSyncInvarNoDelta,
+# define RTTIMENANO_WORKER_LFENCE_SYNC_INVAR_WITH_DELTA 5
+ RTTimeNanoTSLFenceSyncInvarWithDelta,
+# define RTTIMENANO_WORKER_LFENCE_ASYNC 6
+ RTTimeNanoTSLFenceAsync,
+
+# define RTTIMENANO_WORKER_FALLBACK 7
+ rtTimeNanoTSInternalFallback,
+};
+/** The index into g_apfnWorkers for the function to use.
+ * @remarks This cannot be a pointer because that'll break down in RC due to
+ * code relocation. */
+static uint32_t g_iWorker = RTTIMENANO_WORKER_DETECT;
+# else
+/** Pointer to the worker */
+static PFNTIMENANOTSINTERNAL g_pfnWorker = rtTimeNanoTSInternalRediscover;
+# endif /* IN_RC */
+
+
+/**
+ * @interface_method_impl{RTTIMENANOTSDATA,pfnBad}
+ */
+static DECLCALLBACK(void) rtTimeNanoTSInternalBitch(PRTTIMENANOTSDATA pData, uint64_t u64NanoTS, uint64_t u64DeltaPrev,
+ uint64_t u64PrevNanoTS)
+{
+ pData->cBadPrev++;
+ if ((int64_t)u64DeltaPrev < 0)
+ LogRel(("TM: u64DeltaPrev=%RI64 u64PrevNanoTS=0x%016RX64 u64NanoTS=0x%016RX64\n",
+ u64DeltaPrev, u64PrevNanoTS, u64NanoTS));
+ else
+ Log(("TM: u64DeltaPrev=%RI64 u64PrevNanoTS=0x%016RX64 u64NanoTS=0x%016RX64 (debugging?)\n",
+ u64DeltaPrev, u64PrevNanoTS, u64NanoTS));
+}
+
+/**
+ * @interface_method_impl{RTTIMENANOTSDATA,pfnBadCpuIndex}
+ */
+static DECLCALLBACK(uint64_t) rtTimeNanoTSInternalBadCpuIndex(PRTTIMENANOTSDATA pData, PRTITMENANOTSEXTRA pExtra,
+ uint16_t idApic, uint16_t iCpuSet, uint16_t iGipCpu)
+{
+ RT_NOREF_PV(pData); RT_NOREF_PV(idApic); RT_NOREF_PV(iCpuSet); RT_NOREF_PV(iGipCpu);
+# ifndef IN_RC
+ AssertMsgFailed(("idApic=%#x iCpuSet=%#x iGipCpu=%#x\n", idApic, iCpuSet, iGipCpu));
+ if (pExtra)
+ pExtra->uTSCValue = ASMReadTSC();
+ return RTTimeSystemNanoTS();
+# else
+ RTAssertReleasePanic();
+ return 0;
+# endif
+}
+
+
+/**
+ * Fallback function.
+ */
+static DECLCALLBACK(uint64_t) rtTimeNanoTSInternalFallback(PRTTIMENANOTSDATA pData, PRTITMENANOTSEXTRA pExtra)
+{
+ PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
+ if ( pGip
+ && pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC
+ && ( pGip->u32Mode == SUPGIPMODE_INVARIANT_TSC
+ || pGip->u32Mode == SUPGIPMODE_SYNC_TSC
+ || pGip->u32Mode == SUPGIPMODE_ASYNC_TSC))
+ return rtTimeNanoTSInternalRediscover(pData, pExtra);
+ NOREF(pData);
+# ifndef IN_RC
+ if (pExtra)
+ pExtra->uTSCValue = ASMReadTSC();
+ return RTTimeSystemNanoTS();
+# else
+ RTAssertReleasePanic();
+ return 0;
+# endif
+}
+
+
+/**
+ * Called the first time somebody asks for the time or when the GIP
+ * is mapped/unmapped.
+ */
+static DECLCALLBACK(uint64_t) rtTimeNanoTSInternalRediscover(PRTTIMENANOTSDATA pData, PRTITMENANOTSEXTRA pExtra)
+{
+ PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
+# ifdef IN_RC
+ uint32_t iWorker;
+# else
+ PFNTIMENANOTSINTERNAL pfnWorker;
+# endif
+ if ( pGip
+ && pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC
+ && ( pGip->u32Mode == SUPGIPMODE_INVARIANT_TSC
+ || pGip->u32Mode == SUPGIPMODE_SYNC_TSC
+ || pGip->u32Mode == SUPGIPMODE_ASYNC_TSC))
+ {
+ if (ASMCpuId_EDX(1) & X86_CPUID_FEATURE_EDX_SSE2)
+ {
+# ifdef IN_RC
+ iWorker = pGip->u32Mode == SUPGIPMODE_ASYNC_TSC
+ ? RTTIMENANO_WORKER_LFENCE_ASYNC
+ : pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ROUGHLY_ZERO
+ ? RTTIMENANO_WORKER_LFENCE_SYNC_INVAR_NO_DELTA
+ : RTTIMENANO_WORKER_LFENCE_SYNC_INVAR_WITH_DELTA;
+# elif defined(IN_RING0)
+ pfnWorker = pGip->u32Mode == SUPGIPMODE_ASYNC_TSC
+ ? RTTimeNanoTSLFenceAsync
+ : pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ROUGHLY_ZERO
+ ? RTTimeNanoTSLFenceSyncInvarNoDelta
+ : RTTimeNanoTSLFenceSyncInvarWithDelta;
+# else
+ if (pGip->u32Mode == SUPGIPMODE_ASYNC_TSC)
+ {
+ if ( pGip->fGetGipCpu & SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS)
+ pfnWorker = RTTimeNanoTSLFenceAsyncUseIdtrLim;
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS)
+ pfnWorker = RTTimeNanoTSLFenceAsyncUseRdtscp;
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_GROUP_IN_CH_NUMBER_IN_CL)
+ pfnWorker = RTTimeNanoTSLFenceAsyncUseRdtscpGroupChNumCl;
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_0B)
+ pfnWorker = RTTimeNanoTSLFenceAsyncUseApicIdExt0B;
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_8000001E)
+ pfnWorker = RTTimeNanoTSLFenceAsyncUseApicIdExt8000001E;
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID)
+ pfnWorker = RTTimeNanoTSLFenceAsyncUseApicId;
+ else
+ pfnWorker = rtTimeNanoTSInternalFallback;
+ }
+ else
+ {
+ if (pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_PRACTICALLY_ZERO)
+ pfnWorker = RTTimeNanoTSLFenceSyncInvarNoDelta;
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS)
+ pfnWorker = RTTimeNanoTSLFenceSyncInvarWithDeltaUseIdtrLim;
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS)
+ pfnWorker = RTTimeNanoTSLFenceSyncInvarWithDeltaUseRdtscp;
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_0B)
+ pfnWorker = RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicIdExt0B;
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_8000001E)
+ pfnWorker = RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicIdExt8000001E;
+ else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID)
+ pfnWorker = RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicId;
+ else
+ pfnWorker = rtTimeNanoTSInternalFallback;
+ }
+# endif
+ }
+ else
+ {
+# ifdef IN_RC
+ iWorker = pGip->u32Mode == SUPGIPMODE_ASYNC_TSC
+ ? RTTIMENANO_WORKER_LEGACY_ASYNC
+ : pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ROUGHLY_ZERO
+ ? RTTIMENANO_WORKER_LEGACY_SYNC_INVAR_NO_DELTA : RTTIMENANO_WORKER_LEGACY_SYNC_INVAR_WITH_DELTA;
+# elif defined(IN_RING0)
+ pfnWorker = pGip->u32Mode == SUPGIPMODE_ASYNC_TSC
+ ? RTTimeNanoTSLegacyAsync
+ : pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ROUGHLY_ZERO
+ ? RTTimeNanoTSLegacySyncInvarNoDelta
+ : RTTimeNanoTSLegacySyncInvarWithDelta;
+# else
+ if (pGip->u32Mode == SUPGIPMODE_ASYNC_TSC)
+ pfnWorker = pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS
+ ? RTTimeNanoTSLegacyAsyncUseRdtscp
+ : pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_GROUP_IN_CH_NUMBER_IN_CL
+ ? RTTimeNanoTSLegacyAsyncUseRdtscpGroupChNumCl
+ : pGip->fGetGipCpu & SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS
+ ? RTTimeNanoTSLegacyAsyncUseIdtrLim
+ : pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_0B
+ ? RTTimeNanoTSLegacyAsyncUseApicIdExt0B
+ : pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_8000001E
+ ? RTTimeNanoTSLegacyAsyncUseApicIdExt8000001E
+ : pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID
+ ? RTTimeNanoTSLegacyAsyncUseApicId
+ : rtTimeNanoTSInternalFallback;
+ else
+ pfnWorker = pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS
+ ? pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_PRACTICALLY_ZERO
+ ? RTTimeNanoTSLegacySyncInvarNoDelta
+ : RTTimeNanoTSLegacySyncInvarWithDeltaUseRdtscp
+ : pGip->fGetGipCpu & SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS
+ ? pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_PRACTICALLY_ZERO
+ ? RTTimeNanoTSLegacySyncInvarNoDelta
+ : RTTimeNanoTSLegacySyncInvarWithDeltaUseIdtrLim
+ : pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_0B
+ ? pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ROUGHLY_ZERO
+ ? RTTimeNanoTSLegacySyncInvarNoDelta
+ : RTTimeNanoTSLegacySyncInvarWithDeltaUseApicIdExt0B
+ : pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_8000001E
+ ? pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ROUGHLY_ZERO
+ ? RTTimeNanoTSLegacySyncInvarNoDelta
+ : RTTimeNanoTSLegacySyncInvarWithDeltaUseApicIdExt8000001E
+ : pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID
+ ? pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ROUGHLY_ZERO
+ ? RTTimeNanoTSLegacySyncInvarNoDelta
+ : RTTimeNanoTSLegacySyncInvarWithDeltaUseApicId
+ : rtTimeNanoTSInternalFallback;
+# endif
+ }
+ }
+ else
+# ifdef IN_RC
+ iWorker = RTTIMENANO_WORKER_FALLBACK;
+# else
+ pfnWorker = rtTimeNanoTSInternalFallback;
+# endif
+
+# ifdef IN_RC
+ ASMAtomicWriteU32((uint32_t volatile *)&g_iWorker, iWorker);
+ return g_apfnWorkers[iWorker](pData, pExtra);
+# else
+ ASMAtomicWritePtr((void * volatile *)&g_pfnWorker, (void *)(uintptr_t)pfnWorker);
+ return pfnWorker(pData, pExtra);
+# endif
+}
+
+# if defined(IN_RING3) || defined(IN_RING0)
+RTDECL(const char *) RTTimeNanoTSWorkerName(void)
+{
+ static const struct { PFNTIMENANOTSINTERNAL pfnWorker; const char *pszName; } s_aWorkersAndNames[] =
+ {
+# define ENTRY(a_fn) { a_fn, #a_fn }
+ ENTRY(RTTimeNanoTSLegacySyncInvarNoDelta),
+ ENTRY(RTTimeNanoTSLFenceSyncInvarNoDelta),
+# ifdef IN_RING3
+ ENTRY(RTTimeNanoTSLegacyAsyncUseApicId),
+ ENTRY(RTTimeNanoTSLegacyAsyncUseApicIdExt0B),
+ ENTRY(RTTimeNanoTSLegacyAsyncUseApicIdExt8000001E),
+ ENTRY(RTTimeNanoTSLegacyAsyncUseRdtscp),
+ ENTRY(RTTimeNanoTSLegacyAsyncUseRdtscpGroupChNumCl),
+ ENTRY(RTTimeNanoTSLegacyAsyncUseIdtrLim),
+ ENTRY(RTTimeNanoTSLegacySyncInvarWithDeltaUseApicId),
+ ENTRY(RTTimeNanoTSLegacySyncInvarWithDeltaUseApicIdExt0B),
+ ENTRY(RTTimeNanoTSLegacySyncInvarWithDeltaUseApicIdExt8000001E),
+ ENTRY(RTTimeNanoTSLegacySyncInvarWithDeltaUseRdtscp),
+ ENTRY(RTTimeNanoTSLegacySyncInvarWithDeltaUseIdtrLim),
+ ENTRY(RTTimeNanoTSLFenceAsyncUseApicId),
+ ENTRY(RTTimeNanoTSLFenceAsyncUseApicIdExt0B),
+ ENTRY(RTTimeNanoTSLFenceAsyncUseApicIdExt8000001E),
+ ENTRY(RTTimeNanoTSLFenceAsyncUseRdtscp),
+ ENTRY(RTTimeNanoTSLFenceAsyncUseRdtscpGroupChNumCl),
+ ENTRY(RTTimeNanoTSLFenceAsyncUseIdtrLim),
+ ENTRY(RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicId),
+ ENTRY(RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicIdExt0B),
+ ENTRY(RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicIdExt8000001E),
+ ENTRY(RTTimeNanoTSLFenceSyncInvarWithDeltaUseRdtscp),
+ ENTRY(RTTimeNanoTSLFenceSyncInvarWithDeltaUseIdtrLim),
+# else
+ ENTRY(RTTimeNanoTSLegacyAsync),
+ ENTRY(RTTimeNanoTSLegacySyncInvarWithDelta),
+ ENTRY(RTTimeNanoTSLFenceAsync),
+ ENTRY(RTTimeNanoTSLFenceSyncInvarWithDelta),
+# endif
+ ENTRY(rtTimeNanoTSInternalFallback),
+# undef ENTRY
+ };
+ PFNTIMENANOTSINTERNAL pfnWorker = g_pfnWorker;
+ if (pfnWorker == rtTimeNanoTSInternalRediscover)
+ {
+ RTTimeNanoTS();
+ pfnWorker = g_pfnWorker;
+ }
+
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aWorkersAndNames); i++)
+ if (s_aWorkersAndNames[i].pfnWorker == pfnWorker)
+ return s_aWorkersAndNames[i].pszName;
+ AssertFailed();
+ return NULL;
+}
+# endif /* IN_RING3 || IN_RING0 */
+
+#endif /* !IN_GUEST && !RT_NO_GIP */
+
+
+/**
+ * Internal worker for getting the current nanosecond timestamp.
+ */
+DECLINLINE(uint64_t) rtTimeNanoTSInternal(void)
+{
+#if !defined(IN_GUEST) && !defined(RT_NO_GIP)
+# ifdef IN_RC
+ return g_apfnWorkers[g_iWorker](&g_TimeNanoTSData, NULL /*pExtra*/);
+# else
+ return g_pfnWorker(&g_TimeNanoTSData, NULL /*pExtra*/);
+# endif
+#else
+ return RTTimeSystemNanoTS();
+#endif
+}
+
+
+/**
+ * Gets the current nanosecond timestamp.
+ *
+ * @returns nanosecond timestamp.
+ */
+RTDECL(uint64_t) RTTimeNanoTS(void)
+{
+ return rtTimeNanoTSInternal();
+}
+RT_EXPORT_SYMBOL(RTTimeNanoTS);
+
+
+/**
+ * Gets the current millisecond timestamp.
+ *
+ * @returns millisecond timestamp.
+ */
+RTDECL(uint64_t) RTTimeMilliTS(void)
+{
+ return rtTimeNanoTSInternal() / 1000000;
+}
+RT_EXPORT_SYMBOL(RTTimeMilliTS);
+
+
+#if !defined(IN_GUEST) && !defined(RT_NO_GIP)
+/**
+ * Debugging the time api.
+ *
+ * @returns the number of 1ns steps which has been applied by RTTimeNanoTS().
+ */
+RTDECL(uint32_t) RTTimeDbgSteps(void)
+{
+ return g_TimeNanoTSData.c1nsSteps;
+}
+RT_EXPORT_SYMBOL(RTTimeDbgSteps);
+
+
+/**
+ * Debugging the time api.
+ *
+ * @returns the number of times the TSC interval expired RTTimeNanoTS().
+ */
+RTDECL(uint32_t) RTTimeDbgExpired(void)
+{
+ return g_TimeNanoTSData.cExpired;
+}
+RT_EXPORT_SYMBOL(RTTimeDbgExpired);
+
+
+/**
+ * Debugging the time api.
+ *
+ * @returns the number of bad previous values encountered by RTTimeNanoTS().
+ */
+RTDECL(uint32_t) RTTimeDbgBad(void)
+{
+ return g_TimeNanoTSData.cBadPrev;
+}
+RT_EXPORT_SYMBOL(RTTimeDbgBad);
+
+
+/**
+ * Debugging the time api.
+ *
+ * @returns the number of update races in RTTimeNanoTS().
+ */
+RTDECL(uint32_t) RTTimeDbgRaces(void)
+{
+ return g_TimeNanoTSData.cUpdateRaces;
+}
+RT_EXPORT_SYMBOL(RTTimeDbgRaces);
+#endif /* !IN_GUEST && !RT_NO_GIP */
+
diff --git a/src/VBox/Runtime/common/time/timesupA.asm b/src/VBox/Runtime/common/time/timesupA.asm
new file mode 100644
index 00000000..649d3b6f
--- /dev/null
+++ b/src/VBox/Runtime/common/time/timesupA.asm
@@ -0,0 +1,161 @@
+; $Id: timesupA.asm $
+;; @file
+; IPRT - Time using SUPLib, the Assembly Implementation.
+;
+
+;
+; Copyright (C) 2006-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%ifndef IN_GUEST
+
+%include "iprt/asmdefs.mac"
+%include "VBox/sup.mac"
+
+;
+; Use the C reference implementation for now.
+;
+%error "This is out of date, use C code. Not worth it for a couple of ticks in some functions and equal or worse performance in others."
+This is out of date
+This is out of date
+This is out of date
+
+
+;; Keep this in sync with iprt/time.h.
+struc RTTIMENANOTSDATA
+ .pu64Prev RTCCPTR_RES 1
+ .pfnBad RTCCPTR_RES 1
+ .pfnRediscover RTCCPTR_RES 1
+ .pvDummy RTCCPTR_RES 1
+ .c1nsSteps resd 1
+ .cExpired resd 1
+ .cBadPrev resd 1
+ .cUpdateRaces resd 1
+endstruc
+
+
+BEGINDATA
+%undef IN_SUPLIB
+%undef IMPORTED_SUPLIB
+%ifdef IN_SUP_R0
+ %define IN_SUPLIB
+%endif
+%ifdef IN_SUP_R3
+ %define IN_SUPLIB
+%endif
+%ifdef IN_SUP_RC
+ %define IN_SUPLIB
+%endif
+%ifdef IN_SUPLIB
+ extern NAME(g_pSUPGlobalInfoPage)
+%elifdef IN_RING0
+ %ifdef RT_OS_WINDOWS
+ %define IMPORTED_SUPLIB
+ extern IMPNAME(g_SUPGlobalInfoPage)
+ %else
+ extern NAME(g_SUPGlobalInfoPage)
+ %endif
+%else
+ %ifdef RT_OS_WINDOWS
+ %define IMPORTED_SUPLIB
+ extern IMPNAME(g_pSUPGlobalInfoPage)
+ %else
+ extern NAME(g_pSUPGlobalInfoPage)
+ %endif
+%endif
+
+BEGINCODE
+
+;
+; The default stuff that works everywhere.
+; Uses cpuid for serializing.
+;
+%undef ASYNC_GIP
+%undef USE_LFENCE
+%undef WITH_TSC_DELTA
+%undef NEED_APIC_ID
+%define NEED_TRANSACTION_ID
+%define rtTimeNanoTSInternalAsm RTTimeNanoTSLegacySyncNoDelta
+%include "timesupA.mac"
+
+%define rtTimeNanoTSInternalAsm RTTimeNanoTSLegacyInvariantNoDelta
+%include "timesupA.mac"
+
+%define WITH_TSC_DELTA
+%define NEED_APIC_ID
+%define rtTimeNanoTSInternalAsm RTTimeNanoTSLegacySyncWithDelta
+%include "timesupA.mac"
+
+%define rtTimeNanoTSInternalAsm RTTimeNanoTSLegacyInvariantWithDelta
+%include "timesupA.mac"
+
+%define ASYNC_GIP
+%undef WITH_TSC_DELTA
+%define NEED_APIC_ID
+%ifdef IN_RC
+ %undef NEED_TRANSACTION_ID
+%endif
+%define rtTimeNanoTSInternalAsm RTTimeNanoTSLegacyAsync
+%include "timesupA.mac"
+
+
+;
+; Alternative implementation that employs lfence instead of cpuid.
+;
+%undef ASYNC_GIP
+%define USE_LFENCE
+%undef WITH_TSC_DELTA
+%undef NEED_APIC_ID
+%define NEED_TRANSACTION_ID
+%define rtTimeNanoTSInternalAsm RTTimeNanoTSLFenceSyncNoDelta
+%include "timesupA.mac"
+
+%define rtTimeNanoTSInternalAsm RTTimeNanoTSLFenceInvariantNoDelta
+%include "timesupA.mac"
+
+%define WITH_TSC_DELTA
+%define NEED_APIC_ID
+%define rtTimeNanoTSInternalAsm RTTimeNanoTSLFenceSyncWithDelta
+%include "timesupA.mac"
+
+%define rtTimeNanoTSInternalAsm RTTimeNanoTSLFenceInvariantWithDelta
+%include "timesupA.mac"
+
+%define ASYNC_GIP
+%undef WITH_TSC_DELTA
+%define NEED_APIC_ID
+%ifdef IN_RC
+ %undef NEED_TRANSACTION_ID
+%endif
+%define rtTimeNanoTSInternalAsm RTTimeNanoTSLFenceAsync
+%include "timesupA.mac"
+
+
+%endif ; !IN_GUEST
diff --git a/src/VBox/Runtime/common/time/timesupA.mac b/src/VBox/Runtime/common/time/timesupA.mac
new file mode 100644
index 00000000..0344e6ce
--- /dev/null
+++ b/src/VBox/Runtime/common/time/timesupA.mac
@@ -0,0 +1,895 @@
+; $Id: timesupA.mac $
+;; @file
+; IPRT - Time using SUPLib, the Assembly Code Template.
+;
+
+;
+; Copyright (C) 2006-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%ifdef RT_ARCH_X86
+;;
+; The x86 assembly implementation of the assembly routines.
+;
+; @returns Nanosecond timestamp.
+; @param pData Pointer to the nanosecond timestamp data.
+;
+BEGINPROC rtTimeNanoTSInternalAsm
+ ;
+ ; Variable definitions.
+ ;
+%define pData [ebp + 08h]
+%define u64RetNanoTS_Hi [ebp - 04h]
+%define u64RetNanoTS [ebp - 08h]
+%define u32UpdateIntervalNS [ebp - 0ch]
+%define u32UpdateIntervalTSC [ebp - 10h]
+%define u64TSC_Hi [ebp - 14h]
+%define u64TSC [ebp - 18h]
+%define u64CurNanoTS_Hi [ebp - 1ch]
+%define u64CurNanoTS [ebp - 20h]
+%define u64PrevNanoTS_Hi [ebp - 24h]
+%define u64PrevNanoTS [ebp - 28h]
+%define u32TransactionId [ebp - 2ch]
+%define u32ApicIdPlus [ebp - 30h]
+%define TmpVar [ebp - 34h]
+%define SavedEBX [ebp - 38h]
+%define SavedEDI [ebp - 3ch]
+%define SavedESI [ebp - 40h]
+
+ ;
+ ; Prolog.
+ ;
+ push ebp
+ mov ebp, esp
+ sub esp, 40h
+ mov SavedEBX, ebx
+ mov SavedEDI, edi
+ mov SavedESI, esi
+
+
+ ;;
+ ;; Read the GIP data and the previous value.
+ ;;
+.ReadGip:
+
+
+ ;
+ ; Load pGip.
+ ;
+%ifdef IMPORTED_SUPLIB
+ %ifdef IN_RING0
+ mov esi, IMP(g_SUPGlobalInfoPage)
+ %else
+ mov esi, IMP(g_pSUPGlobalInfoPage)
+ mov esi, [esi]
+ %endif
+%else
+ mov esi, [NAME(g_pSUPGlobalInfoPage)]
+%endif
+ or esi, esi
+ jz .Rediscover
+ cmp dword [esi + SUPGLOBALINFOPAGE.u32Magic], SUPGLOBALINFOPAGE_MAGIC
+ jne .Rediscover
+
+ ;
+ ; Calc pGipCPU, setting u32ApicIdPlus if necessary.
+ ;
+%ifdef NEED_APIC_ID
+ ; u8ApicId = ASMGetApicId();
+ mov eax, 1
+ cpuid ; expensive
+ %ifdef NEED_TRANSACTION_ID
+ mov u32ApicIdPlus, ebx
+ %endif
+ ; pGipCpu/pGipCpuDelta = &pGip->aCPU[pGip->aiCpuFromApicId[u8ApicId]];
+ shr ebx, 24
+ movzx ebx, word [esi + ebx * 2 + SUPGLOBALINFOPAGE.aiCpuFromApicId]
+ mov eax, SUPGIPCPU_size
+ mul ebx
+ lea edi, [esi + eax + SUPGLOBALINFOPAGE.aCPUs] ; edi == &pGip->aCPU[u8ApicId];
+%endif
+
+%ifdef NEED_TRANSACTION_ID
+ ;
+ ; Serialized loading of u32TransactionId.
+ ;
+ %ifdef ASYNC_GIP
+ mov ebx, [edi + SUPGIPCPU.u32TransactionId]
+ %else
+ mov ebx, [esi + SUPGLOBALINFOPAGE.aCPUs + SUPGIPCPU.u32TransactionId]
+ %endif
+ mov u32TransactionId, ebx
+ %ifdef USE_LFENCE
+ lfence
+ %else
+ lock xor dword TmpVar, 0
+ %endif
+%endif
+
+ ;
+ ; Load the data and TSC with delta applied.
+ ;
+ mov eax, [esi + SUPGLOBALINFOPAGE.u32UpdateIntervalNS]
+ mov u32UpdateIntervalNS, eax
+%ifdef ASYNC_GIP ; esi is now free.
+ mov edx, [edi + SUPGIPCPU.u32UpdateIntervalTSC]
+%else
+ mov edx, [esi + SUPGLOBALINFOPAGE.aCPUs + SUPGIPCPU.u32UpdateIntervalTSC]
+%endif
+ mov u32UpdateIntervalTSC, edx
+
+ rdtsc
+%ifdef WITH_TSC_DELTA
+ cmp dword [edi + SUPGIPCPU.i64TSCDelta], 0xffffffff
+ je .TscDeltaPossiblyInvalid
+.TscDeltaValid:
+ sub eax, dword [edi + SUPGIPCPU.i64TSCDelta]
+ sbb edx, dword [edi + SUPGIPCPU.i64TSCDelta + 4]
+.TscDeltaNotValid: ; edi is now free.
+%endif
+
+%ifdef ASYNC_GIP
+ mov ecx, [edi + SUPGIPCPU.u64NanoTS]
+ mov esi, [edi + SUPGIPCPU.u64NanoTS + 4]
+%else
+ mov ecx, [esi + SUPGLOBALINFOPAGE.aCPUs + SUPGIPCPU.u64NanoTS]
+ mov ebx, [esi + SUPGLOBALINFOPAGE.aCPUs + SUPGIPCPU.u64NanoTS + 4]
+%endif
+ mov u64CurNanoTS, ecx
+ mov u64CurNanoTS_Hi, ebx
+%ifdef ASYNC_GIP
+ mov ecx, [edi + SUPGIPCPU.u64TSC]
+ mov ebx, [edi + SUPGIPCPU.u64TSC + 4]
+%else
+ mov ecx, [esi + SUPGLOBALINFOPAGE.aCPUs + SUPGIPCPU.u64TSC]
+ mov ebx, [esi + SUPGLOBALINFOPAGE.aCPUs + SUPGIPCPU.u64TSC + 4]
+%endif
+ mov u64TSC, ecx
+ mov u64TSC_Hi, ebx
+
+ ; u64PrevNanoTS = ASMAtomicReadU64(pu64Prev);
+ ; This serializes load/save. And with the dependency on the
+ ; RDTSC result, we try to make sure it has completed as well.
+%ifdef ASYNC_GIP
+ mov esi, pData
+ mov esi, [esi + RTTIMENANOTSDATA.pu64Prev]
+%else
+ mov edi, pData
+ mov edi, [esi + RTTIMENANOTSDATA.pu64Prev]
+%endif
+ mov ebx, eax
+ mov ecx, edx
+%ifdef ASYNC_GIP
+ lock cmpxchg8b [esi]
+%else
+ lock cmpxchg8b [edi]
+%endif
+ mov u64PrevNanoTS, eax
+ mov u64PrevNanoTS_Hi, edx
+
+%undef SAVED_u64RetNanoTS
+%ifdef NEED_TRANSACTION_ID
+ ;
+ ; Check that the GIP and CPU didn't change.
+ ; We've already serialized all the loads and stores at this point.
+ ;
+ %ifdef NEED_APIC_ID
+ mov u64RetNanoTS, ebx
+ mov u64RetNanoTS_Hi, ecx
+ %define SAVED_u64RetNanoTS
+ mov eax, 1
+ cpuid
+ cmp u32ApicIdPlus, ebx
+ jne .ReadGip
+ %endif
+ %ifdef ASYNC_GIP
+ mov esi, [edi + SUPGIPCPU.u32TransactionId]
+ %else
+ mov esi, [esi + SUPGLOBALINFOPAGE.aCPUs + SUPGIPCPU.u32TransactionId]
+ %endif
+ cmp esi, u32TransactionId
+ jne .ReadGip
+ test esi, 1
+ jnz .ReadGip
+%endif ; NEED_TRANSACTION_ID
+%ifdef SAVED_u64RetNanoTS
+ mov ebx, u64RetNanoTS
+ mov ecx, u64RetNanoTS_Hi
+%endif
+
+ ;;
+ ;; Calc the timestamp.
+ ;;
+ ; u64RetNanoTS -= u64TSC;
+ sub ebx, u64TSC
+ sbb ecx, u64TSC_Hi
+
+ ; if (u64RetNanoTS > u32UpdateIntervalTSC) -> jump
+ or ecx, ecx
+ jnz .OverFlow
+ cmp ebx, u32UpdateIntervalTSC
+ ja .OverFlow
+ mov eax, ebx
+.ContinueCalcs: ; eax <= u32UpdateIntervalTSC
+ mul dword u32UpdateIntervalNS
+ div dword u32UpdateIntervalTSC
+ xor edx, edx
+
+ ; u64RetNanoTS += u64CurNanoTS;
+ add eax, u64CurNanoTS
+ adc edx, u64CurNanoTS_Hi
+
+ ;;
+ ;; Compare it with the previous one.
+ ;;
+ ; if (RT_LIKELY( u64RetNanoTS > u64PrevNanoTS
+ ; && u64RetNanoTS < u64PrevNanoTS + UINT64_C(86000000000000) /* 24h */))
+ ;; @todo optimize this compare (/me too tired).
+ mov ecx, u64PrevNanoTS_Hi
+ mov ebx, u64PrevNanoTS
+ cmp edx, ecx
+ ja .Compare2
+ jb .DeltaPrevTooBig
+ cmp eax, ebx
+ jbe .DeltaPrevTooBig
+
+.Compare2:
+ add ebx, 0x6F736000
+ adc ecx, 0x00004E37
+ cmp edx, ecx
+ jb .CompareDone
+ ja .DeltaPrevTooBig
+ cmp eax, ebx
+ jae .DeltaPrevTooBig
+.CompareDone:
+
+
+ ;;
+ ;; Update the previous value with the u64RetNanoTS value.
+ ;;
+.Update:
+ ; if (RT_LIKELY(ASMAtomicCmpXchgU64(&pData->u64Prev, u64RetNanoTS, u64PrevNanoTS)))
+ mov ebx, eax
+ mov ecx, edx
+ mov esi, pData
+ mov esi, [esi + RTTIMENANOTSDATA.pu64Prev]
+ mov eax, u64PrevNanoTS
+ mov edx, u64PrevNanoTS_Hi
+ lock cmpxchg8b [esi]
+ jnz .UpdateFailed
+
+.Updated:
+ mov eax, ebx
+ mov edx, ecx
+
+.Done:
+ mov esi, SavedESI
+ mov edi, SavedEDI
+ mov ebx, SavedEBX
+ leave
+ ret
+
+
+ ;;
+ ;; We've expired the interval, cap it. If we're here for the 2nd
+ ;; time without any GIP update in-between, the checks against
+ ;; pData->u64Prev below will force 1ns stepping.
+ ;;
+.OverFlow:
+ ; u64Delta = u32UpdateIntervalTSC;
+ mov esi, pData
+ inc dword [esi + RTTIMENANOTSDATA.cExpired]
+ mov eax, u32UpdateIntervalTSC
+ jmp .ContinueCalcs
+
+
+ ;;
+ ;; u64DeltaPrev >= 24h
+ ;;
+ ;; eax:edx = u64RetNanoTS (to be adjusted)
+ ;;
+.DeltaPrevTooBig:
+ ; uint64_t u64DeltaPrev = u64RetNanoTS - u64PrevNanoTS;
+ mov ebx, eax
+ sub ebx, u64PrevNanoTS
+ mov ecx, edx
+ sbb ecx, u64PrevNanoTS_Hi ; ebx:ecx = u64DeltaPrev
+
+ ; else if ( (int64_t)u64DeltaPrev <= 0
+ ; && (int64_t)u64DeltaPrev + u32UpdateIntervalNS * 2 >= 0)
+ ; {
+ ; /* Occasional - u64RetNanoTS is in the recent 'past' relative the previous call. */
+ ; pData->c1nsSteps++;
+ ; u64RetNanoTS = u64PrevNanoTS + 1;
+ ; }
+ mov esi, u32UpdateIntervalNS
+ cmp ecx, 0
+ jl .PrevNotZero2ndTest
+ jg .DeltaPrevNotInRecentPast
+ cmp ebx, 0
+ ja .DeltaPrevNotInRecentPast
+
+.PrevNotZero2ndTest:
+ add esi, esi ; ASSUMES: u32UpdateIntervalNS * 2 <= 32-bit.
+ xor edi, edi
+ add esi, ebx
+ adc edi, ecx
+ test edi, edi
+ js .DeltaPrevNotInRecentPast
+
+.DeltaPrevInRecentPast:
+ mov esi, pData
+ inc dword [esi + RTTIMENANOTSDATA.c1nsSteps]
+ mov eax, u64PrevNanoTS
+ mov edx, u64PrevNanoTS_Hi
+ add eax, 1
+ adc edx, 0
+ jmp .Update
+
+.DeltaPrevNotInRecentPast:
+ ; else if (!u64PrevNanoTS) /* We're resuming (see TMVirtualResume). */
+ ; /* do nothing */;
+ cmp dword u64PrevNanoTS, 0
+ jne .DeltaPrevNotZero
+ cmp dword u64PrevNanoTS_Hi, 0
+ jne .DeltaPrevNotZero
+ jmp .Update
+
+.DeltaPrevNotZero:
+ ; else
+ ; {
+ ; /* Something has gone bust, if negative offset it's real bad. */
+ ; rtTimeNanoTSInternalBitch(pVM,
+ ; }
+
+ ; call C function that does the bitching.
+ mov u64RetNanoTS, eax
+ mov u64RetNanoTS_Hi, edx
+
+ mov edi, u64PrevNanoTS_Hi
+ mov esi, u64PrevNanoTS
+ push edi
+ push esi ; 4 - u64PrevNanoTS
+ push ecx
+ push ebx ; 3 - u64DeltaPrev
+ push edx
+ push eax ; 2 - u64RetNanoTS
+ mov eax, pData
+ push eax ; 1 - pData
+ call dword [eax + RTTIMENANOTSDATA.pfnBad]
+ add esp, 4*7
+
+ mov eax, u64RetNanoTS
+ mov edx, u64RetNanoTS_Hi
+ jmp .Update
+
+
+ ;;
+ ;; Attempt updating the previous value, provided we're still ahead of it.
+ ;;
+ ;; There is no point in recalculating u64NanoTS because we got preempted or if
+ ;; we raced somebody while the GIP was updated, since these are events
+ ;; that might occur at any point in the return path as well.
+ ;;
+ ;; eax:edx = *pData->u64Prev
+ ;; ebx:ecx = u64RetNanoTS
+ ;;
+ ALIGNCODE(16)
+.UpdateFailed:
+ mov edi, pData
+ lock inc dword [edi + RTTIMENANOTSDATA.cUpdateRaces]
+ ; for (i = 0; i < 10; i++)
+ mov edi, 10
+.UpdateLoop:
+ ; if (u64PrevNanoTS >= u64NanoTS)
+ ; break;
+ cmp edx, ecx
+ jg .Updated
+ jne .UpdateLoopLess
+ cmp eax, ebx
+ jae .Updated
+.UpdateLoopLess:
+ ; retry
+ lock cmpxchg8b [esi]
+ jz .Updated
+ dec edi
+ jnz .UpdateLoop
+ jmp .Updated
+
+
+ ;;
+ ;; The GIP is seemingly invalid, redo the discovery.
+ ;;
+.Rediscover:
+ mov eax, pData
+ push eax
+ call [eax + RTTIMENANOTSDATA.pfnRediscover]
+ add esp, 4h
+ jmp .Done
+
+
+%ifdef WITH_TSC_DELTA
+ ;;
+ ;; Unlikely branch for when we think the TSC delta might be invalid.
+ ;;
+.TscDeltaPossiblyInvalid:
+ cmp dword [edi + SUPGIPCPU.i64TSCDelta + 4], 0x7fffffff
+ jne .TscDeltaValid
+ jmp .TscDeltaNotValid
+%endif
+
+ ;
+ ; Cleanup variables
+ ;
+%undef pData
+%undef u64Delta_Hi
+%undef u64Delta
+%undef u32UpdateIntervalNS
+%undef u32UpdateIntervalTSC
+%undef u64TSC_Hi
+%undef u64TSC
+%undef u64NanoTS_Hi
+%undef u64NanoTS
+%undef u64PrevNanoTS_Hi
+%undef u64PrevNanoTS
+%undef u32TransactionId
+%undef u8ApicId
+
+%else ; AMD64
+
+;;
+; The AMD64 assembly implementation of the assembly routines.
+;
+; @returns Nanosecond timestamp.
+; @param pData gcc:rdi msc:rcx Pointer to the nanosecond timestamp data.
+;
+BEGINPROC rtTimeNanoTSInternalAsm
+ ;
+ ; Define variables and stack frame.
+ ;
+%define SavedRBX [rbp - 08h]
+%define SavedR12 [rbp - 10h]
+%define SavedR13 [rbp - 18h]
+%define SavedRDI [rbp - 20h]
+%define SavedRSI [rbp - 28h]
+%define TmpVar [rbp - 30h]
+%define TmpVar2 [rbp - 38h]
+%ifdef NEED_TRANSACTION_ID
+ %ifdef NEED_APIC_ID
+ %define SavedR14 [rbp - 40h]
+ %define SavedR15 [rbp - 48h]
+ %endif
+%endif
+
+%define pData rdi
+
+%ifdef ASYNC_GIP
+ %define u64TSC rsi
+ %define pGip rsi
+ %ifdef NEED_APIC_ID
+ %define pGipCPU r8
+ %endif
+%else
+ %define u64TSC r8
+ %define pGip rsi
+ %ifdef NEED_APIC_ID
+ %define pGipCPU r8
+ %endif
+%endif
+%define u32TransactionId r9d
+%define u64CurNanoTS r10
+%define u64PrevNanoTS r11 ; not parameter register
+%define u32UpdateIntervalTSC r12d
+%define u32UpdateIntervalTSC_64 r12
+%define u32UpdateIntervalNS r13d
+%define u32UpdateIntervalNS_64 r13
+%undef u64SavedRetNanoTS
+%undef u32ApicIdPlus
+%ifdef NEED_TRANSACTION_ID
+ %ifdef NEED_APIC_ID
+ %define u64SavedRetNanoTS r14
+ %define u32ApicIdPlus r15d
+ %endif
+%endif
+
+ ;
+ ; The prolog.
+ ;
+ push rbp
+ mov rbp, rsp
+%ifdef ASM_CALL64_MSC
+ sub rsp, 50h+20h
+%else
+ sub rsp, 50h
+%endif
+ mov SavedRBX, rbx
+ mov SavedR12, r12
+ mov SavedR13, r13
+%ifdef ASM_CALL64_MSC
+ mov SavedRDI, rdi
+ mov SavedRSI, rsi
+ mov pData, rcx
+%else
+ ;mov pData, rdi - already in rdi.
+%endif
+%ifdef SavedR14
+ mov SavedR14, r14
+%endif
+%ifdef SavedR15
+ mov SavedR15, r15
+%endif
+
+
+ ;;
+ ;; Data fetch loop.
+ ;; We take great pain ensuring that data consistency here.
+ ;;
+.ReadGip:
+
+ ;
+ ; Load pGip - finding the GIP is fun...
+ ;
+%ifdef RT_OS_WINDOWS
+ %ifdef IMPORTED_SUPLIB
+ %ifdef IN_RING0
+ mov rax, qword IMP(g_SUPGlobalInfoPage)
+ mov pGip, rax
+ %else
+ mov pGip, [IMP(g_pSUPGlobalInfoPage) wrt rip]
+ mov pGip, [pGip]
+ %endif
+ %else
+ mov pGip, [NAME(g_pSUPGlobalInfoPage) wrt rip]
+ %endif
+%else
+ %ifdef IN_RING0
+ mov rax, qword NAME(g_SUPGlobalInfoPage)
+ mov pGip, rax
+ %else
+ mov pGip, [rel NAME(g_pSUPGlobalInfoPage) wrt ..gotpcrel]
+ mov pGip, [pGip]
+ %endif
+%endif
+ or pGip, pGip
+ jz .Rediscover
+ cmp dword [pGip + SUPGLOBALINFOPAGE.u32Magic], SUPGLOBALINFOPAGE_MAGIC
+ jne .Rediscover
+
+ ;
+ ; pGipCPU, setting u32ApicIdPlus if necessary.
+ ;
+%ifdef NEED_APIC_ID
+ ; u8ApicId = ASMGetApicId();
+ mov eax, 1
+ cpuid ; expensive
+ %ifdef NEED_TRANSACTION_ID
+ mov u32ApicIdPlus, ebx
+ %endif
+ ; pGipCPU = &pGip->aCPU[pGip->aiCpuFromApicId[u8ApicId]];
+ shr ebx, 24
+ movzx eax, word [pGip + rbx * 2 + SUPGLOBALINFOPAGE.aiCpuFromApicId]
+ imul eax, SUPGIPCPU_size
+ lea pGipCPU, [pGip + rax + SUPGLOBALINFOPAGE.aCPUs]
+%endif
+
+%ifdef NEED_TRANSACTION_ID
+ ;
+ ; Serialized loading of u32TransactionId.
+ ;
+ %ifdef ASYNC_GIP
+ mov u32TransactionId, [pGipCPU + SUPGIPCPU.u32TransactionId]
+ %else
+ mov u32TransactionId, [pGip + SUPGLOBALINFOPAGE.aCPUs + SUPGIPCPU.u32TransactionId]
+ %endif
+ %ifdef USE_LFENCE
+ lfence
+ %else
+ lock xor dword TmpVar, 0
+ %endif
+%endif
+
+ ;
+ ; Load the data and TSC.
+ ;
+ mov u32UpdateIntervalNS, [pGip + SUPGLOBALINFOPAGE.u32UpdateIntervalNS]
+%ifdef ASYNC_GIP
+ mov u32UpdateIntervalTSC, [pGipCPU + SUPGIPCPU.u32UpdateIntervalTSC]
+%else
+ mov u32UpdateIntervalTSC, [pGip + SUPGLOBALINFOPAGE.aCPUs + SUPGIPCPU.u32UpdateIntervalTSC]
+%endif
+
+ rdtsc
+ mov u64PrevNanoTS, [pData + RTTIMENANOTSDATA.pu64Prev]
+ mov u64PrevNanoTS, [u64PrevNanoTS]
+ shl rdx, 32
+ or rax, rdx ; rax is u64RetNanoTS.
+%ifdef WITH_TSC_DELTA
+ mov rdx, [pGipCPU + SUPGIPCPU.i64TSCDelta]
+ mov u64CurNanoTS, 0x7fffffffffffffff ; INT64_MAX - temporarily borrowing u64CurNanoTS
+ cmp rdx, u64CurNanoTS
+ je .TscDeltaNotValid
+ sub rax, rdx
+.TscDeltaNotValid:
+%endif
+%ifdef u64SavedRetNanoTS ; doing this here may save a tick or so?
+ mov u64SavedRetNanoTS, rax
+%endif
+
+%ifdef ASYNC_GIP
+ mov u64CurNanoTS, [pGipCPU + SUPGIPCPU.u64NanoTS]
+ mov u64TSC, [pGipCPU + SUPGIPCPU.u64TSC] ; transhes pGIP!
+%else
+ mov u64CurNanoTS, [pGip + SUPGLOBALINFOPAGE.aCPUs + SUPGIPCPU.u64NanoTS]
+ mov u64TSC, [pGip + SUPGLOBALINFOPAGE.aCPUs + SUPGIPCPU.u64TSC] ; trashes pGipCPU!
+%endif
+
+
+%ifdef NEED_TRANSACTION_ID
+ ;
+ ; Check that the GIP and CPU didn't change.
+ ;
+ ; It is crucial that the rdtsc instruction has completed before
+ ; we check the transaction id. The LOCK prefixed instruction with
+ ; dependency on the RDTSC result should do the trick, I think.
+ ; CPUID is serializing, so the async path is safe by default.
+ ;
+ %ifdef NEED_APIC_ID
+ mov eax, 1
+ cpuid
+ cmp u32ApicIdPlus, ebx
+ jne .ReadGip
+ %else
+ lock xor qword TmpVar, rax
+ %endif
+ %ifdef ASYNC_GIP
+ cmp u32TransactionId, [pGipCPU + SUPGIPCPU.u32TransactionId]
+ %else
+ cmp u32TransactionId, [pGip + SUPGLOBALINFOPAGE.aCPUs + SUPGIPCPU.u32TransactionId]
+ %endif
+ jne .ReadGip
+ test u32TransactionId, 1
+ jnz .ReadGip
+ %ifdef u64SavedRetNanoTS
+ mov rax, u64SavedRetNanoTS ; rax is u64RetNanoTS.
+ %endif
+%endif ; NEED_TRANSACTION_ID
+
+
+ ;;
+ ;; Calc the timestamp.
+ ;;
+ ; u64RetNanoTS -= u64TSC;
+ sub rax, u64TSC
+ xor edx, edx
+
+ ; if (u64RetNanoTS > u32UpdateIntervalTSC) -> jump
+ cmp rax, u32UpdateIntervalTSC_64
+ ja .OverFlow
+.ContinueCalcs: ; edx = 0; eax <= u32UpdateIntervalTSC
+ mul u32UpdateIntervalNS
+ div u32UpdateIntervalTSC
+
+ ; u64RetNanoTS += u64CurNanoTS;
+ add rax, u64CurNanoTS
+
+
+ ;;
+ ;; Compare it with the previous one.
+ ;;
+ ; if (RT_LIKELY( u64RetNanoTS > u64PrevNanoTS
+ ; && u64RetNanoTS < u64PrevNanoTS + UINT64_C(86000000000000) /* 24h */))
+ ; /* Frequent - less than 24h since last call. */;
+ cmp rax, u64PrevNanoTS
+ jbe .DeltaPrevTooBig
+ mov ecx, 5
+ shl rcx, 44 ; close enough
+ add rcx, u64PrevNanoTS
+ cmp rax, rcx
+ jae .DeltaPrevTooBig
+
+
+ ;;
+ ;; Update the previous value.
+ ;;
+.Update:
+ ; if (RT_LIKELY(ASMAtomicCmpXchgU64(&pData->u64Prev, u64RetNanoTS, u64PrevNanoTS)))
+ mov rbx, [pData + RTTIMENANOTSDATA.pu64Prev]
+ mov rcx, rax
+ mov rax, u64PrevNanoTS
+ lock cmpxchg [rbx], rcx
+ jnz .UpdateFailed
+
+.Updated:
+ mov rax, rcx
+
+.Done:
+ mov rbx, SavedRBX
+ mov r12, SavedR12
+ mov r13, SavedR13
+%ifdef SavedR14
+ mov r14, SavedR14
+%endif
+%ifdef SavedR15
+ mov r15, SavedR15
+%endif
+%ifdef ASM_CALL64_MSC
+ mov rdi, SavedRDI
+ mov rsi, SavedRSI
+%endif
+ leave
+ ret
+
+
+ ;;
+ ;; We've expired the interval, cap it. If we're here for the 2nd
+ ;; time without any GIP update in-between, the checks against
+ ;; pData->u64Prev below will force 1ns stepping.
+ ;;
+ALIGNCODE(16)
+.OverFlow:
+ ; u64RetNanoTS = u32UpdateIntervalTSC;
+ inc dword [pData + RTTIMENANOTSDATA.cExpired]
+ mov eax, u32UpdateIntervalTSC
+ jmp .ContinueCalcs
+
+
+ ;;
+ ;; u64DeltaPrev >= 24h
+ ;;
+ ;; rax = u64RetNanoTS (to be adjusted)
+ ;;
+ALIGNCODE(16)
+.DeltaPrevTooBig:
+ ; uint64_t u64DeltaPrev = u64RetNanoTS - u64PrevNanoTS;
+ mov rbx, rax
+ sub rbx, u64PrevNanoTS
+
+ ; else if ( (int64_t)u64DeltaPrev <= 0
+ ; && (int64_t)u64DeltaPrev + u32UpdateIntervalNS * 2 >= 0)
+ ; {
+ ; /* Occasional - u64NanoTS is in the recent 'past' relative the previous call. */
+ ; pData->c1nsSteps++;
+ ; u64RetNanoTS = u64PrevNanoTS + 1;
+ ; }
+ test rbx, rbx
+ jg .DeltaPrevNotInRecentPast
+
+ lea rdx, [u32UpdateIntervalNS_64 + u32UpdateIntervalNS_64]
+ add rdx, rbx
+ js .DeltaPrevNotInRecentPast
+
+ ; body
+ inc dword [pData + RTTIMENANOTSDATA.c1nsSteps]
+ lea rax, [u64PrevNanoTS + 1]
+ jmp .Update
+
+ ; else if (!u64PrevNanoTS) /* We're resuming (see TMVirtualResume) / first call. */
+ ; /* do nothing */;
+.DeltaPrevNotInRecentPast:
+ or u64PrevNanoTS, u64PrevNanoTS
+ jz .Update
+
+ ; else
+ ; {
+ ; /* Something has gone bust, if negative offset it's real bad. */
+ ; rtTimeNanoTSInternalBitch(pVM,
+ ; }
+
+ ; call C function that does the bitching.
+ mov TmpVar, rax
+ mov TmpVar2, pData
+
+%ifdef ASM_CALL64_MSC
+ mov rcx, pData ; param 1 - pData
+ mov rdx, rax ; param 2 - u64RetNanoTS
+ mov r8, rbx ; param 3 - u64DeltaPrev
+ mov r9, u64PrevNanoTS ; param 4 - u64PrevNanoTS
+%else
+ ;mov rdi, pData - already in rdi; param 1 - pData
+ mov rsi, rax ; param 2 - u64RetNanoTS
+ mov rdx, rbx ; param 3 - u64DeltaPrev
+ mov rcx, u64PrevNanoTS ; param 4 - u64PrevNanoTS
+%endif
+ call qword [pData + RTTIMENANOTSDATA.pfnBad]
+
+ mov rax, TmpVar
+ mov pData, TmpVar2
+ jmp .Update
+
+
+ ;;
+ ;; Attempt updating the previous value, provided we're still ahead of it.
+ ;;
+ ;; There is no point in recalculating u64NanoTS because we got preempted or if
+ ;; we raced somebody while the GIP was updated, since these are events
+ ;; that might occur at any point in the return path as well.
+ ;;
+ ;; rax = *pData->u64Prev;
+ ;; rcx = u64RetNanoTS
+ ;;
+ALIGNCODE(16)
+.UpdateFailed:
+ lock inc dword [pData + RTTIMENANOTSDATA.cUpdateRaces]
+ ; for (i = 0; i < 10; i++)
+ mov edx, 10
+.UpdateLoop:
+ ; if (u64PrevNanoTS >= u64RetNanoTS)
+ ; break;
+ cmp rax, rcx
+ jge .Updated
+.UpdateLoopLess:
+ ; retry
+ lock cmpxchg [rbx], rcx
+ jz .Updated
+ dec edx
+ jnz .UpdateLoop
+ jmp .Updated
+
+
+ ;;
+ ;; The GIP is seemingly invalid, redo the discovery.
+ ;;
+.Rediscover:
+%ifdef ASM_CALL64_MSC
+ mov rcx, pData
+%else
+ ; mov rdi, pData - already in rdi
+%endif
+ call [pData + RTTIMENANOTSDATA.pfnRediscover]
+ jmp .Done
+
+
+ ;
+ ; Cleanup variables
+ ;
+%undef SavedRBX
+%undef SavedR12
+%undef SavedR13
+%undef SavedR14
+%undef SavedR15
+%undef SavedRDI
+%undef SavedRSI
+%undef pData
+%undef TmpVar
+%undef u64TSC
+%undef pGip
+%undef pGipCPU
+%undef u32TransactionId
+%undef u64CurNanoTS
+%undef u64PrevNanoTS
+%undef u32UpdateIntervalTSC
+%undef u32UpdateIntervalTSC_64
+%undef u32UpdateIntervalNS
+%undef u64SavedRetNanoTS
+%undef u32ApicIdPlus
+
+%endif ; AMD64
+ENDPROC rtTimeNanoTSInternalAsm
+
diff --git a/src/VBox/Runtime/common/time/timesupref.cpp b/src/VBox/Runtime/common/time/timesupref.cpp
new file mode 100644
index 00000000..211bbb04
--- /dev/null
+++ b/src/VBox/Runtime/common/time/timesupref.cpp
@@ -0,0 +1,318 @@
+/* $Id: timesupref.cpp $ */
+/** @file
+ * IPRT - Time using SUPLib, the C Implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#if !defined(IN_GUEST) && !defined(RT_NO_GIP)
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/asm-math.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <VBox/sup.h>
+#ifdef IN_RC
+# include <VBox/vmm/vmm.h>
+# include <VBox/vmm/vm.h>
+#endif
+#include "internal/time.h"
+
+
+#define TMPL_MODE_SYNC_INVAR_NO_DELTA 1
+#define TMPL_MODE_SYNC_INVAR_WITH_DELTA 2
+#define TMPL_MODE_ASYNC 3
+
+
+/*
+ * Use the XCHG instruction for some kind of serialization.
+ */
+#define TMPL_READ_FENCE() ASMReadFence()
+
+#undef TMPL_MODE
+#define TMPL_MODE TMPL_MODE_SYNC_INVAR_NO_DELTA
+#undef TMPL_GET_CPU_METHOD
+#define TMPL_GET_CPU_METHOD 0
+#undef rtTimeNanoTSInternalRef
+#define rtTimeNanoTSInternalRef RTTimeNanoTSLegacySyncInvarNoDelta
+#include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLegacySyncInvarNoDelta);
+
+#ifdef IN_RING3
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_SYNC_INVAR_WITH_DELTA
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_APIC_ID
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLegacySyncInvarWithDeltaUseApicId
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLegacySyncInvarWithDeltaUseApicId);
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_SYNC_INVAR_WITH_DELTA
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_APIC_ID_EXT_0B
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLegacySyncInvarWithDeltaUseApicIdExt0B
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLegacySyncInvarWithDeltaUseApicIdExt0B);
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_SYNC_INVAR_WITH_DELTA
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_APIC_ID_EXT_8000001E
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLegacySyncInvarWithDeltaUseApicIdExt8000001E
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLegacySyncInvarWithDeltaUseApicIdExt8000001E);
+
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLegacySyncInvarWithDeltaUseRdtscp
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLegacySyncInvarWithDeltaUseRdtscp);
+
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLegacySyncInvarWithDeltaUseIdtrLim
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLegacySyncInvarWithDeltaUseIdtrLim);
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_ASYNC
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_APIC_ID
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLegacyAsyncUseApicId
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLegacyAsyncUseApicId);
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_ASYNC
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_APIC_ID_EXT_0B
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLegacyAsyncUseApicIdExt0B
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLegacyAsyncUseApicIdExt0B);
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_ASYNC
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_APIC_ID_EXT_8000001E
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLegacyAsyncUseApicIdExt8000001E
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLegacyAsyncUseApicIdExt8000001E);
+
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLegacyAsyncUseRdtscp
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLegacyAsyncUseRdtscp);
+
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLegacyAsyncUseIdtrLim
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLegacyAsyncUseIdtrLim);
+
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_RDTSCP_GROUP_IN_CH_NUMBER_IN_CL
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLegacyAsyncUseRdtscpGroupChNumCl
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLegacyAsyncUseRdtscpGroupChNumCl);
+
+#else /* IN_RC || IN_RING0: Disable interrupts and call getter function. */
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_SYNC_INVAR_WITH_DELTA
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD UINT32_MAX
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLegacySyncInvarWithDelta
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLegacySyncInvarWithDelta);
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_ASYNC
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLegacyAsync
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLegacyAsync);
+
+#endif
+
+
+/*
+ * Use LFENCE for load serialization.
+ */
+#undef TMPL_READ_FENCE
+#define TMPL_READ_FENCE() ASMReadFenceSSE2()
+
+#undef TMPL_MODE
+#define TMPL_MODE TMPL_MODE_SYNC_INVAR_NO_DELTA
+#undef TMPL_GET_CPU_METHOD
+#define TMPL_GET_CPU_METHOD 0
+#undef rtTimeNanoTSInternalRef
+#define rtTimeNanoTSInternalRef RTTimeNanoTSLFenceSyncInvarNoDelta
+#include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLFenceSyncInvarNoDelta);
+
+#ifdef IN_RING3
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_SYNC_INVAR_WITH_DELTA
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_APIC_ID
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicId
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicId);
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_SYNC_INVAR_WITH_DELTA
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_APIC_ID_EXT_0B
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicIdExt0B
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicIdExt0B);
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_SYNC_INVAR_WITH_DELTA
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_APIC_ID_EXT_8000001E
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicIdExt8000001E
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicIdExt8000001E);
+
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLFenceSyncInvarWithDeltaUseRdtscp
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLFenceSyncInvarWithDeltaUseRdtscp);
+
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLFenceSyncInvarWithDeltaUseIdtrLim
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLFenceSyncInvarWithDeltaUseIdtrLim);
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_ASYNC
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_APIC_ID
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLFenceAsyncUseApicId
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLFenceAsyncUseApicId);
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_ASYNC
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_APIC_ID_EXT_0B
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLFenceAsyncUseApicIdExt0B
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLFenceAsyncUseApicIdExt0B);
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_ASYNC
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_APIC_ID_EXT_8000001E
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLFenceAsyncUseApicIdExt8000001E
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLFenceAsyncUseApicIdExt8000001E);
+
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLFenceAsyncUseRdtscp
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLFenceAsyncUseRdtscp);
+
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLFenceAsyncUseIdtrLim
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLFenceAsyncUseIdtrLim);
+
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD SUPGIPGETCPU_RDTSCP_GROUP_IN_CH_NUMBER_IN_CL
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLFenceAsyncUseRdtscpGroupChNumCl
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLFenceAsyncUseRdtscpGroupChNumCl);
+
+#else /* IN_RC || IN_RING0: Disable interrupts and call getter function. */
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_SYNC_INVAR_WITH_DELTA
+# undef TMPL_GET_CPU_METHOD
+# define TMPL_GET_CPU_METHOD UINT32_MAX
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLFenceSyncInvarWithDelta
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLFenceSyncInvarWithDelta);
+
+# undef TMPL_MODE
+# define TMPL_MODE TMPL_MODE_ASYNC
+# undef rtTimeNanoTSInternalRef
+# define rtTimeNanoTSInternalRef RTTimeNanoTSLFenceAsync
+# include "timesupref.h"
+RT_EXPORT_SYMBOL(RTTimeNanoTSLFenceAsync);
+
+#endif
+
+
+#endif /* !IN_GUEST && !RT_NO_GIP */
+
diff --git a/src/VBox/Runtime/common/time/timesupref.h b/src/VBox/Runtime/common/time/timesupref.h
new file mode 100644
index 00000000..02bb820c
--- /dev/null
+++ b/src/VBox/Runtime/common/time/timesupref.h
@@ -0,0 +1,408 @@
+/* $Id: timesupref.h $ */
+/** @file
+ * IPRT - Time using SUPLib, the C Code Template.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/**
+ * The C reference implementation of the assembly routines.
+ *
+ * Calculate NanoTS using the information in the global information page (GIP)
+ * which the support library (SUPLib) exports.
+ *
+ * This function guarantees that the returned timestamp is later (in time) than
+ * any previous calls in the same thread.
+ *
+ * @remark The way the ever increasing time guarantee is currently implemented means
+ * that if you call this function at a frequency higher than 1GHz you're in for
+ * trouble. We currently assume that no idiot will do that for real life purposes.
+ *
+ * @returns Nanosecond timestamp.
+ * @param pData Pointer to the data structure.
+ * @param pExtra Where to return extra time info. Optional.
+ */
+RTDECL(uint64_t) rtTimeNanoTSInternalRef(PRTTIMENANOTSDATA pData, PRTITMENANOTSEXTRA pExtra)
+{
+#if TMPL_MODE == TMPL_MODE_SYNC_INVAR_WITH_DELTA && defined(IN_RING3)
+ PSUPGIPCPU pGipCpuAttemptedTscRecalibration = NULL;
+#endif
+ AssertCompile(RT_IS_POWER_OF_TWO(RTCPUSET_MAX_CPUS));
+
+ for (;;)
+ {
+#ifndef IN_RING3 /* This simplifies and improves everything. */
+ RTCCUINTREG const uFlags = ASMIntDisableFlags();
+#endif
+
+ /*
+ * Check that the GIP is sane and that the premises for this worker function
+ * hasn't changed (CPU onlined with bad delta or missing features).
+ */
+ PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
+ if ( RT_LIKELY(pGip)
+ && RT_LIKELY(pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC)
+#if TMPL_MODE == TMPL_MODE_SYNC_INVAR_WITH_DELTA
+ && RT_LIKELY(pGip->enmUseTscDelta >= SUPGIPUSETSCDELTA_PRACTICALLY_ZERO)
+#else
+ && RT_LIKELY(pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ROUGHLY_ZERO)
+#endif
+#if defined(IN_RING3) && TMPL_GET_CPU_METHOD != 0
+ && RT_LIKELY(pGip->fGetGipCpu & TMPL_GET_CPU_METHOD)
+#endif
+ )
+ {
+ /*
+ * Resolve pGipCpu if needed. If the instruction is serializing, we
+ * read the transaction id first if possible.
+ */
+#if TMPL_MODE == TMPL_MODE_ASYNC || TMPL_MODE == TMPL_MODE_SYNC_INVAR_WITH_DELTA
+# if defined(IN_RING0)
+ uint32_t const iCpuSet = RTMpCurSetIndex();
+ uint16_t const iGipCpu = iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)
+ ? pGip->aiCpuFromCpuSetIdx[iCpuSet] : UINT16_MAX;
+# elif defined(IN_RC)
+ uint32_t const iCpuSet = VMMGetCpu(&g_VM)->iHostCpuSet;
+ uint16_t const iGipCpu = iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)
+ ? pGip->aiCpuFromCpuSetIdx[iCpuSet] : UINT16_MAX;
+# elif TMPL_GET_CPU_METHOD == SUPGIPGETCPU_APIC_ID
+# if TMPL_MODE != TMPL_MODE_ASYNC
+ uint32_t const u32TransactionId = pGip->aCPUs[0].u32TransactionId;
+# endif
+ uint8_t const idApic = ASMGetApicId();
+ uint16_t const iGipCpu = pGip->aiCpuFromApicId[idApic];
+# elif TMPL_GET_CPU_METHOD == SUPGIPGETCPU_APIC_ID_EXT_0B
+# if TMPL_MODE != TMPL_MODE_ASYNC
+ uint32_t const u32TransactionId = pGip->aCPUs[0].u32TransactionId;
+# endif
+ uint32_t const idApic = ASMGetApicIdExt0B();
+ uint16_t const iGipCpu = pGip->aiCpuFromApicId[idApic];
+# elif TMPL_GET_CPU_METHOD == SUPGIPGETCPU_APIC_ID_EXT_8000001E
+# if TMPL_MODE != TMPL_MODE_ASYNC
+ uint32_t const u32TransactionId = pGip->aCPUs[0].u32TransactionId;
+# endif
+ uint32_t const idApic = ASMGetApicIdExt8000001E();
+ uint16_t const iGipCpu = pGip->aiCpuFromApicId[idApic];
+# elif TMPL_GET_CPU_METHOD == SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS \
+ || TMPL_GET_CPU_METHOD == SUPGIPGETCPU_RDTSCP_GROUP_IN_CH_NUMBER_IN_CL
+# if TMPL_MODE != TMPL_MODE_ASYNC
+ uint32_t const u32TransactionId = pGip->aCPUs[0].u32TransactionId;
+# endif
+ uint32_t uAux;
+ ASMReadTscWithAux(&uAux);
+# if TMPL_GET_CPU_METHOD == SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS
+ uint16_t const iCpuSet = uAux & (RTCPUSET_MAX_CPUS - 1);
+# else
+ uint16_t iCpuSet = 0;
+ uint16_t offGipCpuGroup = pGip->aoffCpuGroup[(uAux >> 8) & UINT8_MAX];
+ if (offGipCpuGroup < pGip->cPages * PAGE_SIZE)
+ {
+ PSUPGIPCPUGROUP pGipCpuGroup = (PSUPGIPCPUGROUP)((uintptr_t)pGip + offGipCpuGroup);
+ if ( (uAux & UINT8_MAX) < pGipCpuGroup->cMaxMembers
+ && pGipCpuGroup->aiCpuSetIdxs[uAux & UINT8_MAX] != -1)
+ iCpuSet = pGipCpuGroup->aiCpuSetIdxs[uAux & UINT8_MAX];
+ }
+# endif
+ uint16_t const iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
+# elif TMPL_GET_CPU_METHOD == SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS
+ uint16_t const cbLim = ASMGetIdtrLimit();
+ uint16_t const iCpuSet = (cbLim - 256 * (ARCH_BITS == 64 ? 16 : 8)) & (RTCPUSET_MAX_CPUS - 1);
+ uint16_t const iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
+# else
+# error "What?"
+# endif
+ if (RT_LIKELY(iGipCpu < pGip->cCpus))
+ {
+ PSUPGIPCPU pGipCpu = &pGip->aCPUs[iGipCpu];
+#else
+ {
+#endif
+ /*
+ * Get the transaction ID if necessary and we haven't already
+ * read it before a serializing instruction above. We can skip
+ * this for ASYNC_TSC mode in ring-0 and raw-mode context since
+ * we disable interrupts.
+ */
+#if TMPL_MODE == TMPL_MODE_ASYNC && defined(IN_RING3)
+ uint32_t const u32TransactionId = pGipCpu->u32TransactionId;
+ ASMCompilerBarrier();
+ TMPL_READ_FENCE();
+#elif TMPL_MODE != TMPL_MODE_ASYNC \
+ && TMPL_GET_CPU_METHOD != SUPGIPGETCPU_APIC_ID \
+ && TMPL_GET_CPU_METHOD != SUPGIPGETCPU_APIC_ID_EXT_0B \
+ && TMPL_GET_CPU_METHOD != SUPGIPGETCPU_APIC_ID_EXT_8000001E \
+ && TMPL_GET_CPU_METHOD != SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS \
+ && TMPL_GET_CPU_METHOD != SUPGIPGETCPU_RDTSCP_GROUP_IN_CH_NUMBER_IN_CL
+ uint32_t const u32TransactionId = pGip->aCPUs[0].u32TransactionId;
+ ASMCompilerBarrier();
+ TMPL_READ_FENCE();
+#endif
+
+ /*
+ * Gather all the data we need. The mess at the end is to make
+ * sure all loads are done before we recheck the transaction ID
+ * without triggering serializing twice.
+ */
+ uint32_t u32NanoTSFactor0 = pGip->u32UpdateIntervalNS;
+#if TMPL_MODE == TMPL_MODE_ASYNC
+ uint32_t u32UpdateIntervalTSC = pGipCpu->u32UpdateIntervalTSC;
+ uint64_t u64NanoTS = pGipCpu->u64NanoTS;
+ uint64_t u64TSC = pGipCpu->u64TSC;
+#else
+ uint32_t u32UpdateIntervalTSC = pGip->aCPUs[0].u32UpdateIntervalTSC;
+ uint64_t u64NanoTS = pGip->aCPUs[0].u64NanoTS;
+ uint64_t u64TSC = pGip->aCPUs[0].u64TSC;
+# if TMPL_MODE == TMPL_MODE_SYNC_INVAR_WITH_DELTA
+ int64_t i64TscDelta = pGipCpu->i64TSCDelta;
+# endif
+#endif
+ uint64_t u64PrevNanoTS = ASMAtomicUoReadU64(pData->pu64Prev);
+#if TMPL_GET_CPU_METHOD == SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS \
+ || TMPL_GET_CPU_METHOD == SUPGIPGETCPU_RDTSCP_GROUP_IN_CH_NUMBER_IN_CL
+ ASMCompilerBarrier();
+ uint32_t uAux2;
+ uint64_t u64Delta = ASMReadTscWithAux(&uAux2); /* serializing */
+#else
+ uint64_t u64Delta = ASMReadTSC();
+ ASMCompilerBarrier();
+# if TMPL_GET_CPU_METHOD != SUPGIPGETCPU_APIC_ID /* getting APIC will serialize */ \
+ && TMPL_GET_CPU_METHOD != SUPGIPGETCPU_APIC_ID_EXT_0B \
+ && TMPL_GET_CPU_METHOD != SUPGIPGETCPU_APIC_ID_EXT_8000001E \
+ && (defined(IN_RING3) || TMPL_MODE != TMPL_MODE_ASYNC)
+ TMPL_READ_FENCE(); /* Expensive (~30 ticks). Would like convincing argumentation that let us remove it. */
+# endif
+#endif
+
+ /*
+ * Check that we didn't change CPU.
+ */
+#if defined(IN_RING3) && ( TMPL_MODE == TMPL_MODE_ASYNC || TMPL_MODE == TMPL_MODE_SYNC_INVAR_WITH_DELTA )
+# if TMPL_GET_CPU_METHOD == SUPGIPGETCPU_APIC_ID
+ if (RT_LIKELY(ASMGetApicId() == idApic))
+# elif TMPL_GET_CPU_METHOD == SUPGIPGETCPU_APIC_ID_EXT_0B
+ if (RT_LIKELY(ASMGetApicIdExt0B() == idApic))
+# elif TMPL_GET_CPU_METHOD == SUPGIPGETCPU_APIC_ID_EXT_8000001E
+ if (RT_LIKELY(ASMGetApicIdExt8000001E() == idApic))
+# elif TMPL_GET_CPU_METHOD == SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS \
+ || TMPL_GET_CPU_METHOD == SUPGIPGETCPU_RDTSCP_GROUP_IN_CH_NUMBER_IN_CL
+ if (RT_LIKELY(uAux2 == uAux))
+# elif TMPL_GET_CPU_METHOD == SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS
+ if (RT_LIKELY(ASMGetIdtrLimit() == cbLim))
+# endif
+#endif
+ {
+ /*
+ * Check the transaction ID (see above for R0/RC + ASYNC).
+ */
+#if defined(IN_RING3) || TMPL_MODE != TMPL_MODE_ASYNC
+# if TMPL_MODE == TMPL_MODE_ASYNC
+ if (RT_LIKELY(pGipCpu->u32TransactionId == u32TransactionId && !(u32TransactionId & 1) ))
+# else
+ if (RT_LIKELY(pGip->aCPUs[0].u32TransactionId == u32TransactionId && !(u32TransactionId & 1) ))
+# endif
+#endif
+ {
+
+ /*
+ * Apply the TSC delta. If the delta is invalid and the
+ * execution allows it, try trigger delta recalibration.
+ */
+#if TMPL_MODE == TMPL_MODE_SYNC_INVAR_WITH_DELTA && defined(IN_RING3)
+ if (RT_LIKELY( i64TscDelta != INT64_MAX
+ || pGipCpu == pGipCpuAttemptedTscRecalibration))
+#endif
+ {
+#if TMPL_MODE == TMPL_MODE_SYNC_INVAR_WITH_DELTA
+# ifndef IN_RING3
+ if (RT_LIKELY(i64TscDelta != INT64_MAX))
+# endif
+ u64Delta -= i64TscDelta;
+#endif
+
+ /*
+ * Bingo! We've got a consistent set of data.
+ */
+#ifndef IN_RING3
+ ASMSetFlags(uFlags);
+#endif
+
+ if (pExtra)
+ pExtra->uTSCValue = u64Delta;
+
+ /*
+ * Calc NanoTS delta.
+ */
+ u64Delta -= u64TSC;
+ if (RT_LIKELY(u64Delta <= u32UpdateIntervalTSC))
+ { /* MSVC branch hint, probably pointless. */ }
+ else
+ {
+ /*
+ * We've expired the interval, cap it. If we're here for the 2nd
+ * time without any GIP update in-between, the checks against
+ * *pu64Prev below will force 1ns stepping.
+ */
+ ASMAtomicIncU32(&pData->cExpired);
+ u64Delta = u32UpdateIntervalTSC;
+ }
+#if !defined(_MSC_VER) || !defined(RT_ARCH_X86) /* GCC makes very pretty code from these two inline calls, while MSC cannot. */
+ u64Delta = ASMMult2xU32RetU64((uint32_t)u64Delta, u32NanoTSFactor0);
+ u64Delta = ASMDivU64ByU32RetU32(u64Delta, u32UpdateIntervalTSC);
+#else
+ __asm
+ {
+ mov eax, dword ptr [u64Delta]
+ mul dword ptr [u32NanoTSFactor0]
+ div dword ptr [u32UpdateIntervalTSC]
+ mov dword ptr [u64Delta], eax
+ xor edx, edx
+ mov dword ptr [u64Delta + 4], edx
+ }
+#endif
+
+ /*
+ * Calculate the time and compare it with the previously returned value.
+ */
+ u64NanoTS += u64Delta;
+ uint64_t u64DeltaPrev = u64NanoTS - u64PrevNanoTS;
+ if (RT_LIKELY( u64DeltaPrev > 0
+ && u64DeltaPrev < UINT64_C(86000000000000) /* 24h */))
+ { /* Frequent - less than 24h since last call. */ }
+ else if (RT_LIKELY( (int64_t)u64DeltaPrev <= 0
+ && (int64_t)u64DeltaPrev + u32NanoTSFactor0 * 2 >= 0))
+ {
+ /* Occasional - u64NanoTS is in the recent 'past' relative the previous call. */
+ ASMAtomicIncU32(&pData->c1nsSteps);
+ u64NanoTS = u64PrevNanoTS + 1;
+ }
+ else if (!u64PrevNanoTS)
+ /* We're resuming (see TMVirtualResume). */;
+ else
+ {
+ /* Something has gone bust, if negative offset it's real bad. */
+ ASMAtomicIncU32(&pData->cBadPrev);
+ pData->pfnBad(pData, u64NanoTS, u64DeltaPrev, u64PrevNanoTS);
+ }
+
+ /*
+ * Attempt updating the previous value, provided we're still ahead of it.
+ *
+ * There is no point in recalculating u64NanoTS because we got preempted or if
+ * we raced somebody while the GIP was updated, since these are events
+ * that might occur at any point in the return path as well.
+ */
+ if (RT_LIKELY(ASMAtomicCmpXchgU64(pData->pu64Prev, u64NanoTS, u64PrevNanoTS)))
+ return u64NanoTS;
+
+ ASMAtomicIncU32(&pData->cUpdateRaces);
+ for (int cTries = 25; cTries > 0; cTries--)
+ {
+ u64PrevNanoTS = ASMAtomicReadU64(pData->pu64Prev);
+ if (u64PrevNanoTS >= u64NanoTS)
+ break;
+ if (ASMAtomicCmpXchgU64(pData->pu64Prev, u64NanoTS, u64PrevNanoTS))
+ break;
+ ASMNopPause();
+ }
+ return u64NanoTS;
+ }
+
+#if TMPL_MODE == TMPL_MODE_SYNC_INVAR_WITH_DELTA && defined(IN_RING3)
+ /*
+ * Call into the support driver to try make it recalculate the delta. We
+ * remember which GIP CPU structure we're probably working on so we won't
+ * end up in a loop if the driver for some reason cannot get the job done.
+ */
+ else /* else is unecessary, but helps checking the preprocessor spaghetti. */
+ {
+ pGipCpuAttemptedTscRecalibration = pGipCpu;
+ uint64_t u64TscTmp;
+ uint16_t idApicUpdate;
+ int rc = SUPR3ReadTsc(&u64TscTmp, &idApicUpdate);
+ if (RT_SUCCESS(rc) && idApicUpdate < RT_ELEMENTS(pGip->aiCpuFromApicId))
+ {
+ uint32_t iUpdateGipCpu = pGip->aiCpuFromApicId[idApicUpdate];
+ if (iUpdateGipCpu < pGip->cCpus)
+ pGipCpuAttemptedTscRecalibration = &pGip->aCPUs[iUpdateGipCpu];
+ }
+ }
+#endif
+ }
+ }
+
+ /*
+ * No joy must try again.
+ */
+#ifdef _MSC_VER
+# pragma warning(disable: 4702)
+#endif
+#ifndef IN_RING3
+ ASMSetFlags(uFlags);
+#endif
+ ASMNopPause();
+ continue;
+ }
+
+#if TMPL_MODE == TMPL_MODE_ASYNC || TMPL_MODE == TMPL_MODE_SYNC_INVAR_WITH_DELTA
+ /*
+ * We've got a bad CPU or APIC index of some kind.
+ */
+ else /* else is unecessary, but helps checking the preprocessor spaghetti. */
+ {
+# ifndef IN_RING3
+ ASMSetFlags(uFlags);
+# endif
+# if defined(IN_RING0) \
+ || defined(IN_RC) \
+ || ( TMPL_GET_CPU_METHOD != SUPGIPGETCPU_APIC_ID \
+ && TMPL_GET_CPU_METHOD != SUPGIPGETCPU_APIC_ID_EXT_0B /*?*/ \
+ && TMPL_GET_CPU_METHOD != SUPGIPGETCPU_APIC_ID_EXT_8000001E /*?*/)
+ return pData->pfnBadCpuIndex(pData, pExtra, UINT16_MAX-1, iCpuSet, iGipCpu);
+# else
+ return pData->pfnBadCpuIndex(pData, pExtra, idApic, UINT16_MAX-1, iGipCpu);
+# endif
+ }
+#endif
+ }
+
+ /*
+ * Something changed in the GIP config or it was unmapped, figure out
+ * the right worker function to use now.
+ */
+#ifndef IN_RING3
+ ASMSetFlags(uFlags);
+#endif
+ return pData->pfnRediscover(pData, pExtra);
+ }
+}
+
diff --git a/src/VBox/Runtime/common/time/timesysalias.cpp b/src/VBox/Runtime/common/time/timesysalias.cpp
new file mode 100644
index 00000000..a355a4ed
--- /dev/null
+++ b/src/VBox/Runtime/common/time/timesysalias.cpp
@@ -0,0 +1,67 @@
+/* $Id: timesysalias.cpp $ */
+/** @file
+ * IPRT - Time using RTTimeSystem*.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+
+/**
+ * Gets the current nanosecond timestamp.
+ *
+ * @returns nanosecond timestamp.
+ */
+RTDECL(uint64_t) RTTimeNanoTS(void)
+{
+ return RTTimeSystemNanoTS();
+}
+RT_EXPORT_SYMBOL(RTTimeNanoTS);
+
+
+/**
+ * Gets the current millisecond timestamp.
+ *
+ * @returns millisecond timestamp.
+ */
+RTDECL(uint64_t) RTTimeMilliTS(void)
+{
+ return RTTimeSystemMilliTS();
+}
+RT_EXPORT_SYMBOL(RTTimeMilliTS);
+
diff --git a/src/VBox/Runtime/common/time/timezoneinfo-gen.py b/src/VBox/Runtime/common/time/timezoneinfo-gen.py
new file mode 100755
index 00000000..6f5670bc
--- /dev/null
+++ b/src/VBox/Runtime/common/time/timezoneinfo-gen.py
@@ -0,0 +1,470 @@
+# -*- coding: utf-8 -*-
+# $Id: timezoneinfo-gen.py $
+
+"""
+Generates timezone mapping info from public domain tz data and
+simple windows tables.
+"""
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2017-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+import os;
+import sys;
+import xml.etree.ElementTree as ElementTree;
+
+
+class TzWinZoneEntry(object):
+ def __init__(self):
+ self.sWinName = None;
+ self.sWinTerritory = None;
+ self.fWinGolden = False;
+ self.idxWin = 0;
+
+class TzLinkEntry(TzWinZoneEntry):
+ def __init__(self, sLinkNm, sTarget):
+ TzWinZoneEntry.__init__(self);
+ self.sLinkNm = sLinkNm;
+ self.sTarget = sTarget;
+
+class TzZoneOffset(object):
+ def __init__(self, asFields):
+ self.sOffset = asFields[0]; # GMT offset expression
+ self.sRules = asFields[1] if len(asFields) > 1 and asFields[1] not in [ '-', '' ] else None;
+ self.sFormat = asFields[2] if len(asFields) > 2 and asFields[2] not in [ '-', '' ] else None;
+ self.sUntil = asFields[3] if len(asFields) > 3 and asFields[3] not in [ '-', '' ] else None;
+
+class TzZoneEntry(TzWinZoneEntry):
+ def __init__(self, sName):
+ TzWinZoneEntry.__init__(self);
+ self.sName = sName;
+ self.sTerritory = 'ZZ';
+ self.aOffsets = []; # type: list(TzZoneOffset)
+
+class TzZoneRule(object):
+ def __init__(self, sName, sFrom, sTo, sType, sIn, sOn, sAt, sSave, sLetter):
+ self.sName = sName;
+ self.sFrom = sFrom if sFrom not in [ '-', '' ] else None;
+ self.sTo = sTo if sFrom not in [ '-', '' ] else None;
+ self.sType = sType if sType not in [ '-', '' ] else None;
+ self.sIn = sIn if sIn not in [ '-', '' ] else None;
+ self.sAt = sAt if sAt not in [ '-', '' ] else None;
+ self.sSave = sSave if sSave not in [ '-', '' ] else None;
+ self.sLetter = sLetter if sLetter not in [ '-', '' ] else None;
+
+def info(sMsg):
+ """
+ Outputs an informational message to stderr.
+ """
+ print('info: ' + sMsg, file=sys.stderr);
+
+def warning(sMsg):
+ """
+ Outputs a warning (to stderr).
+ """
+ print('warning: ' + sMsg, file=sys.stderr);
+
+def error(sMsg):
+ """
+ Outputs a warning (to stderr).
+ """
+ print('error: ' + sMsg, file=sys.stderr);
+
+def readTzDataFile(sFile):
+ """ Reads the given data file into memory, stripping comments. """
+ oInFile = open(sFile, 'r');
+ asLines = oInFile.readlines();
+ oInFile.close();
+ iLine = 0;
+ while iLine < len(asLines):
+ offHash = asLines[iLine].find('#');
+ if offHash >= 0:
+ asLines[iLine] = asLines[iLine][:offHash].rstrip();
+ else:
+ asLines[iLine] = asLines[iLine].rstrip();
+ iLine += 1;
+ return asLines;
+
+#
+# tzdata structures.
+#
+g_dZones = {};
+g_dRules = {};
+g_dLinks = {};
+
+def readTzData(sTzDataDir):
+ """
+ Reads in the bits we want from tz data. Assumes 2017b edition.
+ """
+
+ #
+ # Parse the tzdata files.
+ #
+ for sFile in [ 'africa', 'antarctica', 'asia', 'australasia', 'europe', 'northamerica', 'southamerica',
+ 'pacificnew', 'etcetera', 'backward', 'systemv', 'factory', #'backzone'
+ ]:
+ sIn = 'none';
+ asLines = readTzDataFile(os.path.join(sTzDataDir, sFile));
+ iLine = 0;
+ while iLine < len(asLines):
+ sLine = asLines[iLine];
+ sStrippedLine = sLine.strip(); # Fully stripped version.
+ if sStrippedLine:
+ asFields = sLine.split();
+ try:
+ if sLine.startswith('Zone'): # 'Rule' NAME FROM TO TYPE IN ON AT SAVE LETTER/S
+ sIn = 'Zone';
+ oZone = TzZoneEntry(asFields[1]);
+ if oZone.sName in g_dZones: raise Exception('duplicate: %s' % (oZone.sName,));
+ g_dZones[oZone.sName] = oZone;
+ oZone.aOffsets.append(TzZoneOffset(asFields[2:]));
+ elif sLine.startswith('Rule'): # 'Rule' NAME FROM TO TYPE IN ON AT SAVE LETTER/S
+ oRule = TzZoneRule(asFields[1], asFields[2], asFields[3], asFields[4], asFields[5],
+ asFields[6], asFields[7], asFields[8], asFields[9]);
+ if oRule.sName not in g_dRules:
+ g_dRules[oRule] = [oRule,];
+ else:
+ g_dRules[oRule].append(oRule);
+ elif sLine.startswith('Link'):
+ if len(asFields) != 3: raise Exception("malformed link: len(asFields) = %d" % (len(asFields)));
+ oLink = TzLinkEntry(asFields[2].strip(), asFields[1].strip());
+ if oLink.sLinkNm not in g_dLinks:
+ g_dLinks[oLink.sLinkNm] = oLink;
+ elif g_dLinks[oLink.sLinkNm].sTarget != oLink.sTarget:
+ warning('duplicate link for %s: new target %s, previous %s'
+ % (oLink.sLinkNm, oLink.sTarget, g_dLinks[oLink.sLinkNm].sTarget,));
+ elif sIn == 'Zone':
+ oZone.aOffsets.append(TzZoneEntry(asFields[3:]));
+ else:
+ raise Exception('what is this?')
+ except Exception as oXcpt:
+ error("line %u in %s: '%s'" % (iLine + 1, sFile, type(oXcpt) if not str(oXcpt) else str(oXcpt),));
+ info("'%s'" % (asLines[iLine],));
+ return 1;
+ iLine += 1;
+
+ #
+ # Process the country <-> zone mapping file.
+ #
+ asLines = readTzDataFile(os.path.join(sTzDataDir, 'zone.tab'));
+ iLine = 0;
+ while iLine < len(asLines):
+ sLine = asLines[iLine];
+ if sLine and sLine[0] != ' ':
+ asFields = sLine.split('\t');
+ try:
+ sTerritory = asFields[0];
+ if len(sTerritory) != 2: raise Exception('malformed country: %s' % (sTerritory,));
+ sZone = asFields[2];
+ oZone = g_dZones.get(sZone);
+ if oZone:
+ if oZone.sTerritory and oZone.sTerritory != 'ZZ':
+ raise Exception('zone %s already have country %s associated with it (setting %s)'
+ % (sZone, oZone.sTerritory, sTerritory));
+ oZone.sTerritory = sTerritory;
+ else:
+ oLink = g_dLinks.get(sZone);
+ if oLink:
+ pass; # ignore country<->link associations for now.
+ else: raise Exception('country zone not found: %s' % (sZone,));
+
+ except Exception as oXcpt:
+ error("line %u in %s: '%s'" % (iLine + 1, 'zone.tab', type(oXcpt) if not str(oXcpt) else str(oXcpt),));
+ info("'%s'" % (asLines[iLine],));
+ return 1;
+ iLine += 1;
+ return 0
+
+
+def readWindowsToTzMap(sMapXml):
+ """
+ Reads the 'common/supplemental/windowsZones.xml' file from http://cldr.unicode.org/.
+ """
+ oXmlDoc = ElementTree.parse(sMapXml);
+ oMap = oXmlDoc.getroot().find('windowsZones').find('mapTimezones');
+ # <mapZone other="Line Islands Standard Time" territory="001" type="Pacific/Kiritimati"/>
+ for oChild in oMap.findall('mapZone'):
+ sTerritory = oChild.attrib['territory'];
+ sWinZone = oChild.attrib['other'];
+ asUnixZones = oChild.attrib['type'].split();
+ for sZone in asUnixZones:
+ oZone = g_dZones.get(sZone);
+ if oZone:
+ if oZone.sWinName is None or (oZone.sWinTerritory == '001' and oZone.sWinName == sWinZone):
+ oZone.sWinName = sWinZone;
+ oZone.sWinTerritory = sTerritory;
+ if sTerritory == '001':
+ oZone.fWinGolden = True;
+ else:
+ warning('zone "%s" have more than one windows mapping: %s (%s) and now %s (%s)'
+ % (sZone, oZone.sWinName, oZone.sWinTerritory, sWinZone, sTerritory));
+ else:
+ oLink = g_dLinks.get(sZone);
+ if oLink:
+ if oLink.sWinName is None or (oLink.sWinTerritory == '001' and oLink.sWinName == sWinZone):
+ oLink.sWinName = sWinZone;
+ oLink.sWinTerritory = sTerritory;
+ if sTerritory == '001':
+ oLink.fWinGolden = True;
+ else:
+ warning('zone-link "%s" have more than one windows mapping: %s (%s) and now %s (%s)'
+ % (sZone, oLink.sWinName, oLink.sWinTerritory, sWinZone, sTerritory));
+ else:
+ warning('could not find zone "%s" (for mapping win zone "%s" to) - got the right data sets?'
+ % (sZone, sWinZone));
+ return 0;
+
+
+def readWindowsIndexes(sFile):
+ """
+ Reads the windows time zone index from the table in the given file and sets idxWin.
+
+ Assumes format: index{tab}name{tab}(GMT{offset}){space}{cities}
+
+ For instance: https://support.microsoft.com/en-gb/help/973627/microsoft-time-zone-index-values
+ """
+ # Read the file.
+ oInFile = open(sFile, "r");
+ asLines = oInFile.readlines();
+ oInFile.close();
+
+ # Check the header.
+ if not asLines[0].startswith('Index'):
+ error('expected first line of "%s" to start with "Index"' % (sFile,));
+ return 1;
+ fHexIndex = asLines[0].find('hex') > 0;
+ iLine = 1;
+ while iLine < len(asLines):
+ # Parse.
+ asFields = asLines[iLine].split('\t');
+ try:
+ idxWin = int(asFields[0].strip(), 16 if fHexIndex else 10);
+ sWinName = asFields[1].strip();
+ sLocations = ' '.join(asFields[2].split());
+ if sWinName.find('(GMT') >= 0: raise Exception("oops #1");
+ if not sLocations.startswith('(GMT'): raise Exception("oops #2");
+ sStdOffset = sLocations[sLocations.find('(') + 1 : sLocations.find(')')].strip().replace(' ','');
+ sLocations = sLocations[sLocations.find(')') + 1 : ].strip();
+ except Exception as oXcpt:
+ error("line %u in %s: '%s'" % (iLine + 1, sFile, type(oXcpt) if not str(oXcpt) else str(oXcpt),));
+ info("'%s'" % (asLines[iLine],));
+ return 1;
+
+ # Some name adjustments.
+ sWinName = sWinName.lower();
+ if sWinName.startswith('a.u.s.'):
+ sWinName = 'aus' + sWinName[6:];
+ elif sWinName.startswith('u.s. '):
+ sWinName = 'us ' + sWinName[5:];
+ elif sWinName.startswith('s.a. '):
+ sWinName = 'sa ' + sWinName[5:];
+ elif sWinName.startswith('s.e. '):
+ sWinName = 'se ' + sWinName[5:];
+ elif sWinName.startswith('pacific s.a. '):
+ sWinName = 'pacific sa ' + sWinName[13:];
+
+ # Update zone entries with matching windows names.
+ cUpdates = 0;
+ for sZone in g_dZones:
+ oZone = g_dZones[sZone];
+ if oZone.sWinName and oZone.sWinName.lower() == sWinName:
+ oZone.idxWin = idxWin;
+ cUpdates += 1;
+ #info('idxWin=%#x - %s / %s' % (idxWin, oZone.sName, oZone.sWinName,));
+ if cUpdates == 0:
+ warning('No matching zone found for index zone "%s" (%#x, %s)' % (sWinName, idxWin, sLocations));
+
+ # Advance.
+ iLine += 1;
+ return 0;
+
+def getPadding(sField, cchWidth):
+ """ Returns space padding for the given field string. """
+ if len(sField) < cchWidth:
+ return ' ' * (cchWidth - len(sField));
+ return '';
+
+def formatFields(sName, oZone, oWinZone):
+ """ Formats the table fields. """
+
+ # RTTIMEZONEINFO:
+ # const char *pszUnixName;
+ # const char *pszWindowsName;
+ # uint8_t cchUnixName;
+ # uint8_t cchWindowsName;
+ # char szCountry[3];
+ # char szWindowsCountry[3];
+ # uint32_t idxWindows;
+ # uint32_t uReserved;
+
+ asFields = [ '"%s"' % sName, ];
+ if oWinZone.sWinName:
+ asFields.append('"%s"' % oWinZone.sWinName);
+ else:
+ asFields.append('NULL');
+
+ asFields.append('%u' % (len(sName),));
+ if oWinZone.sWinName:
+ asFields.append('%u' % (len(oWinZone.sWinName),));
+ else:
+ asFields.append('0');
+
+ asFields.append('"%s"' % (oZone.sTerritory,));
+ if oWinZone.sWinTerritory:
+ asFields.append('"%s"' % (oWinZone.sWinTerritory,));
+ else:
+ asFields.append('""');
+ asFields.append('%#010x' % (oWinZone.idxWin,));
+
+ asFlags = [];
+ if oWinZone.fWinGolden:
+ asFlags.append('RTTIMEZONEINFO_F_GOLDEN');
+ if asFlags:
+ asFields.append(' | '.join(asFlags));
+ else:
+ asFields.append('0');
+ return asFields;
+
+def produceCode(oDst):
+ """
+ Produces the tables.
+ """
+
+ #
+ # Produce the info table.
+ #
+ aasEntries = [];
+
+ # The straight zones.
+ for sZone in g_dZones:
+ asFields = formatFields(sZone, g_dZones[sZone], g_dZones[sZone]);
+ aasEntries.append(asFields);
+
+ # The links.
+ for sZone in g_dLinks:
+ oLink = g_dLinks[sZone];
+ asFields = formatFields(sZone, g_dZones[oLink.sTarget], oLink);
+ aasEntries.append(asFields);
+
+ # Figure field lengths.
+ acchFields = [ 2, 2, 2, 2, 4, 4, 10, 1 ];
+ for asFields in aasEntries:
+ assert len(asFields) == len(acchFields);
+ for iField, sField in enumerate(asFields):
+ if len(sField) > acchFields[iField]:
+ acchFields[iField] = len(sField);
+
+ # Sort the data on zone name.
+ aasEntries.sort();
+
+ # Do the formatting.
+ oDst.write('/**\n'
+ ' * Static time zone mapping info. Sorted by pszUnixName.\n'
+ ' */\n'
+ 'static const RTTIMEZONEINFO g_aTimeZones[] =\n'
+ '{\n');
+ for iEntry, asFields in enumerate(aasEntries):
+ sLine = ' { ';
+ for iField, sField in enumerate(asFields):
+ sLine += sField;
+ sLine += ', ';
+ sLine += getPadding(sField, acchFields[iField]);
+ sLine += ' }, /* %#05x */\n' % (iEntry,);
+ oDst.write(sLine);
+ oDst.write('};\n'
+ '\n');
+
+ #
+ # Now produce a lookup table for windows time zone names, with indexes into
+ # the g_aTimeZone table.
+ #
+ aasLookup = [];
+ for iEntry, asFields in enumerate(aasEntries):
+ if asFields[1] != 'NULL':
+ aasLookup.append([ asFields[1], # sWinName
+ -1 if asFields[7].find('RTTIMEZONEINFO_F_GOLDEN') >= 0 else 1,
+ asFields[5], # sWinTerritory
+ iEntry,
+ asFields[0]]); # sZone
+ aasLookup.sort();
+
+ oDst.write('/**\n'
+ ' * Windows time zone lookup table. Sorted by name, golden flag and territory.\n'
+ ' */\n'
+ 'static const uint16_t g_aidxWinTimeZones[] = \n'
+ '{\n');
+ for asFields in aasLookup:
+ sLine = ' %#05x, /* %s' % (asFields[3], asFields[0][1:-1]);
+ sLine += getPadding(asFields[0], acchFields[1]);
+ sLine += ' / %s%s' % (asFields[2][1:-1], '+' if asFields[1] < 0 else ' ');
+ if len(asFields[2]) == 2:
+ sLine += ' ';
+ sLine += ' ==> %s */\n' % (asFields[4][1:-1],)
+ oDst.write(sLine);
+
+ oDst.write('};\n'
+ '\n');
+
+ return 0;
+
+
+def main(asArgs):
+ """
+ C-like main function.
+ """
+ if len(asArgs) != 4:
+ error("Takes exacty three arguments: <ms-index-file> <ms-key-file> <tz-data-dir>");
+ return 1;
+ sTzDataDir = asArgs[1];
+ sWinToTzMap = asArgs[2];
+ sWinIndexTable = asArgs[3];
+
+ #
+ # Read in the data first.
+ #
+ iRc = readTzData(sTzDataDir);
+ if iRc == 0:
+ iRc = readWindowsToTzMap(sWinToTzMap);
+ if iRc == 0:
+ iRc = readWindowsIndexes(sWinIndexTable);
+ if iRc == 0:
+ #
+ # Produce the C table.
+ #
+ iRc = produceCode(sys.stdout);
+ return iRc;
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv));
+
diff --git a/src/VBox/Runtime/common/time/timezoneinfo.cpp b/src/VBox/Runtime/common/time/timezoneinfo.cpp
new file mode 100644
index 00000000..b6efb164
--- /dev/null
+++ b/src/VBox/Runtime/common/time/timezoneinfo.cpp
@@ -0,0 +1,1171 @@
+/* $Id: timezoneinfo.cpp $ */
+/** @file
+ * IPRT - Time zone mapping info.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/string.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Static time zone mapping info. Sorted by pszUnixName.
+ */
+static const RTTIMEZONEINFO g_aTimeZones[] =
+{
+ { "Africa/Abidjan", "Greenwich Standard Time", 14, 23, "CI", "CI", 0x0000005a, 0, }, /* 0x000 */
+ { "Africa/Accra", "Greenwich Standard Time", 12, 23, "GH", "GH", 0x0000005a, 0, }, /* 0x001 */
+ { "Africa/Addis_Ababa", "E. Africa Standard Time", 18, 23, "KE", "ET", 0x00000000, 0, }, /* 0x002 */
+ { "Africa/Algiers", "W. Central Africa Standard Time", 14, 31, "DZ", "DZ", 0x00000071, 0, }, /* 0x003 */
+ { "Africa/Asmara", NULL, 13, 0, "KE", "", 0x00000000, 0, }, /* 0x004 */
+ { "Africa/Asmera", "E. Africa Standard Time", 13, 23, "KE", "ER", 0x00000000, 0, }, /* 0x005 */
+ { "Africa/Bamako", "Greenwich Standard Time", 13, 23, "CI", "ML", 0x00000000, 0, }, /* 0x006 */
+ { "Africa/Bangui", "W. Central Africa Standard Time", 13, 31, "NG", "CF", 0x00000000, 0, }, /* 0x007 */
+ { "Africa/Banjul", "Greenwich Standard Time", 13, 23, "CI", "GM", 0x00000000, 0, }, /* 0x008 */
+ { "Africa/Bissau", "Greenwich Standard Time", 13, 23, "GW", "GW", 0x0000005a, 0, }, /* 0x009 */
+ { "Africa/Blantyre", "South Africa Standard Time", 15, 26, "MZ", "MW", 0x00000000, 0, }, /* 0x00a */
+ { "Africa/Brazzaville", "W. Central Africa Standard Time", 18, 31, "NG", "CG", 0x00000000, 0, }, /* 0x00b */
+ { "Africa/Bujumbura", "South Africa Standard Time", 16, 26, "MZ", "BI", 0x00000000, 0, }, /* 0x00c */
+ { "Africa/Cairo", "Egypt Standard Time", 12, 19, "EG", "EG", 0x00000078, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x00d */
+ { "Africa/Casablanca", "Morocco Standard Time", 17, 21, "MA", "MA", 0x8000004d, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x00e */
+ { "Africa/Ceuta", "Romance Standard Time", 12, 21, "ES", "ES", 0x00000069, 0, }, /* 0x00f */
+ { "Africa/Conakry", "Greenwich Standard Time", 14, 23, "CI", "GN", 0x00000000, 0, }, /* 0x010 */
+ { "Africa/Dakar", "Greenwich Standard Time", 12, 23, "CI", "SN", 0x00000000, 0, }, /* 0x011 */
+ { "Africa/Dar_es_Salaam", "E. Africa Standard Time", 20, 23, "KE", "TZ", 0x00000000, 0, }, /* 0x012 */
+ { "Africa/Djibouti", "E. Africa Standard Time", 15, 23, "KE", "DJ", 0x00000000, 0, }, /* 0x013 */
+ { "Africa/Douala", "W. Central Africa Standard Time", 13, 31, "NG", "CM", 0x00000000, 0, }, /* 0x014 */
+ { "Africa/El_Aaiun", "Morocco Standard Time", 15, 21, "EH", "EH", 0x8000004d, 0, }, /* 0x015 */
+ { "Africa/Freetown", "Greenwich Standard Time", 15, 23, "CI", "SL", 0x00000000, 0, }, /* 0x016 */
+ { "Africa/Gaborone", "South Africa Standard Time", 15, 26, "MZ", "BW", 0x00000000, 0, }, /* 0x017 */
+ { "Africa/Harare", "South Africa Standard Time", 13, 26, "MZ", "ZW", 0x00000000, 0, }, /* 0x018 */
+ { "Africa/Johannesburg", "South Africa Standard Time", 19, 26, "ZA", "ZA", 0x0000008c, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x019 */
+ { "Africa/Juba", "E. Africa Standard Time", 11, 23, "SD", "SS", 0x00000000, 0, }, /* 0x01a */
+ { "Africa/Kampala", "E. Africa Standard Time", 14, 23, "KE", "UG", 0x00000000, 0, }, /* 0x01b */
+ { "Africa/Khartoum", "E. Africa Standard Time", 15, 23, "SD", "SD", 0x0000009b, 0, }, /* 0x01c */
+ { "Africa/Kigali", "South Africa Standard Time", 13, 26, "MZ", "RW", 0x00000000, 0, }, /* 0x01d */
+ { "Africa/Kinshasa", "W. Central Africa Standard Time", 15, 31, "NG", "CD", 0x00000000, 0, }, /* 0x01e */
+ { "Africa/Lagos", "W. Central Africa Standard Time", 12, 31, "NG", "NG", 0x00000071, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x01f */
+ { "Africa/Libreville", "W. Central Africa Standard Time", 17, 31, "NG", "GA", 0x00000000, 0, }, /* 0x020 */
+ { "Africa/Lome", "Greenwich Standard Time", 11, 23, "CI", "TG", 0x00000000, 0, }, /* 0x021 */
+ { "Africa/Luanda", "W. Central Africa Standard Time", 13, 31, "NG", "AO", 0x00000000, 0, }, /* 0x022 */
+ { "Africa/Lubumbashi", "South Africa Standard Time", 17, 26, "MZ", "CD", 0x00000000, 0, }, /* 0x023 */
+ { "Africa/Lusaka", "South Africa Standard Time", 13, 26, "MZ", "ZM", 0x00000000, 0, }, /* 0x024 */
+ { "Africa/Malabo", "W. Central Africa Standard Time", 13, 31, "NG", "GQ", 0x00000000, 0, }, /* 0x025 */
+ { "Africa/Maputo", "South Africa Standard Time", 13, 26, "MZ", "MZ", 0x0000008c, 0, }, /* 0x026 */
+ { "Africa/Maseru", "South Africa Standard Time", 13, 26, "ZA", "LS", 0x00000000, 0, }, /* 0x027 */
+ { "Africa/Mbabane", "South Africa Standard Time", 14, 26, "ZA", "SZ", 0x00000000, 0, }, /* 0x028 */
+ { "Africa/Mogadishu", "E. Africa Standard Time", 16, 23, "KE", "SO", 0x00000000, 0, }, /* 0x029 */
+ { "Africa/Monrovia", "Greenwich Standard Time", 15, 23, "LR", "LR", 0x0000005a, 0, }, /* 0x02a */
+ { "Africa/Nairobi", "E. Africa Standard Time", 14, 23, "KE", "KE", 0x0000009b, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x02b */
+ { "Africa/Ndjamena", "W. Central Africa Standard Time", 15, 31, "TD", "TD", 0x00000071, 0, }, /* 0x02c */
+ { "Africa/Niamey", "W. Central Africa Standard Time", 13, 31, "NG", "NE", 0x00000000, 0, }, /* 0x02d */
+ { "Africa/Nouakchott", "Greenwich Standard Time", 17, 23, "CI", "MR", 0x00000000, 0, }, /* 0x02e */
+ { "Africa/Ouagadougou", "Greenwich Standard Time", 18, 23, "CI", "BF", 0x00000000, 0, }, /* 0x02f */
+ { "Africa/Porto-Novo", "W. Central Africa Standard Time", 17, 31, "NG", "BJ", 0x00000000, 0, }, /* 0x030 */
+ { "Africa/Sao_Tome", "Greenwich Standard Time", 15, 23, "CI", "ST", 0x00000000, 0, }, /* 0x031 */
+ { "Africa/Timbuktu", NULL, 15, 0, "CI", "", 0x00000000, 0, }, /* 0x032 */
+ { "Africa/Tripoli", "Libya Standard Time", 14, 19, "LY", "LY", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x033 */
+ { "Africa/Tunis", "W. Central Africa Standard Time", 12, 31, "TN", "TN", 0x00000071, 0, }, /* 0x034 */
+ { "Africa/Windhoek", "Namibia Standard Time", 15, 21, "NA", "NA", 0x80000046, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x035 */
+ { "America/Adak", "Aleutian Standard Time", 12, 22, "US", "US", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x036 */
+ { "America/Anchorage", "Alaskan Standard Time", 17, 21, "US", "US", 0x00000003, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x037 */
+ { "America/Anguilla", "SA Western Standard Time", 16, 24, "TT", "AI", 0x00000000, 0, }, /* 0x038 */
+ { "America/Antigua", "SA Western Standard Time", 15, 24, "TT", "AG", 0x00000000, 0, }, /* 0x039 */
+ { "America/Araguaina", "Tocantins Standard Time", 17, 23, "BR", "BR", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x03a */
+ { "America/Argentina/Buenos_Aires", NULL, 30, 0, "AR", "", 0x00000000, 0, }, /* 0x03b */
+ { "America/Argentina/Catamarca", NULL, 27, 0, "AR", "", 0x00000000, 0, }, /* 0x03c */
+ { "America/Argentina/ComodRivadavia", NULL, 32, 0, "AR", "", 0x00000000, 0, }, /* 0x03d */
+ { "America/Argentina/Cordoba", NULL, 25, 0, "AR", "", 0x00000000, 0, }, /* 0x03e */
+ { "America/Argentina/Jujuy", NULL, 23, 0, "AR", "", 0x00000000, 0, }, /* 0x03f */
+ { "America/Argentina/La_Rioja", "Argentina Standard Time", 26, 23, "AR", "AR", 0x8000004c, 0, }, /* 0x040 */
+ { "America/Argentina/Mendoza", NULL, 25, 0, "AR", "", 0x00000000, 0, }, /* 0x041 */
+ { "America/Argentina/Rio_Gallegos", "Argentina Standard Time", 30, 23, "AR", "AR", 0x8000004c, 0, }, /* 0x042 */
+ { "America/Argentina/Salta", "Argentina Standard Time", 23, 23, "AR", "AR", 0x8000004c, 0, }, /* 0x043 */
+ { "America/Argentina/San_Juan", "Argentina Standard Time", 26, 23, "AR", "AR", 0x8000004c, 0, }, /* 0x044 */
+ { "America/Argentina/San_Luis", "Argentina Standard Time", 26, 23, "AR", "AR", 0x8000004c, 0, }, /* 0x045 */
+ { "America/Argentina/Tucuman", "Argentina Standard Time", 25, 23, "AR", "AR", 0x8000004c, 0, }, /* 0x046 */
+ { "America/Argentina/Ushuaia", "Argentina Standard Time", 25, 23, "AR", "AR", 0x8000004c, 0, }, /* 0x047 */
+ { "America/Aruba", "SA Western Standard Time", 13, 24, "CW", "AW", 0x00000000, 0, }, /* 0x048 */
+ { "America/Asuncion", "Paraguay Standard Time", 16, 22, "PY", "PY", 0x80000051, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x049 */
+ { "America/Atikokan", NULL, 16, 0, "CA", "", 0x00000000, 0, }, /* 0x04a */
+ { "America/Atka", NULL, 12, 0, "US", "", 0x00000000, 0, }, /* 0x04b */
+ { "America/Bahia", "Bahia Standard Time", 13, 19, "BR", "BR", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x04c */
+ { "America/Bahia_Banderas", "Central Standard Time (Mexico)", 22, 30, "MX", "MX", 0x80000043, 0, }, /* 0x04d */
+ { "America/Barbados", "SA Western Standard Time", 16, 24, "BB", "BB", 0x00000037, 0, }, /* 0x04e */
+ { "America/Belem", "SA Eastern Standard Time", 13, 24, "BR", "BR", 0x00000046, 0, }, /* 0x04f */
+ { "America/Belize", "Central America Standard Time", 14, 29, "BZ", "BZ", 0x00000021, 0, }, /* 0x050 */
+ { "America/Blanc-Sablon", "SA Western Standard Time", 20, 24, "CA", "CA", 0x00000037, 0, }, /* 0x051 */
+ { "America/Boa_Vista", "SA Western Standard Time", 17, 24, "BR", "BR", 0x00000037, 0, }, /* 0x052 */
+ { "America/Bogota", "SA Pacific Standard Time", 14, 24, "CO", "CO", 0x0000002d, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x053 */
+ { "America/Boise", "Mountain Standard Time", 13, 22, "US", "US", 0x0000000a, 0, }, /* 0x054 */
+ { "America/Buenos_Aires", "Argentina Standard Time", 20, 23, "AR", "AR", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x055 */
+ { "America/Cambridge_Bay", "Mountain Standard Time", 21, 22, "CA", "CA", 0x0000000a, 0, }, /* 0x056 */
+ { "America/Campo_Grande", "Central Brazilian Standard Time", 20, 31, "BR", "BR", 0x80000048, 0, }, /* 0x057 */
+ { "America/Cancun", "Eastern Standard Time (Mexico)", 14, 30, "MX", "MX", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x058 */
+ { "America/Caracas", "Venezuela Standard Time", 15, 23, "VE", "VE", 0x8000004b, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x059 */
+ { "America/Catamarca", "Argentina Standard Time", 17, 23, "AR", "AR", 0x00000000, 0, }, /* 0x05a */
+ { "America/Cayenne", "SA Eastern Standard Time", 15, 24, "GF", "GF", 0x00000046, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x05b */
+ { "America/Cayman", "SA Pacific Standard Time", 14, 24, "PA", "KY", 0x00000000, 0, }, /* 0x05c */
+ { "America/Chicago", "Central Standard Time", 15, 21, "US", "US", 0x00000014, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x05d */
+ { "America/Chihuahua", "Mountain Standard Time (Mexico)", 17, 31, "MX", "MX", 0x80000044, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x05e */
+ { "America/Coral_Harbour", "SA Pacific Standard Time", 21, 24, "CA", "CA", 0x00000000, 0, }, /* 0x05f */
+ { "America/Cordoba", "Argentina Standard Time", 15, 23, "AR", "AR", 0x00000000, 0, }, /* 0x060 */
+ { "America/Costa_Rica", "Central America Standard Time", 18, 29, "CR", "CR", 0x00000021, 0, }, /* 0x061 */
+ { "America/Creston", "US Mountain Standard Time", 15, 25, "CA", "CA", 0x0000000f, 0, }, /* 0x062 */
+ { "America/Cuiaba", "Central Brazilian Standard Time", 14, 31, "BR", "BR", 0x80000048, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x063 */
+ { "America/Curacao", "SA Western Standard Time", 15, 24, "CW", "CW", 0x00000037, 0, }, /* 0x064 */
+ { "America/Danmarkshavn", "UTC", 20, 3, "GL", "GL", 0x80000050, 0, }, /* 0x065 */
+ { "America/Dawson", "Pacific Standard Time", 14, 21, "CA", "CA", 0x00000004, 0, }, /* 0x066 */
+ { "America/Dawson_Creek", "US Mountain Standard Time", 20, 25, "CA", "CA", 0x0000000f, 0, }, /* 0x067 */
+ { "America/Denver", "Mountain Standard Time", 14, 22, "US", "US", 0x0000000a, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x068 */
+ { "America/Detroit", "Eastern Standard Time", 15, 21, "US", "US", 0x00000023, 0, }, /* 0x069 */
+ { "America/Dominica", "SA Western Standard Time", 16, 24, "TT", "DM", 0x00000000, 0, }, /* 0x06a */
+ { "America/Edmonton", "Mountain Standard Time", 16, 22, "CA", "CA", 0x0000000a, 0, }, /* 0x06b */
+ { "America/Eirunepe", "SA Pacific Standard Time", 16, 24, "BR", "BR", 0x0000002d, 0, }, /* 0x06c */
+ { "America/El_Salvador", "Central America Standard Time", 19, 29, "SV", "SV", 0x00000021, 0, }, /* 0x06d */
+ { "America/Ensenada", NULL, 16, 0, "MX", "", 0x00000000, 0, }, /* 0x06e */
+ { "America/Fort_Nelson", "US Mountain Standard Time", 19, 25, "CA", "CA", 0x0000000f, 0, }, /* 0x06f */
+ { "America/Fort_Wayne", NULL, 18, 0, "US", "", 0x00000000, 0, }, /* 0x070 */
+ { "America/Fortaleza", "SA Eastern Standard Time", 17, 24, "BR", "BR", 0x00000046, 0, }, /* 0x071 */
+ { "America/Glace_Bay", "Atlantic Standard Time", 17, 22, "CA", "CA", 0x00000032, 0, }, /* 0x072 */
+ { "America/Godthab", "Greenland Standard Time", 15, 23, "GL", "GL", 0x00000049, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x073 */
+ { "America/Goose_Bay", "Atlantic Standard Time", 17, 22, "CA", "CA", 0x00000032, 0, }, /* 0x074 */
+ { "America/Grand_Turk", "Turks And Caicos Standard Time", 18, 30, "TC", "TC", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x075 */
+ { "America/Grenada", "SA Western Standard Time", 15, 24, "TT", "GD", 0x00000000, 0, }, /* 0x076 */
+ { "America/Guadeloupe", "SA Western Standard Time", 18, 24, "TT", "GP", 0x00000000, 0, }, /* 0x077 */
+ { "America/Guatemala", "Central America Standard Time", 17, 29, "GT", "GT", 0x00000021, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x078 */
+ { "America/Guayaquil", "SA Pacific Standard Time", 17, 24, "EC", "EC", 0x0000002d, 0, }, /* 0x079 */
+ { "America/Guyana", "SA Western Standard Time", 14, 24, "GY", "GY", 0x00000037, 0, }, /* 0x07a */
+ { "America/Halifax", "Atlantic Standard Time", 15, 22, "CA", "CA", 0x00000032, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x07b */
+ { "America/Havana", "Cuba Standard Time", 14, 18, "CU", "CU", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x07c */
+ { "America/Hermosillo", "US Mountain Standard Time", 18, 25, "MX", "MX", 0x0000000f, 0, }, /* 0x07d */
+ { "America/Indiana/Indianapolis", NULL, 28, 0, "US", "", 0x00000000, 0, }, /* 0x07e */
+ { "America/Indiana/Knox", "Central Standard Time", 20, 21, "US", "US", 0x00000014, 0, }, /* 0x07f */
+ { "America/Indiana/Marengo", "US Eastern Standard Time", 23, 24, "US", "US", 0x00000028, 0, }, /* 0x080 */
+ { "America/Indiana/Petersburg", "Eastern Standard Time", 26, 21, "US", "US", 0x00000023, 0, }, /* 0x081 */
+ { "America/Indiana/Tell_City", "Central Standard Time", 25, 21, "US", "US", 0x00000014, 0, }, /* 0x082 */
+ { "America/Indiana/Vevay", "US Eastern Standard Time", 21, 24, "US", "US", 0x00000028, 0, }, /* 0x083 */
+ { "America/Indiana/Vincennes", "Eastern Standard Time", 25, 21, "US", "US", 0x00000023, 0, }, /* 0x084 */
+ { "America/Indiana/Winamac", "Eastern Standard Time", 23, 21, "US", "US", 0x00000023, 0, }, /* 0x085 */
+ { "America/Indianapolis", "US Eastern Standard Time", 20, 24, "US", "US", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x086 */
+ { "America/Inuvik", "Mountain Standard Time", 14, 22, "CA", "CA", 0x0000000a, 0, }, /* 0x087 */
+ { "America/Iqaluit", "Eastern Standard Time", 15, 21, "CA", "CA", 0x00000023, 0, }, /* 0x088 */
+ { "America/Jamaica", "SA Pacific Standard Time", 15, 24, "JM", "JM", 0x0000002d, 0, }, /* 0x089 */
+ { "America/Jujuy", "Argentina Standard Time", 13, 23, "AR", "AR", 0x00000000, 0, }, /* 0x08a */
+ { "America/Juneau", "Alaskan Standard Time", 14, 21, "US", "US", 0x00000003, 0, }, /* 0x08b */
+ { "America/Kentucky/Louisville", NULL, 27, 0, "US", "", 0x00000000, 0, }, /* 0x08c */
+ { "America/Kentucky/Monticello", "Eastern Standard Time", 27, 21, "US", "US", 0x00000023, 0, }, /* 0x08d */
+ { "America/Knox_IN", NULL, 15, 0, "US", "", 0x00000000, 0, }, /* 0x08e */
+ { "America/Kralendijk", "SA Western Standard Time", 18, 24, "CW", "BQ", 0x00000000, 0, }, /* 0x08f */
+ { "America/La_Paz", "SA Western Standard Time", 14, 24, "BO", "BO", 0x00000037, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x090 */
+ { "America/Lima", "SA Pacific Standard Time", 12, 24, "PE", "PE", 0x0000002d, 0, }, /* 0x091 */
+ { "America/Los_Angeles", "Pacific Standard Time", 19, 21, "US", "US", 0x00000004, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x092 */
+ { "America/Louisville", "Eastern Standard Time", 18, 21, "US", "US", 0x00000000, 0, }, /* 0x093 */
+ { "America/Lower_Princes", "SA Western Standard Time", 21, 24, "CW", "SX", 0x00000000, 0, }, /* 0x094 */
+ { "America/Maceio", "SA Eastern Standard Time", 14, 24, "BR", "BR", 0x00000046, 0, }, /* 0x095 */
+ { "America/Managua", "Central America Standard Time", 15, 29, "NI", "NI", 0x00000021, 0, }, /* 0x096 */
+ { "America/Manaus", "SA Western Standard Time", 14, 24, "BR", "BR", 0x00000037, 0, }, /* 0x097 */
+ { "America/Marigot", "SA Western Standard Time", 15, 24, "TT", "MF", 0x00000000, 0, }, /* 0x098 */
+ { "America/Martinique", "SA Western Standard Time", 18, 24, "MQ", "MQ", 0x00000037, 0, }, /* 0x099 */
+ { "America/Matamoros", "Central Standard Time", 17, 21, "MX", "MX", 0x00000014, 0, }, /* 0x09a */
+ { "America/Mazatlan", "Mountain Standard Time (Mexico)", 16, 31, "MX", "MX", 0x80000044, 0, }, /* 0x09b */
+ { "America/Mendoza", "Argentina Standard Time", 15, 23, "AR", "AR", 0x00000000, 0, }, /* 0x09c */
+ { "America/Menominee", "Central Standard Time", 17, 21, "US", "US", 0x00000014, 0, }, /* 0x09d */
+ { "America/Merida", "Central Standard Time (Mexico)", 14, 30, "MX", "MX", 0x80000043, 0, }, /* 0x09e */
+ { "America/Metlakatla", "Alaskan Standard Time", 18, 21, "US", "US", 0x00000003, 0, }, /* 0x09f */
+ { "America/Mexico_City", "Central Standard Time (Mexico)", 19, 30, "MX", "MX", 0x80000043, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0a0 */
+ { "America/Miquelon", "Saint Pierre Standard Time", 16, 26, "PM", "PM", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0a1 */
+ { "America/Moncton", "Atlantic Standard Time", 15, 22, "CA", "CA", 0x00000032, 0, }, /* 0x0a2 */
+ { "America/Monterrey", "Central Standard Time (Mexico)", 17, 30, "MX", "MX", 0x80000043, 0, }, /* 0x0a3 */
+ { "America/Montevideo", "Montevideo Standard Time", 18, 24, "UY", "UY", 0x80000049, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0a4 */
+ { "America/Montreal", "Eastern Standard Time", 16, 21, "CA", "CA", 0x00000000, 0, }, /* 0x0a5 */
+ { "America/Montserrat", "SA Western Standard Time", 18, 24, "TT", "MS", 0x00000000, 0, }, /* 0x0a6 */
+ { "America/Nassau", "Eastern Standard Time", 14, 21, "BS", "BS", 0x00000023, 0, }, /* 0x0a7 */
+ { "America/New_York", "Eastern Standard Time", 16, 21, "US", "US", 0x00000023, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0a8 */
+ { "America/Nipigon", "Eastern Standard Time", 15, 21, "CA", "CA", 0x00000023, 0, }, /* 0x0a9 */
+ { "America/Nome", "Alaskan Standard Time", 12, 21, "US", "US", 0x00000003, 0, }, /* 0x0aa */
+ { "America/Noronha", "UTC-02", 15, 6, "BR", "BR", 0x00000000, 0, }, /* 0x0ab */
+ { "America/North_Dakota/Beulah", "Central Standard Time", 27, 21, "US", "US", 0x00000014, 0, }, /* 0x0ac */
+ { "America/North_Dakota/Center", "Central Standard Time", 27, 21, "US", "US", 0x00000014, 0, }, /* 0x0ad */
+ { "America/North_Dakota/New_Salem", "Central Standard Time", 30, 21, "US", "US", 0x00000014, 0, }, /* 0x0ae */
+ { "America/Ojinaga", "Mountain Standard Time", 15, 22, "MX", "MX", 0x0000000a, 0, }, /* 0x0af */
+ { "America/Panama", "SA Pacific Standard Time", 14, 24, "PA", "PA", 0x0000002d, 0, }, /* 0x0b0 */
+ { "America/Pangnirtung", "Eastern Standard Time", 19, 21, "CA", "CA", 0x00000023, 0, }, /* 0x0b1 */
+ { "America/Paramaribo", "SA Eastern Standard Time", 18, 24, "SR", "SR", 0x00000046, 0, }, /* 0x0b2 */
+ { "America/Phoenix", "US Mountain Standard Time", 15, 25, "US", "US", 0x0000000f, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0b3 */
+ { "America/Port-au-Prince", "Haiti Standard Time", 22, 19, "HT", "HT", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0b4 */
+ { "America/Port_of_Spain", "SA Western Standard Time", 21, 24, "TT", "TT", 0x00000037, 0, }, /* 0x0b5 */
+ { "America/Porto_Acre", NULL, 18, 0, "BR", "", 0x00000000, 0, }, /* 0x0b6 */
+ { "America/Porto_Velho", "SA Western Standard Time", 19, 24, "BR", "BR", 0x00000037, 0, }, /* 0x0b7 */
+ { "America/Puerto_Rico", "SA Western Standard Time", 19, 24, "PR", "PR", 0x00000037, 0, }, /* 0x0b8 */
+ { "America/Punta_Arenas", "SA Eastern Standard Time", 20, 24, "CL", "CL", 0x00000046, 0, }, /* 0x0b9 */
+ { "America/Rainy_River", "Central Standard Time", 19, 21, "CA", "CA", 0x00000014, 0, }, /* 0x0ba */
+ { "America/Rankin_Inlet", "Central Standard Time", 20, 21, "CA", "CA", 0x00000014, 0, }, /* 0x0bb */
+ { "America/Recife", "SA Eastern Standard Time", 14, 24, "BR", "BR", 0x00000046, 0, }, /* 0x0bc */
+ { "America/Regina", "Canada Central Standard Time", 14, 28, "CA", "CA", 0x00000019, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0bd */
+ { "America/Resolute", "Central Standard Time", 16, 21, "CA", "CA", 0x00000014, 0, }, /* 0x0be */
+ { "America/Rio_Branco", "SA Pacific Standard Time", 18, 24, "BR", "BR", 0x0000002d, 0, }, /* 0x0bf */
+ { "America/Rosario", NULL, 15, 0, "AR", "", 0x00000000, 0, }, /* 0x0c0 */
+ { "America/Santa_Isabel", "Pacific Standard Time (Mexico)", 20, 30, "MX", "MX", 0x00000000, 0, }, /* 0x0c1 */
+ { "America/Santarem", "SA Eastern Standard Time", 16, 24, "BR", "BR", 0x00000046, 0, }, /* 0x0c2 */
+ { "America/Santiago", "Pacific SA Standard Time", 16, 24, "CL", "CL", 0x00000038, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0c3 */
+ { "America/Santo_Domingo", "SA Western Standard Time", 21, 24, "DO", "DO", 0x00000037, 0, }, /* 0x0c4 */
+ { "America/Sao_Paulo", "E. South America Standard Time", 17, 30, "BR", "BR", 0x00000041, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0c5 */
+ { "America/Scoresbysund", "Azores Standard Time", 20, 20, "GL", "GL", 0x00000050, 0, }, /* 0x0c6 */
+ { "America/Shiprock", NULL, 16, 0, "US", "", 0x00000000, 0, }, /* 0x0c7 */
+ { "America/Sitka", "Alaskan Standard Time", 13, 21, "US", "US", 0x00000003, 0, }, /* 0x0c8 */
+ { "America/St_Barthelemy", "SA Western Standard Time", 21, 24, "TT", "BL", 0x00000000, 0, }, /* 0x0c9 */
+ { "America/St_Johns", "Newfoundland Standard Time", 16, 26, "CA", "CA", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0ca */
+ { "America/St_Kitts", "SA Western Standard Time", 16, 24, "TT", "KN", 0x00000000, 0, }, /* 0x0cb */
+ { "America/St_Lucia", "SA Western Standard Time", 16, 24, "TT", "LC", 0x00000000, 0, }, /* 0x0cc */
+ { "America/St_Thomas", "SA Western Standard Time", 17, 24, "TT", "VI", 0x00000000, 0, }, /* 0x0cd */
+ { "America/St_Vincent", "SA Western Standard Time", 18, 24, "TT", "VC", 0x00000000, 0, }, /* 0x0ce */
+ { "America/Swift_Current", "Canada Central Standard Time", 21, 28, "CA", "CA", 0x00000019, 0, }, /* 0x0cf */
+ { "America/Tegucigalpa", "Central America Standard Time", 19, 29, "HN", "HN", 0x00000021, 0, }, /* 0x0d0 */
+ { "America/Thule", "Atlantic Standard Time", 13, 22, "GL", "GL", 0x00000032, 0, }, /* 0x0d1 */
+ { "America/Thunder_Bay", "Eastern Standard Time", 19, 21, "CA", "CA", 0x00000023, 0, }, /* 0x0d2 */
+ { "America/Tijuana", "Pacific Standard Time (Mexico)", 15, 30, "MX", "MX", 0x80000045, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0d3 */
+ { "America/Toronto", "Eastern Standard Time", 15, 21, "CA", "CA", 0x00000023, 0, }, /* 0x0d4 */
+ { "America/Tortola", "SA Western Standard Time", 15, 24, "TT", "VG", 0x00000000, 0, }, /* 0x0d5 */
+ { "America/Vancouver", "Pacific Standard Time", 17, 21, "CA", "CA", 0x00000004, 0, }, /* 0x0d6 */
+ { "America/Virgin", NULL, 14, 0, "TT", "", 0x00000000, 0, }, /* 0x0d7 */
+ { "America/Whitehorse", "Pacific Standard Time", 18, 21, "CA", "CA", 0x00000004, 0, }, /* 0x0d8 */
+ { "America/Winnipeg", "Central Standard Time", 16, 21, "CA", "CA", 0x00000014, 0, }, /* 0x0d9 */
+ { "America/Yakutat", "Alaskan Standard Time", 15, 21, "US", "US", 0x00000003, 0, }, /* 0x0da */
+ { "America/Yellowknife", "Mountain Standard Time", 19, 22, "CA", "CA", 0x0000000a, 0, }, /* 0x0db */
+ { "Antarctica/Casey", "Central Pacific Standard Time", 16, 29, "AQ", "AQ", 0x00000118, 0, }, /* 0x0dc */
+ { "Antarctica/Davis", "SE Asia Standard Time", 16, 21, "AQ", "AQ", 0x000000cd, 0, }, /* 0x0dd */
+ { "Antarctica/DumontDUrville", "West Pacific Standard Time", 25, 26, "AQ", "AQ", 0x00000113, 0, }, /* 0x0de */
+ { "Antarctica/Macquarie", "Central Pacific Standard Time", 20, 29, "AU", "AU", 0x00000118, 0, }, /* 0x0df */
+ { "Antarctica/Mawson", "West Asia Standard Time", 17, 23, "AQ", "AQ", 0x000000b9, 0, }, /* 0x0e0 */
+ { "Antarctica/McMurdo", "New Zealand Standard Time", 18, 25, "NZ", "AQ", 0x00000000, 0, }, /* 0x0e1 */
+ { "Antarctica/Palmer", "SA Eastern Standard Time", 17, 24, "AQ", "AQ", 0x00000046, 0, }, /* 0x0e2 */
+ { "Antarctica/Rothera", "SA Eastern Standard Time", 18, 24, "AQ", "AQ", 0x00000046, 0, }, /* 0x0e3 */
+ { "Antarctica/South_Pole", NULL, 21, 0, "NZ", "", 0x00000000, 0, }, /* 0x0e4 */
+ { "Antarctica/Syowa", "E. Africa Standard Time", 16, 23, "AQ", "AQ", 0x0000009b, 0, }, /* 0x0e5 */
+ { "Antarctica/Troll", NULL, 16, 0, "AQ", "", 0x00000000, 0, }, /* 0x0e6 */
+ { "Antarctica/Vostok", "Central Asia Standard Time", 17, 26, "AQ", "AQ", 0x000000c3, 0, }, /* 0x0e7 */
+ { "Arctic/Longyearbyen", "W. Europe Standard Time", 19, 23, "NO", "SJ", 0x00000000, 0, }, /* 0x0e8 */
+ { "Asia/Aden", "Arab Standard Time", 9, 18, "SA", "YE", 0x00000000, 0, }, /* 0x0e9 */
+ { "Asia/Almaty", "Central Asia Standard Time", 11, 26, "KZ", "KZ", 0x000000c3, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0ea */
+ { "Asia/Amman", "Jordan Standard Time", 10, 20, "JO", "JO", 0x80000042, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0eb */
+ { "Asia/Anadyr", "Russia Time Zone 11", 11, 19, "RU", "RU", 0x00000000, 0, }, /* 0x0ec */
+ { "Asia/Aqtau", "West Asia Standard Time", 10, 23, "KZ", "KZ", 0x000000b9, 0, }, /* 0x0ed */
+ { "Asia/Aqtobe", "West Asia Standard Time", 11, 23, "KZ", "KZ", 0x000000b9, 0, }, /* 0x0ee */
+ { "Asia/Ashgabat", "West Asia Standard Time", 13, 23, "TM", "TM", 0x000000b9, 0, }, /* 0x0ef */
+ { "Asia/Ashkhabad", NULL, 14, 0, "TM", "", 0x00000000, 0, }, /* 0x0f0 */
+ { "Asia/Atyrau", "West Asia Standard Time", 11, 23, "KZ", "KZ", 0x000000b9, 0, }, /* 0x0f1 */
+ { "Asia/Baghdad", "Arabic Standard Time", 12, 20, "IQ", "IQ", 0x0000009e, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0f2 */
+ { "Asia/Bahrain", "Arab Standard Time", 12, 18, "QA", "BH", 0x00000000, 0, }, /* 0x0f3 */
+ { "Asia/Baku", "Azerbaijan Standard Time", 9, 24, "AZ", "AZ", 0x80000040, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0f4 */
+ { "Asia/Bangkok", "SE Asia Standard Time", 12, 21, "TH", "TH", 0x000000cd, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0f5 */
+ { "Asia/Barnaul", "Altai Standard Time", 12, 19, "RU", "RU", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0f6 */
+ { "Asia/Beirut", "Middle East Standard Time", 11, 25, "LB", "LB", 0x80000041, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0f7 */
+ { "Asia/Bishkek", "Central Asia Standard Time", 12, 26, "KG", "KG", 0x000000c3, 0, }, /* 0x0f8 */
+ { "Asia/Brunei", "Singapore Standard Time", 11, 23, "BN", "BN", 0x000000d7, 0, }, /* 0x0f9 */
+ { "Asia/Calcutta", "India Standard Time", 13, 19, "IN", "IN", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0fa */
+ { "Asia/Chita", "Transbaikal Standard Time", 10, 25, "RU", "RU", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0fb */
+ { "Asia/Choibalsan", "Ulaanbaatar Standard Time", 15, 25, "MN", "MN", 0x00000000, 0, }, /* 0x0fc */
+ { "Asia/Chongqing", NULL, 14, 0, "CN", "", 0x00000000, 0, }, /* 0x0fd */
+ { "Asia/Chungking", NULL, 14, 0, "CN", "", 0x00000000, 0, }, /* 0x0fe */
+ { "Asia/Colombo", "Sri Lanka Standard Time", 12, 23, "LK", "LK", 0x000000c8, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x0ff */
+ { "Asia/Dacca", NULL, 10, 0, "BD", "", 0x00000000, 0, }, /* 0x100 */
+ { "Asia/Damascus", "Syria Standard Time", 13, 19, "SY", "SY", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x101 */
+ { "Asia/Dhaka", "Bangladesh Standard Time", 10, 24, "BD", "BD", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x102 */
+ { "Asia/Dili", "Tokyo Standard Time", 9, 19, "TL", "TL", 0x000000eb, 0, }, /* 0x103 */
+ { "Asia/Dubai", "Arabian Standard Time", 10, 21, "AE", "AE", 0x000000a5, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x104 */
+ { "Asia/Dushanbe", "West Asia Standard Time", 13, 23, "TJ", "TJ", 0x000000b9, 0, }, /* 0x105 */
+ { "Asia/Famagusta", "Turkey Standard Time", 14, 20, "CY", "CY", 0x00000000, 0, }, /* 0x106 */
+ { "Asia/Gaza", "West Bank Standard Time", 9, 23, "PS", "PS", 0x00000000, 0, }, /* 0x107 */
+ { "Asia/Harbin", NULL, 11, 0, "CN", "", 0x00000000, 0, }, /* 0x108 */
+ { "Asia/Hebron", "West Bank Standard Time", 11, 23, "PS", "PS", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x109 */
+ { "Asia/Ho_Chi_Minh", NULL, 16, 0, "VN", "", 0x00000000, 0, }, /* 0x10a */
+ { "Asia/Hong_Kong", "China Standard Time", 14, 19, "HK", "HK", 0x000000d2, 0, }, /* 0x10b */
+ { "Asia/Hovd", "W. Mongolia Standard Time", 9, 25, "MN", "MN", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x10c */
+ { "Asia/Irkutsk", "North Asia East Standard Time", 12, 29, "RU", "RU", 0x000000e3, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x10d */
+ { "Asia/Istanbul", NULL, 13, 0, "TR", "", 0x00000000, 0, }, /* 0x10e */
+ { "Asia/Jakarta", "SE Asia Standard Time", 12, 21, "ID", "ID", 0x000000cd, 0, }, /* 0x10f */
+ { "Asia/Jayapura", "Tokyo Standard Time", 13, 19, "ID", "ID", 0x000000eb, 0, }, /* 0x110 */
+ { "Asia/Jerusalem", "Israel Standard Time", 14, 20, "IL", "IL", 0x00000087, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x111 */
+ { "Asia/Kabul", "Afghanistan Standard Time", 10, 25, "AF", "AF", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x112 */
+ { "Asia/Kamchatka", "Russia Time Zone 11", 14, 19, "RU", "RU", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x113 */
+ { "Asia/Karachi", "Pakistan Standard Time", 12, 22, "PK", "PK", 0x8000004e, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x114 */
+ { "Asia/Kashgar", NULL, 12, 0, "CN", "", 0x00000000, 0, }, /* 0x115 */
+ { "Asia/Kathmandu", NULL, 14, 0, "NP", "", 0x00000000, 0, }, /* 0x116 */
+ { "Asia/Katmandu", "Nepal Standard Time", 13, 19, "NP", "NP", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x117 */
+ { "Asia/Khandyga", "Yakutsk Standard Time", 13, 21, "RU", "RU", 0x000000f0, 0, }, /* 0x118 */
+ { "Asia/Kolkata", NULL, 12, 0, "IN", "", 0x00000000, 0, }, /* 0x119 */
+ { "Asia/Krasnoyarsk", "North Asia Standard Time", 16, 24, "RU", "RU", 0x000000cf, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x11a */
+ { "Asia/Kuala_Lumpur", "Singapore Standard Time", 17, 23, "MY", "MY", 0x000000d7, 0, }, /* 0x11b */
+ { "Asia/Kuching", "Singapore Standard Time", 12, 23, "MY", "MY", 0x000000d7, 0, }, /* 0x11c */
+ { "Asia/Kuwait", "Arab Standard Time", 11, 18, "SA", "KW", 0x00000000, 0, }, /* 0x11d */
+ { "Asia/Macao", NULL, 10, 0, "MO", "", 0x00000000, 0, }, /* 0x11e */
+ { "Asia/Macau", "China Standard Time", 10, 19, "MO", "MO", 0x000000d2, 0, }, /* 0x11f */
+ { "Asia/Magadan", "Magadan Standard Time", 12, 21, "RU", "RU", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x120 */
+ { "Asia/Makassar", "Singapore Standard Time", 13, 23, "ID", "ID", 0x000000d7, 0, }, /* 0x121 */
+ { "Asia/Manila", "Singapore Standard Time", 11, 23, "PH", "PH", 0x000000d7, 0, }, /* 0x122 */
+ { "Asia/Muscat", "Arabian Standard Time", 11, 21, "AE", "OM", 0x00000000, 0, }, /* 0x123 */
+ { "Asia/Nicosia", "GTB Standard Time", 12, 17, "CY", "CY", 0x00000082, 0, }, /* 0x124 */
+ { "Asia/Novokuznetsk", "North Asia Standard Time", 17, 24, "RU", "RU", 0x000000cf, 0, }, /* 0x125 */
+ { "Asia/Novosibirsk", "N. Central Asia Standard Time", 16, 29, "RU", "RU", 0x000000c9, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x126 */
+ { "Asia/Omsk", "Omsk Standard Time", 9, 18, "RU", "RU", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x127 */
+ { "Asia/Oral", "West Asia Standard Time", 9, 23, "KZ", "KZ", 0x000000b9, 0, }, /* 0x128 */
+ { "Asia/Phnom_Penh", "SE Asia Standard Time", 15, 21, "TH", "KH", 0x00000000, 0, }, /* 0x129 */
+ { "Asia/Pontianak", "SE Asia Standard Time", 14, 21, "ID", "ID", 0x000000cd, 0, }, /* 0x12a */
+ { "Asia/Pyongyang", "North Korea Standard Time", 14, 25, "KP", "KP", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x12b */
+ { "Asia/Qatar", "Arab Standard Time", 10, 18, "QA", "QA", 0x00000096, 0, }, /* 0x12c */
+ { "Asia/Qyzylorda", "Central Asia Standard Time", 14, 26, "KZ", "KZ", 0x000000c3, 0, }, /* 0x12d */
+ { "Asia/Rangoon", "Myanmar Standard Time", 12, 21, "MM", "MM", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x12e */
+ { "Asia/Riyadh", "Arab Standard Time", 11, 18, "SA", "SA", 0x00000096, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x12f */
+ { "Asia/Saigon", "SE Asia Standard Time", 11, 21, "VN", "VN", 0x00000000, 0, }, /* 0x130 */
+ { "Asia/Sakhalin", "Sakhalin Standard Time", 13, 22, "RU", "RU", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x131 */
+ { "Asia/Samarkand", "West Asia Standard Time", 14, 23, "UZ", "UZ", 0x000000b9, 0, }, /* 0x132 */
+ { "Asia/Seoul", "Korea Standard Time", 10, 19, "KR", "KR", 0x000000e6, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x133 */
+ { "Asia/Shanghai", "China Standard Time", 13, 19, "CN", "CN", 0x000000d2, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x134 */
+ { "Asia/Singapore", "Singapore Standard Time", 14, 23, "SG", "SG", 0x000000d7, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x135 */
+ { "Asia/Srednekolymsk", "Russia Time Zone 10", 18, 19, "RU", "RU", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x136 */
+ { "Asia/Taipei", "Taipei Standard Time", 11, 20, "TW", "TW", 0x000000dc, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x137 */
+ { "Asia/Tashkent", "West Asia Standard Time", 13, 23, "UZ", "UZ", 0x000000b9, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x138 */
+ { "Asia/Tbilisi", "Georgian Standard Time", 12, 22, "GE", "GE", 0x80000047, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x139 */
+ { "Asia/Tehran", "Iran Standard Time", 11, 18, "IR", "IR", 0x000000a0, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x13a */
+ { "Asia/Tel_Aviv", NULL, 13, 0, "IL", "", 0x00000000, 0, }, /* 0x13b */
+ { "Asia/Thimbu", NULL, 11, 0, "BT", "", 0x00000000, 0, }, /* 0x13c */
+ { "Asia/Thimphu", "Bangladesh Standard Time", 12, 24, "BT", "BT", 0x00000000, 0, }, /* 0x13d */
+ { "Asia/Tokyo", "Tokyo Standard Time", 10, 19, "JP", "JP", 0x000000eb, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x13e */
+ { "Asia/Tomsk", "Tomsk Standard Time", 10, 19, "RU", "RU", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x13f */
+ { "Asia/Ujung_Pandang", NULL, 18, 0, "ID", "", 0x00000000, 0, }, /* 0x140 */
+ { "Asia/Ulaanbaatar", "Ulaanbaatar Standard Time", 16, 25, "MN", "MN", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x141 */
+ { "Asia/Ulan_Bator", NULL, 15, 0, "MN", "", 0x00000000, 0, }, /* 0x142 */
+ { "Asia/Urumqi", "Central Asia Standard Time", 11, 26, "CN", "CN", 0x000000c3, 0, }, /* 0x143 */
+ { "Asia/Ust-Nera", "Vladivostok Standard Time", 13, 25, "RU", "RU", 0x0000010e, 0, }, /* 0x144 */
+ { "Asia/Vientiane", "SE Asia Standard Time", 14, 21, "TH", "LA", 0x00000000, 0, }, /* 0x145 */
+ { "Asia/Vladivostok", "Vladivostok Standard Time", 16, 25, "RU", "RU", 0x0000010e, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x146 */
+ { "Asia/Yakutsk", "Yakutsk Standard Time", 12, 21, "RU", "RU", 0x000000f0, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x147 */
+ { "Asia/Yangon", NULL, 11, 0, "MM", "", 0x00000000, 0, }, /* 0x148 */
+ { "Asia/Yekaterinburg", "Ekaterinburg Standard Time", 18, 26, "RU", "RU", 0x000000b4, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x149 */
+ { "Asia/Yerevan", "Caucasus Standard Time", 12, 22, "AM", "AM", 0x000000aa, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x14a */
+ { "Atlantic/Azores", "Azores Standard Time", 15, 20, "PT", "PT", 0x00000050, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x14b */
+ { "Atlantic/Bermuda", "Atlantic Standard Time", 16, 22, "BM", "BM", 0x00000032, 0, }, /* 0x14c */
+ { "Atlantic/Canary", "GMT Standard Time", 15, 17, "ES", "ES", 0x00000055, 0, }, /* 0x14d */
+ { "Atlantic/Cape_Verde", "Cape Verde Standard Time", 19, 24, "CV", "CV", 0x00000053, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x14e */
+ { "Atlantic/Faeroe", "GMT Standard Time", 15, 17, "FO", "FO", 0x00000000, 0, }, /* 0x14f */
+ { "Atlantic/Faroe", NULL, 14, 0, "FO", "", 0x00000000, 0, }, /* 0x150 */
+ { "Atlantic/Jan_Mayen", NULL, 18, 0, "NO", "", 0x00000000, 0, }, /* 0x151 */
+ { "Atlantic/Madeira", "GMT Standard Time", 16, 17, "PT", "PT", 0x00000055, 0, }, /* 0x152 */
+ { "Atlantic/Reykjavik", "Greenwich Standard Time", 18, 23, "IS", "IS", 0x0000005a, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x153 */
+ { "Atlantic/South_Georgia", "UTC-02", 22, 6, "GS", "GS", 0x00000000, 0, }, /* 0x154 */
+ { "Atlantic/St_Helena", "Greenwich Standard Time", 18, 23, "CI", "SH", 0x00000000, 0, }, /* 0x155 */
+ { "Atlantic/Stanley", "SA Eastern Standard Time", 16, 24, "FK", "FK", 0x00000046, 0, }, /* 0x156 */
+ { "Australia/ACT", NULL, 13, 0, "AU", "", 0x00000000, 0, }, /* 0x157 */
+ { "Australia/Adelaide", "Cen. Australia Standard Time", 18, 28, "AU", "AU", 0x000000fa, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x158 */
+ { "Australia/Brisbane", "E. Australia Standard Time", 18, 26, "AU", "AU", 0x00000104, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x159 */
+ { "Australia/Broken_Hill", "Cen. Australia Standard Time", 21, 28, "AU", "AU", 0x000000fa, 0, }, /* 0x15a */
+ { "Australia/Canberra", NULL, 18, 0, "AU", "", 0x00000000, 0, }, /* 0x15b */
+ { "Australia/Currie", "Tasmania Standard Time", 16, 22, "AU", "AU", 0x00000109, 0, }, /* 0x15c */
+ { "Australia/Darwin", "AUS Central Standard Time", 16, 25, "AU", "AU", 0x000000f5, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x15d */
+ { "Australia/Eucla", "Aus Central W. Standard Time", 15, 28, "AU", "AU", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x15e */
+ { "Australia/Hobart", "Tasmania Standard Time", 16, 22, "AU", "AU", 0x00000109, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x15f */
+ { "Australia/LHI", NULL, 13, 0, "AU", "", 0x00000000, 0, }, /* 0x160 */
+ { "Australia/Lindeman", "E. Australia Standard Time", 18, 26, "AU", "AU", 0x00000104, 0, }, /* 0x161 */
+ { "Australia/Lord_Howe", "Lord Howe Standard Time", 19, 23, "AU", "AU", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x162 */
+ { "Australia/Melbourne", "AUS Eastern Standard Time", 19, 25, "AU", "AU", 0x000000ff, 0, }, /* 0x163 */
+ { "Australia/NSW", NULL, 13, 0, "AU", "", 0x00000000, 0, }, /* 0x164 */
+ { "Australia/North", NULL, 15, 0, "AU", "", 0x00000000, 0, }, /* 0x165 */
+ { "Australia/Perth", "W. Australia Standard Time", 15, 26, "AU", "AU", 0x000000e1, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x166 */
+ { "Australia/Queensland", NULL, 20, 0, "AU", "", 0x00000000, 0, }, /* 0x167 */
+ { "Australia/South", NULL, 15, 0, "AU", "", 0x00000000, 0, }, /* 0x168 */
+ { "Australia/Sydney", "AUS Eastern Standard Time", 16, 25, "AU", "AU", 0x000000ff, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x169 */
+ { "Australia/Tasmania", NULL, 18, 0, "AU", "", 0x00000000, 0, }, /* 0x16a */
+ { "Australia/Victoria", NULL, 18, 0, "AU", "", 0x00000000, 0, }, /* 0x16b */
+ { "Australia/West", NULL, 14, 0, "AU", "", 0x00000000, 0, }, /* 0x16c */
+ { "Australia/Yancowinna", NULL, 20, 0, "AU", "", 0x00000000, 0, }, /* 0x16d */
+ { "Brazil/Acre", NULL, 11, 0, "BR", "", 0x00000000, 0, }, /* 0x16e */
+ { "Brazil/DeNoronha", NULL, 16, 0, "BR", "", 0x00000000, 0, }, /* 0x16f */
+ { "Brazil/East", NULL, 11, 0, "BR", "", 0x00000000, 0, }, /* 0x170 */
+ { "Brazil/West", NULL, 11, 0, "BR", "", 0x00000000, 0, }, /* 0x171 */
+ { "CET", NULL, 3, 0, "ZZ", "", 0x00000000, 0, }, /* 0x172 */
+ { "CST6CDT", "Central Standard Time", 7, 21, "ZZ", "ZZ", 0x00000014, 0, }, /* 0x173 */
+ { "Canada/Atlantic", NULL, 15, 0, "CA", "", 0x00000000, 0, }, /* 0x174 */
+ { "Canada/Central", NULL, 14, 0, "CA", "", 0x00000000, 0, }, /* 0x175 */
+ { "Canada/East-Saskatchewan", NULL, 24, 0, "CA", "", 0x00000000, 0, }, /* 0x176 */
+ { "Canada/Eastern", NULL, 14, 0, "CA", "", 0x00000000, 0, }, /* 0x177 */
+ { "Canada/Mountain", NULL, 15, 0, "CA", "", 0x00000000, 0, }, /* 0x178 */
+ { "Canada/Newfoundland", NULL, 19, 0, "CA", "", 0x00000000, 0, }, /* 0x179 */
+ { "Canada/Pacific", NULL, 14, 0, "CA", "", 0x00000000, 0, }, /* 0x17a */
+ { "Canada/Saskatchewan", NULL, 19, 0, "CA", "", 0x00000000, 0, }, /* 0x17b */
+ { "Canada/Yukon", NULL, 12, 0, "CA", "", 0x00000000, 0, }, /* 0x17c */
+ { "Chile/Continental", NULL, 17, 0, "CL", "", 0x00000000, 0, }, /* 0x17d */
+ { "Chile/EasterIsland", NULL, 18, 0, "CL", "", 0x00000000, 0, }, /* 0x17e */
+ { "Cuba", NULL, 4, 0, "CU", "", 0x00000000, 0, }, /* 0x17f */
+ { "EET", NULL, 3, 0, "ZZ", "", 0x00000000, 0, }, /* 0x180 */
+ { "EST", NULL, 3, 0, "ZZ", "", 0x00000000, 0, }, /* 0x181 */
+ { "EST5EDT", "Eastern Standard Time", 7, 21, "ZZ", "ZZ", 0x00000023, 0, }, /* 0x182 */
+ { "Egypt", NULL, 5, 0, "EG", "", 0x00000000, 0, }, /* 0x183 */
+ { "Eire", NULL, 4, 0, "IE", "", 0x00000000, 0, }, /* 0x184 */
+ { "Etc/GMT", "UTC", 7, 3, "ZZ", "ZZ", 0x80000050, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x185 */
+ { "Etc/GMT+0", NULL, 9, 0, "ZZ", "", 0x00000000, 0, }, /* 0x186 */
+ { "Etc/GMT+1", "Cape Verde Standard Time", 9, 24, "ZZ", "ZZ", 0x00000053, 0, }, /* 0x187 */
+ { "Etc/GMT+10", "Hawaiian Standard Time", 10, 22, "ZZ", "ZZ", 0x00000002, 0, }, /* 0x188 */
+ { "Etc/GMT+11", "UTC-11", 10, 6, "ZZ", "ZZ", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x189 */
+ { "Etc/GMT+12", "Dateline Standard Time", 10, 22, "ZZ", "ZZ", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x18a */
+ { "Etc/GMT+2", "UTC-02", 9, 6, "ZZ", "ZZ", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x18b */
+ { "Etc/GMT+3", "SA Eastern Standard Time", 9, 24, "ZZ", "ZZ", 0x00000046, 0, }, /* 0x18c */
+ { "Etc/GMT+4", "SA Western Standard Time", 9, 24, "ZZ", "ZZ", 0x00000037, 0, }, /* 0x18d */
+ { "Etc/GMT+5", "SA Pacific Standard Time", 9, 24, "ZZ", "ZZ", 0x0000002d, 0, }, /* 0x18e */
+ { "Etc/GMT+6", "Central America Standard Time", 9, 29, "ZZ", "ZZ", 0x00000021, 0, }, /* 0x18f */
+ { "Etc/GMT+7", "US Mountain Standard Time", 9, 25, "ZZ", "ZZ", 0x0000000f, 0, }, /* 0x190 */
+ { "Etc/GMT+8", "UTC-08", 9, 6, "ZZ", "ZZ", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x191 */
+ { "Etc/GMT+9", "UTC-09", 9, 6, "ZZ", "ZZ", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x192 */
+ { "Etc/GMT-0", NULL, 9, 0, "ZZ", "", 0x00000000, 0, }, /* 0x193 */
+ { "Etc/GMT-1", "W. Central Africa Standard Time", 9, 31, "ZZ", "ZZ", 0x00000071, 0, }, /* 0x194 */
+ { "Etc/GMT-10", "West Pacific Standard Time", 10, 26, "ZZ", "ZZ", 0x00000113, 0, }, /* 0x195 */
+ { "Etc/GMT-11", "Central Pacific Standard Time", 10, 29, "ZZ", "ZZ", 0x00000118, 0, }, /* 0x196 */
+ { "Etc/GMT-12", "UTC+12", 10, 6, "ZZ", "ZZ", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x197 */
+ { "Etc/GMT-13", "Tonga Standard Time", 10, 19, "ZZ", "ZZ", 0x0000012c, 0, }, /* 0x198 */
+ { "Etc/GMT-14", "Line Islands Standard Time", 10, 26, "ZZ", "ZZ", 0x00000000, 0, }, /* 0x199 */
+ { "Etc/GMT-2", "South Africa Standard Time", 9, 26, "ZZ", "ZZ", 0x0000008c, 0, }, /* 0x19a */
+ { "Etc/GMT-3", "E. Africa Standard Time", 9, 23, "ZZ", "ZZ", 0x0000009b, 0, }, /* 0x19b */
+ { "Etc/GMT-4", "Arabian Standard Time", 9, 21, "ZZ", "ZZ", 0x000000a5, 0, }, /* 0x19c */
+ { "Etc/GMT-5", "West Asia Standard Time", 9, 23, "ZZ", "ZZ", 0x000000b9, 0, }, /* 0x19d */
+ { "Etc/GMT-6", "Central Asia Standard Time", 9, 26, "ZZ", "ZZ", 0x000000c3, 0, }, /* 0x19e */
+ { "Etc/GMT-7", "SE Asia Standard Time", 9, 21, "ZZ", "ZZ", 0x000000cd, 0, }, /* 0x19f */
+ { "Etc/GMT-8", "Singapore Standard Time", 9, 23, "ZZ", "ZZ", 0x000000d7, 0, }, /* 0x1a0 */
+ { "Etc/GMT-9", "Tokyo Standard Time", 9, 19, "ZZ", "ZZ", 0x000000eb, 0, }, /* 0x1a1 */
+ { "Etc/GMT0", NULL, 8, 0, "ZZ", "", 0x00000000, 0, }, /* 0x1a2 */
+ { "Etc/Greenwich", NULL, 13, 0, "ZZ", "", 0x00000000, 0, }, /* 0x1a3 */
+ { "Etc/UCT", NULL, 7, 0, "ZZ", "", 0x00000000, 0, }, /* 0x1a4 */
+ { "Etc/UTC", "UTC", 7, 3, "ZZ", "ZZ", 0x80000050, 0, }, /* 0x1a5 */
+ { "Etc/Universal", NULL, 13, 0, "ZZ", "", 0x00000000, 0, }, /* 0x1a6 */
+ { "Etc/Zulu", NULL, 8, 0, "ZZ", "", 0x00000000, 0, }, /* 0x1a7 */
+ { "Europe/Amsterdam", "W. Europe Standard Time", 16, 23, "NL", "NL", 0x0000006e, 0, }, /* 0x1a8 */
+ { "Europe/Andorra", "W. Europe Standard Time", 14, 23, "AD", "AD", 0x0000006e, 0, }, /* 0x1a9 */
+ { "Europe/Astrakhan", "Astrakhan Standard Time", 16, 23, "RU", "RU", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1aa */
+ { "Europe/Athens", "GTB Standard Time", 13, 17, "GR", "GR", 0x00000082, 0, }, /* 0x1ab */
+ { "Europe/Belfast", NULL, 14, 0, "GB", "", 0x00000000, 0, }, /* 0x1ac */
+ { "Europe/Belgrade", "Central Europe Standard Time", 15, 28, "RS", "RS", 0x0000005f, 0, }, /* 0x1ad */
+ { "Europe/Berlin", "W. Europe Standard Time", 13, 23, "DE", "DE", 0x0000006e, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1ae */
+ { "Europe/Bratislava", "Central Europe Standard Time", 17, 28, "CZ", "SK", 0x00000000, 0, }, /* 0x1af */
+ { "Europe/Brussels", "Romance Standard Time", 15, 21, "BE", "BE", 0x00000069, 0, }, /* 0x1b0 */
+ { "Europe/Bucharest", "GTB Standard Time", 16, 17, "RO", "RO", 0x00000082, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1b1 */
+ { "Europe/Budapest", "Central Europe Standard Time", 15, 28, "HU", "HU", 0x0000005f, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1b2 */
+ { "Europe/Busingen", "W. Europe Standard Time", 15, 23, "CH", "DE", 0x00000000, 0, }, /* 0x1b3 */
+ { "Europe/Chisinau", "E. Europe Standard Time", 15, 23, "MD", "MD", 0x00000073, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1b4 */
+ { "Europe/Copenhagen", "Romance Standard Time", 17, 21, "DK", "DK", 0x00000069, 0, }, /* 0x1b5 */
+ { "Europe/Dublin", "GMT Standard Time", 13, 17, "IE", "IE", 0x00000055, 0, }, /* 0x1b6 */
+ { "Europe/Gibraltar", "W. Europe Standard Time", 16, 23, "GI", "GI", 0x0000006e, 0, }, /* 0x1b7 */
+ { "Europe/Guernsey", "GMT Standard Time", 15, 17, "GB", "GG", 0x00000000, 0, }, /* 0x1b8 */
+ { "Europe/Helsinki", "FLE Standard Time", 15, 17, "FI", "FI", 0x0000007d, 0, }, /* 0x1b9 */
+ { "Europe/Isle_of_Man", "GMT Standard Time", 18, 17, "GB", "IM", 0x00000000, 0, }, /* 0x1ba */
+ { "Europe/Istanbul", "Turkey Standard Time", 15, 20, "TR", "TR", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1bb */
+ { "Europe/Jersey", "GMT Standard Time", 13, 17, "GB", "JE", 0x00000000, 0, }, /* 0x1bc */
+ { "Europe/Kaliningrad", "Kaliningrad Standard Time", 18, 25, "RU", "RU", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1bd */
+ { "Europe/Kiev", "FLE Standard Time", 11, 17, "UA", "UA", 0x0000007d, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1be */
+ { "Europe/Kirov", "Russian Standard Time", 12, 21, "RU", "RU", 0x00000091, 0, }, /* 0x1bf */
+ { "Europe/Lisbon", "GMT Standard Time", 13, 17, "PT", "PT", 0x00000055, 0, }, /* 0x1c0 */
+ { "Europe/Ljubljana", "Central Europe Standard Time", 16, 28, "RS", "SI", 0x00000000, 0, }, /* 0x1c1 */
+ { "Europe/London", "GMT Standard Time", 13, 17, "GB", "GB", 0x00000055, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1c2 */
+ { "Europe/Luxembourg", "W. Europe Standard Time", 17, 23, "LU", "LU", 0x0000006e, 0, }, /* 0x1c3 */
+ { "Europe/Madrid", "Romance Standard Time", 13, 21, "ES", "ES", 0x00000069, 0, }, /* 0x1c4 */
+ { "Europe/Malta", "W. Europe Standard Time", 12, 23, "MT", "MT", 0x0000006e, 0, }, /* 0x1c5 */
+ { "Europe/Mariehamn", "FLE Standard Time", 16, 17, "FI", "AX", 0x00000000, 0, }, /* 0x1c6 */
+ { "Europe/Minsk", "Belarus Standard Time", 12, 21, "BY", "BY", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1c7 */
+ { "Europe/Monaco", "W. Europe Standard Time", 13, 23, "MC", "MC", 0x0000006e, 0, }, /* 0x1c8 */
+ { "Europe/Moscow", "Russian Standard Time", 13, 21, "RU", "RU", 0x00000091, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1c9 */
+ { "Europe/Nicosia", NULL, 14, 0, "CY", "", 0x00000000, 0, }, /* 0x1ca */
+ { "Europe/Oslo", "W. Europe Standard Time", 11, 23, "NO", "NO", 0x0000006e, 0, }, /* 0x1cb */
+ { "Europe/Paris", "Romance Standard Time", 12, 21, "FR", "FR", 0x00000069, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1cc */
+ { "Europe/Podgorica", "Central Europe Standard Time", 16, 28, "RS", "ME", 0x00000000, 0, }, /* 0x1cd */
+ { "Europe/Prague", "Central Europe Standard Time", 13, 28, "CZ", "CZ", 0x0000005f, 0, }, /* 0x1ce */
+ { "Europe/Riga", "FLE Standard Time", 11, 17, "LV", "LV", 0x0000007d, 0, }, /* 0x1cf */
+ { "Europe/Rome", "W. Europe Standard Time", 11, 23, "IT", "IT", 0x0000006e, 0, }, /* 0x1d0 */
+ { "Europe/Samara", "Russia Time Zone 3", 13, 18, "RU", "RU", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1d1 */
+ { "Europe/San_Marino", "W. Europe Standard Time", 17, 23, "IT", "SM", 0x00000000, 0, }, /* 0x1d2 */
+ { "Europe/Sarajevo", "Central European Standard Time", 15, 30, "RS", "BA", 0x00000000, 0, }, /* 0x1d3 */
+ { "Europe/Saratov", "Astrakhan Standard Time", 14, 23, "RU", "RU", 0x00000000, 0, }, /* 0x1d4 */
+ { "Europe/Simferopol", "Russian Standard Time", 17, 21, "RU", "UA", 0x00000091, 0, }, /* 0x1d5 */
+ { "Europe/Skopje", "Central European Standard Time", 13, 30, "RS", "MK", 0x00000000, 0, }, /* 0x1d6 */
+ { "Europe/Sofia", "FLE Standard Time", 12, 17, "BG", "BG", 0x0000007d, 0, }, /* 0x1d7 */
+ { "Europe/Stockholm", "W. Europe Standard Time", 16, 23, "SE", "SE", 0x0000006e, 0, }, /* 0x1d8 */
+ { "Europe/Tallinn", "FLE Standard Time", 14, 17, "EE", "EE", 0x0000007d, 0, }, /* 0x1d9 */
+ { "Europe/Tirane", "Central Europe Standard Time", 13, 28, "AL", "AL", 0x0000005f, 0, }, /* 0x1da */
+ { "Europe/Tiraspol", NULL, 15, 0, "MD", "", 0x00000000, 0, }, /* 0x1db */
+ { "Europe/Ulyanovsk", "Astrakhan Standard Time", 16, 23, "RU", "RU", 0x00000000, 0, }, /* 0x1dc */
+ { "Europe/Uzhgorod", "FLE Standard Time", 15, 17, "UA", "UA", 0x0000007d, 0, }, /* 0x1dd */
+ { "Europe/Vaduz", "W. Europe Standard Time", 12, 23, "CH", "LI", 0x00000000, 0, }, /* 0x1de */
+ { "Europe/Vatican", "W. Europe Standard Time", 14, 23, "IT", "VA", 0x00000000, 0, }, /* 0x1df */
+ { "Europe/Vienna", "W. Europe Standard Time", 13, 23, "AT", "AT", 0x0000006e, 0, }, /* 0x1e0 */
+ { "Europe/Vilnius", "FLE Standard Time", 14, 17, "LT", "LT", 0x0000007d, 0, }, /* 0x1e1 */
+ { "Europe/Volgograd", "Russian Standard Time", 16, 21, "RU", "RU", 0x00000091, 0, }, /* 0x1e2 */
+ { "Europe/Warsaw", "Central European Standard Time", 13, 30, "PL", "PL", 0x00000064, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1e3 */
+ { "Europe/Zagreb", "Central European Standard Time", 13, 30, "RS", "HR", 0x00000000, 0, }, /* 0x1e4 */
+ { "Europe/Zaporozhye", "FLE Standard Time", 17, 17, "UA", "UA", 0x0000007d, 0, }, /* 0x1e5 */
+ { "Europe/Zurich", "W. Europe Standard Time", 13, 23, "CH", "CH", 0x0000006e, 0, }, /* 0x1e6 */
+ { "Factory", NULL, 7, 0, "ZZ", "", 0x00000000, 0, }, /* 0x1e7 */
+ { "GB", NULL, 2, 0, "GB", "", 0x00000000, 0, }, /* 0x1e8 */
+ { "GB-Eire", NULL, 7, 0, "GB", "", 0x00000000, 0, }, /* 0x1e9 */
+ { "GMT", NULL, 3, 0, "ZZ", "", 0x00000000, 0, }, /* 0x1ea */
+ { "GMT+0", NULL, 5, 0, "ZZ", "", 0x00000000, 0, }, /* 0x1eb */
+ { "GMT-0", NULL, 5, 0, "ZZ", "", 0x00000000, 0, }, /* 0x1ec */
+ { "GMT0", NULL, 4, 0, "ZZ", "", 0x00000000, 0, }, /* 0x1ed */
+ { "Greenwich", NULL, 9, 0, "ZZ", "", 0x00000000, 0, }, /* 0x1ee */
+ { "HST", NULL, 3, 0, "ZZ", "", 0x00000000, 0, }, /* 0x1ef */
+ { "Hongkong", NULL, 8, 0, "HK", "", 0x00000000, 0, }, /* 0x1f0 */
+ { "Iceland", NULL, 7, 0, "IS", "", 0x00000000, 0, }, /* 0x1f1 */
+ { "Indian/Antananarivo", "E. Africa Standard Time", 19, 23, "KE", "MG", 0x00000000, 0, }, /* 0x1f2 */
+ { "Indian/Chagos", "Central Asia Standard Time", 13, 26, "IO", "IO", 0x000000c3, 0, }, /* 0x1f3 */
+ { "Indian/Christmas", "SE Asia Standard Time", 16, 21, "CX", "CX", 0x000000cd, 0, }, /* 0x1f4 */
+ { "Indian/Cocos", "Myanmar Standard Time", 12, 21, "CC", "CC", 0x000000cb, 0, }, /* 0x1f5 */
+ { "Indian/Comoro", "E. Africa Standard Time", 13, 23, "KE", "KM", 0x00000000, 0, }, /* 0x1f6 */
+ { "Indian/Kerguelen", "West Asia Standard Time", 16, 23, "TF", "TF", 0x000000b9, 0, }, /* 0x1f7 */
+ { "Indian/Mahe", "Mauritius Standard Time", 11, 23, "SC", "SC", 0x8000004f, 0, }, /* 0x1f8 */
+ { "Indian/Maldives", "West Asia Standard Time", 15, 23, "MV", "MV", 0x000000b9, 0, }, /* 0x1f9 */
+ { "Indian/Mauritius", "Mauritius Standard Time", 16, 23, "MU", "MU", 0x8000004f, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x1fa */
+ { "Indian/Mayotte", "E. Africa Standard Time", 14, 23, "KE", "YT", 0x00000000, 0, }, /* 0x1fb */
+ { "Indian/Reunion", "Mauritius Standard Time", 14, 23, "RE", "RE", 0x8000004f, 0, }, /* 0x1fc */
+ { "Iran", NULL, 4, 0, "IR", "", 0x00000000, 0, }, /* 0x1fd */
+ { "Israel", NULL, 6, 0, "IL", "", 0x00000000, 0, }, /* 0x1fe */
+ { "Jamaica", NULL, 7, 0, "JM", "", 0x00000000, 0, }, /* 0x1ff */
+ { "Japan", NULL, 5, 0, "JP", "", 0x00000000, 0, }, /* 0x200 */
+ { "Kwajalein", NULL, 9, 0, "MH", "", 0x00000000, 0, }, /* 0x201 */
+ { "Libya", NULL, 5, 0, "LY", "", 0x00000000, 0, }, /* 0x202 */
+ { "MET", NULL, 3, 0, "ZZ", "", 0x00000000, 0, }, /* 0x203 */
+ { "MST", NULL, 3, 0, "ZZ", "", 0x00000000, 0, }, /* 0x204 */
+ { "MST7MDT", "Mountain Standard Time", 7, 22, "ZZ", "ZZ", 0x0000000a, 0, }, /* 0x205 */
+ { "Mexico/BajaNorte", NULL, 16, 0, "MX", "", 0x00000000, 0, }, /* 0x206 */
+ { "Mexico/BajaSur", NULL, 14, 0, "MX", "", 0x00000000, 0, }, /* 0x207 */
+ { "Mexico/General", NULL, 14, 0, "MX", "", 0x00000000, 0, }, /* 0x208 */
+ { "NZ", NULL, 2, 0, "NZ", "", 0x00000000, 0, }, /* 0x209 */
+ { "NZ-CHAT", NULL, 7, 0, "NZ", "", 0x00000000, 0, }, /* 0x20a */
+ { "Navajo", NULL, 6, 0, "US", "", 0x00000000, 0, }, /* 0x20b */
+ { "PRC", NULL, 3, 0, "CN", "", 0x00000000, 0, }, /* 0x20c */
+ { "PST8PDT", "Pacific Standard Time", 7, 21, "ZZ", "ZZ", 0x00000004, 0, }, /* 0x20d */
+ { "Pacific/Apia", "Samoa Standard Time", 12, 19, "WS", "WS", 0x00000001, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x20e */
+ { "Pacific/Auckland", "New Zealand Standard Time", 16, 25, "NZ", "NZ", 0x00000122, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x20f */
+ { "Pacific/Bougainville", "Bougainville Standard Time", 20, 26, "PG", "PG", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x210 */
+ { "Pacific/Chatham", "Chatham Islands Standard Time", 15, 29, "NZ", "NZ", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x211 */
+ { "Pacific/Chuuk", NULL, 13, 0, "FM", "", 0x00000000, 0, }, /* 0x212 */
+ { "Pacific/Easter", "Easter Island Standard Time", 14, 27, "CL", "CL", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x213 */
+ { "Pacific/Efate", "Central Pacific Standard Time", 13, 29, "VU", "VU", 0x00000118, 0, }, /* 0x214 */
+ { "Pacific/Enderbury", "Tonga Standard Time", 17, 19, "KI", "KI", 0x0000012c, 0, }, /* 0x215 */
+ { "Pacific/Fakaofo", "Tonga Standard Time", 15, 19, "TK", "TK", 0x0000012c, 0, }, /* 0x216 */
+ { "Pacific/Fiji", "Fiji Standard Time", 12, 18, "FJ", "FJ", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x217 */
+ { "Pacific/Funafuti", "UTC+12", 16, 6, "TV", "TV", 0x00000000, 0, }, /* 0x218 */
+ { "Pacific/Galapagos", "Central America Standard Time", 17, 29, "EC", "EC", 0x00000021, 0, }, /* 0x219 */
+ { "Pacific/Gambier", "UTC-09", 15, 6, "PF", "PF", 0x00000000, 0, }, /* 0x21a */
+ { "Pacific/Guadalcanal", "Central Pacific Standard Time", 19, 29, "SB", "SB", 0x00000118, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x21b */
+ { "Pacific/Guam", "West Pacific Standard Time", 12, 26, "GU", "GU", 0x00000113, 0, }, /* 0x21c */
+ { "Pacific/Honolulu", "Hawaiian Standard Time", 16, 22, "US", "US", 0x00000002, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x21d */
+ { "Pacific/Johnston", "Hawaiian Standard Time", 16, 22, "US", "UM", 0x00000000, 0, }, /* 0x21e */
+ { "Pacific/Kiritimati", "Line Islands Standard Time", 18, 26, "KI", "KI", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x21f */
+ { "Pacific/Kosrae", "Central Pacific Standard Time", 14, 29, "FM", "FM", 0x00000118, 0, }, /* 0x220 */
+ { "Pacific/Kwajalein", "UTC+12", 17, 6, "MH", "MH", 0x00000000, 0, }, /* 0x221 */
+ { "Pacific/Majuro", "UTC+12", 14, 6, "MH", "MH", 0x00000000, 0, }, /* 0x222 */
+ { "Pacific/Marquesas", "Marquesas Standard Time", 17, 23, "PF", "PF", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x223 */
+ { "Pacific/Midway", "UTC-11", 14, 6, "AS", "UM", 0x00000000, 0, }, /* 0x224 */
+ { "Pacific/Nauru", "UTC+12", 13, 6, "NR", "NR", 0x00000000, 0, }, /* 0x225 */
+ { "Pacific/Niue", "UTC-11", 12, 6, "NU", "NU", 0x00000000, 0, }, /* 0x226 */
+ { "Pacific/Norfolk", "Norfolk Standard Time", 15, 21, "NF", "NF", 0x00000000, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x227 */
+ { "Pacific/Noumea", "Central Pacific Standard Time", 14, 29, "NC", "NC", 0x00000118, 0, }, /* 0x228 */
+ { "Pacific/Pago_Pago", "UTC-11", 17, 6, "AS", "AS", 0x00000000, 0, }, /* 0x229 */
+ { "Pacific/Palau", "Tokyo Standard Time", 13, 19, "PW", "PW", 0x000000eb, 0, }, /* 0x22a */
+ { "Pacific/Pitcairn", "UTC-08", 16, 6, "PN", "PN", 0x00000000, 0, }, /* 0x22b */
+ { "Pacific/Pohnpei", NULL, 15, 0, "FM", "", 0x00000000, 0, }, /* 0x22c */
+ { "Pacific/Ponape", "Central Pacific Standard Time", 14, 29, "FM", "FM", 0x00000000, 0, }, /* 0x22d */
+ { "Pacific/Port_Moresby", "West Pacific Standard Time", 20, 26, "PG", "PG", 0x00000113, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x22e */
+ { "Pacific/Rarotonga", "Hawaiian Standard Time", 17, 22, "CK", "CK", 0x00000002, 0, }, /* 0x22f */
+ { "Pacific/Saipan", "West Pacific Standard Time", 14, 26, "GU", "MP", 0x00000000, 0, }, /* 0x230 */
+ { "Pacific/Samoa", NULL, 13, 0, "AS", "", 0x00000000, 0, }, /* 0x231 */
+ { "Pacific/Tahiti", "Hawaiian Standard Time", 14, 22, "PF", "PF", 0x00000002, 0, }, /* 0x232 */
+ { "Pacific/Tarawa", "UTC+12", 14, 6, "KI", "KI", 0x00000000, 0, }, /* 0x233 */
+ { "Pacific/Tongatapu", "Tonga Standard Time", 17, 19, "TO", "TO", 0x0000012c, RTTIMEZONEINFO_F_GOLDEN, }, /* 0x234 */
+ { "Pacific/Truk", "West Pacific Standard Time", 12, 26, "FM", "FM", 0x00000000, 0, }, /* 0x235 */
+ { "Pacific/Wake", "UTC+12", 12, 6, "UM", "UM", 0x00000000, 0, }, /* 0x236 */
+ { "Pacific/Wallis", "UTC+12", 14, 6, "WF", "WF", 0x00000000, 0, }, /* 0x237 */
+ { "Pacific/Yap", NULL, 11, 0, "FM", "", 0x00000000, 0, }, /* 0x238 */
+ { "Poland", NULL, 6, 0, "PL", "", 0x00000000, 0, }, /* 0x239 */
+ { "Portugal", NULL, 8, 0, "PT", "", 0x00000000, 0, }, /* 0x23a */
+ { "ROC", NULL, 3, 0, "TW", "", 0x00000000, 0, }, /* 0x23b */
+ { "ROK", NULL, 3, 0, "KR", "", 0x00000000, 0, }, /* 0x23c */
+ { "Singapore", NULL, 9, 0, "SG", "", 0x00000000, 0, }, /* 0x23d */
+ { "Turkey", NULL, 6, 0, "TR", "", 0x00000000, 0, }, /* 0x23e */
+ { "UCT", NULL, 3, 0, "ZZ", "", 0x00000000, 0, }, /* 0x23f */
+ { "US/Alaska", NULL, 9, 0, "US", "", 0x00000000, 0, }, /* 0x240 */
+ { "US/Aleutian", NULL, 11, 0, "US", "", 0x00000000, 0, }, /* 0x241 */
+ { "US/Arizona", NULL, 10, 0, "US", "", 0x00000000, 0, }, /* 0x242 */
+ { "US/Central", NULL, 10, 0, "US", "", 0x00000000, 0, }, /* 0x243 */
+ { "US/East-Indiana", NULL, 15, 0, "US", "", 0x00000000, 0, }, /* 0x244 */
+ { "US/Eastern", NULL, 10, 0, "US", "", 0x00000000, 0, }, /* 0x245 */
+ { "US/Hawaii", NULL, 9, 0, "US", "", 0x00000000, 0, }, /* 0x246 */
+ { "US/Indiana-Starke", NULL, 17, 0, "US", "", 0x00000000, 0, }, /* 0x247 */
+ { "US/Michigan", NULL, 11, 0, "US", "", 0x00000000, 0, }, /* 0x248 */
+ { "US/Mountain", NULL, 11, 0, "US", "", 0x00000000, 0, }, /* 0x249 */
+ { "US/Pacific", NULL, 10, 0, "US", "", 0x00000000, 0, }, /* 0x24a */
+ { "US/Pacific-New", NULL, 14, 0, "US", "", 0x00000000, 0, }, /* 0x24b */
+ { "US/Samoa", NULL, 8, 0, "AS", "", 0x00000000, 0, }, /* 0x24c */
+ { "UTC", NULL, 3, 0, "ZZ", "", 0x00000000, 0, }, /* 0x24d */
+ { "Universal", NULL, 9, 0, "ZZ", "", 0x00000000, 0, }, /* 0x24e */
+ { "W-SU", NULL, 4, 0, "RU", "", 0x00000000, 0, }, /* 0x24f */
+ { "WET", NULL, 3, 0, "ZZ", "", 0x00000000, 0, }, /* 0x250 */
+ { "Zulu", NULL, 4, 0, "ZZ", "", 0x00000000, 0, }, /* 0x251 */
+};
+
+/**
+ * Windows time zone lookup table. Sorted by name, golden flag and territory.
+ */
+static const uint16_t g_aidxWinTimeZones[] =
+{
+ 0x15d, /* AUS Central Standard Time / AU+ ==> Australia/Darwin */
+ 0x169, /* AUS Eastern Standard Time / AU+ ==> Australia/Sydney */
+ 0x163, /* AUS Eastern Standard Time / AU ==> Australia/Melbourne */
+ 0x112, /* Afghanistan Standard Time / AF+ ==> Asia/Kabul */
+ 0x037, /* Alaskan Standard Time / US+ ==> America/Anchorage */
+ 0x08b, /* Alaskan Standard Time / US ==> America/Juneau */
+ 0x09f, /* Alaskan Standard Time / US ==> America/Metlakatla */
+ 0x0aa, /* Alaskan Standard Time / US ==> America/Nome */
+ 0x0c8, /* Alaskan Standard Time / US ==> America/Sitka */
+ 0x0da, /* Alaskan Standard Time / US ==> America/Yakutat */
+ 0x036, /* Aleutian Standard Time / US+ ==> America/Adak */
+ 0x0f6, /* Altai Standard Time / RU+ ==> Asia/Barnaul */
+ 0x12f, /* Arab Standard Time / SA+ ==> Asia/Riyadh */
+ 0x0f3, /* Arab Standard Time / BH ==> Asia/Bahrain */
+ 0x11d, /* Arab Standard Time / KW ==> Asia/Kuwait */
+ 0x12c, /* Arab Standard Time / QA ==> Asia/Qatar */
+ 0x0e9, /* Arab Standard Time / YE ==> Asia/Aden */
+ 0x104, /* Arabian Standard Time / AE+ ==> Asia/Dubai */
+ 0x123, /* Arabian Standard Time / OM ==> Asia/Muscat */
+ 0x19c, /* Arabian Standard Time / ZZ ==> Etc/GMT-4 */
+ 0x0f2, /* Arabic Standard Time / IQ+ ==> Asia/Baghdad */
+ 0x055, /* Argentina Standard Time / AR+ ==> America/Buenos_Aires */
+ 0x040, /* Argentina Standard Time / AR ==> America/Argentina/La_Rioja */
+ 0x042, /* Argentina Standard Time / AR ==> America/Argentina/Rio_Gallegos */
+ 0x043, /* Argentina Standard Time / AR ==> America/Argentina/Salta */
+ 0x044, /* Argentina Standard Time / AR ==> America/Argentina/San_Juan */
+ 0x045, /* Argentina Standard Time / AR ==> America/Argentina/San_Luis */
+ 0x046, /* Argentina Standard Time / AR ==> America/Argentina/Tucuman */
+ 0x047, /* Argentina Standard Time / AR ==> America/Argentina/Ushuaia */
+ 0x05a, /* Argentina Standard Time / AR ==> America/Catamarca */
+ 0x060, /* Argentina Standard Time / AR ==> America/Cordoba */
+ 0x08a, /* Argentina Standard Time / AR ==> America/Jujuy */
+ 0x09c, /* Argentina Standard Time / AR ==> America/Mendoza */
+ 0x1aa, /* Astrakhan Standard Time / RU+ ==> Europe/Astrakhan */
+ 0x1d4, /* Astrakhan Standard Time / RU ==> Europe/Saratov */
+ 0x1dc, /* Astrakhan Standard Time / RU ==> Europe/Ulyanovsk */
+ 0x07b, /* Atlantic Standard Time / CA+ ==> America/Halifax */
+ 0x14c, /* Atlantic Standard Time / BM ==> Atlantic/Bermuda */
+ 0x072, /* Atlantic Standard Time / CA ==> America/Glace_Bay */
+ 0x074, /* Atlantic Standard Time / CA ==> America/Goose_Bay */
+ 0x0a2, /* Atlantic Standard Time / CA ==> America/Moncton */
+ 0x0d1, /* Atlantic Standard Time / GL ==> America/Thule */
+ 0x15e, /* Aus Central W. Standard Time / AU+ ==> Australia/Eucla */
+ 0x0f4, /* Azerbaijan Standard Time / AZ+ ==> Asia/Baku */
+ 0x14b, /* Azores Standard Time / PT+ ==> Atlantic/Azores */
+ 0x0c6, /* Azores Standard Time / GL ==> America/Scoresbysund */
+ 0x04c, /* Bahia Standard Time / BR+ ==> America/Bahia */
+ 0x102, /* Bangladesh Standard Time / BD+ ==> Asia/Dhaka */
+ 0x13d, /* Bangladesh Standard Time / BT ==> Asia/Thimphu */
+ 0x1c7, /* Belarus Standard Time / BY+ ==> Europe/Minsk */
+ 0x210, /* Bougainville Standard Time / PG+ ==> Pacific/Bougainville */
+ 0x0bd, /* Canada Central Standard Time / CA+ ==> America/Regina */
+ 0x0cf, /* Canada Central Standard Time / CA ==> America/Swift_Current */
+ 0x14e, /* Cape Verde Standard Time / CV+ ==> Atlantic/Cape_Verde */
+ 0x187, /* Cape Verde Standard Time / ZZ ==> Etc/GMT+1 */
+ 0x14a, /* Caucasus Standard Time / AM+ ==> Asia/Yerevan */
+ 0x158, /* Cen. Australia Standard Time / AU+ ==> Australia/Adelaide */
+ 0x15a, /* Cen. Australia Standard Time / AU ==> Australia/Broken_Hill */
+ 0x078, /* Central America Standard Time / GT+ ==> America/Guatemala */
+ 0x050, /* Central America Standard Time / BZ ==> America/Belize */
+ 0x061, /* Central America Standard Time / CR ==> America/Costa_Rica */
+ 0x219, /* Central America Standard Time / EC ==> Pacific/Galapagos */
+ 0x0d0, /* Central America Standard Time / HN ==> America/Tegucigalpa */
+ 0x096, /* Central America Standard Time / NI ==> America/Managua */
+ 0x06d, /* Central America Standard Time / SV ==> America/El_Salvador */
+ 0x18f, /* Central America Standard Time / ZZ ==> Etc/GMT+6 */
+ 0x0ea, /* Central Asia Standard Time / KZ+ ==> Asia/Almaty */
+ 0x0e7, /* Central Asia Standard Time / AQ ==> Antarctica/Vostok */
+ 0x143, /* Central Asia Standard Time / CN ==> Asia/Urumqi */
+ 0x1f3, /* Central Asia Standard Time / IO ==> Indian/Chagos */
+ 0x0f8, /* Central Asia Standard Time / KG ==> Asia/Bishkek */
+ 0x12d, /* Central Asia Standard Time / KZ ==> Asia/Qyzylorda */
+ 0x19e, /* Central Asia Standard Time / ZZ ==> Etc/GMT-6 */
+ 0x063, /* Central Brazilian Standard Time / BR+ ==> America/Cuiaba */
+ 0x057, /* Central Brazilian Standard Time / BR ==> America/Campo_Grande */
+ 0x1b2, /* Central Europe Standard Time / HU+ ==> Europe/Budapest */
+ 0x1da, /* Central Europe Standard Time / AL ==> Europe/Tirane */
+ 0x1ce, /* Central Europe Standard Time / CZ ==> Europe/Prague */
+ 0x1cd, /* Central Europe Standard Time / ME ==> Europe/Podgorica */
+ 0x1ad, /* Central Europe Standard Time / RS ==> Europe/Belgrade */
+ 0x1c1, /* Central Europe Standard Time / SI ==> Europe/Ljubljana */
+ 0x1af, /* Central Europe Standard Time / SK ==> Europe/Bratislava */
+ 0x1e3, /* Central European Standard Time / PL+ ==> Europe/Warsaw */
+ 0x1d3, /* Central European Standard Time / BA ==> Europe/Sarajevo */
+ 0x1e4, /* Central European Standard Time / HR ==> Europe/Zagreb */
+ 0x1d6, /* Central European Standard Time / MK ==> Europe/Skopje */
+ 0x21b, /* Central Pacific Standard Time / SB+ ==> Pacific/Guadalcanal */
+ 0x0dc, /* Central Pacific Standard Time / AQ ==> Antarctica/Casey */
+ 0x0df, /* Central Pacific Standard Time / AU ==> Antarctica/Macquarie */
+ 0x220, /* Central Pacific Standard Time / FM ==> Pacific/Kosrae */
+ 0x22d, /* Central Pacific Standard Time / FM ==> Pacific/Ponape */
+ 0x228, /* Central Pacific Standard Time / NC ==> Pacific/Noumea */
+ 0x214, /* Central Pacific Standard Time / VU ==> Pacific/Efate */
+ 0x196, /* Central Pacific Standard Time / ZZ ==> Etc/GMT-11 */
+ 0x0a0, /* Central Standard Time (Mexico) / MX+ ==> America/Mexico_City */
+ 0x04d, /* Central Standard Time (Mexico) / MX ==> America/Bahia_Banderas */
+ 0x09e, /* Central Standard Time (Mexico) / MX ==> America/Merida */
+ 0x0a3, /* Central Standard Time (Mexico) / MX ==> America/Monterrey */
+ 0x05d, /* Central Standard Time / US+ ==> America/Chicago */
+ 0x0ba, /* Central Standard Time / CA ==> America/Rainy_River */
+ 0x0bb, /* Central Standard Time / CA ==> America/Rankin_Inlet */
+ 0x0be, /* Central Standard Time / CA ==> America/Resolute */
+ 0x0d9, /* Central Standard Time / CA ==> America/Winnipeg */
+ 0x09a, /* Central Standard Time / MX ==> America/Matamoros */
+ 0x07f, /* Central Standard Time / US ==> America/Indiana/Knox */
+ 0x082, /* Central Standard Time / US ==> America/Indiana/Tell_City */
+ 0x09d, /* Central Standard Time / US ==> America/Menominee */
+ 0x0ac, /* Central Standard Time / US ==> America/North_Dakota/Beulah */
+ 0x0ad, /* Central Standard Time / US ==> America/North_Dakota/Center */
+ 0x0ae, /* Central Standard Time / US ==> America/North_Dakota/New_Salem */
+ 0x173, /* Central Standard Time / ZZ ==> CST6CDT */
+ 0x211, /* Chatham Islands Standard Time / NZ+ ==> Pacific/Chatham */
+ 0x134, /* China Standard Time / CN+ ==> Asia/Shanghai */
+ 0x10b, /* China Standard Time / HK ==> Asia/Hong_Kong */
+ 0x11f, /* China Standard Time / MO ==> Asia/Macau */
+ 0x07c, /* Cuba Standard Time / CU+ ==> America/Havana */
+ 0x18a, /* Dateline Standard Time / ZZ+ ==> Etc/GMT+12 */
+ 0x02b, /* E. Africa Standard Time / KE+ ==> Africa/Nairobi */
+ 0x0e5, /* E. Africa Standard Time / AQ ==> Antarctica/Syowa */
+ 0x013, /* E. Africa Standard Time / DJ ==> Africa/Djibouti */
+ 0x005, /* E. Africa Standard Time / ER ==> Africa/Asmera */
+ 0x002, /* E. Africa Standard Time / ET ==> Africa/Addis_Ababa */
+ 0x1f6, /* E. Africa Standard Time / KM ==> Indian/Comoro */
+ 0x1f2, /* E. Africa Standard Time / MG ==> Indian/Antananarivo */
+ 0x01c, /* E. Africa Standard Time / SD ==> Africa/Khartoum */
+ 0x029, /* E. Africa Standard Time / SO ==> Africa/Mogadishu */
+ 0x01a, /* E. Africa Standard Time / SS ==> Africa/Juba */
+ 0x012, /* E. Africa Standard Time / TZ ==> Africa/Dar_es_Salaam */
+ 0x01b, /* E. Africa Standard Time / UG ==> Africa/Kampala */
+ 0x1fb, /* E. Africa Standard Time / YT ==> Indian/Mayotte */
+ 0x19b, /* E. Africa Standard Time / ZZ ==> Etc/GMT-3 */
+ 0x159, /* E. Australia Standard Time / AU+ ==> Australia/Brisbane */
+ 0x161, /* E. Australia Standard Time / AU ==> Australia/Lindeman */
+ 0x1b4, /* E. Europe Standard Time / MD+ ==> Europe/Chisinau */
+ 0x0c5, /* E. South America Standard Time / BR+ ==> America/Sao_Paulo */
+ 0x213, /* Easter Island Standard Time / CL+ ==> Pacific/Easter */
+ 0x058, /* Eastern Standard Time (Mexico) / MX+ ==> America/Cancun */
+ 0x0a8, /* Eastern Standard Time / US+ ==> America/New_York */
+ 0x0a7, /* Eastern Standard Time / BS ==> America/Nassau */
+ 0x088, /* Eastern Standard Time / CA ==> America/Iqaluit */
+ 0x0a5, /* Eastern Standard Time / CA ==> America/Montreal */
+ 0x0a9, /* Eastern Standard Time / CA ==> America/Nipigon */
+ 0x0b1, /* Eastern Standard Time / CA ==> America/Pangnirtung */
+ 0x0d2, /* Eastern Standard Time / CA ==> America/Thunder_Bay */
+ 0x0d4, /* Eastern Standard Time / CA ==> America/Toronto */
+ 0x069, /* Eastern Standard Time / US ==> America/Detroit */
+ 0x081, /* Eastern Standard Time / US ==> America/Indiana/Petersburg */
+ 0x084, /* Eastern Standard Time / US ==> America/Indiana/Vincennes */
+ 0x085, /* Eastern Standard Time / US ==> America/Indiana/Winamac */
+ 0x08d, /* Eastern Standard Time / US ==> America/Kentucky/Monticello */
+ 0x093, /* Eastern Standard Time / US ==> America/Louisville */
+ 0x182, /* Eastern Standard Time / ZZ ==> EST5EDT */
+ 0x00d, /* Egypt Standard Time / EG+ ==> Africa/Cairo */
+ 0x149, /* Ekaterinburg Standard Time / RU+ ==> Asia/Yekaterinburg */
+ 0x1be, /* FLE Standard Time / UA+ ==> Europe/Kiev */
+ 0x1c6, /* FLE Standard Time / AX ==> Europe/Mariehamn */
+ 0x1d7, /* FLE Standard Time / BG ==> Europe/Sofia */
+ 0x1d9, /* FLE Standard Time / EE ==> Europe/Tallinn */
+ 0x1b9, /* FLE Standard Time / FI ==> Europe/Helsinki */
+ 0x1e1, /* FLE Standard Time / LT ==> Europe/Vilnius */
+ 0x1cf, /* FLE Standard Time / LV ==> Europe/Riga */
+ 0x1dd, /* FLE Standard Time / UA ==> Europe/Uzhgorod */
+ 0x1e5, /* FLE Standard Time / UA ==> Europe/Zaporozhye */
+ 0x217, /* Fiji Standard Time / FJ+ ==> Pacific/Fiji */
+ 0x1c2, /* GMT Standard Time / GB+ ==> Europe/London */
+ 0x14d, /* GMT Standard Time / ES ==> Atlantic/Canary */
+ 0x14f, /* GMT Standard Time / FO ==> Atlantic/Faeroe */
+ 0x1b8, /* GMT Standard Time / GG ==> Europe/Guernsey */
+ 0x1b6, /* GMT Standard Time / IE ==> Europe/Dublin */
+ 0x1ba, /* GMT Standard Time / IM ==> Europe/Isle_of_Man */
+ 0x1bc, /* GMT Standard Time / JE ==> Europe/Jersey */
+ 0x152, /* GMT Standard Time / PT ==> Atlantic/Madeira */
+ 0x1c0, /* GMT Standard Time / PT ==> Europe/Lisbon */
+ 0x1b1, /* GTB Standard Time / RO+ ==> Europe/Bucharest */
+ 0x124, /* GTB Standard Time / CY ==> Asia/Nicosia */
+ 0x1ab, /* GTB Standard Time / GR ==> Europe/Athens */
+ 0x139, /* Georgian Standard Time / GE+ ==> Asia/Tbilisi */
+ 0x073, /* Greenland Standard Time / GL+ ==> America/Godthab */
+ 0x153, /* Greenwich Standard Time / IS+ ==> Atlantic/Reykjavik */
+ 0x02f, /* Greenwich Standard Time / BF ==> Africa/Ouagadougou */
+ 0x000, /* Greenwich Standard Time / CI ==> Africa/Abidjan */
+ 0x001, /* Greenwich Standard Time / GH ==> Africa/Accra */
+ 0x008, /* Greenwich Standard Time / GM ==> Africa/Banjul */
+ 0x010, /* Greenwich Standard Time / GN ==> Africa/Conakry */
+ 0x009, /* Greenwich Standard Time / GW ==> Africa/Bissau */
+ 0x02a, /* Greenwich Standard Time / LR ==> Africa/Monrovia */
+ 0x006, /* Greenwich Standard Time / ML ==> Africa/Bamako */
+ 0x02e, /* Greenwich Standard Time / MR ==> Africa/Nouakchott */
+ 0x155, /* Greenwich Standard Time / SH ==> Atlantic/St_Helena */
+ 0x016, /* Greenwich Standard Time / SL ==> Africa/Freetown */
+ 0x011, /* Greenwich Standard Time / SN ==> Africa/Dakar */
+ 0x031, /* Greenwich Standard Time / ST ==> Africa/Sao_Tome */
+ 0x021, /* Greenwich Standard Time / TG ==> Africa/Lome */
+ 0x0b4, /* Haiti Standard Time / HT+ ==> America/Port-au-Prince */
+ 0x21d, /* Hawaiian Standard Time / US+ ==> Pacific/Honolulu */
+ 0x22f, /* Hawaiian Standard Time / CK ==> Pacific/Rarotonga */
+ 0x232, /* Hawaiian Standard Time / PF ==> Pacific/Tahiti */
+ 0x21e, /* Hawaiian Standard Time / UM ==> Pacific/Johnston */
+ 0x188, /* Hawaiian Standard Time / ZZ ==> Etc/GMT+10 */
+ 0x0fa, /* India Standard Time / IN+ ==> Asia/Calcutta */
+ 0x13a, /* Iran Standard Time / IR+ ==> Asia/Tehran */
+ 0x111, /* Israel Standard Time / IL+ ==> Asia/Jerusalem */
+ 0x0eb, /* Jordan Standard Time / JO+ ==> Asia/Amman */
+ 0x1bd, /* Kaliningrad Standard Time / RU+ ==> Europe/Kaliningrad */
+ 0x133, /* Korea Standard Time / KR+ ==> Asia/Seoul */
+ 0x033, /* Libya Standard Time / LY+ ==> Africa/Tripoli */
+ 0x21f, /* Line Islands Standard Time / KI+ ==> Pacific/Kiritimati */
+ 0x199, /* Line Islands Standard Time / ZZ ==> Etc/GMT-14 */
+ 0x162, /* Lord Howe Standard Time / AU+ ==> Australia/Lord_Howe */
+ 0x120, /* Magadan Standard Time / RU+ ==> Asia/Magadan */
+ 0x223, /* Marquesas Standard Time / PF+ ==> Pacific/Marquesas */
+ 0x1fa, /* Mauritius Standard Time / MU+ ==> Indian/Mauritius */
+ 0x1fc, /* Mauritius Standard Time / RE ==> Indian/Reunion */
+ 0x1f8, /* Mauritius Standard Time / SC ==> Indian/Mahe */
+ 0x0f7, /* Middle East Standard Time / LB+ ==> Asia/Beirut */
+ 0x0a4, /* Montevideo Standard Time / UY+ ==> America/Montevideo */
+ 0x00e, /* Morocco Standard Time / MA+ ==> Africa/Casablanca */
+ 0x015, /* Morocco Standard Time / EH ==> Africa/El_Aaiun */
+ 0x05e, /* Mountain Standard Time (Mexico) / MX+ ==> America/Chihuahua */
+ 0x09b, /* Mountain Standard Time (Mexico) / MX ==> America/Mazatlan */
+ 0x068, /* Mountain Standard Time / US+ ==> America/Denver */
+ 0x056, /* Mountain Standard Time / CA ==> America/Cambridge_Bay */
+ 0x06b, /* Mountain Standard Time / CA ==> America/Edmonton */
+ 0x087, /* Mountain Standard Time / CA ==> America/Inuvik */
+ 0x0db, /* Mountain Standard Time / CA ==> America/Yellowknife */
+ 0x0af, /* Mountain Standard Time / MX ==> America/Ojinaga */
+ 0x054, /* Mountain Standard Time / US ==> America/Boise */
+ 0x205, /* Mountain Standard Time / ZZ ==> MST7MDT */
+ 0x12e, /* Myanmar Standard Time / MM+ ==> Asia/Rangoon */
+ 0x1f5, /* Myanmar Standard Time / CC ==> Indian/Cocos */
+ 0x126, /* N. Central Asia Standard Time / RU+ ==> Asia/Novosibirsk */
+ 0x035, /* Namibia Standard Time / NA+ ==> Africa/Windhoek */
+ 0x117, /* Nepal Standard Time / NP+ ==> Asia/Katmandu */
+ 0x20f, /* New Zealand Standard Time / NZ+ ==> Pacific/Auckland */
+ 0x0e1, /* New Zealand Standard Time / AQ ==> Antarctica/McMurdo */
+ 0x0ca, /* Newfoundland Standard Time / CA+ ==> America/St_Johns */
+ 0x227, /* Norfolk Standard Time / NF+ ==> Pacific/Norfolk */
+ 0x10d, /* North Asia East Standard Time / RU+ ==> Asia/Irkutsk */
+ 0x11a, /* North Asia Standard Time / RU+ ==> Asia/Krasnoyarsk */
+ 0x125, /* North Asia Standard Time / RU ==> Asia/Novokuznetsk */
+ 0x12b, /* North Korea Standard Time / KP+ ==> Asia/Pyongyang */
+ 0x127, /* Omsk Standard Time / RU+ ==> Asia/Omsk */
+ 0x0c3, /* Pacific SA Standard Time / CL+ ==> America/Santiago */
+ 0x0d3, /* Pacific Standard Time (Mexico) / MX+ ==> America/Tijuana */
+ 0x0c1, /* Pacific Standard Time (Mexico) / MX ==> America/Santa_Isabel */
+ 0x092, /* Pacific Standard Time / US+ ==> America/Los_Angeles */
+ 0x066, /* Pacific Standard Time / CA ==> America/Dawson */
+ 0x0d6, /* Pacific Standard Time / CA ==> America/Vancouver */
+ 0x0d8, /* Pacific Standard Time / CA ==> America/Whitehorse */
+ 0x20d, /* Pacific Standard Time / ZZ ==> PST8PDT */
+ 0x114, /* Pakistan Standard Time / PK+ ==> Asia/Karachi */
+ 0x049, /* Paraguay Standard Time / PY+ ==> America/Asuncion */
+ 0x1cc, /* Romance Standard Time / FR+ ==> Europe/Paris */
+ 0x1b0, /* Romance Standard Time / BE ==> Europe/Brussels */
+ 0x1b5, /* Romance Standard Time / DK ==> Europe/Copenhagen */
+ 0x00f, /* Romance Standard Time / ES ==> Africa/Ceuta */
+ 0x1c4, /* Romance Standard Time / ES ==> Europe/Madrid */
+ 0x136, /* Russia Time Zone 10 / RU+ ==> Asia/Srednekolymsk */
+ 0x113, /* Russia Time Zone 11 / RU+ ==> Asia/Kamchatka */
+ 0x0ec, /* Russia Time Zone 11 / RU ==> Asia/Anadyr */
+ 0x1d1, /* Russia Time Zone 3 / RU+ ==> Europe/Samara */
+ 0x1c9, /* Russian Standard Time / RU+ ==> Europe/Moscow */
+ 0x1bf, /* Russian Standard Time / RU ==> Europe/Kirov */
+ 0x1e2, /* Russian Standard Time / RU ==> Europe/Volgograd */
+ 0x1d5, /* Russian Standard Time / UA ==> Europe/Simferopol */
+ 0x05b, /* SA Eastern Standard Time / GF+ ==> America/Cayenne */
+ 0x0e2, /* SA Eastern Standard Time / AQ ==> Antarctica/Palmer */
+ 0x0e3, /* SA Eastern Standard Time / AQ ==> Antarctica/Rothera */
+ 0x04f, /* SA Eastern Standard Time / BR ==> America/Belem */
+ 0x071, /* SA Eastern Standard Time / BR ==> America/Fortaleza */
+ 0x095, /* SA Eastern Standard Time / BR ==> America/Maceio */
+ 0x0bc, /* SA Eastern Standard Time / BR ==> America/Recife */
+ 0x0c2, /* SA Eastern Standard Time / BR ==> America/Santarem */
+ 0x0b9, /* SA Eastern Standard Time / CL ==> America/Punta_Arenas */
+ 0x156, /* SA Eastern Standard Time / FK ==> Atlantic/Stanley */
+ 0x0b2, /* SA Eastern Standard Time / SR ==> America/Paramaribo */
+ 0x18c, /* SA Eastern Standard Time / ZZ ==> Etc/GMT+3 */
+ 0x053, /* SA Pacific Standard Time / CO+ ==> America/Bogota */
+ 0x06c, /* SA Pacific Standard Time / BR ==> America/Eirunepe */
+ 0x0bf, /* SA Pacific Standard Time / BR ==> America/Rio_Branco */
+ 0x05f, /* SA Pacific Standard Time / CA ==> America/Coral_Harbour */
+ 0x079, /* SA Pacific Standard Time / EC ==> America/Guayaquil */
+ 0x089, /* SA Pacific Standard Time / JM ==> America/Jamaica */
+ 0x05c, /* SA Pacific Standard Time / KY ==> America/Cayman */
+ 0x0b0, /* SA Pacific Standard Time / PA ==> America/Panama */
+ 0x091, /* SA Pacific Standard Time / PE ==> America/Lima */
+ 0x18e, /* SA Pacific Standard Time / ZZ ==> Etc/GMT+5 */
+ 0x090, /* SA Western Standard Time / BO+ ==> America/La_Paz */
+ 0x039, /* SA Western Standard Time / AG ==> America/Antigua */
+ 0x038, /* SA Western Standard Time / AI ==> America/Anguilla */
+ 0x048, /* SA Western Standard Time / AW ==> America/Aruba */
+ 0x04e, /* SA Western Standard Time / BB ==> America/Barbados */
+ 0x0c9, /* SA Western Standard Time / BL ==> America/St_Barthelemy */
+ 0x08f, /* SA Western Standard Time / BQ ==> America/Kralendijk */
+ 0x052, /* SA Western Standard Time / BR ==> America/Boa_Vista */
+ 0x097, /* SA Western Standard Time / BR ==> America/Manaus */
+ 0x0b7, /* SA Western Standard Time / BR ==> America/Porto_Velho */
+ 0x051, /* SA Western Standard Time / CA ==> America/Blanc-Sablon */
+ 0x064, /* SA Western Standard Time / CW ==> America/Curacao */
+ 0x06a, /* SA Western Standard Time / DM ==> America/Dominica */
+ 0x0c4, /* SA Western Standard Time / DO ==> America/Santo_Domingo */
+ 0x076, /* SA Western Standard Time / GD ==> America/Grenada */
+ 0x077, /* SA Western Standard Time / GP ==> America/Guadeloupe */
+ 0x07a, /* SA Western Standard Time / GY ==> America/Guyana */
+ 0x0cb, /* SA Western Standard Time / KN ==> America/St_Kitts */
+ 0x0cc, /* SA Western Standard Time / LC ==> America/St_Lucia */
+ 0x098, /* SA Western Standard Time / MF ==> America/Marigot */
+ 0x099, /* SA Western Standard Time / MQ ==> America/Martinique */
+ 0x0a6, /* SA Western Standard Time / MS ==> America/Montserrat */
+ 0x0b8, /* SA Western Standard Time / PR ==> America/Puerto_Rico */
+ 0x094, /* SA Western Standard Time / SX ==> America/Lower_Princes */
+ 0x0b5, /* SA Western Standard Time / TT ==> America/Port_of_Spain */
+ 0x0ce, /* SA Western Standard Time / VC ==> America/St_Vincent */
+ 0x0d5, /* SA Western Standard Time / VG ==> America/Tortola */
+ 0x0cd, /* SA Western Standard Time / VI ==> America/St_Thomas */
+ 0x18d, /* SA Western Standard Time / ZZ ==> Etc/GMT+4 */
+ 0x0f5, /* SE Asia Standard Time / TH+ ==> Asia/Bangkok */
+ 0x0dd, /* SE Asia Standard Time / AQ ==> Antarctica/Davis */
+ 0x1f4, /* SE Asia Standard Time / CX ==> Indian/Christmas */
+ 0x10f, /* SE Asia Standard Time / ID ==> Asia/Jakarta */
+ 0x12a, /* SE Asia Standard Time / ID ==> Asia/Pontianak */
+ 0x129, /* SE Asia Standard Time / KH ==> Asia/Phnom_Penh */
+ 0x145, /* SE Asia Standard Time / LA ==> Asia/Vientiane */
+ 0x130, /* SE Asia Standard Time / VN ==> Asia/Saigon */
+ 0x19f, /* SE Asia Standard Time / ZZ ==> Etc/GMT-7 */
+ 0x0a1, /* Saint Pierre Standard Time / PM+ ==> America/Miquelon */
+ 0x131, /* Sakhalin Standard Time / RU+ ==> Asia/Sakhalin */
+ 0x20e, /* Samoa Standard Time / WS+ ==> Pacific/Apia */
+ 0x135, /* Singapore Standard Time / SG+ ==> Asia/Singapore */
+ 0x0f9, /* Singapore Standard Time / BN ==> Asia/Brunei */
+ 0x121, /* Singapore Standard Time / ID ==> Asia/Makassar */
+ 0x11b, /* Singapore Standard Time / MY ==> Asia/Kuala_Lumpur */
+ 0x11c, /* Singapore Standard Time / MY ==> Asia/Kuching */
+ 0x122, /* Singapore Standard Time / PH ==> Asia/Manila */
+ 0x1a0, /* Singapore Standard Time / ZZ ==> Etc/GMT-8 */
+ 0x019, /* South Africa Standard Time / ZA+ ==> Africa/Johannesburg */
+ 0x00c, /* South Africa Standard Time / BI ==> Africa/Bujumbura */
+ 0x017, /* South Africa Standard Time / BW ==> Africa/Gaborone */
+ 0x023, /* South Africa Standard Time / CD ==> Africa/Lubumbashi */
+ 0x027, /* South Africa Standard Time / LS ==> Africa/Maseru */
+ 0x00a, /* South Africa Standard Time / MW ==> Africa/Blantyre */
+ 0x026, /* South Africa Standard Time / MZ ==> Africa/Maputo */
+ 0x01d, /* South Africa Standard Time / RW ==> Africa/Kigali */
+ 0x028, /* South Africa Standard Time / SZ ==> Africa/Mbabane */
+ 0x024, /* South Africa Standard Time / ZM ==> Africa/Lusaka */
+ 0x018, /* South Africa Standard Time / ZW ==> Africa/Harare */
+ 0x19a, /* South Africa Standard Time / ZZ ==> Etc/GMT-2 */
+ 0x0ff, /* Sri Lanka Standard Time / LK+ ==> Asia/Colombo */
+ 0x101, /* Syria Standard Time / SY+ ==> Asia/Damascus */
+ 0x137, /* Taipei Standard Time / TW+ ==> Asia/Taipei */
+ 0x15f, /* Tasmania Standard Time / AU+ ==> Australia/Hobart */
+ 0x15c, /* Tasmania Standard Time / AU ==> Australia/Currie */
+ 0x03a, /* Tocantins Standard Time / BR+ ==> America/Araguaina */
+ 0x13e, /* Tokyo Standard Time / JP+ ==> Asia/Tokyo */
+ 0x110, /* Tokyo Standard Time / ID ==> Asia/Jayapura */
+ 0x22a, /* Tokyo Standard Time / PW ==> Pacific/Palau */
+ 0x103, /* Tokyo Standard Time / TL ==> Asia/Dili */
+ 0x1a1, /* Tokyo Standard Time / ZZ ==> Etc/GMT-9 */
+ 0x13f, /* Tomsk Standard Time / RU+ ==> Asia/Tomsk */
+ 0x234, /* Tonga Standard Time / TO+ ==> Pacific/Tongatapu */
+ 0x215, /* Tonga Standard Time / KI ==> Pacific/Enderbury */
+ 0x216, /* Tonga Standard Time / TK ==> Pacific/Fakaofo */
+ 0x198, /* Tonga Standard Time / ZZ ==> Etc/GMT-13 */
+ 0x0fb, /* Transbaikal Standard Time / RU+ ==> Asia/Chita */
+ 0x1bb, /* Turkey Standard Time / TR+ ==> Europe/Istanbul */
+ 0x106, /* Turkey Standard Time / CY ==> Asia/Famagusta */
+ 0x075, /* Turks And Caicos Standard Time / TC+ ==> America/Grand_Turk */
+ 0x086, /* US Eastern Standard Time / US+ ==> America/Indianapolis */
+ 0x080, /* US Eastern Standard Time / US ==> America/Indiana/Marengo */
+ 0x083, /* US Eastern Standard Time / US ==> America/Indiana/Vevay */
+ 0x0b3, /* US Mountain Standard Time / US+ ==> America/Phoenix */
+ 0x062, /* US Mountain Standard Time / CA ==> America/Creston */
+ 0x067, /* US Mountain Standard Time / CA ==> America/Dawson_Creek */
+ 0x06f, /* US Mountain Standard Time / CA ==> America/Fort_Nelson */
+ 0x07d, /* US Mountain Standard Time / MX ==> America/Hermosillo */
+ 0x190, /* US Mountain Standard Time / ZZ ==> Etc/GMT+7 */
+ 0x185, /* UTC / ZZ+ ==> Etc/GMT */
+ 0x065, /* UTC / GL ==> America/Danmarkshavn */
+ 0x1a5, /* UTC / ZZ ==> Etc/UTC */
+ 0x197, /* UTC+12 / ZZ+ ==> Etc/GMT-12 */
+ 0x233, /* UTC+12 / KI ==> Pacific/Tarawa */
+ 0x221, /* UTC+12 / MH ==> Pacific/Kwajalein */
+ 0x222, /* UTC+12 / MH ==> Pacific/Majuro */
+ 0x225, /* UTC+12 / NR ==> Pacific/Nauru */
+ 0x218, /* UTC+12 / TV ==> Pacific/Funafuti */
+ 0x236, /* UTC+12 / UM ==> Pacific/Wake */
+ 0x237, /* UTC+12 / WF ==> Pacific/Wallis */
+ 0x18b, /* UTC-02 / ZZ+ ==> Etc/GMT+2 */
+ 0x0ab, /* UTC-02 / BR ==> America/Noronha */
+ 0x154, /* UTC-02 / GS ==> Atlantic/South_Georgia */
+ 0x191, /* UTC-08 / ZZ+ ==> Etc/GMT+8 */
+ 0x22b, /* UTC-08 / PN ==> Pacific/Pitcairn */
+ 0x192, /* UTC-09 / ZZ+ ==> Etc/GMT+9 */
+ 0x21a, /* UTC-09 / PF ==> Pacific/Gambier */
+ 0x189, /* UTC-11 / ZZ+ ==> Etc/GMT+11 */
+ 0x229, /* UTC-11 / AS ==> Pacific/Pago_Pago */
+ 0x226, /* UTC-11 / NU ==> Pacific/Niue */
+ 0x224, /* UTC-11 / UM ==> Pacific/Midway */
+ 0x141, /* Ulaanbaatar Standard Time / MN+ ==> Asia/Ulaanbaatar */
+ 0x0fc, /* Ulaanbaatar Standard Time / MN ==> Asia/Choibalsan */
+ 0x059, /* Venezuela Standard Time / VE+ ==> America/Caracas */
+ 0x146, /* Vladivostok Standard Time / RU+ ==> Asia/Vladivostok */
+ 0x144, /* Vladivostok Standard Time / RU ==> Asia/Ust-Nera */
+ 0x166, /* W. Australia Standard Time / AU+ ==> Australia/Perth */
+ 0x01f, /* W. Central Africa Standard Time / NG+ ==> Africa/Lagos */
+ 0x022, /* W. Central Africa Standard Time / AO ==> Africa/Luanda */
+ 0x030, /* W. Central Africa Standard Time / BJ ==> Africa/Porto-Novo */
+ 0x01e, /* W. Central Africa Standard Time / CD ==> Africa/Kinshasa */
+ 0x007, /* W. Central Africa Standard Time / CF ==> Africa/Bangui */
+ 0x00b, /* W. Central Africa Standard Time / CG ==> Africa/Brazzaville */
+ 0x014, /* W. Central Africa Standard Time / CM ==> Africa/Douala */
+ 0x003, /* W. Central Africa Standard Time / DZ ==> Africa/Algiers */
+ 0x020, /* W. Central Africa Standard Time / GA ==> Africa/Libreville */
+ 0x025, /* W. Central Africa Standard Time / GQ ==> Africa/Malabo */
+ 0x02d, /* W. Central Africa Standard Time / NE ==> Africa/Niamey */
+ 0x02c, /* W. Central Africa Standard Time / TD ==> Africa/Ndjamena */
+ 0x034, /* W. Central Africa Standard Time / TN ==> Africa/Tunis */
+ 0x194, /* W. Central Africa Standard Time / ZZ ==> Etc/GMT-1 */
+ 0x1ae, /* W. Europe Standard Time / DE+ ==> Europe/Berlin */
+ 0x1a9, /* W. Europe Standard Time / AD ==> Europe/Andorra */
+ 0x1e0, /* W. Europe Standard Time / AT ==> Europe/Vienna */
+ 0x1e6, /* W. Europe Standard Time / CH ==> Europe/Zurich */
+ 0x1b3, /* W. Europe Standard Time / DE ==> Europe/Busingen */
+ 0x1b7, /* W. Europe Standard Time / GI ==> Europe/Gibraltar */
+ 0x1d0, /* W. Europe Standard Time / IT ==> Europe/Rome */
+ 0x1de, /* W. Europe Standard Time / LI ==> Europe/Vaduz */
+ 0x1c3, /* W. Europe Standard Time / LU ==> Europe/Luxembourg */
+ 0x1c8, /* W. Europe Standard Time / MC ==> Europe/Monaco */
+ 0x1c5, /* W. Europe Standard Time / MT ==> Europe/Malta */
+ 0x1a8, /* W. Europe Standard Time / NL ==> Europe/Amsterdam */
+ 0x1cb, /* W. Europe Standard Time / NO ==> Europe/Oslo */
+ 0x1d8, /* W. Europe Standard Time / SE ==> Europe/Stockholm */
+ 0x0e8, /* W. Europe Standard Time / SJ ==> Arctic/Longyearbyen */
+ 0x1d2, /* W. Europe Standard Time / SM ==> Europe/San_Marino */
+ 0x1df, /* W. Europe Standard Time / VA ==> Europe/Vatican */
+ 0x10c, /* W. Mongolia Standard Time / MN+ ==> Asia/Hovd */
+ 0x138, /* West Asia Standard Time / UZ+ ==> Asia/Tashkent */
+ 0x0e0, /* West Asia Standard Time / AQ ==> Antarctica/Mawson */
+ 0x0ed, /* West Asia Standard Time / KZ ==> Asia/Aqtau */
+ 0x0ee, /* West Asia Standard Time / KZ ==> Asia/Aqtobe */
+ 0x0f1, /* West Asia Standard Time / KZ ==> Asia/Atyrau */
+ 0x128, /* West Asia Standard Time / KZ ==> Asia/Oral */
+ 0x1f9, /* West Asia Standard Time / MV ==> Indian/Maldives */
+ 0x1f7, /* West Asia Standard Time / TF ==> Indian/Kerguelen */
+ 0x105, /* West Asia Standard Time / TJ ==> Asia/Dushanbe */
+ 0x0ef, /* West Asia Standard Time / TM ==> Asia/Ashgabat */
+ 0x132, /* West Asia Standard Time / UZ ==> Asia/Samarkand */
+ 0x19d, /* West Asia Standard Time / ZZ ==> Etc/GMT-5 */
+ 0x109, /* West Bank Standard Time / PS+ ==> Asia/Hebron */
+ 0x107, /* West Bank Standard Time / PS ==> Asia/Gaza */
+ 0x22e, /* West Pacific Standard Time / PG+ ==> Pacific/Port_Moresby */
+ 0x0de, /* West Pacific Standard Time / AQ ==> Antarctica/DumontDUrville */
+ 0x235, /* West Pacific Standard Time / FM ==> Pacific/Truk */
+ 0x21c, /* West Pacific Standard Time / GU ==> Pacific/Guam */
+ 0x230, /* West Pacific Standard Time / MP ==> Pacific/Saipan */
+ 0x195, /* West Pacific Standard Time / ZZ ==> Etc/GMT-10 */
+ 0x147, /* Yakutsk Standard Time / RU+ ==> Asia/Yakutsk */
+ 0x118, /* Yakutsk Standard Time / RU ==> Asia/Khandyga */
+};
+
+
+
+
+RTDECL(PCRTTIMEZONEINFO) RTTimeZoneGetInfoByUnixName(const char *pszName)
+{
+ /*
+ * Try a case sensitive binary search first.
+ */
+ /** @todo binary searching */
+
+ /*
+ * Fallback: Linear case-insensitive search.
+ */
+ size_t const cchName = strlen(pszName);
+ for (size_t i = 0; i < RT_ELEMENTS(g_aTimeZones); i++)
+ if ( g_aTimeZones[i].cchUnixName == cchName
+ && RTStrICmpAscii(pszName, g_aTimeZones[i].pszUnixName) == 0)
+ return &g_aTimeZones[i];
+ return NULL;
+}
+
+
+RTDECL(PCRTTIMEZONEINFO) RTTimeZoneGetInfoByWindowsName(const char *pszName)
+{
+ /*
+ * Try a case sensitive binary search first.
+ */
+ /** @todo binary searching */
+
+ /*
+ * Fallback: Linear case-insensitive search.
+ */
+ size_t const cchName = strlen(pszName);
+ for (size_t i = 0; i < RT_ELEMENTS(g_aidxWinTimeZones); i++)
+ {
+ PCRTTIMEZONEINFO pZone = &g_aTimeZones[g_aidxWinTimeZones[i]];
+ if ( pZone->cchWindowsName == cchName
+ && RTStrICmpAscii(pszName, pZone->pszWindowsName) == 0)
+ return pZone;
+ }
+ return NULL;
+}
+
+
+RTDECL(PCRTTIMEZONEINFO) RTTimeZoneGetInfoByWindowsIndex(uint32_t idxZone)
+{
+ for (size_t i = 0; i < RT_ELEMENTS(g_aidxWinTimeZones); i++)
+ {
+ PCRTTIMEZONEINFO pZone = &g_aTimeZones[g_aidxWinTimeZones[i]];
+ if (pZone->idxWindows == idxZone)
+ return pZone;
+ }
+ return NULL;
+}
+