1 | /* $Id: RTTimeFormatDurationEx.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * IPRT - RTTimeFormatInterval.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2022-2024 Oracle and/or its affiliates.
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox base platform packages, as
|
---|
10 | * available from https://www.virtualbox.org.
|
---|
11 | *
|
---|
12 | * This program is free software; you can redistribute it and/or
|
---|
13 | * modify it under the terms of the GNU General Public License
|
---|
14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
15 | * License.
|
---|
16 | *
|
---|
17 | * This program is distributed in the hope that it will be useful, but
|
---|
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
20 | * General Public License for more details.
|
---|
21 | *
|
---|
22 | * You should have received a copy of the GNU General Public License
|
---|
23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
24 | *
|
---|
25 | * The contents of this file may alternatively be used under the terms
|
---|
26 | * of the Common Development and Distribution License Version 1.0
|
---|
27 | * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
|
---|
28 | * in the VirtualBox distribution, in which case the provisions of the
|
---|
29 | * CDDL are applicable instead of those of the GPL.
|
---|
30 | *
|
---|
31 | * You may elect to license modified versions of this file under the
|
---|
32 | * terms and conditions of either the GPL or the CDDL or both.
|
---|
33 | *
|
---|
34 | * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
|
---|
35 | */
|
---|
36 |
|
---|
37 |
|
---|
38 | /*********************************************************************************************************************************
|
---|
39 | * Header Files *
|
---|
40 | *********************************************************************************************************************************/
|
---|
41 | #include "internal/iprt.h"
|
---|
42 | #include <iprt/time.h>
|
---|
43 |
|
---|
44 | #include <iprt/assert.h>
|
---|
45 | #include <iprt/errcore.h>
|
---|
46 | #include <iprt/string.h>
|
---|
47 |
|
---|
48 |
|
---|
49 | static size_t rtTimeFormatDurationNumberEx(char *psz, uint32_t uValue, size_t cchValue)
|
---|
50 | {
|
---|
51 | switch (cchValue)
|
---|
52 | {
|
---|
53 | case 10:
|
---|
54 | *psz++ = (uint8_t)(uValue / 1000000000) + '0';
|
---|
55 | uValue %= 1000000000;
|
---|
56 | RT_FALL_THROUGH();
|
---|
57 | case 9:
|
---|
58 | *psz++ = (uint8_t)(uValue / 100000000) + '0';
|
---|
59 | uValue %= 100000000;
|
---|
60 | RT_FALL_THROUGH();
|
---|
61 | case 8:
|
---|
62 | *psz++ = (uint8_t)(uValue / 10000000) + '0';
|
---|
63 | uValue %= 10000000;
|
---|
64 | RT_FALL_THROUGH();
|
---|
65 | case 7:
|
---|
66 | *psz++ = (uint8_t)(uValue / 1000000) + '0';
|
---|
67 | uValue %= 1000000;
|
---|
68 | RT_FALL_THROUGH();
|
---|
69 | case 6:
|
---|
70 | *psz++ = (uint8_t)(uValue / 100000) + '0';
|
---|
71 | uValue %= 100000;
|
---|
72 | RT_FALL_THROUGH();
|
---|
73 | case 5:
|
---|
74 | *psz++ = (uint8_t)(uValue / 10000) + '0';
|
---|
75 | uValue %= 10000;
|
---|
76 | RT_FALL_THROUGH();
|
---|
77 | case 4:
|
---|
78 | *psz++ = (uint8_t)(uValue / 1000) + '0';
|
---|
79 | uValue %= 1000;
|
---|
80 | RT_FALL_THROUGH();
|
---|
81 | case 3:
|
---|
82 | *psz++ = (uint8_t)(uValue / 100) + '0';
|
---|
83 | uValue %= 100;
|
---|
84 | RT_FALL_THROUGH();
|
---|
85 | case 2:
|
---|
86 | *psz++ = (uint8_t)(uValue / 10) + '0';
|
---|
87 | uValue %= 10;
|
---|
88 | RT_FALL_THROUGH();
|
---|
89 | case 1:
|
---|
90 | *psz++ = (uint8_t)uValue + '0';
|
---|
91 | break;
|
---|
92 | }
|
---|
93 | return cchValue;
|
---|
94 | }
|
---|
95 |
|
---|
96 |
|
---|
97 | static size_t rtTimeFormatDurationNumber(char *psz, uint32_t uValue)
|
---|
98 | {
|
---|
99 | size_t cchValue;
|
---|
100 | if (uValue < 10)
|
---|
101 | cchValue = 1;
|
---|
102 | else if (uValue < 100)
|
---|
103 | cchValue = 2;
|
---|
104 | else if (uValue < 1000)
|
---|
105 | cchValue = 3;
|
---|
106 | else if (uValue < 10000)
|
---|
107 | cchValue = 4;
|
---|
108 | else if (uValue < 100000)
|
---|
109 | cchValue = 5;
|
---|
110 | else if (uValue < 1000000)
|
---|
111 | cchValue = 6;
|
---|
112 | else if (uValue < 10000000)
|
---|
113 | cchValue = 7;
|
---|
114 | else if (uValue < 100000000)
|
---|
115 | cchValue = 8;
|
---|
116 | else if (uValue < 1000000000)
|
---|
117 | cchValue = 9;
|
---|
118 | else
|
---|
119 | cchValue = 10;
|
---|
120 | return rtTimeFormatDurationNumberEx(psz, uValue, cchValue);
|
---|
121 | }
|
---|
122 |
|
---|
123 |
|
---|
124 | static ssize_t rtTimeFormatDurationCopyOutResult(char *pszDst, size_t cbDst, const char *pszValue, size_t cchValue)
|
---|
125 | {
|
---|
126 | if (cbDst > cchValue)
|
---|
127 | {
|
---|
128 | memcpy(pszDst, pszValue, cchValue);
|
---|
129 | pszDst[cchValue] = '\0';
|
---|
130 | return cchValue;
|
---|
131 | }
|
---|
132 | if (cbDst)
|
---|
133 | {
|
---|
134 | memcpy(pszDst, pszValue, cbDst);
|
---|
135 | pszDst[cbDst - 1] = '\0';
|
---|
136 | }
|
---|
137 | return VERR_BUFFER_OVERFLOW;
|
---|
138 | }
|
---|
139 |
|
---|
140 |
|
---|
141 | /**
|
---|
142 | * Formats duration as best we can according to ISO-8601.
|
---|
143 | *
|
---|
144 | * The returned value is on the form "[-]PnnnnnWnDTnnHnnMnn.fffffffffS", where a
|
---|
145 | * sequence of 'n' can be between 1 and the given lenght, and all but the
|
---|
146 | * "nn.fffffffffS" part is optional and will only be outputted when the duration
|
---|
147 | * is sufficiently large. The code currently does not omit any inbetween
|
---|
148 | * elements other than the day count (D), so an exactly 7 day duration is
|
---|
149 | * formatted as "P1WT0H0M0.000000000S" when @a cFractionDigits is 9.
|
---|
150 | *
|
---|
151 | * @returns Number of characters in the output on success. VERR_BUFFER_OVEFLOW
|
---|
152 | * on failure.
|
---|
153 | * @retval VERR_OUT_OF_RANGE if @a cFractionDigits is too large.
|
---|
154 | * @param pszDst Pointer to the output buffer. In case of overflow,
|
---|
155 | * the max number of characters will be written and
|
---|
156 | * zero terminated, provided @a cbDst isn't zero.
|
---|
157 | * @param cbDst The size of the output buffer.
|
---|
158 | * @param pDuration The duration to format.
|
---|
159 | * @param cFractionDigits Number of digits in the second fraction part. Zero
|
---|
160 | * for whole no fraction. Max is 9 (nano seconds).
|
---|
161 | */
|
---|
162 | RTDECL(ssize_t) RTTimeFormatDurationEx(char *pszDst, size_t cbDst, PCRTTIMESPEC pDuration, uint32_t cFractionDigits)
|
---|
163 | {
|
---|
164 | AssertReturn(cFractionDigits <= 9, VERR_OUT_OF_RANGE);
|
---|
165 | AssertReturn(cbDst != 0, VERR_BUFFER_OVERFLOW);
|
---|
166 |
|
---|
167 | /*
|
---|
168 | * Get the seconds and .
|
---|
169 | */
|
---|
170 | int64_t cNanoSecsSigned = RTTimeSpecGetNano(pDuration);
|
---|
171 |
|
---|
172 | /* Special case: zero interval */
|
---|
173 | if (cNanoSecsSigned == 0)
|
---|
174 | return rtTimeFormatDurationCopyOutResult(pszDst, cbDst, RT_STR_TUPLE("PT0S"));
|
---|
175 |
|
---|
176 | char szTmp[64];
|
---|
177 | size_t offTmp = 0;
|
---|
178 |
|
---|
179 | /* Negative intervals aren't really allowed by the standard, but we slap a
|
---|
180 | minus in from of the 'P' and get on with it. */
|
---|
181 | if (cNanoSecsSigned < 0)
|
---|
182 | {
|
---|
183 | szTmp[offTmp++] = '-';
|
---|
184 | cNanoSecsSigned = -cNanoSecsSigned;
|
---|
185 | }
|
---|
186 | uint64_t cNanoSecs = (uint64_t)cNanoSecsSigned;
|
---|
187 |
|
---|
188 | /* Emit the duration indicator: */
|
---|
189 | szTmp[offTmp++] = 'P';
|
---|
190 | size_t const offPostP = offTmp;
|
---|
191 |
|
---|
192 | /* Any full weeks? */
|
---|
193 | if (cNanoSecs >= RT_NS_1WEEK)
|
---|
194 | {
|
---|
195 | uint64_t const cWeeks = cNanoSecs / RT_NS_1WEEK; /* (the max value here is 15250) */
|
---|
196 | cNanoSecs %= RT_NS_1WEEK;
|
---|
197 | offTmp += rtTimeFormatDurationNumber(&szTmp[offTmp], (uint32_t)cWeeks);
|
---|
198 | szTmp[offTmp++] = 'W';
|
---|
199 | }
|
---|
200 |
|
---|
201 | /* Any full days?*/
|
---|
202 | if (cNanoSecs >= RT_NS_1DAY)
|
---|
203 | {
|
---|
204 | uint8_t const cDays = (uint8_t)(cNanoSecs / RT_NS_1DAY);
|
---|
205 | cNanoSecs %= RT_NS_1DAY;
|
---|
206 | szTmp[offTmp++] = '0' + cDays;
|
---|
207 | szTmp[offTmp++] = 'D';
|
---|
208 | }
|
---|
209 |
|
---|
210 | szTmp[offTmp++] = 'T';
|
---|
211 |
|
---|
212 | /* Hours: */
|
---|
213 | if (cNanoSecs >= RT_NS_1HOUR || offTmp > offPostP + 1)
|
---|
214 | {
|
---|
215 | uint8_t const cHours = (uint8_t)(cNanoSecs / RT_NS_1HOUR);
|
---|
216 | cNanoSecs %= RT_NS_1HOUR;
|
---|
217 | offTmp += rtTimeFormatDurationNumber(&szTmp[offTmp], cHours);
|
---|
218 | szTmp[offTmp++] = 'H';
|
---|
219 | }
|
---|
220 |
|
---|
221 | /* Minutes: */
|
---|
222 | if (cNanoSecs >= RT_NS_1MIN || offTmp > offPostP + 1)
|
---|
223 | {
|
---|
224 | uint8_t const cMins = (uint8_t)(cNanoSecs / RT_NS_1MIN);
|
---|
225 | cNanoSecs %= RT_NS_1MIN;
|
---|
226 | offTmp += rtTimeFormatDurationNumber(&szTmp[offTmp], cMins);
|
---|
227 | szTmp[offTmp++] = 'M';
|
---|
228 | }
|
---|
229 |
|
---|
230 | /* Seconds: */
|
---|
231 | uint8_t const cSecs = (uint8_t)(cNanoSecs / RT_NS_1SEC);
|
---|
232 | cNanoSecs %= RT_NS_1SEC;
|
---|
233 | offTmp += rtTimeFormatDurationNumber(&szTmp[offTmp], cSecs);
|
---|
234 | if (cFractionDigits > 0)
|
---|
235 | {
|
---|
236 | szTmp[offTmp++] = '.';
|
---|
237 | static uint32_t const s_auFactors[9] = { 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1 };
|
---|
238 | offTmp += rtTimeFormatDurationNumberEx(&szTmp[offTmp], (uint32_t)(cNanoSecs / s_auFactors[cFractionDigits - 1]),
|
---|
239 | cFractionDigits);
|
---|
240 | }
|
---|
241 | szTmp[offTmp++] = 'S';
|
---|
242 | szTmp[offTmp] = '\0';
|
---|
243 |
|
---|
244 | return rtTimeFormatDurationCopyOutResult(pszDst, cbDst, szTmp, offTmp);
|
---|
245 | }
|
---|
246 | RT_EXPORT_SYMBOL(RTTimeFormatDurationEx);
|
---|
247 |
|
---|
248 |
|
---|
249 | /**
|
---|
250 | * Formats duration as best we can according to ISO-8601, with no fraction.
|
---|
251 | *
|
---|
252 | * See RTTimeFormatDurationEx for details.
|
---|
253 | *
|
---|
254 | * @returns Number of characters in the output on success. VERR_BUFFER_OVEFLOW
|
---|
255 | * on failure.
|
---|
256 | * @param pszDst Pointer to the output buffer. In case of overflow,
|
---|
257 | * the max number of characters will be written and
|
---|
258 | * zero terminated, provided @a cbDst isn't zero.
|
---|
259 | * @param cbDst The size of the output buffer.
|
---|
260 | * @param pDuration The duration to format.
|
---|
261 | */
|
---|
262 | RTDECL(int) RTTimeFormatDuration(char *pszDst, size_t cbDst, PCRTTIMESPEC pDuration)
|
---|
263 | {
|
---|
264 | return RTTimeFormatDurationEx(pszDst, cbDst, pDuration, 0);
|
---|
265 | }
|
---|
266 | RT_EXPORT_SYMBOL(RTTimeFormatDuration);
|
---|
267 |
|
---|