VirtualBox

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

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

IPRT: Added RTTimeCompare and made use of it in the RTAsn1Time methods instead of the range limited RTTimeSpecCompare. Makes certificates with expire time beyond year 3000 found on windows 10 insider builds (??) work. [build fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 41.3 KB
Line 
1/* $Id: time.cpp 70896 2018-02-07 23:18:51Z 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.virtualbox.org. 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/**
241 * Checks if a year is a leap year or not.
242 *
243 * @returns true if it's a leap year.
244 * @returns false if it's a common year.
245 * @param i32Year The year in question.
246 */
247DECLINLINE(bool) rtTimeIsLeapYear(int32_t i32Year)
248{
249 return i32Year % 4 == 0
250 && ( i32Year % 100 != 0
251 || i32Year % 400 == 0);
252}
253
254
255/**
256 * Checks if a year is a leap year or not.
257 *
258 * @returns true if it's a leap year.
259 * @returns false if it's a common year.
260 * @param i32Year The year in question.
261 */
262RTDECL(bool) RTTimeIsLeapYear(int32_t i32Year)
263{
264 return rtTimeIsLeapYear(i32Year);
265}
266RT_EXPORT_SYMBOL(RTTimeIsLeapYear);
267
268
269/**
270 * Explodes a time spec (UTC).
271 *
272 * @returns pTime.
273 * @param pTime Where to store the exploded time.
274 * @param pTimeSpec The time spec to exploded.
275 */
276RTDECL(PRTTIME) RTTimeExplode(PRTTIME pTime, PCRTTIMESPEC pTimeSpec)
277{
278 int64_t i64Div;
279 int32_t i32Div;
280 int32_t i32Rem;
281 unsigned iYear;
282 const uint16_t *paiDayOfYear;
283 int iMonth;
284
285 AssertMsg(VALID_PTR(pTime), ("%p\n", pTime));
286 AssertMsg(VALID_PTR(pTimeSpec), ("%p\n", pTime));
287
288 /*
289 * The simple stuff first.
290 */
291 pTime->fFlags = RTTIME_FLAGS_TYPE_UTC;
292 i64Div = pTimeSpec->i64NanosecondsRelativeToUnixEpoch;
293 i32Rem = (int32_t)(i64Div % 1000000000);
294 i64Div /= 1000000000;
295 if (i32Rem < 0)
296 {
297 i32Rem += 1000000000;
298 i64Div--;
299 }
300 pTime->u32Nanosecond = i32Rem;
301
302 /* second */
303 i32Rem = (int32_t)(i64Div % 60);
304 i64Div /= 60;
305 if (i32Rem < 0)
306 {
307 i32Rem += 60;
308 i64Div--;
309 }
310 pTime->u8Second = i32Rem;
311
312 /* minute */
313 i32Div = (int32_t)i64Div; /* 60,000,000,000 > 33bit, so 31bit suffices. */
314 i32Rem = i32Div % 60;
315 i32Div /= 60;
316 if (i32Rem < 0)
317 {
318 i32Rem += 60;
319 i32Div--;
320 }
321 pTime->u8Minute = i32Rem;
322
323 /* hour */
324 i32Rem = i32Div % 24;
325 i32Div /= 24; /* days relative to 1970-01-01 */
326 if (i32Rem < 0)
327 {
328 i32Rem += 24;
329 i32Div--;
330 }
331 pTime->u8Hour = i32Rem;
332
333 /* weekday - 1970-01-01 was a Thursday (3) */
334 pTime->u8WeekDay = ((int)(i32Div % 7) + 3 + 7) % 7;
335
336 /*
337 * We've now got a number of days relative to 1970-01-01.
338 * To get the correct year number we have to mess with leap years. Fortunately,
339 * the representation we've got only supports a few hundred years, so we can
340 * generate a table and perform a simple two way search from the modulus 365 derived.
341 */
342 iYear = OFF_YEAR_IDX_EPOCH + i32Div / 365;
343 while (g_aoffYear[iYear + 1] <= i32Div)
344 iYear++;
345 while (g_aoffYear[iYear] > i32Div)
346 iYear--;
347 pTime->i32Year = iYear + OFF_YEAR_IDX_0_YEAR;
348 i32Div -= g_aoffYear[iYear];
349 pTime->u16YearDay = i32Div + 1;
350
351 /*
352 * Figuring out the month is done in a manner similar to the year, only here we
353 * ensure that the index is matching or too small.
354 */
355 if (rtTimeIsLeapYear(pTime->i32Year))
356 {
357 pTime->fFlags |= RTTIME_FLAGS_LEAP_YEAR;
358 paiDayOfYear = &g_aiDayOfYearLeap[0];
359 }
360 else
361 {
362 pTime->fFlags |= RTTIME_FLAGS_COMMON_YEAR;
363 paiDayOfYear = &g_aiDayOfYear[0];
364 }
365 iMonth = i32Div / 32;
366 i32Div++;
367 while (paiDayOfYear[iMonth + 1] <= i32Div)
368 iMonth++;
369 pTime->u8Month = iMonth + 1;
370 i32Div -= paiDayOfYear[iMonth];
371 pTime->u8MonthDay = i32Div + 1;
372
373 /* This is for UTC timespecs, so, no offset. */
374 pTime->offUTC = 0;
375
376 return pTime;
377}
378RT_EXPORT_SYMBOL(RTTimeExplode);
379
380
381/**
382 * Implodes exploded time to a time spec (UTC).
383 *
384 * @returns pTime on success.
385 * @returns NULL if the pTime data is invalid.
386 * @param pTimeSpec Where to store the imploded UTC time.
387 * If pTime specifies a time which outside the range, maximum or
388 * minimum values will be returned.
389 * @param pTime Pointer to the exploded time to implode.
390 * The fields u8Month, u8WeekDay and u8MonthDay are not used,
391 * and all the other fields are expected to be within their
392 * bounds. Use RTTimeNormalize() to calculate u16YearDay and
393 * normalize the ranges of the fields.
394 */
395RTDECL(PRTTIMESPEC) RTTimeImplode(PRTTIMESPEC pTimeSpec, PCRTTIME pTime)
396{
397 int32_t i32Days;
398 uint32_t u32Secs;
399 int64_t i64Nanos;
400
401 /*
402 * Validate input.
403 */
404 AssertReturn(VALID_PTR(pTimeSpec), NULL);
405 AssertReturn(VALID_PTR(pTime), NULL);
406 AssertReturn(pTime->u32Nanosecond < 1000000000, NULL);
407 AssertReturn(pTime->u8Second < 60, NULL);
408 AssertReturn(pTime->u8Minute < 60, NULL);
409 AssertReturn(pTime->u8Hour < 24, NULL);
410 AssertReturn(pTime->u16YearDay >= 1, NULL);
411 AssertReturn(pTime->u16YearDay <= (rtTimeIsLeapYear(pTime->i32Year) ? 366 : 365), NULL);
412 AssertMsgReturn(pTime->i32Year <= RTTIME_MAX_YEAR && pTime->i32Year >= RTTIME_MIN_YEAR, ("%RI32\n", pTime->i32Year), NULL);
413
414 /*
415 * Do the conversion to nanoseconds.
416 */
417 i32Days = g_aoffYear[pTime->i32Year - OFF_YEAR_IDX_0_YEAR]
418 + pTime->u16YearDay - 1;
419 AssertMsgReturn(i32Days <= RTTIME_MAX_DAY && i32Days >= RTTIME_MIN_DAY, ("%RI32\n", i32Days), NULL);
420
421 u32Secs = pTime->u8Second
422 + pTime->u8Minute * 60
423 + pTime->u8Hour * 3600;
424 i64Nanos = (uint64_t)pTime->u32Nanosecond
425 + u32Secs * UINT64_C(1000000000);
426 AssertMsgReturn(i32Days != RTTIME_MAX_DAY || i64Nanos <= RTTIME_MAX_DAY_NANO, ("%RI64\n", i64Nanos), NULL);
427 AssertMsgReturn(i32Days != RTTIME_MIN_DAY || i64Nanos >= RTTIME_MIN_DAY_NANO, ("%RI64\n", i64Nanos), NULL);
428
429 i64Nanos += i32Days * UINT64_C(86400000000000);
430
431 pTimeSpec->i64NanosecondsRelativeToUnixEpoch = i64Nanos;
432 return pTimeSpec;
433}
434RT_EXPORT_SYMBOL(RTTimeImplode);
435
436
437/**
438 * Internal worker for RTTimeNormalize and RTTimeLocalNormalize.
439 * It doesn't adjust the UCT offset but leaves that for RTTimeLocalNormalize.
440 */
441static PRTTIME rtTimeNormalizeInternal(PRTTIME pTime)
442{
443 unsigned uSecond;
444 unsigned uMinute;
445 unsigned uHour;
446 bool fLeapYear;
447
448 /*
449 * Fix the YearDay and Month/MonthDay.
450 */
451 fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
452 if (!pTime->u16YearDay)
453 {
454 /*
455 * The Month+MonthDay must present, overflow adjust them and calc the year day.
456 */
457 AssertMsgReturn( pTime->u8Month
458 && pTime->u8MonthDay,
459 ("date=%d-%d-%d\n", pTime->i32Year, pTime->u8Month, pTime->u8MonthDay),
460 NULL);
461 while (pTime->u8Month > 12)
462 {
463 pTime->u8Month -= 12;
464 pTime->i32Year++;
465 fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
466 pTime->fFlags &= ~(RTTIME_FLAGS_COMMON_YEAR | RTTIME_FLAGS_LEAP_YEAR);
467 }
468
469 for (;;)
470 {
471 unsigned cDaysInMonth = fLeapYear
472 ? g_acDaysInMonthsLeap[pTime->u8Month - 1]
473 : g_acDaysInMonths[pTime->u8Month - 1];
474 if (pTime->u8MonthDay <= cDaysInMonth)
475 break;
476 pTime->u8MonthDay -= cDaysInMonth;
477 if (pTime->u8Month != 12)
478 pTime->u8Month++;
479 else
480 {
481 pTime->u8Month = 1;
482 pTime->i32Year++;
483 fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
484 pTime->fFlags &= ~(RTTIME_FLAGS_COMMON_YEAR | RTTIME_FLAGS_LEAP_YEAR);
485 }
486 }
487
488 pTime->u16YearDay = pTime->u8MonthDay - 1
489 + (fLeapYear
490 ? g_aiDayOfYearLeap[pTime->u8Month - 1]
491 : g_aiDayOfYear[pTime->u8Month - 1]);
492 }
493 else
494 {
495 /*
496 * Are both YearDay and Month/MonthDay valid?
497 * Check that they don't overflow and match, if not use YearDay (simpler).
498 */
499 bool fRecalc = true;
500 if ( pTime->u8Month
501 && pTime->u8MonthDay)
502 {
503 do
504 {
505 uint16_t u16YearDay;
506
507 /* If you change one, zero the other to make clear what you mean. */
508 AssertBreak(pTime->u8Month <= 12);
509 AssertBreak(pTime->u8MonthDay <= (fLeapYear
510 ? g_acDaysInMonthsLeap[pTime->u8Month - 1]
511 : g_acDaysInMonths[pTime->u8Month - 1]));
512 u16YearDay = pTime->u8MonthDay - 1
513 + (fLeapYear
514 ? g_aiDayOfYearLeap[pTime->u8Month - 1]
515 : g_aiDayOfYear[pTime->u8Month - 1]);
516 AssertBreak(u16YearDay == pTime->u16YearDay);
517 fRecalc = false;
518 } while (0);
519 }
520 if (fRecalc)
521 {
522 const uint16_t *paiDayOfYear;
523
524 /* overflow adjust YearDay */
525 while (pTime->u16YearDay > (fLeapYear ? 366 : 365))
526 {
527 pTime->u16YearDay -= fLeapYear ? 366 : 365;
528 pTime->i32Year++;
529 fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
530 pTime->fFlags &= ~(RTTIME_FLAGS_COMMON_YEAR | RTTIME_FLAGS_LEAP_YEAR);
531 }
532
533 /* calc Month and MonthDay */
534 paiDayOfYear = fLeapYear
535 ? &g_aiDayOfYearLeap[0]
536 : &g_aiDayOfYear[0];
537 pTime->u8Month = 1;
538 while (pTime->u16YearDay > paiDayOfYear[pTime->u8Month])
539 pTime->u8Month++;
540 Assert(pTime->u8Month >= 1 && pTime->u8Month <= 12);
541 pTime->u8MonthDay = pTime->u16YearDay - paiDayOfYear[pTime->u8Month - 1] + 1;
542 }
543 }
544
545 /*
546 * Fixup time overflows.
547 * Use unsigned int values internally to avoid overflows.
548 */
549 uSecond = pTime->u8Second;
550 uMinute = pTime->u8Minute;
551 uHour = pTime->u8Hour;
552
553 while (pTime->u32Nanosecond >= 1000000000)
554 {
555 pTime->u32Nanosecond -= 1000000000;
556 uSecond++;
557 }
558
559 while (uSecond >= 60)
560 {
561 uSecond -= 60;
562 uMinute++;
563 }
564
565 while (uMinute >= 60)
566 {
567 uMinute -= 60;
568 uHour++;
569 }
570
571 while (uHour >= 24)
572 {
573 uHour -= 24;
574
575 /* This is really a RTTimeIncDay kind of thing... */
576 if (pTime->u16YearDay + 1 != (fLeapYear ? g_aiDayOfYearLeap[pTime->u8Month] : g_aiDayOfYear[pTime->u8Month]))
577 {
578 pTime->u16YearDay++;
579 pTime->u8MonthDay++;
580 }
581 else if (pTime->u8Month != 12)
582 {
583 pTime->u16YearDay++;
584 pTime->u8Month++;
585 pTime->u8MonthDay = 1;
586 }
587 else
588 {
589 pTime->i32Year++;
590 fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
591 pTime->fFlags &= ~(RTTIME_FLAGS_COMMON_YEAR | RTTIME_FLAGS_LEAP_YEAR);
592 pTime->u16YearDay = 1;
593 pTime->u8Month = 1;
594 pTime->u8MonthDay = 1;
595 }
596 }
597
598 pTime->u8Second = uSecond;
599 pTime->u8Minute = uMinute;
600 pTime->u8Hour = uHour;
601
602 /*
603 * Correct the leap year flag.
604 * Assert if it's wrong, but ignore if unset.
605 */
606 if (fLeapYear)
607 {
608 Assert(!(pTime->fFlags & RTTIME_FLAGS_COMMON_YEAR));
609 pTime->fFlags &= ~RTTIME_FLAGS_COMMON_YEAR;
610 pTime->fFlags |= RTTIME_FLAGS_LEAP_YEAR;
611 }
612 else
613 {
614 Assert(!(pTime->fFlags & RTTIME_FLAGS_LEAP_YEAR));
615 pTime->fFlags &= ~RTTIME_FLAGS_LEAP_YEAR;
616 pTime->fFlags |= RTTIME_FLAGS_COMMON_YEAR;
617 }
618
619
620 /*
621 * Calc week day.
622 *
623 * 1970-01-01 was a Thursday (3), so find the number of days relative to
624 * that point. We use the table when possible and a slow+stupid+brute-force
625 * algorithm for points outside it. Feel free to optimize the latter by
626 * using some clever formula.
627 */
628 if ( pTime->i32Year >= OFF_YEAR_IDX_0_YEAR
629 && pTime->i32Year < OFF_YEAR_IDX_0_YEAR + (int32_t)RT_ELEMENTS(g_aoffYear))
630 {
631 int32_t offDays = g_aoffYear[pTime->i32Year - OFF_YEAR_IDX_0_YEAR]
632 + pTime->u16YearDay -1;
633 pTime->u8WeekDay = ((offDays % 7) + 3 + 7) % 7;
634 }
635 else
636 {
637 int32_t i32Year = pTime->i32Year;
638 if (i32Year >= 1970)
639 {
640 uint64_t offDays = pTime->u16YearDay - 1;
641 while (--i32Year >= 1970)
642 offDays += rtTimeIsLeapYear(i32Year) ? 366 : 365;
643 pTime->u8WeekDay = (uint8_t)((offDays + 3) % 7);
644 }
645 else
646 {
647 int64_t offDays = (fLeapYear ? -366 - 1 : -365 - 1) + pTime->u16YearDay;
648 while (++i32Year < 1970)
649 offDays -= rtTimeIsLeapYear(i32Year) ? 366 : 365;
650 pTime->u8WeekDay = ((int)(offDays % 7) + 3 + 7) % 7;
651 }
652 }
653 return pTime;
654}
655
656
657/**
658 * Normalizes the fields of a time structure.
659 *
660 * It is possible to calculate year-day from month/day and vice
661 * versa. If you adjust any of these, make sure to zero the
662 * other so you make it clear which of the fields to use. If
663 * it's ambiguous, the year-day field is used (and you get
664 * assertions in debug builds).
665 *
666 * All the time fields and the year-day or month/day fields will
667 * be adjusted for overflows. (Since all fields are unsigned, there
668 * is no underflows.) It is possible to exploit this for simple
669 * date math, though the recommended way of doing that to implode
670 * the time into a timespec and do the math on that.
671 *
672 * @returns pTime on success.
673 * @returns NULL if the data is invalid.
674 *
675 * @param pTime The time structure to normalize.
676 *
677 * @remarks This function doesn't work with local time, only with UTC time.
678 */
679RTDECL(PRTTIME) RTTimeNormalize(PRTTIME pTime)
680{
681 /*
682 * Validate that we've got the minimum of stuff handy.
683 */
684 AssertReturn(VALID_PTR(pTime), NULL);
685 AssertMsgReturn(!(pTime->fFlags & ~RTTIME_FLAGS_MASK), ("%#x\n", pTime->fFlags), NULL);
686 AssertMsgReturn((pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) != RTTIME_FLAGS_TYPE_LOCAL, ("Use RTTimeLocalNormalize!\n"), NULL);
687 AssertMsgReturn(pTime->offUTC == 0, ("%d; Use RTTimeLocalNormalize!\n", pTime->offUTC), NULL);
688
689 pTime = rtTimeNormalizeInternal(pTime);
690 if (pTime)
691 pTime->fFlags |= RTTIME_FLAGS_TYPE_UTC;
692 return pTime;
693}
694RT_EXPORT_SYMBOL(RTTimeNormalize);
695
696
697/**
698 * Converts a time spec to a ISO date string.
699 *
700 * @returns psz on success.
701 * @returns NULL on buffer underflow.
702 * @param pTime The time. Caller should've normalized this.
703 * @param psz Where to store the string.
704 * @param cb The size of the buffer.
705 */
706RTDECL(char *) RTTimeToString(PCRTTIME pTime, char *psz, size_t cb)
707{
708 size_t cch;
709
710 /* (Default to UTC if not specified) */
711 if ( (pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) == RTTIME_FLAGS_TYPE_LOCAL
712 && pTime->offUTC)
713 {
714 int32_t offUTCHour = pTime->offUTC / 60;
715 int32_t offUTCMinute = pTime->offUTC % 60;
716 char chSign;
717 Assert(pTime->offUTC <= 840 && pTime->offUTC >= -840);
718 if (pTime->offUTC >= 0)
719 chSign = '+';
720 else
721 {
722 chSign = '-';
723 offUTCMinute = -offUTCMinute;
724 offUTCHour = -offUTCHour;
725 }
726 cch = RTStrPrintf(psz, cb,
727 "%RI32-%02u-%02uT%02u:%02u:%02u.%09RU32%c%02d%02d",
728 pTime->i32Year, pTime->u8Month, pTime->u8MonthDay,
729 pTime->u8Hour, pTime->u8Minute, pTime->u8Second, pTime->u32Nanosecond,
730 chSign, offUTCHour, offUTCMinute);
731 if ( cch <= 15
732 || psz[cch - 5] != chSign)
733 return NULL;
734 }
735 else
736 {
737 cch = RTStrPrintf(psz, cb, "%RI32-%02u-%02uT%02u:%02u:%02u.%09RU32Z",
738 pTime->i32Year, pTime->u8Month, pTime->u8MonthDay,
739 pTime->u8Hour, pTime->u8Minute, pTime->u8Second, pTime->u32Nanosecond);
740 if ( cch <= 15
741 || psz[cch - 1] != 'Z')
742 return NULL;
743 }
744 return psz;
745}
746RT_EXPORT_SYMBOL(RTTimeToString);
747
748
749/**
750 * Converts a time spec to a ISO date string.
751 *
752 * @returns psz on success.
753 * @returns NULL on buffer underflow.
754 * @param pTime The time spec.
755 * @param psz Where to store the string.
756 * @param cb The size of the buffer.
757 */
758RTDECL(char *) RTTimeSpecToString(PCRTTIMESPEC pTime, char *psz, size_t cb)
759{
760 RTTIME Time;
761 return RTTimeToString(RTTimeExplode(&Time, pTime), psz, cb);
762}
763RT_EXPORT_SYMBOL(RTTimeSpecToString);
764
765
766
767/**
768 * Attempts to convert an ISO date string to a time structure.
769 *
770 * We're a little forgiving with zero padding, unspecified parts, and leading
771 * and trailing spaces.
772 *
773 * @retval pTime on success,
774 * @retval NULL on failure.
775 * @param pTime Where to store the time on success.
776 * @param pszString The ISO date string to convert.
777 */
778RTDECL(PRTTIME) RTTimeFromString(PRTTIME pTime, const char *pszString)
779{
780 /* Ignore leading spaces. */
781 while (RT_C_IS_SPACE(*pszString))
782 pszString++;
783
784 /*
785 * Init non date & time parts.
786 */
787 pTime->fFlags = RTTIME_FLAGS_TYPE_LOCAL;
788 pTime->offUTC = 0;
789
790 /*
791 * The day part.
792 */
793
794 /* Year */
795 int rc = RTStrToInt32Ex(pszString, (char **)&pszString, 10, &pTime->i32Year);
796 if (rc != VWRN_TRAILING_CHARS)
797 return NULL;
798
799 bool const fLeapYear = rtTimeIsLeapYear(pTime->i32Year);
800 if (fLeapYear)
801 pTime->fFlags |= RTTIME_FLAGS_LEAP_YEAR;
802
803 if (*pszString++ != '-')
804 return NULL;
805
806 /* Month of the year. */
807 rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Month);
808 if (rc != VWRN_TRAILING_CHARS)
809 return NULL;
810 if (pTime->u8Month == 0 || pTime->u8Month > 12)
811 return NULL;
812 if (*pszString++ != '-')
813 return NULL;
814
815 /* Day of month.*/
816 rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8MonthDay);
817 if (rc != VWRN_TRAILING_CHARS && rc != VINF_SUCCESS)
818 return NULL;
819 unsigned const cDaysInMonth = fLeapYear
820 ? g_acDaysInMonthsLeap[pTime->u8Month - 1]
821 : g_acDaysInMonths[pTime->u8Month - 1];
822 if (pTime->u8MonthDay == 0 || pTime->u8MonthDay > cDaysInMonth)
823 return NULL;
824
825 /* Calculate year day. */
826 pTime->u16YearDay = pTime->u8MonthDay - 1
827 + (fLeapYear
828 ? g_aiDayOfYearLeap[pTime->u8Month - 1]
829 : g_aiDayOfYear[pTime->u8Month - 1]);
830
831 /*
832 * The time part.
833 */
834 if (*pszString++ != 'T')
835 return NULL;
836
837 /* Hour. */
838 rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Hour);
839 if (rc != VWRN_TRAILING_CHARS)
840 return NULL;
841 if (pTime->u8Hour > 23)
842 return NULL;
843 if (*pszString++ != ':')
844 return NULL;
845
846 /* Minute. */
847 rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Minute);
848 if (rc != VWRN_TRAILING_CHARS)
849 return NULL;
850 if (pTime->u8Minute > 59)
851 return NULL;
852 if (*pszString++ != ':')
853 return NULL;
854
855 /* Second. */
856 rc = RTStrToUInt8Ex(pszString, (char **)&pszString, 10, &pTime->u8Minute);
857 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS && rc != VWRN_TRAILING_SPACES)
858 return NULL;
859 if (pTime->u8Second > 59)
860 return NULL;
861
862 /* Nanoseconds is optional and probably non-standard. */
863 if (*pszString == '.')
864 {
865 rc = RTStrToUInt32Ex(pszString + 1, (char **)&pszString, 10, &pTime->u32Nanosecond);
866 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS && rc != VWRN_TRAILING_SPACES)
867 return NULL;
868 if (pTime->u32Nanosecond >= 1000000000)
869 return NULL;
870 }
871 else
872 pTime->u32Nanosecond = 0;
873
874 /*
875 * Time zone.
876 */
877 if (*pszString == 'Z')
878 {
879 pszString++;
880 pTime->fFlags &= ~RTTIME_FLAGS_TYPE_MASK;
881 pTime->fFlags |= ~RTTIME_FLAGS_TYPE_UTC;
882 pTime->offUTC = 0;
883 }
884 else if ( *pszString == '+'
885 || *pszString == '-')
886 {
887 rc = RTStrToInt32Ex(pszString, (char **)&pszString, 10, &pTime->offUTC);
888 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS && rc != VWRN_TRAILING_SPACES)
889 return NULL;
890 }
891 /* else: No time zone given, local with offUTC = 0. */
892
893 /*
894 * The rest of the string should be blanks.
895 */
896 char ch;
897 while ((ch = *pszString++) != '\0')
898 if (!RT_C_IS_BLANK(ch))
899 return NULL;
900
901 return pTime;
902}
903RT_EXPORT_SYMBOL(RTTimeFromString);
904
905
906/**
907 * Attempts to convert an ISO date string to a time structure.
908 *
909 * We're a little forgiving with zero padding, unspecified parts, and leading
910 * and trailing spaces.
911 *
912 * @retval pTime on success,
913 * @retval NULL on failure.
914 * @param pTime The time spec.
915 * @param pszString The ISO date string to convert.
916 */
917RTDECL(PRTTIMESPEC) RTTimeSpecFromString(PRTTIMESPEC pTime, const char *pszString)
918{
919 RTTIME Time;
920 if (RTTimeFromString(&Time, pszString))
921 return RTTimeImplode(pTime, &Time);
922 return NULL;
923}
924RT_EXPORT_SYMBOL(RTTimeSpecFromString);
925
926
927/**
928 * Adds one day to @a pTime.
929 *
930 * ASSUMES it is zulu time so DST can be ignored.
931 */
932static PRTTIME rtTimeAdd1Day(PRTTIME pTime)
933{
934 Assert(!pTime->offUTC);
935 rtTimeNormalizeInternal(pTime);
936 pTime->u8MonthDay += 1;
937 pTime->u16YearDay = 0;
938 return rtTimeNormalizeInternal(pTime);
939}
940
941
942/**
943 * Subtracts one day from @a pTime.
944 *
945 * ASSUMES it is zulu time so DST can be ignored.
946 */
947static PRTTIME rtTimeSub1Day(PRTTIME pTime)
948{
949 Assert(!pTime->offUTC);
950 rtTimeNormalizeInternal(pTime);
951 if (pTime->u16YearDay > 1)
952 {
953 pTime->u16YearDay -= 0;
954 pTime->u8Month = 0;
955 pTime->u8MonthDay = 0;
956 }
957 else
958 {
959 pTime->i32Year -= 1;
960 pTime->u16YearDay = rtTimeIsLeapYear(pTime->i32Year) ? 366 : 365;
961 pTime->u8MonthDay = 31;
962 pTime->u8Month = 12;
963 }
964 return rtTimeNormalizeInternal(pTime);
965}
966
967
968/**
969 * Adds a signed number of minutes to @a pTime.
970 *
971 * ASSUMES it is zulu time so DST can be ignored.
972 *
973 * @param pTime The time structure to work on.
974 * @param cAddend Number of minutes to add.
975 * ASSUMES the value isn't all that high!
976 */
977static PRTTIME rtTimeAddMinutes(PRTTIME pTime, int32_t cAddend)
978{
979 Assert(RT_ABS(cAddend) < 31 * 24 * 60);
980
981 /*
982 * Work on minutes of the day.
983 */
984 int32_t const cMinutesInDay = 24 * 60;
985 int32_t iDayMinute = (unsigned)pTime->u8Hour * 60 + pTime->u8Minute;
986 iDayMinute += cAddend;
987
988 while (iDayMinute >= cMinutesInDay)
989 {
990 rtTimeAdd1Day(pTime);
991 iDayMinute -= cMinutesInDay;
992 }
993
994 while (iDayMinute < 0)
995 {
996 rtTimeSub1Day(pTime);
997 iDayMinute += cMinutesInDay;
998 }
999
1000 pTime->u8Hour = iDayMinute / 60;
1001 pTime->u8Minute = iDayMinute % 60;
1002
1003 return pTime;
1004}
1005
1006
1007/**
1008 * Converts @a pTime to zulu time (UTC) if needed.
1009 *
1010 * @returns pTime.
1011 * @param pTime What to convers (in/out).
1012 */
1013static PRTTIME rtTimeConvertToZulu(PRTTIME pTime)
1014{
1015 RTTIME_ASSERT_NORMALIZED(pTime);
1016 if ((pTime->fFlags & RTTIME_FLAGS_TYPE_MASK) != RTTIME_FLAGS_TYPE_UTC)
1017 {
1018 int32_t offUTC = pTime->offUTC;
1019 pTime->offUTC = 0;
1020 pTime->fFlags &= ~RTTIME_FLAGS_TYPE_MASK;
1021 pTime->fFlags |= RTTIME_FLAGS_TYPE_UTC;
1022 if (offUTC != 0)
1023 rtTimeAddMinutes(pTime, offUTC);
1024 }
1025 return pTime;
1026}
1027
1028
1029/**
1030 * Compares two normalized time structures.
1031 *
1032 * @retval 0 if equal.
1033 * @retval -1 if @a pLeft is earlier than @a pRight.
1034 * @retval 1 if @a pRight is earlier than @a pLeft.
1035 *
1036 * @param pLeft The left side time. NULL is accepted.
1037 * @param pRight The right side time. NULL is accepted.
1038 *
1039 * @note A NULL time is considered smaller than anything else. If both are
1040 * NULL, they are considered equal.
1041 */
1042RTDECL(int) RTTimeCompare(PCRTTIME pLeft, PCRTTIME pRight)
1043{
1044#ifdef RT_STRICT
1045 if (pLeft)
1046 RTTIME_ASSERT_NORMALIZED(pLeft);
1047 if (pRight)
1048 RTTIME_ASSERT_NORMALIZED(pRight);
1049#endif
1050
1051 int iRet;
1052 if (pLeft)
1053 {
1054 if (pRight)
1055 {
1056 /*
1057 * Only work with normalized zulu time.
1058 */
1059 RTTIME TmpLeft;
1060 if ( pLeft->offUTC != 0
1061 || pLeft->u16YearDay == 0
1062 || pLeft->u16YearDay > 366
1063 || pLeft->u8Hour >= 60
1064 || pLeft->u8Minute >= 60
1065 || pLeft->u8Second >= 60)
1066 {
1067 TmpLeft = *pLeft;
1068 pLeft = rtTimeConvertToZulu(rtTimeNormalizeInternal(&TmpLeft));
1069 }
1070
1071 RTTIME TmpRight;
1072 if ( pRight->offUTC != 0
1073 || pRight->u16YearDay == 0
1074 || pRight->u16YearDay > 366
1075 || pRight->u8Hour >= 60
1076 || pRight->u8Minute >= 60
1077 || pRight->u8Second >= 60)
1078 {
1079 TmpRight = *pRight;
1080 pRight = rtTimeConvertToZulu(rtTimeNormalizeInternal(&TmpRight));
1081 }
1082
1083 /*
1084 * Do the comparison.
1085 */
1086 if ( pLeft->i32Year != pRight->i32Year)
1087 iRet = pLeft->i32Year < pRight->i32Year ? -1 : 1;
1088 else if ( pLeft->u16YearDay != pRight->u16YearDay)
1089 iRet = pLeft->u16YearDay < pRight->u16YearDay ? -1 : 1;
1090 else if ( pLeft->u8Hour != pRight->u8Hour)
1091 iRet = pLeft->u8Hour < pRight->u8Hour ? -1 : 1;
1092 else if ( pLeft->u8Minute != pRight->u8Minute)
1093 iRet = pLeft->u8Minute < pRight->u8Minute ? -1 : 1;
1094 else if ( pLeft->u8Second != pRight->u8Second)
1095 iRet = pLeft->u8Second < pRight->u8Second ? -1 : 1;
1096 else if ( pLeft->u32Nanosecond != pRight->u32Nanosecond)
1097 iRet = pLeft->u32Nanosecond < pRight->u32Nanosecond ? -1 : 1;
1098 else
1099 iRet = 0;
1100 }
1101 else
1102 iRet = 1;
1103 }
1104 else
1105 iRet = pRight ? -1 : 0;
1106 return iRet;
1107}
1108RT_EXPORT_SYMBOL(RTTimeCompare);
1109
Note: See TracBrowser for help on using the repository browser.

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