1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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
)
44 return aDaysInMonth
[nMonth
-1];
47 if (ImpIsLeapYear(nYear
))
48 return aDaysInMonth
[nMonth
-1] + 1;
50 return aDaysInMonth
[nMonth
-1];
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
);
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);
73 return DateToDays( GetDay(), GetMonth(), GetYear() );
76 long Date::DateToDays( sal_uInt16 nDay
, sal_uInt16 nMonth
, sal_uInt16 nYear
)
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
);
90 static void DaysToDate( long nDays
,
91 sal_uInt16
& rDay
, sal_uInt16
& rMonth
, sal_uInt16
& rYear
)
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);
111 if ( nTempDays
> 365 )
113 if ( (nTempDays
!= 366) || !ImpIsLeapYear( rYear
) )
124 while ( (sal_uIntPtr
)nTempDays
> ImplDaysInMonth( rMonth
, rYear
) )
126 nTempDays
-= ImplDaysInMonth( rMonth
, rYear
);
129 rDay
= (sal_uInt16
)nTempDays
;
132 Date::Date( DateInitSystem
)
135 SYSTEMTIME aDateTime
;
136 GetLocalTime( &aDateTime
);
139 nDate
= ((sal_uIntPtr
)aDateTime
.wDay
) +
140 (((sal_uIntPtr
)aDateTime
.wMonth
)*100) +
141 (((sal_uIntPtr
)aDateTime
.wYear
)*10000);
147 nTmpTime
= time( 0 );
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);
157 nDate
= 1 + 100 + (((sal_uIntPtr
)1900)*10000);
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
207 sal_uInt16
Date::GetWeekOfYear( DayOfWeek eStartDay
,
208 sal_Int16 nMinimumNumberOfDaysInWeek
) const
211 short n1WDay
= (short)Date( 1, 1, GetYear() ).GetDayOfWeek();
212 short nDayOfYear
= (short)GetDayOfYear();
214 // weekdays start at 0, thus decrement one
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
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) )
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
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
)
257 else if ( n1WDay
== nMinimumNumberOfDaysInWeek
)
260 else if ( n1WDay
== nMinimumNumberOfDaysInWeek
+ 1 )
262 // Year after leapyear
263 if ( Date( 1, 1, GetYear()-1 ).IsLeapYear() )
272 if ( (nWeek
== 1) || (nDayOfYear
+ n1WDay
> 6) )
275 nWeek
+= (nDayOfYear
+ n1WDay
) / 7;
277 nWeek
= (nDayOfYear
+ n1WDay
) / 7;
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;
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) )
321 if ( !nDay
|| (nDay
> ImplDaysInMonth( nMonth
, nYear
)) )
323 else if ( nYear
<= 1582 )
327 else if ( nMonth
< 10 )
329 else if ( (nMonth
== 10) && (nDay
< 15) )
336 bool Date::IsValidDate() const
338 return IsValidDate( GetDay(), GetMonth(), GetYear());
342 bool Date::IsValidDate( sal_uInt16 nDay
, sal_uInt16 nMonth
, sal_uInt16 nYear
)
344 if ( !nMonth
|| (nMonth
> 12) )
346 if ( !nDay
|| (nDay
> ImplDaysInMonth( nMonth
, nYear
)) )
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
))
368 bool Date::Normalize( sal_uInt16
& rDay
, sal_uInt16
& rMonth
, sal_uInt16
& rYear
)
370 if (IsValidDate( rDay
, rMonth
, rYear
))
375 rYear
+= rMonth
/ 12;
376 rMonth
= rMonth
% 12;
396 while (rDay
> (nDays
= ImplDaysInMonth( rMonth
, rYear
)))
416 Date
& Date::operator +=( long nDays
)
425 long nTempDays
= GetAsNormalizedDays();
428 if ( nTempDays
> MAX_DAYS
)
429 nDate
= 31 + (12*100) + (((sal_uIntPtr
)9999)*10000);
430 else if ( nTempDays
<= 0 )
434 DaysToDate( nTempDays
, nDay
, nMonth
, nYear
);
435 nDate
= ((sal_uIntPtr
)nDay
) + (((sal_uIntPtr
)nMonth
)*100) + (((sal_uIntPtr
)nYear
)*10000);
441 Date
& Date::operator -=( long nDays
)
450 long nTempDays
= GetAsNormalizedDays();
453 if ( nTempDays
> MAX_DAYS
)
454 nDate
= 31 + (12*100) + (((sal_uIntPtr
)9999)*10000);
455 else if ( nTempDays
<= 0 )
459 DaysToDate( nTempDays
, nDay
, nMonth
, nYear
);
460 nDate
= ((sal_uIntPtr
)nDay
) + (((sal_uIntPtr
)nMonth
)*100) + (((sal_uIntPtr
)nYear
)*10000);
466 Date
& Date::operator ++()
471 long nTempDays
= GetAsNormalizedDays();
473 if ( nTempDays
< MAX_DAYS
)
476 DaysToDate( nTempDays
, nDay
, nMonth
, nYear
);
477 nDate
= ((sal_uIntPtr
)nDay
) + (((sal_uIntPtr
)nMonth
)*100) + (((sal_uIntPtr
)nYear
)*10000);
483 Date
& Date::operator --()
488 long nTempDays
= GetAsNormalizedDays();
493 DaysToDate( nTempDays
, nDay
, nMonth
, nYear
);
494 nDate
= ((sal_uIntPtr
)nDay
) + (((sal_uIntPtr
)nMonth
)*100) + (((sal_uIntPtr
)nYear
)*10000);
499 Date
Date::operator ++( int )
501 Date aOldDate
= *this;
506 Date
Date::operator --( int )
508 Date aOldDate
= *this;
513 Date
operator +( const Date
& rDate
, long nDays
)
520 Date
operator -( const Date
& rDate
, long nDays
)
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: */