diff options
Diffstat (limited to 'src/VBox/Runtime/common/time')
-rw-r--r-- | src/VBox/Runtime/common/time/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/Runtime/common/time/time.cpp | 1644 | ||||
-rw-r--r-- | src/VBox/Runtime/common/time/timeprog.cpp | 97 | ||||
-rw-r--r-- | src/VBox/Runtime/common/time/timesup.cpp | 399 | ||||
-rw-r--r-- | src/VBox/Runtime/common/time/timesupA.asm | 151 | ||||
-rw-r--r-- | src/VBox/Runtime/common/time/timesupA.mac | 885 | ||||
-rw-r--r-- | src/VBox/Runtime/common/time/timesupref.cpp | 308 | ||||
-rw-r--r-- | src/VBox/Runtime/common/time/timesupref.h | 394 | ||||
-rw-r--r-- | src/VBox/Runtime/common/time/timesysalias.cpp | 57 | ||||
-rwxr-xr-x | src/VBox/Runtime/common/time/timezoneinfo-gen.py | 460 | ||||
-rw-r--r-- | src/VBox/Runtime/common/time/timezoneinfo.cpp | 1161 |
11 files changed, 5556 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/time.cpp b/src/VBox/Runtime/common/time/time.cpp new file mode 100644 index 00000000..72ce9796 --- /dev/null +++ b/src/VBox/Runtime/common/time/time.cpp @@ -0,0 +1,1644 @@ +/* $Id: time.cpp $ */ +/** @file + * IPRT - Time. + */ + +/* + * Copyright (C) 2006-2020 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + + +/********************************************************************************************************************************* +* 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); +} + + +/** + * 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. + */ +RTDECL(bool) RTTimeIsLeapYear(int32_t i32Year) +{ + return rtTimeIsLeapYear(i32Year); +} +RT_EXPORT_SYMBOL(RTTimeIsLeapYear); + + +/** + * Explodes a time spec (UTC). + * + * @returns pTime. + * @param pTime Where to store the exploded time. + * @param pTimeSpec The time spec to exploded. + */ +RTDECL(PRTTIME) RTTimeExplode(PRTTIME pTime, PCRTTIMESPEC pTimeSpec) +{ + int64_t i64Div; + int32_t i32Div; + int32_t i32Rem; + unsigned iYear; + const uint16_t *paiDayOfYear; + int iMonth; + + AssertMsg(VALID_PTR(pTime), ("%p\n", pTime)); + AssertMsg(VALID_PTR(pTimeSpec), ("%p\n", pTime)); + + /* + * 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); + + +/** + * Implodes exploded time to a time spec (UTC). + * + * @returns pTime on success. + * @returns NULL if the pTime data is invalid. + * @param pTimeSpec Where to store the imploded UTC time. + * If pTime specifies a time which outside the range, maximum or + * minimum values will be returned. + * @param pTime Pointer to the exploded time to implode. + * The fields u8Month, u8WeekDay and u8MonthDay are not used, + * and all the other fields are expected to be within their + * bounds. Use RTTimeNormalize() or RTTimeLocalNormalize() to + * calculate u16YearDay and normalize the ranges of the fields. + */ +RTDECL(PRTTIMESPEC) RTTimeImplode(PRTTIMESPEC pTimeSpec, PCRTTIME pTime) +{ + int32_t i32Days; + uint32_t u32Secs; + int64_t i64Nanos; + + /* + * Validate input. + */ + AssertReturn(VALID_PTR(pTimeSpec), NULL); + AssertReturn(VALID_PTR(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; +} + + +/** + * Normalizes the fields of a time structure. + * + * It is possible to calculate year-day from month/day and vice + * versa. If you adjust any of these, make sure to zero the + * other so you make it clear which of the fields to use. If + * it's ambiguous, the year-day field is used (and you get + * assertions in debug builds). + * + * All the time fields and the year-day or month/day fields will + * be adjusted for overflows. (Since all fields are unsigned, there + * is no underflows.) It is possible to exploit this for simple + * date math, though the recommended way of doing that to implode + * the time into a timespec and do the math on that. + * + * @returns pTime on success. + * @returns NULL if the data is invalid. + * + * @param pTime The time structure to normalize. + * + * @remarks This function doesn't work with local time, only with UTC time. + */ +RTDECL(PRTTIME) RTTimeNormalize(PRTTIME pTime) +{ + /* + * Validate that we've got the minimum of stuff handy. + */ + AssertReturn(VALID_PTR(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); + + +/** + * Normalizes the fields of a time structure, assuming local time. + * + * It is possible to calculate year-day from month/day and vice + * versa. If you adjust any of these, make sure to zero the + * other so you make it clear which of the fields to use. If + * it's ambiguous, the year-day field is used (and you get + * assertions in debug builds). + * + * All the time fields and the year-day or month/day fields will + * be adjusted for overflows. (Since all fields are unsigned, there + * is no underflows.) It is possible to exploit this for simple + * date math, though the recommended way of doing that to implode + * the time into a timespec and do the math on that. + * + * @returns pTime on success. + * @returns NULL if the data is invalid. + * + * @param pTime The time structure to normalize. + * + * @remarks This function doesn't work with UTC time, only with local time. + */ +RTDECL(PRTTIME) RTTimeLocalNormalize(PRTTIME pTime) +{ + /* + * Validate that we've got the minimum of stuff handy. + */ + AssertReturn(VALID_PTR(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); + + +/** + * Converts a time spec to a ISO date string. + * + * @returns psz on success. + * @returns NULL on buffer underflow. + * @param pTime The time. Caller should've normalized this. + * @param psz Where to store the string. + * @param cb The size of the buffer. + */ +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); + + +/** + * Converts a time spec to a ISO date string, extended version. + * + * @returns Output string length on success (positive), VERR_BUFFER_OVERFLOW + * (negative) or VERR_OUT_OF_RANGE (negative) on failure. + * @param pTime The time. Caller should've normalized this. + * @param psz Where to store the string. + * @param cb The size of the buffer. + * @param cFractionDigits Number of digits in the fraction. Max is 9. + */ +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); + + +/** + * Converts a time spec to a ISO date string. + * + * @returns psz on success. + * @returns NULL on buffer underflow. + * @param pTime The time spec. + * @param psz Where to store the string. + * @param cb The size of the buffer. + */ +RTDECL(char *) RTTimeSpecToString(PCRTTIMESPEC pTime, char *psz, size_t cb) +{ + RTTIME Time; + return RTTimeToString(RTTimeExplode(&Time, pTime), psz, cb); +} +RT_EXPORT_SYMBOL(RTTimeSpecToString); + + + +/** + * Attempts to convert an ISO date string to a time structure. + * + * We're a little forgiving with zero padding, unspecified parts, and leading + * and trailing spaces. + * + * @retval pTime on success, + * @retval NULL on failure. + * @param pTime Where to store the time on success. + * @param pszString The ISO date string to convert. + */ +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); + + +/** + * Attempts to convert an ISO date string to a time structure. + * + * We're a little forgiving with zero padding, unspecified parts, and leading + * and trailing spaces. + * + * @retval pTime on success, + * @retval NULL on failure. + * @param pTime The time spec. + * @param pszString The ISO date string to convert. + */ +RTDECL(PRTTIMESPEC) RTTimeSpecFromString(PRTTIMESPEC pTime, const char *pszString) +{ + RTTIME Time; + if (RTTimeFromString(&Time, pszString)) + return RTTimeImplode(pTime, &Time); + return NULL; +} +RT_EXPORT_SYMBOL(RTTimeSpecFromString); + + +/** + * Formats the given time on a RTC-2822 compliant format. + * + * @returns Output string length on success (positive), VERR_BUFFER_OVERFLOW + * (negative) on failure. + * @param pTime The time. Caller should've normalized this. + * @param psz Where to store the string. + * @param cb The size of the buffer. + */ +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); + + +/** + * Attempts to convert an RFC-2822 date string to a time structure. + * + * We're a little forgiving with zero padding, unspecified parts, and leading + * and trailing spaces. + * + * @retval pTime on success, + * @retval NULL on failure. + * @param pTime Where to store the time on success. + * @param pszString The ISO date string to convert. + */ +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; +} + + +/** + * Converts a time structure to UTC, relying on UTC offset information if it contains local time. + * + * @returns pTime on success. + * @returns NULL if the data is invalid. + * @param pTime The time structure to convert. + */ +RTDECL(PRTTIME) RTTimeConvertToZulu(PRTTIME pTime) +{ + /* + * Validate that we've got the minimum of stuff handy. + */ + AssertReturn(VALID_PTR(pTime), NULL); + AssertMsgReturn(!(pTime->fFlags & ~RTTIME_FLAGS_MASK), ("%#x\n", pTime->fFlags), NULL); + + return rtTimeConvertToZulu(rtTimeNormalizeInternal(pTime)); +} +RT_EXPORT_SYMBOL(RTTimeConvertToZulu); + + +/** + * Compares two normalized time structures. + * + * @retval 0 if equal. + * @retval -1 if @a pLeft is earlier than @a pRight. + * @retval 1 if @a pRight is earlier than @a pLeft. + * + * @param pLeft The left side time. NULL is accepted. + * @param pRight The right side time. NULL is accepted. + * + * @note A NULL time is considered smaller than anything else. If both are + * NULL, they are considered equal. + */ +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..a3bbf581 --- /dev/null +++ b/src/VBox/Runtime/common/time/timeprog.cpp @@ -0,0 +1,97 @@ +/* $Id: timeprog.cpp $ */ +/** @file + * IPRT - Time Relative to Program Start. + */ + +/* + * Copyright (C) 2006-2020 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + + +/********************************************************************************************************************************* +* 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..5ce96ff9 --- /dev/null +++ b/src/VBox/Runtime/common/time/timesup.cpp @@ -0,0 +1,399 @@ +/* $Id: timesup.cpp $ */ +/** @file + * IPRT - Time using SUPLib. + */ + +/* + * Copyright (C) 2006-2020 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + + +/********************************************************************************************************************************* +* 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); +static DECLCALLBACK(uint64_t) rtTimeNanoTSInternalRediscover(PRTTIMENANOTSDATA pData); +static DECLCALLBACK(uint64_t) rtTimeNanoTSInternalBadCpuIndex(PRTTIMENANOTSDATA pData, 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, 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)); + return RTTimeSystemNanoTS(); +# else + RTAssertReleasePanic(); + return 0; +# endif +} + + +/** + * Fallback function. + */ +static DECLCALLBACK(uint64_t) rtTimeNanoTSInternalFallback(PRTTIMENANOTSDATA pData) +{ + 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); + NOREF(pData); +# ifndef IN_RC + 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) +{ + 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) + pfnWorker = pGip->fGetGipCpu & SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS + ? RTTimeNanoTSLFenceAsyncUseIdtrLim + : pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS + ? RTTimeNanoTSLFenceAsyncUseRdtscp + : pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_GROUP_IN_CH_NUMBER_IN_CL + ? RTTimeNanoTSLFenceAsyncUseRdtscpGroupChNumCl + : pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_0B + ? RTTimeNanoTSLFenceAsyncUseApicIdExt0B + : pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_8000001E + ? RTTimeNanoTSLFenceAsyncUseApicIdExt8000001E + : pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID + ? RTTimeNanoTSLFenceAsyncUseApicId + : rtTimeNanoTSInternalFallback; + else + pfnWorker = pGip->fGetGipCpu & SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS + ? pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_PRACTICALLY_ZERO + ? RTTimeNanoTSLFenceSyncInvarNoDelta + : RTTimeNanoTSLFenceSyncInvarWithDeltaUseIdtrLim + : pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS + ? pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_PRACTICALLY_ZERO + ? RTTimeNanoTSLFenceSyncInvarNoDelta + : RTTimeNanoTSLFenceSyncInvarWithDeltaUseRdtscp + : pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_0B + ? pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ROUGHLY_ZERO + ? RTTimeNanoTSLFenceSyncInvarNoDelta + : RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicIdExt0B + : pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_8000001E + ? pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ROUGHLY_ZERO + ? RTTimeNanoTSLFenceSyncInvarNoDelta + : RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicIdExt8000001E + : pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID + ? pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ROUGHLY_ZERO + ? RTTimeNanoTSLFenceSyncInvarNoDelta + : RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicId + : 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); +# else + ASMAtomicWritePtr((void * volatile *)&g_pfnWorker, (void *)(uintptr_t)pfnWorker); + return pfnWorker(pData); +# endif +} + +#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); +# else + return g_pfnWorker(&g_TimeNanoTSData); +# 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..8b4f01bc --- /dev/null +++ b/src/VBox/Runtime/common/time/timesupA.asm @@ -0,0 +1,151 @@ +; $Id: timesupA.asm $ +;; @file +; IPRT - Time using SUPLib, the Assembly Implementation. +; + +; +; Copyright (C) 2006-2020 Oracle Corporation +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; General Public License (GPL) as published by the Free Software +; Foundation, in version 2 as it comes in the "COPYING" file of the +; VirtualBox OSE distribution. VirtualBox OSE is distributed in the +; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL) only, as it comes in the "COPYING.CDDL" file of the +; VirtualBox OSE 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. +; + +%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..a186f8c4 --- /dev/null +++ b/src/VBox/Runtime/common/time/timesupA.mac @@ -0,0 +1,885 @@ +; $Id: timesupA.mac $ +;; @file +; IPRT - Time using SUPLib, the Assembly Code Template. +; + +; +; Copyright (C) 2006-2020 Oracle Corporation +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; General Public License (GPL) as published by the Free Software +; Foundation, in version 2 as it comes in the "COPYING" file of the +; VirtualBox OSE distribution. VirtualBox OSE is distributed in the +; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL) only, as it comes in the "COPYING.CDDL" file of the +; VirtualBox OSE 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. +; + +%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..5e8baf48 --- /dev/null +++ b/src/VBox/Runtime/common/time/timesupref.cpp @@ -0,0 +1,308 @@ +/* $Id: timesupref.cpp $ */ +/** @file + * IPRT - Time using SUPLib, the C Implementation. + */ + +/* + * Copyright (C) 2006-2020 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#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..84322972 --- /dev/null +++ b/src/VBox/Runtime/common/time/timesupref.h @@ -0,0 +1,394 @@ +/* $Id: timesupref.h $ */ +/** @file + * IPRT - Time using SUPLib, the C Code Template. + */ + +/* + * Copyright (C) 2006-2020 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + + +/** + * 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. + */ +RTDECL(uint64_t) rtTimeNanoTSInternalRef(PRTTIMENANOTSDATA pData) +{ +#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 + + /* + * 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, UINT16_MAX-1, iCpuSet, iGipCpu); +# else + return pData->pfnBadCpuIndex(pData, 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); + } +} + diff --git a/src/VBox/Runtime/common/time/timesysalias.cpp b/src/VBox/Runtime/common/time/timesysalias.cpp new file mode 100644 index 00000000..50c6645b --- /dev/null +++ b/src/VBox/Runtime/common/time/timesysalias.cpp @@ -0,0 +1,57 @@ +/* $Id: timesysalias.cpp $ */ +/** @file + * IPRT - Time using RTTimeSystem*. + */ + +/* + * Copyright (C) 2006-2020 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + + +/********************************************************************************************************************************* +* 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..ad719a23 --- /dev/null +++ b/src/VBox/Runtime/common/time/timezoneinfo-gen.py @@ -0,0 +1,460 @@ +# -*- 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-2020 Oracle Corporation + +This file is part of VirtualBox Open Source Edition (OSE), as +available from http://www.virtualbox.org. This file is free software; +you can redistribute it and/or modify it under the terms of the GNU +General Public License (GPL) as published by the Free Software +Foundation, in version 2 as it comes in the "COPYING" file of the +VirtualBox OSE distribution. VirtualBox OSE is distributed in the +hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL) only, as it comes in the "COPYING.CDDL" file of the +VirtualBox OSE 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. +""" +__version__ = "$Revision: 135976 $" + +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..11f7e6e1 --- /dev/null +++ b/src/VBox/Runtime/common/time/timezoneinfo.cpp @@ -0,0 +1,1161 @@ +/* $Id: timezoneinfo.cpp $ */ +/** @file + * IPRT - Time zone mapping info. + */ + +/* + * Copyright (C) 2017-2020 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + + +/********************************************************************************************************************************* +* 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; +} + |