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>
31 struct tm
*localtime_r(const time_t *timep
, struct tm
*buffer
);
35 static const sal_uInt16 aDaysInMonth
[12] = { 31, 28, 31, 30, 31, 30,
36 31, 31, 30, 31, 30, 31 };
38 #define MAX_DAYS 3636532
40 inline bool ImpIsLeapYear( sal_uInt16 nYear
)
42 return ( ( ((nYear
% 4) == 0) && ((nYear
% 100) != 0) ) ||
43 ( (nYear
% 400) == 0 ) );
46 // All callers must have sanitized or normalized month and year values!
47 inline sal_uInt16
ImplDaysInMonth( sal_uInt16 nMonth
, sal_uInt16 nYear
)
50 return aDaysInMonth
[nMonth
-1];
53 if (ImpIsLeapYear(nYear
))
54 return aDaysInMonth
[nMonth
-1] + 1;
56 return aDaysInMonth
[nMonth
-1];
61 sal_uInt16
Date::GetDaysInMonth( sal_uInt16 nMonth
, sal_uInt16 nYear
)
63 SAL_WARN_IF( nMonth
< 1 || 12 < nMonth
, "tools", "Date::GetDaysInMonth - nMonth out of bounds " << nMonth
);
68 return ImplDaysInMonth( nMonth
, nYear
);
71 long Date::DateToDays( sal_uInt16 nDay
, sal_uInt16 nMonth
, sal_uInt16 nYear
)
75 Normalize( nDay
, nMonth
, nYear
);
77 nDays
= ((sal_uIntPtr
)nYear
-1) * 365;
78 nDays
+= ((nYear
-1) / 4) - ((nYear
-1) / 100) + ((nYear
-1) / 400);
79 for( sal_uInt16 i
= 1; i
< nMonth
; i
++ )
80 nDays
+= ImplDaysInMonth(i
,nYear
);
85 static void DaysToDate( long nDays
,
86 sal_uInt16
& rDay
, sal_uInt16
& rMonth
, sal_uInt16
& rYear
)
94 nTempDays
= (long)nDays
;
95 rYear
= (sal_uInt16
)((nTempDays
/ 365) - i
);
96 nTempDays
-= ((sal_uIntPtr
)rYear
-1) * 365;
97 nTempDays
-= ((rYear
-1) / 4) - ((rYear
-1) / 100) + ((rYear
-1) / 400);
106 if ( nTempDays
> 365 )
108 if ( (nTempDays
!= 366) || !ImpIsLeapYear( rYear
) )
119 while ( (sal_uIntPtr
)nTempDays
> ImplDaysInMonth( rMonth
, rYear
) )
121 nTempDays
-= ImplDaysInMonth( rMonth
, rYear
);
124 rDay
= (sal_uInt16
)nTempDays
;
127 Date::Date( DateInitSystem
)
130 SYSTEMTIME aDateTime
;
131 GetLocalTime( &aDateTime
);
134 nDate
= ((sal_uIntPtr
)aDateTime
.wDay
) +
135 (((sal_uIntPtr
)aDateTime
.wMonth
)*100) +
136 (((sal_uIntPtr
)aDateTime
.wYear
)*10000);
142 nTmpTime
= time( 0 );
145 if ( localtime_r( &nTmpTime
, &aTime
) )
147 nDate
= ((sal_uIntPtr
)aTime
.tm_mday
) +
148 (((sal_uIntPtr
)(aTime
.tm_mon
+1))*100) +
149 (((sal_uIntPtr
)(aTime
.tm_year
+1900))*10000);
152 nDate
= 1 + 100 + (((sal_uIntPtr
)1900)*10000);
156 void Date::SetDay( sal_uInt16 nNewDay
)
158 sal_uIntPtr nMonth
= GetMonth();
159 sal_uIntPtr nYear
= GetYear();
161 nDate
= ((sal_uIntPtr
)(nNewDay
%100)) + (nMonth
*100) + (nYear
*10000);
164 void Date::SetMonth( sal_uInt16 nNewMonth
)
166 sal_uIntPtr nDay
= GetDay();
167 sal_uIntPtr nYear
= GetYear();
169 nDate
= nDay
+ (((sal_uIntPtr
)(nNewMonth
%100))*100) + (nYear
*10000);
172 void Date::SetYear( sal_uInt16 nNewYear
)
174 sal_uIntPtr nDay
= GetDay();
175 sal_uIntPtr nMonth
= GetMonth();
177 nDate
= nDay
+ (nMonth
*100) + (((sal_uIntPtr
)(nNewYear
%10000))*10000);
180 DayOfWeek
Date::GetDayOfWeek() const
182 return (DayOfWeek
)((sal_uIntPtr
)(DateToDays( GetDay(), GetMonth(), GetYear() )-1) % 7);
185 sal_uInt16
Date::GetDayOfYear() const
187 sal_uInt16 nDay
= GetDay();
188 sal_uInt16 nMonth
= GetMonth();
189 sal_uInt16 nYear
= GetYear();
190 Normalize( nDay
, nMonth
, nYear
);
192 for( sal_uInt16 i
= 1; i
< nMonth
; i
++ )
193 nDay
= nDay
+ ::ImplDaysInMonth( i
, nYear
); // += yields a warning on MSVC, so don't use it
197 sal_uInt16
Date::GetWeekOfYear( DayOfWeek eStartDay
,
198 sal_Int16 nMinimumNumberOfDaysInWeek
) const
201 short n1WDay
= (short)Date( 1, 1, GetYear() ).GetDayOfWeek();
202 short nDayOfYear
= (short)GetDayOfYear();
204 // weekdays start at 0, thus decrement one
206 // account for StartDay
207 n1WDay
= (n1WDay
+(7-(short)eStartDay
)) % 7;
209 if (nMinimumNumberOfDaysInWeek
< 1 || 7 < nMinimumNumberOfDaysInWeek
)
211 SAL_WARN( "tools.datetime", "Date::GetWeekOfYear: invalid nMinimumNumberOfDaysInWeek" );
212 nMinimumNumberOfDaysInWeek
= 4;
215 if ( nMinimumNumberOfDaysInWeek
== 1 )
217 nWeek
= ((n1WDay
+nDayOfYear
)/7) + 1;
218 // Set to 53rd week only if we're not in the
219 // first week of the new year
222 else if ( nWeek
== 53 )
224 short nDaysInYear
= (short)GetDaysInYear();
225 short nDaysNextYear
= (short)Date( 1, 1, GetYear()+1 ).GetDayOfWeek();
226 nDaysNextYear
= (nDaysNextYear
+(7-(short)eStartDay
)) % 7;
227 if ( nDayOfYear
> (nDaysInYear
-nDaysNextYear
-1) )
231 else if ( nMinimumNumberOfDaysInWeek
== 7 )
233 nWeek
= ((n1WDay
+nDayOfYear
)/7);
234 // First week of a year is equal to the last week of the previous year
237 Date
aLastDatePrevYear( 31, 12, GetYear()-1 );
238 nWeek
= aLastDatePrevYear
.GetWeekOfYear( eStartDay
, nMinimumNumberOfDaysInWeek
);
241 else // ( nMinimumNumberOfDaysInWeek == somehing_else, commentary examples for 4 )
243 // x_monday - thursday
244 if ( n1WDay
< nMinimumNumberOfDaysInWeek
)
247 else if ( n1WDay
== nMinimumNumberOfDaysInWeek
)
250 else if ( n1WDay
== nMinimumNumberOfDaysInWeek
+ 1 )
252 // Year after leapyear
253 if ( Date( 1, 1, GetYear()-1 ).IsLeapYear() )
262 if ( (nWeek
== 1) || (nDayOfYear
+ n1WDay
> 6) )
265 nWeek
+= (nDayOfYear
+ n1WDay
) / 7;
267 nWeek
= (nDayOfYear
+ n1WDay
) / 7;
270 // next x_Sunday == first x_Sunday in the new year
271 // == still the same week!
272 long nTempDays
= DateToDays( GetDay(), GetMonth(), GetYear() );
273 nTempDays
+= 6 - (GetDayOfWeek()+(7-(short)eStartDay
)) % 7;
277 DaysToDate( nTempDays
, nDay
, nMonth
, nYear
);
278 nWeek
= Date( nDay
, nMonth
, nYear
).GetWeekOfYear( eStartDay
, nMinimumNumberOfDaysInWeek
);
283 return (sal_uInt16
)nWeek
;
286 sal_uInt16
Date::GetDaysInMonth() const
288 sal_uInt16 nDay
= GetDay();
289 sal_uInt16 nMonth
= GetMonth();
290 sal_uInt16 nYear
= GetYear();
291 Normalize( nDay
, nMonth
, nYear
);
293 return ImplDaysInMonth( nMonth
, nYear
);
296 bool Date::IsLeapYear() const
298 sal_uInt16 nYear
= GetYear();
299 return ImpIsLeapYear( nYear
);
302 bool Date::IsValidAndGregorian() const
304 sal_uInt16 nDay
= GetDay();
305 sal_uInt16 nMonth
= GetMonth();
306 sal_uInt16 nYear
= GetYear();
308 if ( !nMonth
|| (nMonth
> 12) )
310 if ( !nDay
|| (nDay
> ImplDaysInMonth( nMonth
, nYear
)) )
312 else if ( nYear
<= 1582 )
316 else if ( nMonth
< 10 )
318 else if ( (nMonth
== 10) && (nDay
< 15) )
325 bool Date::IsValidDate() const
327 return IsValidDate( GetDay(), GetMonth(), GetYear());
331 bool Date::IsValidDate( sal_uInt16 nDay
, sal_uInt16 nMonth
, sal_uInt16 nYear
)
333 if ( !nMonth
|| (nMonth
> 12) )
335 if ( !nDay
|| (nDay
> ImplDaysInMonth( nMonth
, nYear
)) )
340 bool Date::Normalize()
342 sal_uInt16 nDay
= GetDay();
343 sal_uInt16 nMonth
= GetMonth();
344 sal_uInt16 nYear
= GetYear();
346 if (!Normalize( nDay
, nMonth
, nYear
))
357 bool Date::Normalize( sal_uInt16
& rDay
, sal_uInt16
& rMonth
, sal_uInt16
& rYear
)
359 if (IsValidDate( rDay
, rMonth
, rYear
))
364 rYear
+= rMonth
/ 12;
365 rMonth
= rMonth
% 12;
385 while (rDay
> (nDays
= ImplDaysInMonth( rMonth
, rYear
)))
405 Date
& Date::operator +=( long nDays
)
410 long nTempDays
= DateToDays( GetDay(), GetMonth(), GetYear() );
413 if ( nTempDays
> MAX_DAYS
)
414 nDate
= 31 + (12*100) + (((sal_uIntPtr
)9999)*10000);
415 else if ( nTempDays
<= 0 )
419 DaysToDate( nTempDays
, nDay
, nMonth
, nYear
);
420 nDate
= ((sal_uIntPtr
)nDay
) + (((sal_uIntPtr
)nMonth
)*100) + (((sal_uIntPtr
)nYear
)*10000);
426 Date
& Date::operator -=( long nDays
)
431 long nTempDays
= DateToDays( GetDay(), GetMonth(), GetYear() );
434 if ( nTempDays
> MAX_DAYS
)
435 nDate
= 31 + (12*100) + (((sal_uIntPtr
)9999)*10000);
436 else if ( nTempDays
<= 0 )
440 DaysToDate( nTempDays
, nDay
, nMonth
, nYear
);
441 nDate
= ((sal_uIntPtr
)nDay
) + (((sal_uIntPtr
)nMonth
)*100) + (((sal_uIntPtr
)nYear
)*10000);
447 Date
& Date::operator ++()
452 long nTempDays
= DateToDays( GetDay(), GetMonth(), GetYear() );
454 if ( nTempDays
< MAX_DAYS
)
457 DaysToDate( nTempDays
, nDay
, nMonth
, nYear
);
458 nDate
= ((sal_uIntPtr
)nDay
) + (((sal_uIntPtr
)nMonth
)*100) + (((sal_uIntPtr
)nYear
)*10000);
464 Date
& Date::operator --()
469 long nTempDays
= DateToDays( GetDay(), GetMonth(), GetYear() );
474 DaysToDate( nTempDays
, nDay
, nMonth
, nYear
);
475 nDate
= ((sal_uIntPtr
)nDay
) + (((sal_uIntPtr
)nMonth
)*100) + (((sal_uIntPtr
)nYear
)*10000);
480 Date
Date::operator ++( int )
482 Date aOldDate
= *this;
487 Date
Date::operator --( int )
489 Date aOldDate
= *this;
494 Date
operator +( const Date
& rDate
, long nDays
)
501 Date
operator -( const Date
& rDate
, long nDays
)
508 long operator -( const Date
& rDate1
, const Date
& rDate2
)
510 sal_uIntPtr nTempDays1
= Date::DateToDays( rDate1
.GetDay(), rDate1
.GetMonth(),
512 sal_uIntPtr nTempDays2
= Date::DateToDays( rDate2
.GetDay(), rDate2
.GetMonth(),
514 return nTempDays1
- nTempDays2
;
517 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */