Branch libreoffice-5-0-4
[LibreOffice.git] / tools / source / datetime / tdate.cxx
blobb970d51da5ba9a408162cc736bafa94426249850
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #if defined( WNT )
21 #include <windows.h>
22 #else
23 #include <time.h>
24 #endif
26 #include <tools/date.hxx>
27 #include <sal/log.hxx>
29 static const sal_uInt16 aDaysInMonth[12] = { 31, 28, 31, 30, 31, 30,
30 31, 31, 30, 31, 30, 31 };
32 #define MAX_DAYS 3636532
34 inline bool ImpIsLeapYear( sal_uInt16 nYear )
36 return ( ( ((nYear % 4) == 0) && ((nYear % 100) != 0) ) ||
37 ( (nYear % 400) == 0 ) );
40 // All callers must have sanitized or normalized month and year values!
41 inline sal_uInt16 ImplDaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear )
43 if ( nMonth != 2 )
44 return aDaysInMonth[nMonth-1];
45 else
47 if (ImpIsLeapYear(nYear))
48 return aDaysInMonth[nMonth-1] + 1;
49 else
50 return aDaysInMonth[nMonth-1];
54 // static
55 sal_uInt16 Date::GetDaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear )
57 SAL_WARN_IF( nMonth < 1 || 12 < nMonth, "tools", "Date::GetDaysInMonth - nMonth out of bounds " << nMonth);
58 if (nMonth < 1)
59 nMonth = 1;
60 else if (12 < nMonth)
61 nMonth = 12;
62 return ImplDaysInMonth( nMonth, nYear);
65 long Date::GetAsNormalizedDays() const
67 // This is a very common datum we often calculate from.
68 if (nDate == 18991230) // 1899-12-30
70 assert(DateToDays( GetDay(), GetMonth(), GetYear() ) == 693594);
71 return 693594;
73 return DateToDays( GetDay(), GetMonth(), GetYear() );
76 long Date::DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear )
78 long nDays;
80 Normalize( nDay, nMonth, nYear);
82 nDays = ((sal_uIntPtr)nYear-1) * 365;
83 nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400);
84 for( sal_uInt16 i = 1; i < nMonth; i++ )
85 nDays += ImplDaysInMonth(i,nYear);
86 nDays += nDay;
87 return nDays;
90 static void DaysToDate( long nDays,
91 sal_uInt16& rDay, sal_uInt16& rMonth, sal_uInt16& rYear )
93 long nTempDays;
94 long i = 0;
95 bool bCalc;
99 nTempDays = (long)nDays;
100 rYear = (sal_uInt16)((nTempDays / 365) - i);
101 nTempDays -= ((sal_uIntPtr)rYear-1) * 365;
102 nTempDays -= ((rYear-1) / 4) - ((rYear-1) / 100) + ((rYear-1) / 400);
103 bCalc = false;
104 if ( nTempDays < 1 )
106 i++;
107 bCalc = true;
109 else
111 if ( nTempDays > 365 )
113 if ( (nTempDays != 366) || !ImpIsLeapYear( rYear ) )
115 i--;
116 bCalc = true;
121 while ( bCalc );
123 rMonth = 1;
124 while ( (sal_uIntPtr)nTempDays > ImplDaysInMonth( rMonth, rYear ) )
126 nTempDays -= ImplDaysInMonth( rMonth, rYear );
127 rMonth++;
129 rDay = (sal_uInt16)nTempDays;
132 Date::Date( DateInitSystem )
134 #if defined WNT
135 SYSTEMTIME aDateTime;
136 GetLocalTime( &aDateTime );
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;
146 // get current time
147 nTmpTime = time( 0 );
149 // compute date
150 if ( localtime_r( &nTmpTime, &aTime ) )
152 nDate = ((sal_uIntPtr)aTime.tm_mday) +
153 (((sal_uIntPtr)(aTime.tm_mon+1))*100) +
154 (((sal_uIntPtr)(aTime.tm_year+1900))*10000);
156 else
157 nDate = 1 + 100 + (((sal_uIntPtr)1900)*10000);
158 #endif
161 Date::Date( const ::com::sun::star::util::DateTime& rDateTime )
163 init( rDateTime.Day, rDateTime.Month, rDateTime.Year );
166 void Date::SetDay( sal_uInt16 nNewDay )
168 sal_uIntPtr nMonth = GetMonth();
169 sal_uIntPtr nYear = GetYear();
171 nDate = ((sal_uIntPtr)(nNewDay%100)) + (nMonth*100) + (nYear*10000);
174 void Date::SetMonth( sal_uInt16 nNewMonth )
176 sal_uIntPtr nDay = GetDay();
177 sal_uIntPtr nYear = GetYear();
179 nDate = nDay + (((sal_uIntPtr)(nNewMonth%100))*100) + (nYear*10000);
182 void Date::SetYear( sal_uInt16 nNewYear )
184 sal_uIntPtr nDay = GetDay();
185 sal_uIntPtr nMonth = GetMonth();
187 nDate = nDay + (nMonth*100) + (((sal_uIntPtr)(nNewYear%10000))*10000);
190 DayOfWeek Date::GetDayOfWeek() const
192 return (DayOfWeek)((sal_uIntPtr)(GetAsNormalizedDays()-1) % 7);
195 sal_uInt16 Date::GetDayOfYear() const
197 sal_uInt16 nDay = GetDay();
198 sal_uInt16 nMonth = GetMonth();
199 sal_uInt16 nYear = GetYear();
200 Normalize( nDay, nMonth, nYear);
202 for( sal_uInt16 i = 1; i < nMonth; i++ )
203 nDay = nDay + ::ImplDaysInMonth( i, nYear ); // += yields a warning on MSVC, so don't use it
204 return nDay;
207 sal_uInt16 Date::GetWeekOfYear( DayOfWeek eStartDay,
208 sal_Int16 nMinimumNumberOfDaysInWeek ) const
210 short nWeek;
211 short n1WDay = (short)Date( 1, 1, GetYear() ).GetDayOfWeek();
212 short nDayOfYear = (short)GetDayOfYear();
214 // weekdays start at 0, thus decrement one
215 nDayOfYear--;
216 // account for StartDay
217 n1WDay = (n1WDay+(7-(short)eStartDay)) % 7;
219 if (nMinimumNumberOfDaysInWeek < 1 || 7 < nMinimumNumberOfDaysInWeek)
221 SAL_WARN( "tools.datetime", "Date::GetWeekOfYear: invalid nMinimumNumberOfDaysInWeek" );
222 nMinimumNumberOfDaysInWeek = 4;
225 if ( nMinimumNumberOfDaysInWeek == 1 )
227 nWeek = ((n1WDay+nDayOfYear)/7) + 1;
228 // Set to 53rd week only if we're not in the
229 // first week of the new year
230 if ( nWeek == 54 )
231 nWeek = 1;
232 else if ( nWeek == 53 )
234 short nDaysInYear = (short)GetDaysInYear();
235 short nDaysNextYear = (short)Date( 1, 1, GetYear()+1 ).GetDayOfWeek();
236 nDaysNextYear = (nDaysNextYear+(7-(short)eStartDay)) % 7;
237 if ( nDayOfYear > (nDaysInYear-nDaysNextYear-1) )
238 nWeek = 1;
241 else if ( nMinimumNumberOfDaysInWeek == 7 )
243 nWeek = ((n1WDay+nDayOfYear)/7);
244 // First week of a year is equal to the last week of the previous year
245 if ( nWeek == 0 )
247 Date aLastDatePrevYear( 31, 12, GetYear()-1 );
248 nWeek = aLastDatePrevYear.GetWeekOfYear( eStartDay, nMinimumNumberOfDaysInWeek );
251 else // ( nMinimumNumberOfDaysInWeek == somehing_else, commentary examples for 4 )
253 // x_monday - thursday
254 if ( n1WDay < nMinimumNumberOfDaysInWeek )
255 nWeek = 1;
256 // Friday
257 else if ( n1WDay == nMinimumNumberOfDaysInWeek )
258 nWeek = 53;
259 // Saturday
260 else if ( n1WDay == nMinimumNumberOfDaysInWeek + 1 )
262 // Year after leapyear
263 if ( Date( 1, 1, GetYear()-1 ).IsLeapYear() )
264 nWeek = 53;
265 else
266 nWeek = 52;
268 // Sunday
269 else
270 nWeek = 52;
272 if ( (nWeek == 1) || (nDayOfYear + n1WDay > 6) )
274 if ( nWeek == 1 )
275 nWeek += (nDayOfYear + n1WDay) / 7;
276 else
277 nWeek = (nDayOfYear + n1WDay) / 7;
278 if ( nWeek == 53 )
280 // next x_Sunday == first x_Sunday in the new year
281 // == still the same week!
282 long nTempDays = GetAsNormalizedDays();
284 nTempDays += 6 - (GetDayOfWeek()+(7-(short)eStartDay)) % 7;
285 sal_uInt16 nDay;
286 sal_uInt16 nMonth;
287 sal_uInt16 nYear;
288 DaysToDate( nTempDays, nDay, nMonth, nYear );
289 nWeek = Date( nDay, nMonth, nYear ).GetWeekOfYear( eStartDay, nMinimumNumberOfDaysInWeek );
294 return (sal_uInt16)nWeek;
297 sal_uInt16 Date::GetDaysInMonth() const
299 sal_uInt16 nDay = GetDay();
300 sal_uInt16 nMonth = GetMonth();
301 sal_uInt16 nYear = GetYear();
302 Normalize( nDay, nMonth, nYear);
304 return ImplDaysInMonth( nMonth, nYear );
307 bool Date::IsLeapYear() const
309 sal_uInt16 nYear = GetYear();
310 return ImpIsLeapYear( nYear );
313 bool Date::IsValidAndGregorian() const
315 sal_uInt16 nDay = GetDay();
316 sal_uInt16 nMonth = GetMonth();
317 sal_uInt16 nYear = GetYear();
319 if ( !nMonth || (nMonth > 12) )
320 return false;
321 if ( !nDay || (nDay > ImplDaysInMonth( nMonth, nYear )) )
322 return false;
323 else if ( nYear <= 1582 )
325 if ( nYear < 1582 )
326 return false;
327 else if ( nMonth < 10 )
328 return false;
329 else if ( (nMonth == 10) && (nDay < 15) )
330 return false;
333 return true;
336 bool Date::IsValidDate() const
338 return IsValidDate( GetDay(), GetMonth(), GetYear());
341 //static
342 bool Date::IsValidDate( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear )
344 if ( !nMonth || (nMonth > 12) )
345 return false;
346 if ( !nDay || (nDay > ImplDaysInMonth( nMonth, nYear )) )
347 return false;
348 return true;
351 bool Date::Normalize()
353 sal_uInt16 nDay = GetDay();
354 sal_uInt16 nMonth = GetMonth();
355 sal_uInt16 nYear = GetYear();
357 if (!Normalize( nDay, nMonth, nYear))
358 return false;
360 SetDay( nDay);
361 SetMonth( nMonth);
362 SetYear( nYear);
364 return true;
367 //static
368 bool Date::Normalize( sal_uInt16 & rDay, sal_uInt16 & rMonth, sal_uInt16 & rYear )
370 if (IsValidDate( rDay, rMonth, rYear))
371 return false;
373 if (rMonth > 12)
375 rYear += rMonth / 12;
376 rMonth = rMonth % 12;
378 if (!rMonth)
380 if (!rYear)
382 rYear = 0;
383 rMonth = 1;
384 if (rDay > 31)
385 rDay -= 31;
386 else
387 rDay = 1;
389 else
391 --rYear;
392 rMonth = 12;
395 sal_uInt16 nDays;
396 while (rDay > (nDays = ImplDaysInMonth( rMonth, rYear)))
398 rDay -= nDays;
399 if (rMonth < 12)
400 ++rMonth;
401 else
403 ++rYear;
404 rMonth = 1;
407 if (rYear > 9999)
409 rDay = 31;
410 rMonth = 12;
411 rYear = 9999;
413 return true;
416 Date& Date::operator +=( long nDays )
418 sal_uInt16 nDay;
419 sal_uInt16 nMonth;
420 sal_uInt16 nYear;
422 if (nDays == 0)
423 return *this;
425 long nTempDays = GetAsNormalizedDays();
427 nTempDays += nDays;
428 if ( nTempDays > MAX_DAYS )
429 nDate = 31 + (12*100) + (((sal_uIntPtr)9999)*10000);
430 else if ( nTempDays <= 0 )
431 nDate = 1 + 100;
432 else
434 DaysToDate( nTempDays, nDay, nMonth, nYear );
435 nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
438 return *this;
441 Date& Date::operator -=( long nDays )
443 sal_uInt16 nDay;
444 sal_uInt16 nMonth;
445 sal_uInt16 nYear;
447 if (nDays == 0)
448 return *this;
450 long nTempDays = GetAsNormalizedDays();
452 nTempDays -= nDays;
453 if ( nTempDays > MAX_DAYS )
454 nDate = 31 + (12*100) + (((sal_uIntPtr)9999)*10000);
455 else if ( nTempDays <= 0 )
456 nDate = 1 + 100;
457 else
459 DaysToDate( nTempDays, nDay, nMonth, nYear );
460 nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
463 return *this;
466 Date& Date::operator ++()
468 sal_uInt16 nDay;
469 sal_uInt16 nMonth;
470 sal_uInt16 nYear;
471 long nTempDays = GetAsNormalizedDays();
473 if ( nTempDays < MAX_DAYS )
475 nTempDays++;
476 DaysToDate( nTempDays, nDay, nMonth, nYear );
477 nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
480 return *this;
483 Date& Date::operator --()
485 sal_uInt16 nDay;
486 sal_uInt16 nMonth;
487 sal_uInt16 nYear;
488 long nTempDays = GetAsNormalizedDays();
490 if ( nTempDays > 1 )
492 nTempDays--;
493 DaysToDate( nTempDays, nDay, nMonth, nYear );
494 nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
496 return *this;
499 Date Date::operator ++( int )
501 Date aOldDate = *this;
502 Date::operator++();
503 return aOldDate;
506 Date Date::operator --( int )
508 Date aOldDate = *this;
509 Date::operator--();
510 return aOldDate;
513 Date operator +( const Date& rDate, long nDays )
515 Date aDate( rDate );
516 aDate += nDays;
517 return aDate;
520 Date operator -( const Date& rDate, long nDays )
522 Date aDate( rDate );
523 aDate -= nDays;
524 return aDate;
527 long operator -( const Date& rDate1, const Date& rDate2 )
529 sal_uIntPtr nTempDays1 = rDate1.GetAsNormalizedDays();
530 sal_uIntPtr nTempDays2 = rDate2.GetAsNormalizedDays();
532 return nTempDays1 - nTempDays2;
535 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */