VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/time/time.cpp@ 72140

Last change on this file since 72140 was 72140, checked in by vboxsync, 7 years ago

IPRT/time: misc fixes (incorrect offUTC conversion sign, forgotten storing of offUTC, incorrect month/day calculation for first day of month, avoiding assertions when leap year needs to be recalulated) for local time, make RTTimeImplode process local time correctly, plus an implementation of RTTimeLocalNormalize and RTTimeConvertToZulu. Much improved testcase exercising overflow handling in the local time case.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 44.5 KB
Line 
1/* $Id: time.cpp 72140 2018-05-07 14:21:31Z vboxsync $ */
2/** @file
3 * IPRT - Time.
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_TIME
32#include <iprt/time.h>
33#include "internal/iprt.h"
34
35#include <iprt/ctype.h>
36#include <iprt/string.h>
37#include <iprt/assert.h>
38#include "internal/time.h"
39
40
41/*********************************************************************************************************************************
42* Defined Constants And Macros *
43*********************************************************************************************************************************/
44/** The max year we possibly could implode. */
45#define RTTIME_MAX_YEAR (292 + 1970)
46/** The min year we possibly could implode. */
47#define RTTIME_MIN_YEAR (-293 + 1970)
48
49/** The max day supported by our time representation. (2262-04-11T23-47-16.854775807) */
50#define RTTIME_MAX_DAY (365*292+71 + 101-1)
51/** The min day supported by our time representation. (1677-09-21T00-12-43.145224192) */
52#define RTTIME_MIN_DAY (365*-293-70 + 264-1)
53
54/** The max nano second into the max day. (2262-04-11T23-47-16.854775807) */
55#define RTTIME_MAX_DAY_NANO ( INT64_C(1000000000) * (23*3600 + 47*60 + 16) + 854775807 )
56/** The min nano second into the min day. (1677-09-21T00-12-43.145224192) */
57#define RTTIME_MIN_DAY_NANO ( INT64_C(1000000000) * (00*3600 + 12*60 + 43) + 145224192 )
58
59/**
60 * Asserts that a_pTime is normalized.
61 */
62#define RTTIME_ASSERT_NORMALIZED(a_pTime) \
63 do \
64 { \
65 Assert(RT_ABS((a_pTime)->offUTC) <= 840); \
66 Assert((a_pTime)->u32Nanosecond < 1000000000); \
67 Assert((a_pTime)->u8Second < 60); \
68 Assert((a_pTime)->u8Minute < 60); \
69 Assert((a_pTime)->u8Hour < 24); \
70 Assert((a_pTime)->u8Month >= 1 && (a_pTime)->u8Month <= 12); \
71 Assert((a_pTime)->u8WeekDay < 7); \
72 Assert((a_pTime)->u16YearDay >= 1); \
73 Assert((a_pTime)->u16YearDay <= (rtTimeIsLeapYear((a_pTime)->i32Year) ? 366 : 365)); \
74 Assert((a_pTime)->u8MonthDay >= 1 && (a_pTime)->u8MonthDay <= 31); \
75 } while (0)
76
77
78/*********************************************************************************************************************************
79* Global Variables *
80*********************************************************************************************************************************/
81/**
82 * Days per month in a common year.
83 */
84static const uint8_t g_acDaysInMonths[12] =
85{
86 /*Jan Feb Mar Arp May Jun Jul Aug Sep Oct Nov Dec */
87 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
88};
89
90/**
91 * Days per month in a leap year.
92 */
93static const uint8_t g_acDaysInMonthsLeap[12] =
94{
95 /*Jan Feb Mar Arp May Jun Jul Aug Sep Oct Nov Dec */
96 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
97};
98
99/**
100 * The day of year for each month in a common year.
101 */
102static const uint16_t g_aiDayOfYear[12 + 1] =
103{
104 1, /* Jan */
105 1+31, /* Feb */
106 1+31+28, /* Mar */
107 1+31+28+31, /* Apr */
108 1+31+28+31+30, /* May */
109 1+31+28+31+30+31, /* Jun */
110 1+31+28+31+30+31+30, /* Jul */
111 1+31+28+31+30+31+30+31, /* Aug */
112 1+31+28+31+30+31+30+31+31, /* Sep */
113 1+31+28+31+30+31+30+31+31+30, /* Oct */
114 1+31+28+31+30+31+30+31+31+30+31, /* Nov */
115 1+31+28+31+30+31+30+31+31+30+31+30, /* Dec */
116 1+31+28+31+30+31+30+31+31+30+31+30+31
117};
118
119/**
120 * The day of year for each month in a leap year.
121 */
122static const uint16_t g_aiDayOfYearLeap[12 + 1] =
123{
124 1, /* Jan */
125 1+31, /* Feb */
126 1+31+29, /* Mar */
127 1+31+29+31, /* Apr */
128 1+31+29+31+30, /* May */
129 1+31+29+31+30+31, /* Jun */
130 1+31+29+31+30+31+30, /* Jul */
131 1+31+29+31+30+31+30+31, /* Aug */
132 1+31+29+31+30+31+30+31+31, /* Sep */
133 1+31+29+31+30+31+30+31+31+30, /* Oct */
134 1+31+29+31+30+31+30+31+31+30+31, /* Nov */
135 1+31+29+31+30+31+30+31+31+30+31+30, /* Dec */
136 1+31+29+31+30+31+30+31+31+30+31+30+31
137};
138
139/** The index of 1970 in g_aoffYear */
140#define OFF_YEAR_IDX_EPOCH 300
141/** The year of the first index. */
142#define OFF_YEAR_IDX_0_YEAR 1670
143
144/**
145 * The number of days the 1st of January a year is offseted from 1970-01-01.
146 */
147static const int32_t g_aoffYear[] =
148{
149/*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,
150/*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,
151/*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,
152/*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,
153/*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,
154/*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,
155/*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,
156/*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,
157/*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,
158/*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,
159/*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,
160/*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,
161/*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,
162/*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,
163/*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,
164/*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,
165/*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,
166/*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,
167/*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,
168/*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,
169/*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,
170/*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,
171/*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,
172/*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,
173/*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,
174/*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,
175/*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 ,
176/*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 ,
177/*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 ,
178/*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 ,
179/*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 ,
180/*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 ,
181/*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 ,
182/*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 ,
183/*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 ,
184/*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 ,
185/*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 ,
186/*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 ,
187/*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 ,
188/*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 ,
189/*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 ,
190/*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 ,
191/*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 ,
192/*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 ,
193/*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 ,
194/*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 ,
195/*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 ,
196/*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 ,
197/*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 ,
198/*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 ,
199/*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 ,
200/*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 ,
201/*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 ,
202/*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 ,
203/*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 ,
204/*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 ,
205/*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 ,
206/*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 ,
207/*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 ,
208/*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
209};
210
211/* generator code:
212#include <stdio.h>
213bool isLeapYear(int iYear)
214{
215 return iYear % 4 == 0 && (iYear % 100 != 0 || iYear % 400 == 0);
216}
217void printYear(int iYear, int iLeap)
218{
219 if (!(iYear % 10))
220 printf("\n/" "*%d:*" "/", iYear + 1970);
221 printf(" 365*%4d+%-3d,", iYear, iLeap);
222}
223int main()
224{
225 int iYear = 0;
226 int iLeap = 0;
227 while (iYear > -300)
228 iLeap -= isLeapYear(1970 + --iYear);
229 while (iYear < 300)
230 {
231 printYear(iYear, iLeap);
232 iLeap += isLeapYear(1970 + iYear++);
233 }
234 printf("\n");
235 return 0;
236}
237*/
238
239/*********************************************************************************************************************************
240* Internal Functions *
241*********************************************************************************************************************************/
242static PRTTIME rtTimeConvertToZulu(PRTTIME pTime);
243
244
245/**
246 * Checks if a year is a leap year or not.
247 *
248 * @returns true if it's a leap year.
249 * @returns false if it's a common year.
250 * @param i32Year The year in question.
251 */
252DECLINLINE(bool) rtTimeIsLeapYear(int32_t i32Year)
253{
254 return i32Year % 4 == 0
255 && ( i32Year % 100 != 0
256 || i32Year % 400 == 0);
257}
258
259
260/**
261 * Checks if a year is a leap year or not.
262 *
263 * @returns true if it's a leap year.
264 * @returns false if it's a common year.
265 * @param i32Year The year in question.
266 */
267RTDECL(bool) RTTimeIsLeapYear(int32_t i32Year)
268{
269 return rtTimeIsLeapYear(i32Year);
270}
271RT_EXPORT_SYMBOL(RTTimeIsLeapYear);
272
273
274/**
275 * Explodes a time spec (UTC).
276 *
277 * @returns pTime.
278 * @param pTime Where to store the exploded time.
279 * @param pTimeSpec The time spec to exploded.
280 */
281RTDECL(PRTTIME) RTTimeExplode(PRTTIME pTime, PCRTTIMESPEC pTimeSpec)
282{
283 int64_t i64Div;
284 int32_t i32Div;
285 int32_t i32Rem;
286 unsigned iYear;
287 const uint16_t *paiDayOfYear;
288 int iMonth;
289
290 AssertMsg(VALID_PTR(pTime), ("%p\n", pTime));
291 AssertMsg(VALID_PTR(pTimeSpec), ("%p\n", pTime));
292
293 /*
294 * The simple stuff first.
295 */
296 pTime->fFlags = RTTIME_FLAGS_TYPE_UTC;
297 i64Div = pTimeSpec->i64NanosecondsRelativeToUnixEpoch;
298 i32Rem = (int32_t)(i64Div % 1000000000);
299 i64Div /= 1000000000;
300 if (i32Rem < 0)
301 {
302 i32Rem += 1000000000;
303 i64Div--;
304 }
305 pTime->u32Nanosecond = i32Rem;
306
307 /* second */
308 i32Rem = (int32_t)(i64Div % 60);
309 i64Div /= 60;
310 if (i32Rem < 0)
311 {
312 i32Rem += 60;
313 i64Div--;
314 }
315 pTime->u8Second = i32Rem;
316
317 /* minute */
318 i32Div = (int32_t)i64Div; /* 60,000,000,000 > 33bit, so 31bit suffices. */
319 i32Rem = i32Div % 60;
320 i32Div /= 60;
321 if (i32Rem < 0)
322 {
323 i32Rem += 60;
324 i32Div--;
325 }
326 pTime->u8Minute = i32Rem;
327
328 /* hour */
329 i32Rem = i32Div % 24;
330 i32Div /= 24; /* days relative to 1970-01-01 */
331 if (i32Rem < 0)
332 {
333 i32Rem += 24;
334 i32Div--;
335 }
336 pTime->u8Hour = i32Rem;
337
338 /* weekday - 1970-01-01 was a Thursday (3) */
339 pTime->u8WeekDay = ((int)(i32Div % 7) + 3 + 7) % 7;
340
341 /*
342 * We've now got a number of days relative to 1970-01-01.
343 * To get the correct year number we have to mess with leap years. Fortunately,
344 * the representation we've got only supports a few hundred years, so we can
345 * generate a table and perform a simple two way search from the modulus 365 derived.
346 */
347 iYear = OFF_YEAR_IDX_EPOCH + i32Div / 365;
348 while (g_aoffYear[iYear + 1] <= i32Div)
349 iYear++;
350 while (g_aoffYear[iYear] > i32Div)
351 iYear--;
352 pTime->i32Year = iYear + OFF_YEAR_IDX_0_YEAR;
353 i32Div -= g_aoffYear[iYear];
354 pTime->u16YearDay = i32Div + 1;
355
356 /*
357 * Figuring out the month is done in a manner similar to the year, only here we
358 * ensure that the index is matching or too small.
359 */
360 if (rtTimeIsLeapYear(pTime->i32Year))
361 {
362 pTime->fFlags |= RTTIME_FLAGS_LEAP_YEAR;
363 paiDayOfYear = &g_aiDayOfYearLeap[0];
364 }
365 else
366 {
367 pTime->fFlags |= RTTIME_FLAGS_COMMON_YEAR;
368 paiDayOfYear = &g_aiDayOfYear[0];
369 }
370 iMonth = i32Div / 32;
371 i32Div++;
372 while (paiDayOfYear[iMonth + 1] <= i32Div)
373 iMonth++;
374 pTime->u8Month = iMonth + 1;
375 i32Div -= paiDayOfYear[iMonth];
376 pTime->u8MonthDay = i32Div + 1;
377
378 /* This is for UTC timespecs, so, no offset. */
379 pTime->offUTC = 0;
380
381 return pTime;
382}
383RT_EXPORT_SYMBOL(RTTimeExplode);
384
385
386/**
387 * Implodes exploded time to a time spec (UTC).
388 *
389 * @returns pTime on success.
390 * @returns NULL if the pTime data is invalid.
391 * @param pTimeSpec Where to store the imploded UTC time.
392 * If pTime specifies a time which outside the range, maximum or
393 * minimum values will be returned.
394 * @param pTime Pointer to the exploded time to implode.
395 * The fields u8Month, u8WeekDay and u8MonthDay are not used,
396 * and all the other fields are expected to be within their
397 * bounds. Use RTTimeNormalize() or RTTimeLocalNormalize() to
398 * calculate u16YearDay and normalize the ranges of the fields.
399 */
400RTDECL(PRTTIMESPEC) RTTimeImplode(PRTTIMESPEC pTimeSpec, PCRTTIME pTime)
401{
402 int32_t i32Days;
403 uint32_t u32Secs;
404 int64_t i64Nanos;
405
406 /*
407 * Validate input.
408 */
409 AssertReturn(VALID_PTR(pTimeSpec), NULL);
410 AssertReturn(VALID_PTR(pTime), NULL);
411 AssertReturn(pTime->u32Nanosecond < 1000000000, NULL);
412 AssertReturn(pTime->u8Second < 60, NULL);
413 AssertReturn(pTime->u8Minute < 60, NULL);
414 AssertReturn(pTime->u8Hour < 24, NULL);
415 AssertReturn(pTime->u16YearDay >= 1, NULL);
416 AssertReturn(pTime->u16YearDay <= (rtTimeIsLeapYear(pTime->i32Year) ? 366 : 365), NULL);
417 AssertMsgReturn(pTime->i32Year <= RTTIME_MAX_YEAR && pTime->i32Year >= RTTIME_MIN_YEAR, ("%RI32\n", pTime->i32Year), NULL);
418
419 RTTIME TimeUTC;
420 if ((pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) != RTTIME_FLAGS_TYPE_UTC)
421 {
422 TimeUTC = *pTime;
423 pTime = rtTimeConvertToZulu(&TimeUTC);
424 }
425
426 /*
427 * Do the conversion to nanoseconds.
428 */
429 i32Days = g_aoffYear[pTime->i32Year - OFF_YEAR_IDX_0_YEAR]
430 + pTime->u16YearDay - 1;
431 AssertMsgReturn(i32Days <= RTTIME_MAX_DAY && i32Days >= RTTIME_MIN_DAY, ("%RI32\n", i32Days), NULL);
432
433 u32Secs = pTime->u8Second
434 + pTime->u8Minute * 60
435 + pTime->u8Hour * 3600;
436 i64Nanos = (uint64_t)pTime->u32Nanosecond
437 + u32Secs * UINT64_C(1000000000);
438 AssertMsgReturn(i32Days != RTTIME_MAX_DAY || i64Nanos <= RTTIME_MAX_DAY_NANO, ("%RI64\n", i64Nanos), NULL);
439 AssertMsgReturn(i32Days != RTTIME_MIN_DAY || i64Nanos >= RTTIME_MIN_DAY_NANO, ("%RI64\n", i64Nanos), NULL);
440
441 i64Nanos += i32Days * UINT64_C(86400000000000);
442
443 pTimeSpec->i64NanosecondsRelativeToUnixEpoch = i64Nanos;
444 return pTimeSpec;
445}
446RT_EXPORT_SYMBOL(RTTimeImplode);
447
448
449/**
450 * Internal worker for RTTimeNormalize and RTTimeLocalNormalize.
451 */
452static PRTTIME rtTimeNormalizeInternal(PRTTIME pTime)
453{
454 unsigned uSecond;
455 unsigned uMinute;
456 unsigned uHour;
457 bool fLeapYear;
458
459 /*
460 * Fix the YearDay and Month/MonthDay.
461 */
462 fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
463 if (!pTime->u16YearDay)
464 {
465 /*
466 * The Month+MonthDay must present, overflow adjust them and calc the year day.
467 */
468 AssertMsgReturn( pTime->u8Month
469 && pTime->u8MonthDay,
470 ("date=%d-%d-%d\n", pTime->i32Year, pTime->u8Month, pTime->u8MonthDay),
471 NULL);
472 while (pTime->u8Month > 12)
473 {
474 pTime->u8Month -= 12;
475 pTime->i32Year++;
476 fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
477 pTime->fFlags &= ~(RTTIME_FLAGS_COMMON_YEAR | RTTIME_FLAGS_LEAP_YEAR);
478 }
479
480 for (;;)
481 {
482 unsigned cDaysInMonth = fLeapYear
483 ? g_acDaysInMonthsLeap[pTime->u8Month - 1]
484 : g_acDaysInMonths[pTime->u8Month - 1];
485 if (pTime->u8MonthDay <= cDaysInMonth)
486 break;
487 pTime->u8MonthDay -= cDaysInMonth;
488 if (pTime->u8Month != 12)
489 pTime->u8Month++;
490 else
491 {
492 pTime->u8Month = 1;
493 pTime->i32Year++;
494 fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
495 pTime->fFlags &= ~(RTTIME_FLAGS_COMMON_YEAR | RTTIME_FLAGS_LEAP_YEAR);
496 }
497 }
498
499 pTime->u16YearDay = pTime->u8MonthDay - 1
500 + (fLeapYear
501 ? g_aiDayOfYearLeap[pTime->u8Month - 1]
502 : g_aiDayOfYear[pTime->u8Month - 1]);
503 }
504 else
505 {
506 /*
507 * Are both YearDay and Month/MonthDay valid?
508 * Check that they don't overflow and match, if not use YearDay (simpler).
509 */
510 bool fRecalc = true;
511 if ( pTime->u8Month
512 && pTime->u8MonthDay)
513 {
514 do
515 {
516 uint16_t u16YearDay;
517
518 /* If you change one, zero the other to make clear what you mean. */
519 AssertBreak(pTime->u8Month <= 12);
520 AssertBreak(pTime->u8MonthDay <= (fLeapYear
521 ? g_acDaysInMonthsLeap[pTime->u8Month - 1]
522 : g_acDaysInMonths[pTime->u8Month - 1]));
523 u16YearDay = pTime->u8MonthDay - 1
524 + (fLeapYear
525 ? g_aiDayOfYearLeap[pTime->u8Month - 1]
526 : g_aiDayOfYear[pTime->u8Month - 1]);
527 AssertBreak(u16YearDay == pTime->u16YearDay);
528 fRecalc = false;
529 } while (0);
530 }
531 if (fRecalc)
532 {
533 const uint16_t *paiDayOfYear;
534
535 /* overflow adjust YearDay */
536 while (pTime->u16YearDay > (fLeapYear ? 366 : 365))
537 {
538 pTime->u16YearDay -= fLeapYear ? 366 : 365;
539 pTime->i32Year++;
540 fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
541 pTime->fFlags &= ~(RTTIME_FLAGS_COMMON_YEAR | RTTIME_FLAGS_LEAP_YEAR);
542 }
543
544 /* calc Month and MonthDay */
545 paiDayOfYear = fLeapYear
546 ? &g_aiDayOfYearLeap[0]
547 : &g_aiDayOfYear[0];
548 pTime->u8Month = 1;
549 while (pTime->u16YearDay > paiDayOfYear[pTime->u8Month] - 1)
550 pTime->u8Month++;
551 Assert(pTime->u8Month >= 1 && pTime->u8Month <= 12);
552 pTime->u8MonthDay = pTime->u16YearDay - paiDayOfYear[pTime->u8Month - 1] + 1;
553 }
554 }
555
556 /*
557 * Fixup time overflows.
558 * Use unsigned int values internally to avoid overflows.
559 */
560 uSecond = pTime->u8Second;
561 uMinute = pTime->u8Minute;
562 uHour = pTime->u8Hour;
563
564 while (pTime->u32Nanosecond >= 1000000000)
565 {
566 pTime->u32Nanosecond -= 1000000000;
567 uSecond++;
568 }
569
570 while (uSecond >= 60)
571 {
572 uSecond -= 60;
573 uMinute++;
574 }
575
576 while (uMinute >= 60)
577 {
578 uMinute -= 60;
579 uHour++;
580 }
581
582 while (uHour >= 24)
583 {
584 uHour -= 24;
585
586 /* This is really a RTTimeIncDay kind of thing... */
587 if (pTime->u16YearDay + 1 != (fLeapYear ? g_aiDayOfYearLeap[pTime->u8Month] : g_aiDayOfYear[pTime->u8Month]))
588 {
589 pTime->u16YearDay++;
590 pTime->u8MonthDay++;
591 }
592 else if (pTime->u8Month != 12)
593 {
594 pTime->u16YearDay++;
595 pTime->u8Month++;
596 pTime->u8MonthDay = 1;
597 }
598 else
599 {
600 pTime->i32Year++;
601 fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
602 pTime->fFlags &= ~(RTTIME_FLAGS_COMMON_YEAR | RTTIME_FLAGS_LEAP_YEAR);
603 pTime->u16YearDay = 1;
604 pTime->u8Month = 1;
605 pTime->u8MonthDay = 1;
606 }
607 }
608
609 pTime->u8Second = uSecond;
610 pTime->u8Minute = uMinute;
611 pTime->u8Hour = uHour;
612
613 /*
614 * Correct the leap year flag.
615 * Assert if it's wrong, but ignore if unset.
616 */
617 if (fLeapYear)
618 {
619 Assert(!(pTime->fFlags & RTTIME_FLAGS_COMMON_YEAR));
620 pTime->fFlags &= ~RTTIME_FLAGS_COMMON_YEAR;
621 pTime->fFlags |= RTTIME_FLAGS_LEAP_YEAR;
622 }
623 else
624 {
625 Assert(!(pTime->fFlags & RTTIME_FLAGS_LEAP_YEAR));
626 pTime->fFlags &= ~RTTIME_FLAGS_LEAP_YEAR;
627 pTime->fFlags |= RTTIME_FLAGS_COMMON_YEAR;
628 }
629
630
631 /*
632 * Calc week day.
633 *
634 * 1970-01-01 was a Thursday (3), so find the number of days relative to
635 * that point. We use the table when possible and a slow+stupid+brute-force
636 * algorithm for points outside it. Feel free to optimize the latter by
637 * using some clever formula.
638 */
639 if ( pTime->i32Year >= OFF_YEAR_IDX_0_YEAR
640 && pTime->i32Year < OFF_YEAR_IDX_0_YEAR + (int32_t)RT_ELEMENTS(g_aoffYear))
641 {
642 int32_t offDays = g_aoffYear[pTime->i32Year - OFF_YEAR_IDX_0_YEAR]
643 + pTime->u16YearDay -1;
644 pTime->u8WeekDay = ((offDays % 7) + 3 + 7) % 7;
645 }
646 else
647 {
648 int32_t i32Year = pTime->i32Year;
649 if (i32Year >= 1970)
650 {
651 uint64_t offDays = pTime->u16YearDay - 1;
652 while (--i32Year >= 1970)
653 offDays += rtTimeIsLeapYear(i32Year) ? 366 : 365;
654 pTime->u8WeekDay = (uint8_t)((offDays + 3) % 7);
655 }
656 else
657 {
658 int64_t offDays = (fLeapYear ? -366 - 1 : -365 - 1) + pTime->u16YearDay;
659 while (++i32Year < 1970)
660 offDays -= rtTimeIsLeapYear(i32Year) ? 366 : 365;
661 pTime->u8WeekDay = ((int)(offDays % 7) + 3 + 7) % 7;
662 }
663 }
664 return pTime;
665}
666
667
668/**
669 * Normalizes the fields of a time structure.
670 *
671 * It is possible to calculate year-day from month/day and vice
672 * versa. If you adjust any of these, make sure to zero the
673 * other so you make it clear which of the fields to use. If
674 * it's ambiguous, the year-day field is used (and you get
675 * assertions in debug builds).
676 *
677 * All the time fields and the year-day or month/day fields will
678 * be adjusted for overflows. (Since all fields are unsigned, there
679 * is no underflows.) It is possible to exploit this for simple
680 * date math, though the recommended way of doing that to implode
681 * the time into a timespec and do the math on that.
682 *
683 * @returns pTime on success.
684 * @returns NULL if the data is invalid.
685 *
686 * @param pTime The time structure to normalize.
687 *
688 * @remarks This function doesn't work with local time, only with UTC time.
689 */
690RTDECL(PRTTIME) RTTimeNormalize(PRTTIME pTime)
691{
692 /*
693 * Validate that we've got the minimum of stuff handy.
694 */
695 AssertReturn(VALID_PTR(pTime), NULL);
696 AssertMsgReturn(!(pTime->fFlags & ~RTTIME_FLAGS_MASK), ("%#x\n", pTime->fFlags), NULL);
697 AssertMsgReturn((pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) != RTTIME_FLAGS_TYPE_LOCAL, ("Use RTTimeLocalNormalize!\n"), NULL);
698 AssertMsgReturn(pTime->offUTC == 0, ("%d; Use RTTimeLocalNormalize!\n", pTime->offUTC), NULL);
699
700 pTime = rtTimeNormalizeInternal(pTime);
701 if (pTime)
702 pTime->fFlags |= RTTIME_FLAGS_TYPE_UTC;
703 return pTime;
704}
705RT_EXPORT_SYMBOL(RTTimeNormalize);
706
707
708/**
709 * Normalizes the fields of a time structure, assuming local time.
710 *
711 * It is possible to calculate year-day from month/day and vice
712 * versa. If you adjust any of these, make sure to zero the
713 * other so you make it clear which of the fields to use. If
714 * it's ambiguous, the year-day field is used (and you get
715 * assertions in debug builds).
716 *
717 * All the time fields and the year-day or month/day fields will
718 * be adjusted for overflows. (Since all fields are unsigned, there
719 * is no underflows.) It is possible to exploit this for simple
720 * date math, though the recommended way of doing that to implode
721 * the time into a timespec and do the math on that.
722 *
723 * @returns pTime on success.
724 * @returns NULL if the data is invalid.
725 *
726 * @param pTime The time structure to normalize.
727 *
728 * @remarks This function doesn't work with UTC time, only with local time.
729 */
730RTDECL(PRTTIME) RTTimeLocalNormalize(PRTTIME pTime)
731{
732 /*
733 * Validate that we've got the minimum of stuff handy.
734 */
735 AssertReturn(VALID_PTR(pTime), NULL);
736 AssertMsgReturn(!(pTime->fFlags & ~RTTIME_FLAGS_MASK), ("%#x\n", pTime->fFlags), NULL);
737 AssertMsgReturn((pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) != RTTIME_FLAGS_TYPE_UTC, ("Use RTTimeNormalize!\n"), NULL);
738
739 pTime = rtTimeNormalizeInternal(pTime);
740 if (pTime)
741 pTime->fFlags |= RTTIME_FLAGS_TYPE_LOCAL;
742 return pTime;
743}
744RT_EXPORT_SYMBOL(RTTimeLocalNormalize);
745
746
747/**
748 * Converts a time spec to a ISO date string.
749 *
750 * @returns psz on success.
751 * @returns NULL on buffer underflow.
752 * @param pTime The time. Caller should've normalized this.
753 * @param psz Where to store the string.
754 * @param cb The size of the buffer.
755 */
756RTDECL(char *) RTTimeToString(PCRTTIME pTime, char *psz, size_t cb)
757{
758 size_t cch;
759
760 /* (Default to UTC if not specified) */
761 if ( (pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) == RTTIME_FLAGS_TYPE_LOCAL
762 && pTime->offUTC)
763 {
764 int32_t offUTC = pTime->offUTC;
765 Assert(offUTC <= 840 && offUTC >= -840);
766 char chSign;
767 if (offUTC >= 0)
768 chSign = '+';
769 else
770 {
771 chSign = '-';
772 offUTC = -offUTC;
773 }
774 uint32_t offUTCHour = (uint32_t)offUTC / 60;
775 uint32_t offUTCMinute = (uint32_t)offUTC % 60;
776 cch = RTStrPrintf(psz, cb,
777 "%RI32-%02u-%02uT%02u:%02u:%02u.%09RU32%c%02d%:02d",
778 pTime->i32Year, pTime->u8Month, pTime->u8MonthDay,
779 pTime->u8Hour, pTime->u8Minute, pTime->u8Second, pTime->u32Nanosecond,
780 chSign, offUTCHour, offUTCMinute);
781 if ( cch <= 15
782 || psz[cch - 6] != chSign)
783 return NULL;
784 }
785 else
786 {
787 cch = RTStrPrintf(psz, cb, "%RI32-%02u-%02uT%02u:%02u:%02u.%09RU32Z",
788 pTime->i32Year, pTime->u8Month, pTime->u8MonthDay,
789 pTime->u8Hour, pTime->u8Minute, pTime->u8Second, pTime->u32Nanosecond);
790 if ( cch <= 15
791 || psz[cch - 1] != 'Z')
792 return NULL;
793 }
794 return psz;
795}
796RT_EXPORT_SYMBOL(RTTimeToString);
797
798
799/**
800 * Converts a time spec to a ISO date string.
801 *
802 * @returns psz on success.
803 * @returns NULL on buffer underflow.
804 * @param pTime The time spec.
805 * @param psz Where to store the string.
806 * @param cb The size of the buffer.
807 */
808RTDECL(char *) RTTimeSpecToString(PCRTTIMESPEC pTime, char *psz, size_t cb)
809{
810 RTTIME Time;
811 return RTTimeToString(RTTimeExplode(&Time, pTime), psz, cb);
812}
813RT_EXPORT_SYMBOL(RTTimeSpecToString);
814
815
816
817/**
818 * Attempts to convert an ISO date string to a time structure.
819 *
820 * We're a little forgiving with zero padding, unspecified parts, and leading
821 * and trailing spaces.
822 *
823 * @retval pTime on success,
824 * @retval NULL on failure.
825 * @param pTime Where to store the time on success.
826 * @param pszString The ISO date string to convert.
827 */
828RTDECL(PRTTIME) RTTimeFromString(PRTTIME pTime, const char *pszString)
829{
830 /* Ignore leading spaces. */
831 while (RT_C_IS_SPACE(*pszString))
832 pszString++;
833
834 /*
835 * Init non date & time parts.
836 */
837 pTime->fFlags = RTTIME_FLAGS_TYPE_LOCAL;
838 pTime->offUTC = 0;
839
840 /*
841 * The day part.
842 */
843
844 /* Year */
845 int rc = RTStrToInt32Ex(pszString, (char **)&pszString, 10, &pTime->i32Year);
846 if (rc != VWRN_TRAILING_CHARS)
847 return NULL;
848
849 bool const fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
850 if (fLeapYear)
851 pTime->fFlags |= RTTIME_FLAGS_LEAP_YEAR;
852
853 if (*pszString++ != '-')
854 return NULL;
855
856 /* Month of the year. */
857 rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Month);
858 if (rc != VWRN_TRAILING_CHARS)
859 return NULL;
860 if (pTime->u8Month == 0 || pTime->u8Month > 12)
861 return NULL;
862 if (*pszString++ != '-')
863 return NULL;
864
865 /* Day of month.*/
866 rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8MonthDay);
867 if (rc != VWRN_TRAILING_CHARS && rc != VINF_SUCCESS)
868 return NULL;
869 unsigned const cDaysInMonth = fLeapYear
870 ? g_acDaysInMonthsLeap[pTime->u8Month - 1]
871 : g_acDaysInMonths[pTime->u8Month - 1];
872 if (pTime->u8MonthDay == 0 || pTime->u8MonthDay > cDaysInMonth)
873 return NULL;
874
875 /* Calculate year day. */
876 pTime->u16YearDay = pTime->u8MonthDay - 1
877 + (fLeapYear
878 ? g_aiDayOfYearLeap[pTime->u8Month - 1]
879 : g_aiDayOfYear[pTime->u8Month - 1]);
880
881 /*
882 * The time part.
883 */
884 if (*pszString++ != 'T')
885 return NULL;
886
887 /* Hour. */
888 rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Hour);
889 if (rc != VWRN_TRAILING_CHARS)
890 return NULL;
891 if (pTime->u8Hour > 23)
892 return NULL;
893 if (*pszString++ != ':')
894 return NULL;
895
896 /* Minute. */
897 rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Minute);
898 if (rc != VWRN_TRAILING_CHARS)
899 return NULL;
900 if (pTime->u8Minute > 59)
901 return NULL;
902 if (*pszString++ != ':')
903 return NULL;
904
905 /* Second. */
906 rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Second);
907 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS && rc != VWRN_TRAILING_SPACES)
908 return NULL;
909 if (pTime->u8Second > 59)
910 return NULL;
911
912 /* Nanoseconds is optional and probably non-standard. */
913 if (*pszString == '.')
914 {
915 rc = RTStrToUInt32Ex(pszString + 1, (char **)&pszString, 10, &pTime->u32Nanosecond);
916 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS && rc != VWRN_TRAILING_SPACES)
917 return NULL;
918 if (pTime->u32Nanosecond >= 1000000000)
919 return NULL;
920 }
921 else
922 pTime->u32Nanosecond = 0;
923
924 /*
925 * Time zone.
926 */
927 if (*pszString == 'Z')
928 {
929 pszString++;
930 pTime->fFlags &= ~RTTIME_FLAGS_TYPE_MASK;
931 pTime->fFlags |= ~RTTIME_FLAGS_TYPE_UTC;
932 pTime->offUTC = 0;
933 }
934 else if ( *pszString == '+'
935 || *pszString == '-')
936 {
937 int8_t cUtcHours = 0;
938 rc = RTStrToInt8Ex(pszString, (char **)&pszString, 10, &cUtcHours);
939 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS && rc != VWRN_TRAILING_SPACES)
940 return NULL;
941 uint8_t cUtcMin = 0;
942 if (*pszString == ':')
943 {
944 rc = RTStrToUInt8Ex(pszString + 1, (char **)&pszString, 10, &cUtcMin);
945 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
946 return NULL;
947 }
948 else if (*pszString && !RT_C_IS_BLANK(*pszString))
949 return NULL;
950 if (cUtcHours >= 0)
951 pTime->offUTC = cUtcHours * 60 + cUtcMin;
952 else
953 pTime->offUTC = cUtcHours * 60 - cUtcMin;
954 if (RT_ABS(pTime->offUTC) > 840)
955 return NULL;
956 }
957 /* else: No time zone given, local with offUTC = 0. */
958
959 /*
960 * The rest of the string should be blanks.
961 */
962 char ch;
963 while ((ch = *pszString++) != '\0')
964 if (!RT_C_IS_BLANK(ch))
965 return NULL;
966
967 return pTime;
968}
969RT_EXPORT_SYMBOL(RTTimeFromString);
970
971
972/**
973 * Attempts to convert an ISO date string to a time structure.
974 *
975 * We're a little forgiving with zero padding, unspecified parts, and leading
976 * and trailing spaces.
977 *
978 * @retval pTime on success,
979 * @retval NULL on failure.
980 * @param pTime The time spec.
981 * @param pszString The ISO date string to convert.
982 */
983RTDECL(PRTTIMESPEC) RTTimeSpecFromString(PRTTIMESPEC pTime, const char *pszString)
984{
985 RTTIME Time;
986 if (RTTimeFromString(&Time, pszString))
987 return RTTimeImplode(pTime, &Time);
988 return NULL;
989}
990RT_EXPORT_SYMBOL(RTTimeSpecFromString);
991
992
993/**
994 * Adds one day to @a pTime.
995 *
996 * ASSUMES it is zulu time so DST can be ignored.
997 */
998static PRTTIME rtTimeAdd1Day(PRTTIME pTime)
999{
1000 Assert(!pTime->offUTC);
1001 rtTimeNormalizeInternal(pTime);
1002 pTime->u8MonthDay += 1;
1003 pTime->u16YearDay = 0;
1004 return rtTimeNormalizeInternal(pTime);
1005}
1006
1007
1008/**
1009 * Subtracts one day from @a pTime.
1010 *
1011 * ASSUMES it is zulu time so DST can be ignored.
1012 */
1013static PRTTIME rtTimeSub1Day(PRTTIME pTime)
1014{
1015 Assert(!pTime->offUTC);
1016 rtTimeNormalizeInternal(pTime);
1017 if (pTime->u16YearDay > 1)
1018 {
1019 pTime->u16YearDay -= 1;
1020 pTime->u8Month = 0;
1021 pTime->u8MonthDay = 0;
1022 }
1023 else
1024 {
1025 pTime->i32Year -= 1;
1026 pTime->u16YearDay = rtTimeIsLeapYear(pTime->i32Year) ? 366 : 365;
1027 pTime->u8MonthDay = 31;
1028 pTime->u8Month = 12;
1029 pTime->fFlags &= ~(RTTIME_FLAGS_COMMON_YEAR | RTTIME_FLAGS_LEAP_YEAR);
1030 }
1031 return rtTimeNormalizeInternal(pTime);
1032}
1033
1034
1035/**
1036 * Adds a signed number of minutes to @a pTime.
1037 *
1038 * ASSUMES it is zulu time so DST can be ignored.
1039 *
1040 * @param pTime The time structure to work on.
1041 * @param cAddend Number of minutes to add.
1042 * ASSUMES the value isn't all that high!
1043 */
1044static PRTTIME rtTimeAddMinutes(PRTTIME pTime, int32_t cAddend)
1045{
1046 Assert(RT_ABS(cAddend) < 31 * 24 * 60);
1047
1048 /*
1049 * Work on minutes of the day.
1050 */
1051 int32_t const cMinutesInDay = 24 * 60;
1052 int32_t iDayMinute = (unsigned)pTime->u8Hour * 60 + pTime->u8Minute;
1053 iDayMinute += cAddend;
1054
1055 while (iDayMinute >= cMinutesInDay)
1056 {
1057 rtTimeAdd1Day(pTime);
1058 iDayMinute -= cMinutesInDay;
1059 }
1060
1061 while (iDayMinute < 0)
1062 {
1063 rtTimeSub1Day(pTime);
1064 iDayMinute += cMinutesInDay;
1065 }
1066
1067 pTime->u8Hour = iDayMinute / 60;
1068 pTime->u8Minute = iDayMinute % 60;
1069
1070 return pTime;
1071}
1072
1073
1074/**
1075 * Converts @a pTime to zulu time (UTC) if needed.
1076 *
1077 * @returns pTime.
1078 * @param pTime What to convert (in/out).
1079 */
1080static PRTTIME rtTimeConvertToZulu(PRTTIME pTime)
1081{
1082 RTTIME_ASSERT_NORMALIZED(pTime);
1083 if ((pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) != RTTIME_FLAGS_TYPE_UTC)
1084 {
1085 int32_t offUTC = pTime->offUTC;
1086 pTime->offUTC = 0;
1087 pTime->fFlags &= ~RTTIME_FLAGS_TYPE_MASK;
1088 pTime->fFlags |= RTTIME_FLAGS_TYPE_UTC;
1089 if (offUTC != 0)
1090 rtTimeAddMinutes(pTime, -offUTC);
1091 }
1092 return pTime;
1093}
1094
1095
1096/**
1097 * Converts a time structure to UTC, relying on UTC offset information if it contains local time.
1098 *
1099 * @returns pTime on success.
1100 * @returns NULL if the data is invalid.
1101 * @param pTime The time structure to convert.
1102 */
1103PRTTIME RTTimeConvertToZulu(PRTTIME pTime)
1104{
1105 /*
1106 * Validate that we've got the minimum of stuff handy.
1107 */
1108 AssertReturn(VALID_PTR(pTime), NULL);
1109 AssertMsgReturn(!(pTime->fFlags & ~RTTIME_FLAGS_MASK), ("%#x\n", pTime->fFlags), NULL);
1110
1111 return rtTimeConvertToZulu(rtTimeNormalizeInternal(pTime));
1112}
1113
1114
1115/**
1116 * Compares two normalized time structures.
1117 *
1118 * @retval 0 if equal.
1119 * @retval -1 if @a pLeft is earlier than @a pRight.
1120 * @retval 1 if @a pRight is earlier than @a pLeft.
1121 *
1122 * @param pLeft The left side time. NULL is accepted.
1123 * @param pRight The right side time. NULL is accepted.
1124 *
1125 * @note A NULL time is considered smaller than anything else. If both are
1126 * NULL, they are considered equal.
1127 */
1128RTDECL(int) RTTimeCompare(PCRTTIME pLeft, PCRTTIME pRight)
1129{
1130#ifdef RT_STRICT
1131 if (pLeft)
1132 RTTIME_ASSERT_NORMALIZED(pLeft);
1133 if (pRight)
1134 RTTIME_ASSERT_NORMALIZED(pRight);
1135#endif
1136
1137 int iRet;
1138 if (pLeft)
1139 {
1140 if (pRight)
1141 {
1142 /*
1143 * Only work with normalized zulu time.
1144 */
1145 RTTIME TmpLeft;
1146 if ( pLeft->offUTC != 0
1147 || pLeft->u16YearDay == 0
1148 || pLeft->u16YearDay > 366
1149 || pLeft->u8Hour >= 60
1150 || pLeft->u8Minute >= 60
1151 || pLeft->u8Second >= 60)
1152 {
1153 TmpLeft = *pLeft;
1154 pLeft = rtTimeConvertToZulu(rtTimeNormalizeInternal(&TmpLeft));
1155 }
1156
1157 RTTIME TmpRight;
1158 if ( pRight->offUTC != 0
1159 || pRight->u16YearDay == 0
1160 || pRight->u16YearDay > 366
1161 || pRight->u8Hour >= 60
1162 || pRight->u8Minute >= 60
1163 || pRight->u8Second >= 60)
1164 {
1165 TmpRight = *pRight;
1166 pRight = rtTimeConvertToZulu(rtTimeNormalizeInternal(&TmpRight));
1167 }
1168
1169 /*
1170 * Do the comparison.
1171 */
1172 if ( pLeft->i32Year != pRight->i32Year)
1173 iRet = pLeft->i32Year < pRight->i32Year ? -1 : 1;
1174 else if ( pLeft->u16YearDay != pRight->u16YearDay)
1175 iRet = pLeft->u16YearDay < pRight->u16YearDay ? -1 : 1;
1176 else if ( pLeft->u8Hour != pRight->u8Hour)
1177 iRet = pLeft->u8Hour < pRight->u8Hour ? -1 : 1;
1178 else if ( pLeft->u8Minute != pRight->u8Minute)
1179 iRet = pLeft->u8Minute < pRight->u8Minute ? -1 : 1;
1180 else if ( pLeft->u8Second != pRight->u8Second)
1181 iRet = pLeft->u8Second < pRight->u8Second ? -1 : 1;
1182 else if ( pLeft->u32Nanosecond != pRight->u32Nanosecond)
1183 iRet = pLeft->u32Nanosecond < pRight->u32Nanosecond ? -1 : 1;
1184 else
1185 iRet = 0;
1186 }
1187 else
1188 iRet = 1;
1189 }
1190 else
1191 iRet = pRight ? -1 : 0;
1192 return iRet;
1193}
1194RT_EXPORT_SYMBOL(RTTimeCompare);
1195
Note: See TracBrowser for help on using the repository browser.

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