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 : #ifdef MACOSX
30 : extern "C" {
31 : struct tm *localtime_r(const time_t *timep, struct tm *buffer);
32 : }
33 : #endif
34 :
35 : static sal_uInt16 aDaysInMonth[12] = { 31, 28, 31, 30, 31, 30,
36 : 31, 31, 30, 31, 30, 31 };
37 :
38 : #define MAX_DAYS 3636532
39 :
40 3618 : inline sal_Bool ImpIsLeapYear( sal_uInt16 nYear )
41 : {
42 : return ( ( ((nYear % 4) == 0) && ((nYear % 100) != 0) ) ||
43 3618 : ( (nYear % 400) == 0 ) );
44 : }
45 :
46 : // All callers must have sanitized or normalized month and year values!
47 43767 : inline sal_uInt16 DaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear )
48 : {
49 43767 : if ( nMonth != 2 )
50 40149 : return aDaysInMonth[nMonth-1];
51 : else
52 : {
53 3618 : if (ImpIsLeapYear(nYear))
54 1584 : return aDaysInMonth[nMonth-1] + 1;
55 : else
56 2034 : return aDaysInMonth[nMonth-1];
57 : }
58 : }
59 :
60 3716 : long Date::DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear )
61 : {
62 : long nDays;
63 :
64 3716 : Normalize( nDay, nMonth, nYear);
65 :
66 3716 : nDays = ((sal_uIntPtr)nYear-1) * 365;
67 3716 : nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400);
68 36457 : for( sal_uInt16 i = 1; i < nMonth; i++ )
69 32741 : nDays += DaysInMonth(i,nYear);
70 3716 : nDays += nDay;
71 3716 : return nDays;
72 : }
73 :
74 1944 : static void DaysToDate( long nDays,
75 : sal_uInt16& rDay, sal_uInt16& rMonth, sal_uInt16& rYear )
76 : {
77 : long nTempDays;
78 1944 : long i = 0;
79 : sal_Bool bCalc;
80 :
81 2102 : do
82 : {
83 2102 : nTempDays = (long)nDays;
84 2102 : rYear = (sal_uInt16)((nTempDays / 365) - i);
85 2102 : nTempDays -= ((sal_uIntPtr)rYear-1) * 365;
86 2102 : nTempDays -= ((rYear-1) / 4) - ((rYear-1) / 100) + ((rYear-1) / 400);
87 2102 : bCalc = sal_False;
88 2102 : if ( nTempDays < 1 )
89 : {
90 158 : i++;
91 158 : bCalc = sal_True;
92 : }
93 : else
94 : {
95 1944 : if ( nTempDays > 365 )
96 : {
97 0 : if ( (nTempDays != 366) || !ImpIsLeapYear( rYear ) )
98 : {
99 0 : i--;
100 0 : bCalc = sal_True;
101 : }
102 : }
103 : }
104 : }
105 : while ( bCalc );
106 :
107 1944 : rMonth = 1;
108 5804 : while ( (sal_uIntPtr)nTempDays > DaysInMonth( rMonth, rYear ) )
109 : {
110 1916 : nTempDays -= DaysInMonth( rMonth, rYear );
111 1916 : rMonth++;
112 : }
113 1944 : rDay = (sal_uInt16)nTempDays;
114 1944 : }
115 :
116 2451 : Date::Date( DateInitSystem )
117 : {
118 : #if defined WNT
119 : SYSTEMTIME aDateTime;
120 : GetLocalTime( &aDateTime );
121 :
122 : // Combine to date
123 : nDate = ((sal_uIntPtr)aDateTime.wDay) +
124 : (((sal_uIntPtr)aDateTime.wMonth)*100) +
125 : (((sal_uIntPtr)aDateTime.wYear)*10000);
126 : #else
127 : time_t nTmpTime;
128 : struct tm aTime;
129 :
130 : // get current time
131 2451 : nTmpTime = time( 0 );
132 :
133 : // compute date
134 2451 : if ( localtime_r( &nTmpTime, &aTime ) )
135 : {
136 : nDate = ((sal_uIntPtr)aTime.tm_mday) +
137 : (((sal_uIntPtr)(aTime.tm_mon+1))*100) +
138 2451 : (((sal_uIntPtr)(aTime.tm_year+1900))*10000);
139 : }
140 : else
141 0 : nDate = 1 + 100 + (((sal_uIntPtr)1900)*10000);
142 : #endif
143 2451 : }
144 :
145 8 : void Date::SetDay( sal_uInt16 nNewDay )
146 : {
147 8 : sal_uIntPtr nMonth = GetMonth();
148 8 : sal_uIntPtr nYear = GetYear();
149 :
150 8 : nDate = ((sal_uIntPtr)(nNewDay%100)) + (nMonth*100) + (nYear*10000);
151 8 : }
152 :
153 8 : void Date::SetMonth( sal_uInt16 nNewMonth )
154 : {
155 8 : sal_uIntPtr nDay = GetDay();
156 8 : sal_uIntPtr nYear = GetYear();
157 :
158 8 : nDate = nDay + (((sal_uIntPtr)(nNewMonth%100))*100) + (nYear*10000);
159 8 : }
160 :
161 16 : void Date::SetYear( sal_uInt16 nNewYear )
162 : {
163 16 : sal_uIntPtr nDay = GetDay();
164 16 : sal_uIntPtr nMonth = GetMonth();
165 :
166 16 : nDate = nDay + (nMonth*100) + (((sal_uIntPtr)(nNewYear%10000))*10000);
167 16 : }
168 :
169 0 : DayOfWeek Date::GetDayOfWeek() const
170 : {
171 0 : return (DayOfWeek)((sal_uIntPtr)(DateToDays( GetDay(), GetMonth(), GetYear() )-1) % 7);
172 : }
173 :
174 0 : sal_uInt16 Date::GetDayOfYear() const
175 : {
176 0 : sal_uInt16 nDay = GetDay();
177 0 : sal_uInt16 nMonth = GetMonth();
178 0 : sal_uInt16 nYear = GetYear();
179 0 : Normalize( nDay, nMonth, nYear);
180 :
181 0 : for( sal_uInt16 i = 1; i < nMonth; i++ )
182 0 : nDay = nDay + ::DaysInMonth( i, nYear ); // += yields a warning on MSVC, so don't use it
183 0 : return nDay;
184 : }
185 :
186 0 : sal_uInt16 Date::GetWeekOfYear( DayOfWeek eStartDay,
187 : sal_Int16 nMinimumNumberOfDaysInWeek ) const
188 : {
189 : short nWeek;
190 0 : short n1WDay = (short)Date( 1, 1, GetYear() ).GetDayOfWeek();
191 0 : short nDayOfYear = (short)GetDayOfYear();
192 :
193 : // weekdays start at 0, thus decrement one
194 0 : nDayOfYear--;
195 : // account for StartDay
196 0 : n1WDay = (n1WDay+(7-(short)eStartDay)) % 7;
197 :
198 0 : if (nMinimumNumberOfDaysInWeek < 1 || 7 < nMinimumNumberOfDaysInWeek)
199 : {
200 : SAL_WARN( "tools", "Date::GetWeekOfYear: invalid nMinimumNumberOfDaysInWeek" );
201 0 : nMinimumNumberOfDaysInWeek = 4;
202 : }
203 :
204 0 : if ( nMinimumNumberOfDaysInWeek == 1 )
205 : {
206 0 : nWeek = ((n1WDay+nDayOfYear)/7) + 1;
207 : // Set to 53rd week only if we're not in the
208 : // first week of the new year
209 0 : if ( nWeek == 54 )
210 0 : nWeek = 1;
211 0 : else if ( nWeek == 53 )
212 : {
213 0 : short nDaysInYear = (short)GetDaysInYear();
214 0 : short nDaysNextYear = (short)Date( 1, 1, GetYear()+1 ).GetDayOfWeek();
215 0 : nDaysNextYear = (nDaysNextYear+(7-(short)eStartDay)) % 7;
216 0 : if ( nDayOfYear > (nDaysInYear-nDaysNextYear-1) )
217 0 : nWeek = 1;
218 : }
219 : }
220 0 : else if ( nMinimumNumberOfDaysInWeek == 7 )
221 : {
222 0 : nWeek = ((n1WDay+nDayOfYear)/7);
223 : // First week of a year is equal to the last week of the previous year
224 0 : if ( nWeek == 0 )
225 : {
226 0 : Date aLastDatePrevYear( 31, 12, GetYear()-1 );
227 0 : nWeek = aLastDatePrevYear.GetWeekOfYear( eStartDay, nMinimumNumberOfDaysInWeek );
228 : }
229 : }
230 : else // ( nMinimumNumberOfDaysInWeek == somehing_else, commentary examples for 4 )
231 : {
232 : // x_monday - thursday
233 0 : if ( n1WDay < nMinimumNumberOfDaysInWeek )
234 0 : nWeek = 1;
235 : // Friday
236 0 : else if ( n1WDay == nMinimumNumberOfDaysInWeek )
237 0 : nWeek = 53;
238 : // Saturday
239 0 : else if ( n1WDay == nMinimumNumberOfDaysInWeek + 1 )
240 : {
241 : // Year after leapyear
242 0 : if ( Date( 1, 1, GetYear()-1 ).IsLeapYear() )
243 0 : nWeek = 53;
244 : else
245 0 : nWeek = 52;
246 : }
247 : // Sunday
248 : else
249 0 : nWeek = 52;
250 :
251 0 : if ( (nWeek == 1) || (nDayOfYear + n1WDay > 6) )
252 : {
253 0 : if ( nWeek == 1 )
254 0 : nWeek += (nDayOfYear + n1WDay) / 7;
255 : else
256 0 : nWeek = (nDayOfYear + n1WDay) / 7;
257 0 : if ( nWeek == 53 )
258 : {
259 : // next x_Sunday == first x_Sunday in the new year
260 : // == still the same week!
261 0 : long nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() );
262 0 : nTempDays += 6 - (GetDayOfWeek()+(7-(short)eStartDay)) % 7;
263 : sal_uInt16 nDay;
264 : sal_uInt16 nMonth;
265 : sal_uInt16 nYear;
266 0 : DaysToDate( nTempDays, nDay, nMonth, nYear );
267 0 : nWeek = Date( nDay, nMonth, nYear ).GetWeekOfYear( eStartDay, nMinimumNumberOfDaysInWeek );
268 : }
269 : }
270 : }
271 :
272 0 : return (sal_uInt16)nWeek;
273 : }
274 :
275 761 : sal_uInt16 Date::GetDaysInMonth() const
276 : {
277 761 : sal_uInt16 nDay = GetDay();
278 761 : sal_uInt16 nMonth = GetMonth();
279 761 : sal_uInt16 nYear = GetYear();
280 761 : Normalize( nDay, nMonth, nYear);
281 :
282 761 : return DaysInMonth( nMonth, nYear );
283 : }
284 :
285 0 : sal_Bool Date::IsLeapYear() const
286 : {
287 0 : sal_uInt16 nYear = GetYear();
288 0 : return ImpIsLeapYear( nYear );
289 : }
290 :
291 6 : sal_Bool Date::IsValidAndGregorian() const
292 : {
293 6 : sal_uInt16 nDay = GetDay();
294 6 : sal_uInt16 nMonth = GetMonth();
295 6 : sal_uInt16 nYear = GetYear();
296 :
297 6 : if ( !nMonth || (nMonth > 12) )
298 2 : return sal_False;
299 4 : if ( !nDay || (nDay > DaysInMonth( nMonth, nYear )) )
300 0 : return sal_False;
301 4 : else if ( nYear <= 1582 )
302 : {
303 0 : if ( nYear < 1582 )
304 0 : return sal_False;
305 0 : else if ( nMonth < 10 )
306 0 : return sal_False;
307 0 : else if ( (nMonth == 10) && (nDay < 15) )
308 0 : return sal_False;
309 : }
310 :
311 4 : return sal_True;
312 : }
313 :
314 0 : bool Date::IsValidDate() const
315 : {
316 0 : return IsValidDate( GetDay(), GetMonth(), GetYear());
317 : }
318 :
319 : //static
320 4485 : bool Date::IsValidDate( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear )
321 : {
322 4485 : if ( !nMonth || (nMonth > 12) )
323 0 : return false;
324 4485 : if ( !nDay || (nDay > DaysInMonth( nMonth, nYear )) )
325 0 : return false;
326 4485 : return true;
327 : }
328 :
329 8 : bool Date::Normalize()
330 : {
331 8 : sal_uInt16 nDay = GetDay();
332 8 : sal_uInt16 nMonth = GetMonth();
333 8 : sal_uInt16 nYear = GetYear();
334 :
335 8 : if (!Normalize( nDay, nMonth, nYear))
336 8 : return false;
337 :
338 0 : SetDay( nDay);
339 0 : SetMonth( nMonth);
340 0 : SetYear( nYear);
341 :
342 0 : return true;
343 : }
344 :
345 : //static
346 4485 : bool Date::Normalize( sal_uInt16 & rDay, sal_uInt16 & rMonth, sal_uInt16 & rYear )
347 : {
348 4485 : if (IsValidDate( rDay, rMonth, rYear))
349 4485 : return false;
350 :
351 0 : if (rMonth > 12)
352 : {
353 0 : rYear += rMonth / 12;
354 0 : rMonth = rMonth % 12;
355 : }
356 0 : if (!rMonth)
357 : {
358 0 : if (!rYear)
359 : {
360 0 : rYear = 0;
361 0 : rMonth = 1;
362 0 : if (rDay > 31)
363 0 : rDay -= 31;
364 : else
365 0 : rDay = 1;
366 : }
367 : else
368 : {
369 0 : --rYear;
370 0 : rMonth = 12;
371 : }
372 : }
373 : sal_uInt16 nDays;
374 0 : while (rDay > (nDays = DaysInMonth( rMonth, rYear)))
375 : {
376 0 : rDay -= nDays;
377 0 : if (rMonth < 12)
378 0 : ++rMonth;
379 : else
380 : {
381 0 : ++rYear;
382 0 : rMonth = 1;
383 : }
384 : }
385 0 : if (rYear > 9999)
386 : {
387 0 : rDay = 31;
388 0 : rMonth = 12;
389 0 : rYear = 9999;
390 : }
391 0 : return true;
392 : }
393 :
394 1938 : Date& Date::operator +=( long nDays )
395 : {
396 : sal_uInt16 nDay;
397 : sal_uInt16 nMonth;
398 : sal_uInt16 nYear;
399 1938 : long nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() );
400 :
401 1938 : nTempDays += nDays;
402 1938 : if ( nTempDays > MAX_DAYS )
403 0 : nDate = 31 + (12*100) + (((sal_uIntPtr)9999)*10000);
404 1938 : else if ( nTempDays <= 0 )
405 0 : nDate = 1 + 100;
406 : else
407 : {
408 1938 : DaysToDate( nTempDays, nDay, nMonth, nYear );
409 1938 : nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
410 : }
411 :
412 1938 : return *this;
413 : }
414 :
415 6 : Date& Date::operator -=( long nDays )
416 : {
417 : sal_uInt16 nDay;
418 : sal_uInt16 nMonth;
419 : sal_uInt16 nYear;
420 6 : long nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() );
421 :
422 6 : nTempDays -= nDays;
423 6 : if ( nTempDays > MAX_DAYS )
424 0 : nDate = 31 + (12*100) + (((sal_uIntPtr)9999)*10000);
425 6 : else if ( nTempDays <= 0 )
426 0 : nDate = 1 + 100;
427 : else
428 : {
429 6 : DaysToDate( nTempDays, nDay, nMonth, nYear );
430 6 : nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
431 : }
432 :
433 6 : return *this;
434 : }
435 :
436 0 : Date& Date::operator ++()
437 : {
438 : sal_uInt16 nDay;
439 : sal_uInt16 nMonth;
440 : sal_uInt16 nYear;
441 0 : long nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() );
442 :
443 0 : if ( nTempDays < MAX_DAYS )
444 : {
445 0 : nTempDays++;
446 0 : DaysToDate( nTempDays, nDay, nMonth, nYear );
447 0 : nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
448 : }
449 :
450 0 : return *this;
451 : }
452 :
453 0 : Date& Date::operator --()
454 : {
455 : sal_uInt16 nDay;
456 : sal_uInt16 nMonth;
457 : sal_uInt16 nYear;
458 0 : long nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() );
459 :
460 0 : if ( nTempDays > 1 )
461 : {
462 0 : nTempDays--;
463 0 : DaysToDate( nTempDays, nDay, nMonth, nYear );
464 0 : nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
465 : }
466 0 : return *this;
467 : }
468 :
469 0 : Date Date::operator ++( int )
470 : {
471 0 : Date aOldDate = *this;
472 0 : Date::operator++();
473 0 : return aOldDate;
474 : }
475 :
476 0 : Date Date::operator --( int )
477 : {
478 0 : Date aOldDate = *this;
479 0 : Date::operator--();
480 0 : return aOldDate;
481 : }
482 :
483 1296 : Date operator +( const Date& rDate, long nDays )
484 : {
485 1296 : Date aDate( rDate );
486 1296 : aDate += nDays;
487 1296 : return aDate;
488 : }
489 :
490 6 : Date operator -( const Date& rDate, long nDays )
491 : {
492 6 : Date aDate( rDate );
493 6 : aDate -= nDays;
494 6 : return aDate;
495 : }
496 :
497 886 : long operator -( const Date& rDate1, const Date& rDate2 )
498 : {
499 1772 : sal_uIntPtr nTempDays1 = Date::DateToDays( rDate1.GetDay(), rDate1.GetMonth(),
500 2658 : rDate1.GetYear() );
501 1772 : sal_uIntPtr nTempDays2 = Date::DateToDays( rDate2.GetDay(), rDate2.GetMonth(),
502 2658 : rDate2.GetYear() );
503 886 : return nTempDays1 - nTempDays2;
504 : }
505 :
506 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|