Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #if defined( WNT )
21 : #include <windows.h>
22 : #else
23 : #include <time.h>
24 : #endif
25 :
26 : #include <tools/date.hxx>
27 : #include <sal/log.hxx>
28 :
29 : static const sal_uInt16 aDaysInMonth[12] = { 31, 28, 31, 30, 31, 30,
30 : 31, 31, 30, 31, 30, 31 };
31 :
32 : #define MAX_DAYS 3636532
33 :
34 52956 : inline bool ImpIsLeapYear( sal_uInt16 nYear )
35 : {
36 101414 : return ( ( ((nYear % 4) == 0) && ((nYear % 100) != 0) ) ||
37 101414 : ( (nYear % 400) == 0 ) );
38 : }
39 :
40 : // All callers must have sanitized or normalized month and year values!
41 557350 : inline sal_uInt16 ImplDaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear )
42 : {
43 557350 : if ( nMonth != 2 )
44 504402 : return aDaysInMonth[nMonth-1];
45 : else
46 : {
47 52948 : if (ImpIsLeapYear(nYear))
48 4536 : return aDaysInMonth[nMonth-1] + 1;
49 : else
50 48412 : return aDaysInMonth[nMonth-1];
51 : }
52 : }
53 :
54 : // static
55 8 : sal_uInt16 Date::GetDaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear )
56 : {
57 : SAL_WARN_IF( nMonth < 1 || 12 < nMonth, "tools", "Date::GetDaysInMonth - nMonth out of bounds " << nMonth);
58 8 : if (nMonth < 1)
59 0 : nMonth = 1;
60 8 : else if (12 < nMonth)
61 0 : nMonth = 12;
62 8 : return ImplDaysInMonth( nMonth, nYear);
63 : }
64 :
65 42434 : long Date::GetAsNormalizedDays() const
66 : {
67 : // This is a very common datum we often calculate from.
68 42434 : if (nDate == 18991230) // 1899-12-30
69 : {
70 : assert(DateToDays( GetDay(), GetMonth(), GetYear() ) == 693594);
71 15824 : return 693594;
72 : }
73 26610 : return DateToDays( GetDay(), GetMonth(), GetYear() );
74 : }
75 :
76 26676 : long Date::DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear )
77 : {
78 : long nDays;
79 :
80 26676 : Normalize( nDay, nMonth, nYear);
81 :
82 26676 : nDays = ((sal_uIntPtr)nYear-1) * 365;
83 26676 : nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400);
84 198640 : for( sal_uInt16 i = 1; i < nMonth; i++ )
85 171964 : nDays += ImplDaysInMonth(i,nYear);
86 26676 : nDays += nDay;
87 26676 : return nDays;
88 : }
89 :
90 20922 : static void DaysToDate( long nDays,
91 : sal_uInt16& rDay, sal_uInt16& rMonth, sal_uInt16& rYear )
92 : {
93 : long nTempDays;
94 20922 : long i = 0;
95 : bool bCalc;
96 :
97 36418 : do
98 : {
99 36418 : nTempDays = (long)nDays;
100 36418 : rYear = (sal_uInt16)((nTempDays / 365) - i);
101 36418 : nTempDays -= ((sal_uIntPtr)rYear-1) * 365;
102 36418 : nTempDays -= ((rYear-1) / 4) - ((rYear-1) / 100) + ((rYear-1) / 400);
103 36418 : bCalc = false;
104 36418 : if ( nTempDays < 1 )
105 : {
106 15496 : i++;
107 15496 : bCalc = true;
108 : }
109 : else
110 : {
111 20922 : if ( nTempDays > 365 )
112 : {
113 8 : if ( (nTempDays != 366) || !ImpIsLeapYear( rYear ) )
114 : {
115 0 : i--;
116 0 : bCalc = true;
117 : }
118 : }
119 : }
120 : }
121 : while ( bCalc );
122 :
123 20922 : rMonth = 1;
124 205344 : while ( (sal_uIntPtr)nTempDays > ImplDaysInMonth( rMonth, rYear ) )
125 : {
126 163500 : nTempDays -= ImplDaysInMonth( rMonth, rYear );
127 163500 : rMonth++;
128 : }
129 20922 : rDay = (sal_uInt16)nTempDays;
130 20922 : }
131 :
132 32977 : Date::Date( DateInitSystem )
133 : {
134 : #if defined WNT
135 : SYSTEMTIME aDateTime;
136 : GetLocalTime( &aDateTime );
137 :
138 : // Combine to date
139 : nDate = ((sal_uIntPtr)aDateTime.wDay) +
140 : (((sal_uIntPtr)aDateTime.wMonth)*100) +
141 : (((sal_uIntPtr)aDateTime.wYear)*10000);
142 : #else
143 : time_t nTmpTime;
144 : struct tm aTime;
145 :
146 : // get current time
147 32977 : nTmpTime = time( 0 );
148 :
149 : // compute date
150 32977 : if ( localtime_r( &nTmpTime, &aTime ) )
151 : {
152 : nDate = ((sal_uIntPtr)aTime.tm_mday) +
153 32977 : (((sal_uIntPtr)(aTime.tm_mon+1))*100) +
154 65954 : (((sal_uIntPtr)(aTime.tm_year+1900))*10000);
155 : }
156 : else
157 0 : nDate = 1 + 100 + (((sal_uIntPtr)1900)*10000);
158 : #endif
159 32977 : }
160 :
161 144 : void Date::SetDay( sal_uInt16 nNewDay )
162 : {
163 144 : sal_uIntPtr nMonth = GetMonth();
164 144 : sal_uIntPtr nYear = GetYear();
165 :
166 144 : nDate = ((sal_uIntPtr)(nNewDay%100)) + (nMonth*100) + (nYear*10000);
167 144 : }
168 :
169 150 : void Date::SetMonth( sal_uInt16 nNewMonth )
170 : {
171 150 : sal_uIntPtr nDay = GetDay();
172 150 : sal_uIntPtr nYear = GetYear();
173 :
174 150 : nDate = nDay + (((sal_uIntPtr)(nNewMonth%100))*100) + (nYear*10000);
175 150 : }
176 :
177 450 : void Date::SetYear( sal_uInt16 nNewYear )
178 : {
179 450 : sal_uIntPtr nDay = GetDay();
180 450 : sal_uIntPtr nMonth = GetMonth();
181 :
182 450 : nDate = nDay + (nMonth*100) + (((sal_uIntPtr)(nNewYear%10000))*10000);
183 450 : }
184 :
185 92 : DayOfWeek Date::GetDayOfWeek() const
186 : {
187 92 : return (DayOfWeek)((sal_uIntPtr)(GetAsNormalizedDays()-1) % 7);
188 : }
189 :
190 244 : sal_uInt16 Date::GetDayOfYear() const
191 : {
192 244 : sal_uInt16 nDay = GetDay();
193 244 : sal_uInt16 nMonth = GetMonth();
194 244 : sal_uInt16 nYear = GetYear();
195 244 : Normalize( nDay, nMonth, nYear);
196 :
197 798 : for( sal_uInt16 i = 1; i < nMonth; i++ )
198 554 : nDay = nDay + ::ImplDaysInMonth( i, nYear ); // += yields a warning on MSVC, so don't use it
199 244 : return nDay;
200 : }
201 :
202 4 : sal_uInt16 Date::GetWeekOfYear( DayOfWeek eStartDay,
203 : sal_Int16 nMinimumNumberOfDaysInWeek ) const
204 : {
205 : short nWeek;
206 4 : short n1WDay = (short)Date( 1, 1, GetYear() ).GetDayOfWeek();
207 4 : short nDayOfYear = (short)GetDayOfYear();
208 :
209 : // weekdays start at 0, thus decrement one
210 4 : nDayOfYear--;
211 : // account for StartDay
212 4 : n1WDay = (n1WDay+(7-(short)eStartDay)) % 7;
213 :
214 4 : if (nMinimumNumberOfDaysInWeek < 1 || 7 < nMinimumNumberOfDaysInWeek)
215 : {
216 : SAL_WARN( "tools.datetime", "Date::GetWeekOfYear: invalid nMinimumNumberOfDaysInWeek" );
217 0 : nMinimumNumberOfDaysInWeek = 4;
218 : }
219 :
220 4 : if ( nMinimumNumberOfDaysInWeek == 1 )
221 : {
222 0 : nWeek = ((n1WDay+nDayOfYear)/7) + 1;
223 : // Set to 53rd week only if we're not in the
224 : // first week of the new year
225 0 : if ( nWeek == 54 )
226 0 : nWeek = 1;
227 0 : else if ( nWeek == 53 )
228 : {
229 0 : short nDaysInYear = (short)GetDaysInYear();
230 0 : short nDaysNextYear = (short)Date( 1, 1, GetYear()+1 ).GetDayOfWeek();
231 0 : nDaysNextYear = (nDaysNextYear+(7-(short)eStartDay)) % 7;
232 0 : if ( nDayOfYear > (nDaysInYear-nDaysNextYear-1) )
233 0 : nWeek = 1;
234 : }
235 : }
236 4 : else if ( nMinimumNumberOfDaysInWeek == 7 )
237 : {
238 0 : nWeek = ((n1WDay+nDayOfYear)/7);
239 : // First week of a year is equal to the last week of the previous year
240 0 : if ( nWeek == 0 )
241 : {
242 0 : Date aLastDatePrevYear( 31, 12, GetYear()-1 );
243 0 : nWeek = aLastDatePrevYear.GetWeekOfYear( eStartDay, nMinimumNumberOfDaysInWeek );
244 : }
245 : }
246 : else // ( nMinimumNumberOfDaysInWeek == somehing_else, commentary examples for 4 )
247 : {
248 : // x_monday - thursday
249 4 : if ( n1WDay < nMinimumNumberOfDaysInWeek )
250 2 : nWeek = 1;
251 : // Friday
252 2 : else if ( n1WDay == nMinimumNumberOfDaysInWeek )
253 0 : nWeek = 53;
254 : // Saturday
255 2 : else if ( n1WDay == nMinimumNumberOfDaysInWeek + 1 )
256 : {
257 : // Year after leapyear
258 0 : if ( Date( 1, 1, GetYear()-1 ).IsLeapYear() )
259 0 : nWeek = 53;
260 : else
261 0 : nWeek = 52;
262 : }
263 : // Sunday
264 : else
265 2 : nWeek = 52;
266 :
267 4 : if ( (nWeek == 1) || (nDayOfYear + n1WDay > 6) )
268 : {
269 2 : if ( nWeek == 1 )
270 2 : nWeek += (nDayOfYear + n1WDay) / 7;
271 : else
272 0 : nWeek = (nDayOfYear + n1WDay) / 7;
273 2 : if ( nWeek == 53 )
274 : {
275 : // next x_Sunday == first x_Sunday in the new year
276 : // == still the same week!
277 0 : long nTempDays = GetAsNormalizedDays();
278 :
279 0 : nTempDays += 6 - (GetDayOfWeek()+(7-(short)eStartDay)) % 7;
280 : sal_uInt16 nDay;
281 : sal_uInt16 nMonth;
282 : sal_uInt16 nYear;
283 0 : DaysToDate( nTempDays, nDay, nMonth, nYear );
284 0 : nWeek = Date( nDay, nMonth, nYear ).GetWeekOfYear( eStartDay, nMinimumNumberOfDaysInWeek );
285 : }
286 : }
287 : }
288 :
289 4 : return (sal_uInt16)nWeek;
290 : }
291 :
292 4486 : sal_uInt16 Date::GetDaysInMonth() const
293 : {
294 4486 : sal_uInt16 nDay = GetDay();
295 4486 : sal_uInt16 nMonth = GetMonth();
296 4486 : sal_uInt16 nYear = GetYear();
297 4486 : Normalize( nDay, nMonth, nYear);
298 :
299 4486 : return ImplDaysInMonth( nMonth, nYear );
300 : }
301 :
302 0 : bool Date::IsLeapYear() const
303 : {
304 0 : sal_uInt16 nYear = GetYear();
305 0 : return ImpIsLeapYear( nYear );
306 : }
307 :
308 542 : bool Date::IsValidAndGregorian() const
309 : {
310 542 : sal_uInt16 nDay = GetDay();
311 542 : sal_uInt16 nMonth = GetMonth();
312 542 : sal_uInt16 nYear = GetYear();
313 :
314 542 : if ( !nMonth || (nMonth > 12) )
315 14 : return false;
316 528 : if ( !nDay || (nDay > ImplDaysInMonth( nMonth, nYear )) )
317 0 : return false;
318 528 : else if ( nYear <= 1582 )
319 : {
320 0 : if ( nYear < 1582 )
321 0 : return false;
322 0 : else if ( nMonth < 10 )
323 0 : return false;
324 0 : else if ( (nMonth == 10) && (nDay < 15) )
325 0 : return false;
326 : }
327 :
328 528 : return true;
329 : }
330 :
331 470 : bool Date::IsValidDate() const
332 : {
333 470 : return IsValidDate( GetDay(), GetMonth(), GetYear());
334 : }
335 :
336 : //static
337 31888 : bool Date::IsValidDate( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear )
338 : {
339 31888 : if ( !nMonth || (nMonth > 12) )
340 26 : return false;
341 31862 : if ( !nDay || (nDay > ImplDaysInMonth( nMonth, nYear )) )
342 0 : return false;
343 31862 : return true;
344 : }
345 :
346 12 : bool Date::Normalize()
347 : {
348 12 : sal_uInt16 nDay = GetDay();
349 12 : sal_uInt16 nMonth = GetMonth();
350 12 : sal_uInt16 nYear = GetYear();
351 :
352 12 : if (!Normalize( nDay, nMonth, nYear))
353 12 : return false;
354 :
355 0 : SetDay( nDay);
356 0 : SetMonth( nMonth);
357 0 : SetYear( nYear);
358 :
359 0 : return true;
360 : }
361 :
362 : //static
363 31418 : bool Date::Normalize( sal_uInt16 & rDay, sal_uInt16 & rMonth, sal_uInt16 & rYear )
364 : {
365 31418 : if (IsValidDate( rDay, rMonth, rYear))
366 31392 : return false;
367 :
368 26 : if (rMonth > 12)
369 : {
370 0 : rYear += rMonth / 12;
371 0 : rMonth = rMonth % 12;
372 : }
373 26 : if (!rMonth)
374 : {
375 26 : if (!rYear)
376 : {
377 26 : rYear = 0;
378 26 : rMonth = 1;
379 26 : if (rDay > 31)
380 22 : rDay -= 31;
381 : else
382 4 : rDay = 1;
383 : }
384 : else
385 : {
386 0 : --rYear;
387 0 : rMonth = 12;
388 : }
389 : }
390 : sal_uInt16 nDays;
391 52 : while (rDay > (nDays = ImplDaysInMonth( rMonth, rYear)))
392 : {
393 0 : rDay -= nDays;
394 0 : if (rMonth < 12)
395 0 : ++rMonth;
396 : else
397 : {
398 0 : ++rYear;
399 0 : rMonth = 1;
400 : }
401 : }
402 26 : if (rYear > 9999)
403 : {
404 0 : rDay = 31;
405 0 : rMonth = 12;
406 0 : rYear = 9999;
407 : }
408 26 : return true;
409 : }
410 :
411 20834 : Date& Date::operator +=( long nDays )
412 : {
413 : sal_uInt16 nDay;
414 : sal_uInt16 nMonth;
415 : sal_uInt16 nYear;
416 :
417 20834 : if (nDays == 0)
418 32 : return *this;
419 :
420 20802 : long nTempDays = GetAsNormalizedDays();
421 :
422 20802 : nTempDays += nDays;
423 20802 : if ( nTempDays > MAX_DAYS )
424 0 : nDate = 31 + (12*100) + (((sal_uIntPtr)9999)*10000);
425 20802 : else if ( nTempDays <= 0 )
426 0 : nDate = 1 + 100;
427 : else
428 : {
429 20802 : DaysToDate( nTempDays, nDay, nMonth, nYear );
430 20802 : nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
431 : }
432 :
433 20802 : return *this;
434 : }
435 :
436 108 : Date& Date::operator -=( long nDays )
437 : {
438 : sal_uInt16 nDay;
439 : sal_uInt16 nMonth;
440 : sal_uInt16 nYear;
441 :
442 108 : if (nDays == 0)
443 0 : return *this;
444 :
445 108 : long nTempDays = GetAsNormalizedDays();
446 :
447 108 : nTempDays -= nDays;
448 108 : if ( nTempDays > MAX_DAYS )
449 0 : nDate = 31 + (12*100) + (((sal_uIntPtr)9999)*10000);
450 108 : else if ( nTempDays <= 0 )
451 0 : nDate = 1 + 100;
452 : else
453 : {
454 108 : DaysToDate( nTempDays, nDay, nMonth, nYear );
455 108 : nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
456 : }
457 :
458 108 : return *this;
459 : }
460 :
461 12 : Date& Date::operator ++()
462 : {
463 : sal_uInt16 nDay;
464 : sal_uInt16 nMonth;
465 : sal_uInt16 nYear;
466 12 : long nTempDays = GetAsNormalizedDays();
467 :
468 12 : if ( nTempDays < MAX_DAYS )
469 : {
470 12 : nTempDays++;
471 12 : DaysToDate( nTempDays, nDay, nMonth, nYear );
472 12 : nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
473 : }
474 :
475 12 : return *this;
476 : }
477 :
478 0 : Date& Date::operator --()
479 : {
480 : sal_uInt16 nDay;
481 : sal_uInt16 nMonth;
482 : sal_uInt16 nYear;
483 0 : long nTempDays = GetAsNormalizedDays();
484 :
485 0 : if ( nTempDays > 1 )
486 : {
487 0 : nTempDays--;
488 0 : DaysToDate( nTempDays, nDay, nMonth, nYear );
489 0 : nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
490 : }
491 0 : return *this;
492 : }
493 :
494 4 : Date Date::operator ++( int )
495 : {
496 4 : Date aOldDate = *this;
497 4 : Date::operator++();
498 4 : return aOldDate;
499 : }
500 :
501 0 : Date Date::operator --( int )
502 : {
503 0 : Date aOldDate = *this;
504 0 : Date::operator--();
505 0 : return aOldDate;
506 : }
507 :
508 13778 : Date operator +( const Date& rDate, long nDays )
509 : {
510 13778 : Date aDate( rDate );
511 13778 : aDate += nDays;
512 13778 : return aDate;
513 : }
514 :
515 108 : Date operator -( const Date& rDate, long nDays )
516 : {
517 108 : Date aDate( rDate );
518 108 : aDate -= nDays;
519 108 : return aDate;
520 : }
521 :
522 10710 : long operator -( const Date& rDate1, const Date& rDate2 )
523 : {
524 10710 : sal_uIntPtr nTempDays1 = rDate1.GetAsNormalizedDays();
525 10710 : sal_uIntPtr nTempDays2 = rDate2.GetAsNormalizedDays();
526 :
527 10710 : return nTempDays1 - nTempDays2;
528 : }
529 :
530 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|