xref: /trunk/main/tools/source/datetime/tdate.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_tools.hxx"
30 
31 #if defined( OS2 )
32 #define INCL_DOSDATETIME
33 #include <svpm.h>
34 #elif defined( WNT )
35 #ifdef _MSC_VER
36 #pragma warning (push,1)
37 #endif
38 #include <tools/svwin.h>
39 #ifdef _MSC_VER
40 #pragma warning (pop)
41 #endif
42 #else
43 #include <time.h>
44 #endif
45 
46 #include <tools/debug.hxx>
47 #include <tools/date.hxx>
48 #ifdef  MACOSX
49 extern "C" {
50 struct tm *localtime_r(const time_t *timep, struct tm *buffer);
51 }
52 #endif
53 
54 // =======================================================================
55 
56 static sal_uInt16 aDaysInMonth[12] = { 31, 28, 31, 30, 31, 30,
57 								   31, 31, 30, 31, 30, 31 };
58 
59 #define MAX_DAYS	3636532
60 
61 // =======================================================================
62 
63 inline sal_Bool ImpIsLeapYear( sal_uInt16 nYear )
64 {
65 	return (
66                  ( ((nYear % 4) == 0) && ((nYear % 100) != 0) ) ||
67                  ( (nYear % 400) == 0 )
68                );
69 }
70 
71 // -----------------------------------------------------------------------
72 
73 inline sal_uInt16 DaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear )
74 {
75 	if ( nMonth != 2 )
76 		return aDaysInMonth[nMonth-1];
77 	else
78 	{
79 		if (ImpIsLeapYear(nYear))
80 			return aDaysInMonth[nMonth-1] + 1;
81 		else
82 			return aDaysInMonth[nMonth-1];
83 	}
84 }
85 
86 // -----------------------------------------------------------------------
87 
88 long Date::DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear )
89 {
90 	long nDays;
91 
92 	nDays = ((sal_uIntPtr)nYear-1) * 365;
93 	nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400);
94 	for( sal_uInt16 i = 1; i < nMonth; i++ )
95 		nDays += DaysInMonth(i,nYear);
96 	nDays += nDay;
97 	return nDays;
98 }
99 
100 // -----------------------------------------------------------------------
101 
102 static void DaysToDate( long nDays,
103 						sal_uInt16& rDay, sal_uInt16& rMonth, sal_uInt16& rYear )
104 {
105 	long	nTempDays;
106 	long	i = 0;
107 	sal_Bool	bCalc;
108 
109 	do
110 	{
111 		nTempDays = (long)nDays;
112 		rYear = (sal_uInt16)((nTempDays / 365) - i);
113 		nTempDays -= ((sal_uIntPtr)rYear-1) * 365;
114 		nTempDays -= ((rYear-1) / 4) - ((rYear-1) / 100) + ((rYear-1) / 400);
115 		bCalc = sal_False;
116 		if ( nTempDays < 1 )
117 		{
118 			i++;
119 			bCalc = sal_True;
120 		}
121 		else
122 		{
123 			if ( nTempDays > 365 )
124 			{
125 				if ( (nTempDays != 366) || !ImpIsLeapYear( rYear ) )
126 				{
127 					i--;
128 					bCalc = sal_True;
129 				}
130 			}
131 		}
132 	}
133 	while ( bCalc );
134 
135 	rMonth = 1;
136 	while ( (sal_uIntPtr)nTempDays > DaysInMonth( rMonth, rYear ) )
137 	{
138 		nTempDays -= DaysInMonth( rMonth, rYear );
139 		rMonth++;
140 	}
141 	rDay = (sal_uInt16)nTempDays;
142 }
143 
144 // =======================================================================
145 
146 Date::Date()
147 {
148 #if defined( OS2 )
149 	DATETIME aDateTime;
150 	DosGetDateTime( &aDateTime );
151 
152 	// Datum zusammenbauen
153 	nDate = ((sal_uIntPtr)aDateTime.day) +
154 			(((sal_uIntPtr)aDateTime.month)*100) +
155 			(((sal_uIntPtr)aDateTime.year)*10000);
156 #elif defined WNT
157 	SYSTEMTIME aDateTime;
158 	GetLocalTime( &aDateTime );
159 
160 	// Datum zusammenbauen
161 	nDate = ((sal_uIntPtr)aDateTime.wDay) +
162 			(((sal_uIntPtr)aDateTime.wMonth)*100) +
163 			(((sal_uIntPtr)aDateTime.wYear)*10000);
164 #else
165 	time_t	   nTmpTime;
166 	struct tm aTime;
167 
168 	// Zeit ermitteln
169 	nTmpTime = time( 0 );
170 
171 	// Datum zusammenbauen
172 	if ( localtime_r( &nTmpTime, &aTime ) )
173 	{
174 		nDate = ((sal_uIntPtr)aTime.tm_mday) +
175 				(((sal_uIntPtr)(aTime.tm_mon+1))*100) +
176 				(((sal_uIntPtr)(aTime.tm_year+1900))*10000);
177 	}
178 	else
179 		nDate = 1 + 100 + (((sal_uIntPtr)1900)*10000);
180 #endif
181 }
182 
183 // -----------------------------------------------------------------------
184 
185 void Date::SetDay( sal_uInt16 nNewDay )
186 {
187 	sal_uIntPtr  nMonth  = GetMonth();
188 	sal_uIntPtr  nYear   = GetYear();
189 
190 	nDate = ((sal_uIntPtr)(nNewDay%100)) + (nMonth*100) + (nYear*10000);
191 }
192 
193 // -----------------------------------------------------------------------
194 
195 void Date::SetMonth( sal_uInt16 nNewMonth )
196 {
197 	sal_uIntPtr  nDay 	 = GetDay();
198 	sal_uIntPtr  nYear	 = GetYear();
199 
200 	nDate = nDay + (((sal_uIntPtr)(nNewMonth%100))*100) + (nYear*10000);
201 }
202 
203 // -----------------------------------------------------------------------
204 
205 void Date::SetYear( sal_uInt16 nNewYear )
206 {
207 	sal_uIntPtr  nDay 	= GetDay();
208 	sal_uIntPtr  nMonth	= GetMonth();
209 
210 	nDate = nDay + (nMonth*100) + (((sal_uIntPtr)(nNewYear%10000))*10000);
211 }
212 
213 // -----------------------------------------------------------------------
214 
215 DayOfWeek Date::GetDayOfWeek() const
216 {
217 	return (DayOfWeek)((sal_uIntPtr)(DateToDays( GetDay(), GetMonth(), GetYear() )-1) % 7);
218 }
219 
220 // -----------------------------------------------------------------------
221 
222 sal_uInt16 Date::GetDayOfYear() const
223 {
224 	sal_uInt16 nDay = GetDay();
225 	for( sal_uInt16 i = 1; i < GetMonth(); i++ )
226          nDay = nDay + ::DaysInMonth( i, GetYear() );   // += yields a warning on MSVC, so don't use it
227 	return nDay;
228 }
229 
230 // -----------------------------------------------------------------------
231 
232 sal_uInt16 Date::GetWeekOfYear( DayOfWeek eStartDay,
233 							sal_Int16 nMinimumNumberOfDaysInWeek ) const
234 {
235 	short nWeek;
236 	short n1WDay = (short)Date( 1, 1, GetYear() ).GetDayOfWeek();
237 	short nDayOfYear = (short)GetDayOfYear();
238 
239 	// Wochentage beginnen bei 0, deshalb einen abziehen
240 	nDayOfYear--;
241 	// StartDay beruecksichtigen
242 	n1WDay = (n1WDay+(7-(short)eStartDay)) % 7;
243 
244     if (nMinimumNumberOfDaysInWeek < 1 || 7 < nMinimumNumberOfDaysInWeek)
245     {
246         DBG_ERRORFILE("Date::GetWeekOfYear: invalid nMinimumNumberOfDaysInWeek");
247         nMinimumNumberOfDaysInWeek = 4;
248     }
249 
250 	if ( nMinimumNumberOfDaysInWeek == 1 )
251 	{
252 		nWeek = ((n1WDay+nDayOfYear)/7) + 1;
253 		// 53te-Woche nur dann, wenn wir nicht schon in der ersten
254 		// Woche des neuen Jahres liegen
255 		if ( nWeek == 54 )
256 			nWeek = 1;
257 		else if ( nWeek == 53 )
258 		{
259 			short nDaysInYear = (short)GetDaysInYear();
260 			short nDaysNextYear = (short)Date( 1, 1, GetYear()+1 ).GetDayOfWeek();
261 			nDaysNextYear = (nDaysNextYear+(7-(short)eStartDay)) % 7;
262 			if ( nDayOfYear > (nDaysInYear-nDaysNextYear-1) )
263 				nWeek = 1;
264 		}
265 	}
266 	else if ( nMinimumNumberOfDaysInWeek == 7 )
267 	{
268 		nWeek = ((n1WDay+nDayOfYear)/7);
269 		// Erste Woche eines Jahres entspricht der letzen Woche des
270 		// vorherigen Jahres
271 		if ( nWeek == 0 )
272 		{
273 			Date aLastDatePrevYear( 31, 12, GetYear()-1 );
274 			nWeek = aLastDatePrevYear.GetWeekOfYear( eStartDay, nMinimumNumberOfDaysInWeek );
275 		}
276 	}
277 	else // ( nMinimumNumberOfDaysInWeek == somehing_else, commentary examples for 4 )
278 	{
279 		// x_monday - thursday
280 		if ( n1WDay < nMinimumNumberOfDaysInWeek )
281 			nWeek = 1;
282 		// friday
283 		else if ( n1WDay == nMinimumNumberOfDaysInWeek )
284 			nWeek = 53;
285 		// saturday
286 		else if ( n1WDay == nMinimumNumberOfDaysInWeek + 1 )
287 		{
288 			// Jahr nach Schaltjahr
289 			if ( Date( 1, 1, GetYear()-1 ).IsLeapYear() )
290 				nWeek = 53;
291 			else
292 				nWeek = 52;
293 		}
294 		// sunday
295 		else
296 			nWeek = 52;
297 
298 		if ( (nWeek == 1) || (nDayOfYear + n1WDay > 6) )
299 		{
300 			if ( nWeek == 1 )
301 				nWeek += (nDayOfYear + n1WDay) / 7;
302 			else
303 				nWeek = (nDayOfYear + n1WDay) / 7;
304 			if ( nWeek == 53 )
305 			{
306 				// naechster x_Sonntag == erster x_Sonntag im neuen Jahr
307 				//					   == noch gleiche Woche
308 				long nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() );
309 				nTempDays +=  6 - (GetDayOfWeek()+(7-(short)eStartDay)) % 7;
310 				sal_uInt16	nDay;
311 				sal_uInt16	nMonth;
312 				sal_uInt16	nYear;
313 				DaysToDate( nTempDays, nDay, nMonth, nYear );
314 				nWeek = Date( nDay, nMonth, nYear ).GetWeekOfYear( eStartDay, nMinimumNumberOfDaysInWeek );
315 			}
316 		}
317 	}
318 
319 	return (sal_uInt16)nWeek;
320 }
321 
322 // -----------------------------------------------------------------------
323 
324 sal_uInt16 Date::GetDaysInMonth() const
325 {
326 	return DaysInMonth( GetMonth(), GetYear() );
327 }
328 
329 // -----------------------------------------------------------------------
330 
331 sal_Bool Date::IsLeapYear() const
332 {
333 	sal_uInt16 nYear = GetYear();
334 	return ImpIsLeapYear( nYear );
335 }
336 
337 // -----------------------------------------------------------------------
338 
339 sal_Bool Date::IsValid() const
340 {
341 	sal_uInt16 nDay   = GetDay();
342 	sal_uInt16 nMonth = GetMonth();
343 	sal_uInt16 nYear  = GetYear();
344 
345 	if ( !nMonth || (nMonth > 12) )
346 		return sal_False;
347 	if ( !nDay || (nDay > DaysInMonth( nMonth, nYear )) )
348 		return sal_False;
349 	else if ( nYear <= 1582 )
350 	{
351 		if ( nYear < 1582 )
352 			return sal_False;
353 		else if ( nMonth < 10 )
354 			return sal_False;
355 		else if ( (nMonth == 10) && (nDay < 15) )
356 			return sal_False;
357 	}
358 
359 	return sal_True;
360 }
361 
362 // -----------------------------------------------------------------------
363 
364 Date& Date::operator +=( long nDays )
365 {
366 	sal_uInt16	nDay;
367 	sal_uInt16	nMonth;
368 	sal_uInt16	nYear;
369 	long	nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() );
370 
371 	nTempDays += nDays;
372 	if ( nTempDays > MAX_DAYS )
373 		nDate = 31 + (12*100) + (((sal_uIntPtr)9999)*10000);
374 	else if ( nTempDays <= 0 )
375 		nDate = 1 + 100;
376 	else
377 	{
378 		DaysToDate( nTempDays, nDay, nMonth, nYear );
379 		nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
380 	}
381 
382 	return *this;
383 }
384 
385 // -----------------------------------------------------------------------
386 
387 Date& Date::operator -=( long nDays )
388 {
389 	sal_uInt16	nDay;
390 	sal_uInt16	nMonth;
391 	sal_uInt16	nYear;
392 	long	nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() );
393 
394 	nTempDays -= nDays;
395 	if ( nTempDays > MAX_DAYS )
396 		nDate = 31 + (12*100) + (((sal_uIntPtr)9999)*10000);
397 	else if ( nTempDays <= 0 )
398 		nDate = 1 + 100;
399 	else
400 	{
401 		DaysToDate( nTempDays, nDay, nMonth, nYear );
402 		nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
403 	}
404 
405 	return *this;
406 }
407 
408 // -----------------------------------------------------------------------
409 
410 Date& Date::operator ++()
411 {
412 	sal_uInt16	nDay;
413 	sal_uInt16	nMonth;
414 	sal_uInt16	nYear;
415 	long	nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() );
416 
417 	if ( nTempDays < MAX_DAYS )
418 	{
419 		nTempDays++;
420 		DaysToDate( nTempDays, nDay, nMonth, nYear );
421 		nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
422 	}
423 
424 	return *this;
425 }
426 
427 // -----------------------------------------------------------------------
428 
429 Date& Date::operator --()
430 {
431 	sal_uInt16	nDay;
432 	sal_uInt16	nMonth;
433 	sal_uInt16	nYear;
434 	long	nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() );
435 
436 	if ( nTempDays > 1 )
437 	{
438 		nTempDays--;
439 		DaysToDate( nTempDays, nDay, nMonth, nYear );
440 		nDate = ((sal_uIntPtr)nDay) + (((sal_uIntPtr)nMonth)*100) + (((sal_uIntPtr)nYear)*10000);
441 	}
442 	return *this;
443 }
444 
445 #ifndef MPW33
446 
447 // -----------------------------------------------------------------------
448 
449 Date Date::operator ++( int )
450 {
451 	Date aOldDate = *this;
452 	Date::operator++();
453 	return aOldDate;
454 }
455 
456 // -----------------------------------------------------------------------
457 
458 Date Date::operator --( int )
459 {
460 	Date aOldDate = *this;
461 	Date::operator--();
462 	return aOldDate;
463 }
464 
465 #endif
466 
467 // -----------------------------------------------------------------------
468 
469 Date operator +( const Date& rDate, long nDays )
470 {
471 	Date aDate( rDate );
472 	aDate += nDays;
473 	return aDate;
474 }
475 
476 // -----------------------------------------------------------------------
477 
478 Date operator -( const Date& rDate, long nDays )
479 {
480 	Date aDate( rDate );
481 	aDate -= nDays;
482 	return aDate;
483 }
484 
485 // -----------------------------------------------------------------------
486 
487 long operator -( const Date& rDate1, const Date& rDate2 )
488 {
489     sal_uIntPtr  nTempDays1 = Date::DateToDays( rDate1.GetDay(), rDate1.GetMonth(),
490 									rDate1.GetYear() );
491     sal_uIntPtr  nTempDays2 = Date::DateToDays( rDate2.GetDay(), rDate2.GetMonth(),
492 									rDate2.GetYear() );
493 	return nTempDays1 - nTempDays2;
494 }
495