1999-08-16 21:50:52 +00:00
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2005-03-08 22:49:59 +00:00
* Copyright ( C ) 1997 - 2005 , International Business Machines Corporation and *
1999-11-22 20:25:35 +00:00
* others . All Rights Reserved . *
1999-08-16 21:50:52 +00:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* File CALENDAR . CPP
*
* Modification History :
*
* Date Name Description
* 02 / 03 / 97 clhuang Creation .
* 04 / 22 / 97 aliu Cleaned up , fixed memory leak , made
* setWeekCountData ( ) more robust .
* Moved platform code to TPlatformUtilities .
* 05 / 01 / 97 aliu Made equals ( ) , before ( ) , after ( ) arguments const .
* 05 / 20 / 97 aliu Changed logic of when to compute fields and time
* to fix bugs .
* 08 / 12 / 97 aliu Added equivalentTo . Misc other fixes .
* 07 / 28 / 98 stephen Sync up with JDK 1.2
* 09 / 02 / 98 stephen Sync with JDK 1.2 8 / 31 build ( getActualMin / Max )
* 03 / 17 / 99 stephen Changed adoptTimeZone ( ) - now fAreFieldsSet is
* set to FALSE to force update of time .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
2002-09-20 01:54:48 +00:00
# include "unicode/utypes.h"
# if !UCONFIG_NO_FORMATTING
1999-12-28 23:57:50 +00:00
# include "unicode/gregocal.h"
2003-10-26 10:24:58 +00:00
# include "gregoimp.h"
2003-05-16 22:05:41 +00:00
# include "buddhcal.h"
2003-05-20 17:02:52 +00:00
# include "japancal.h"
2003-11-19 05:38:55 +00:00
# include "islamcal.h"
2003-12-09 20:44:56 +00:00
# include "hebrwcal.h"
# include "chnsecal.h"
2000-04-07 23:10:09 +00:00
# include "unicode/calendar.h"
2002-07-12 21:42:24 +00:00
# include "cpputils.h"
2004-12-21 06:55:20 +00:00
# include "servloc.h"
2003-05-20 17:02:52 +00:00
# include "ucln_in.h"
2003-05-25 07:12:19 +00:00
# include "cstring.h"
2004-01-20 23:06:38 +00:00
# include "locbased.h"
2004-08-31 17:49:04 +00:00
# include "uresimp.h"
1999-08-16 21:50:52 +00:00
2004-09-12 23:07:29 +00:00
# if !UCONFIG_NO_SERVICE
static ICULocaleService * gService = NULL ;
# endif
// INTERNAL - for cleanup
U_CDECL_BEGIN
static UBool calendar_cleanup ( void ) {
# if !UCONFIG_NO_SERVICE
if ( gService ) {
delete gService ;
gService = NULL ;
}
# endif
return TRUE ;
}
U_CDECL_END
2001-10-08 23:26:58 +00:00
2003-05-15 22:31:16 +00:00
// ------------------------------------------
//
// Registration
//
//-------------------------------------------
//#define U_DEBUG_CALSVC 1
2003-05-16 22:05:41 +00:00
//
2003-05-15 22:31:16 +00:00
2003-10-26 10:24:58 +00:00
# if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
2003-05-15 22:31:16 +00:00
# include <stdio.h>
2003-10-26 10:24:58 +00:00
/**
* convert a UCalendarDateFields into a string - for debugging
* @ param f field enum
* @ return static string to the field name
* @ internal
*/
static const char * fldName ( UCalendarDateFields f ) {
switch ( f ) {
# define FIELD_NAME_STR(x) case x: return (#x+5)
FIELD_NAME_STR ( UCAL_ERA ) ;
FIELD_NAME_STR ( UCAL_YEAR ) ;
FIELD_NAME_STR ( UCAL_MONTH ) ;
FIELD_NAME_STR ( UCAL_WEEK_OF_YEAR ) ;
FIELD_NAME_STR ( UCAL_WEEK_OF_MONTH ) ;
FIELD_NAME_STR ( UCAL_DATE ) ;
FIELD_NAME_STR ( UCAL_DAY_OF_YEAR ) ;
FIELD_NAME_STR ( UCAL_DAY_OF_WEEK ) ;
FIELD_NAME_STR ( UCAL_DAY_OF_WEEK_IN_MONTH ) ;
FIELD_NAME_STR ( UCAL_AM_PM ) ;
FIELD_NAME_STR ( UCAL_HOUR ) ;
FIELD_NAME_STR ( UCAL_HOUR_OF_DAY ) ;
FIELD_NAME_STR ( UCAL_MINUTE ) ;
FIELD_NAME_STR ( UCAL_SECOND ) ;
FIELD_NAME_STR ( UCAL_MILLISECOND ) ;
FIELD_NAME_STR ( UCAL_ZONE_OFFSET ) ;
FIELD_NAME_STR ( UCAL_DST_OFFSET ) ;
FIELD_NAME_STR ( UCAL_YEAR_WOY ) ;
FIELD_NAME_STR ( UCAL_DOW_LOCAL ) ;
FIELD_NAME_STR ( UCAL_EXTENDED_YEAR ) ;
FIELD_NAME_STR ( UCAL_JULIAN_DAY ) ;
FIELD_NAME_STR ( UCAL_MILLISECONDS_IN_DAY ) ;
# undef FIELD_NAME_STR
default :
return " ?? " ;
}
}
2005-06-29 16:33:06 +00:00
// from CalendarTest::calToStr - but doesn't modify contents.
void ucal_dump ( const Calendar & cal ) {
cal . dump ( ) ;
}
void Calendar : : dump ( ) const {
int i ;
fprintf ( stderr , " @calendar=%s, timeset=%c, fieldset=%c, allfields=%c, virtualset=%c, t=%.2f " ,
getType ( ) , fIsTimeSet ? ' y ' : ' n ' , fAreFieldsSet ? ' y ' : ' n ' , fAreAllFieldsSet ? ' y ' : ' n ' ,
fAreFieldsVirtuallySet ? ' y ' : ' n ' ,
fTime ) ;
// can add more things here: DST, zone, etc.
fprintf ( stderr , " \n " ) ;
for ( i = 0 ; i < UCAL_FIELD_COUNT ; i + + ) {
int n ;
const char * f = fldName ( ( UCalendarDateFields ) i ) ;
fprintf ( stderr , " %25s: %-11ld " , f , fFields [ i ] ) ;
if ( fStamp [ i ] = = kUnset ) {
fprintf ( stderr , " (unset) " ) ;
} else if ( fStamp [ i ] = = kInternallySet ) {
fprintf ( stderr , " (internally set) " ) ;
//} else if(fStamp[i] == kInternalDefault) {
// fprintf(stderr, " (internal default) ");
} else {
fprintf ( stderr , " %%%d " , fStamp [ i ] ) ;
}
fprintf ( stderr , " \n " ) ;
}
}
U_CFUNC void ucal_dump ( UCalendar * cal ) {
ucal_dump ( * ( ( Calendar * ) cal ) ) ;
}
2003-05-15 22:31:16 +00:00
# endif
2004-11-25 00:25:52 +00:00
static const char * const gCalendarKeywords [ ] = {
" gregorian " ,
" japanese " ,
" buddhist " ,
" islamic-civil " ,
" islamic " ,
" hebrew " ,
" chinese " ,
NULL
} ;
2004-05-18 21:35:50 +00:00
2004-09-12 23:07:29 +00:00
U_NAMESPACE_BEGIN
2004-11-25 00:25:52 +00:00
static UBool isStandardSupportedKeyword ( const char * keyword , UErrorCode & status ) {
2004-07-18 01:37:13 +00:00
if ( U_FAILURE ( status ) ) {
return FALSE ;
}
2004-11-25 00:25:52 +00:00
for ( int32_t i = 0 ; gCalendarKeywords [ i ] ! = NULL ; i + + ) {
if ( uprv_strcmp ( gCalendarKeywords [ i ] , keyword ) = = 0 ) {
2004-07-18 01:37:13 +00:00
return TRUE ;
}
}
return FALSE ;
}
2004-11-25 00:25:52 +00:00
static void getCalendarKeyword ( const UnicodeString & id , char * targetBuffer , int32_t targetBufferSize ) {
UnicodeString calendarKeyword = UNICODE_STRING_SIMPLE ( " calendar= " ) ;
int32_t calKeyLen = calendarKeyword . length ( ) ;
int32_t keyLen = 0 ;
int32_t keywordIdx = id . indexOf ( ( UChar ) 0x003D ) ; /* '=' */
if ( id [ 0 ] = = 0x40 /*'@'*/
2005-03-09 05:48:19 +00:00
& & id . compareBetween ( 1 , keywordIdx + 1 , calendarKeyword , 0 , calKeyLen ) = = 0 )
2004-11-25 00:25:52 +00:00
{
2005-03-09 05:48:19 +00:00
keyLen = id . extract ( keywordIdx + 1 , id . length ( ) , targetBuffer , targetBufferSize , US_INV ) ;
2004-11-25 00:25:52 +00:00
}
targetBuffer [ keyLen ] = 0 ;
}
2004-07-18 01:37:13 +00:00
static Calendar * createStandardCalendar ( char * calType , const Locale & canLoc , UErrorCode & status ) {
# ifdef U_DEBUG_CALSVC
fprintf ( stderr , " BasicCalendarFactory %p: creating type for %s \n " ,
this , ( const char * ) curLoc . getName ( ) ) ;
fflush ( stderr ) ;
# endif
2004-11-25 00:25:52 +00:00
if ( ! calType | | ! * calType | | ! uprv_strcmp ( calType , " gregorian " ) ) { // Gregorian (default)
2004-07-18 01:37:13 +00:00
return new GregorianCalendar ( canLoc , status ) ;
2004-11-25 00:25:52 +00:00
} else if ( ! uprv_strcmp ( calType , " japanese " ) ) {
2004-07-18 01:37:13 +00:00
return new JapaneseCalendar ( canLoc , status ) ;
2004-11-25 00:25:52 +00:00
} else if ( ! uprv_strcmp ( calType , " buddhist " ) ) {
2004-07-18 01:37:13 +00:00
return new BuddhistCalendar ( canLoc , status ) ;
2004-11-25 00:25:52 +00:00
} else if ( ! uprv_strcmp ( calType , " islamic-civil " ) ) {
2004-07-18 01:37:13 +00:00
return new IslamicCalendar ( canLoc , status , IslamicCalendar : : CIVIL ) ;
2004-11-25 00:25:52 +00:00
} else if ( ! uprv_strcmp ( calType , " islamic " ) ) {
2004-07-18 01:37:13 +00:00
return new IslamicCalendar ( canLoc , status , IslamicCalendar : : ASTRONOMICAL ) ;
2004-11-25 00:25:52 +00:00
} else if ( ! uprv_strcmp ( calType , " hebrew " ) ) {
2004-07-18 01:37:13 +00:00
return new HebrewCalendar ( canLoc , status ) ;
2004-11-25 00:25:52 +00:00
//} else if(!uprv_strcmp(calType, "chinese")) {
2004-07-18 01:37:13 +00:00
//return new ChineseCalendar(canLoc, status);
} else {
status = U_UNSUPPORTED_ERROR ;
return NULL ;
}
}
# if !UCONFIG_NO_SERVICE
// -------------------------------------
2003-05-15 22:31:16 +00:00
/**
* a Calendar Factory which creates the " basic " calendar types , that is , those
* shipped with ICU .
*/
class BasicCalendarFactory : public LocaleKeyFactory {
public :
/**
* @ param calendarType static const string ( caller owns storage - will be aliased ) to calendar type
*/
2004-05-18 21:35:50 +00:00
BasicCalendarFactory ( )
: LocaleKeyFactory ( LocaleKeyFactory : : INVISIBLE ) { }
2003-05-15 22:31:16 +00:00
virtual ~ BasicCalendarFactory ( ) { }
protected :
2005-01-11 20:58:27 +00:00
//virtual UBool isSupportedID( const UnicodeString& id, UErrorCode& status) const {
// if(U_FAILURE(status)) {
// return FALSE;
// }
// char keyword[ULOC_FULLNAME_CAPACITY];
// getCalendarKeyword(id, keyword, (int32_t)sizeof(keyword));
// return isStandardSupportedKeyword(keyword, status);
//}
2003-05-15 22:31:16 +00:00
virtual void updateVisibleIDs ( Hashtable & result , UErrorCode & status ) const
{
if ( U_SUCCESS ( status ) ) {
2004-11-25 00:25:52 +00:00
for ( int32_t i = 0 ; gCalendarKeywords [ i ] ! = NULL ; i + + ) {
UnicodeString id ( ( UChar ) 0x40 ) ; /* '@' a variant character */
id . append ( UNICODE_STRING_SIMPLE ( " calendar= " ) ) ;
id . append ( UnicodeString ( gCalendarKeywords [ i ] , - 1 , US_INV ) ) ;
2004-05-18 21:35:50 +00:00
result . put ( id , ( void * ) this , status ) ;
}
2003-05-15 22:31:16 +00:00
}
}
2003-05-17 23:24:25 +00:00
virtual UObject * create ( const ICUServiceKey & key , const ICUService * /*service*/ , UErrorCode & status ) const {
2003-11-24 20:02:11 +00:00
# ifdef U_DEBUG_CALSVC
if ( key . getDynamicClassID ( ) ! = LocaleKey : : getStaticClassID ( ) ) {
2004-05-18 21:35:50 +00:00
fprintf ( stderr , " ::create - not a LocaleKey! \n " ) ;
2003-11-24 20:02:11 +00:00
}
# endif
2003-05-15 22:31:16 +00:00
const LocaleKey & lkey = ( LocaleKey & ) key ;
Locale curLoc ; // current locale
Locale canLoc ; // Canonical locale
lkey . currentLocale ( curLoc ) ;
lkey . canonicalLocale ( canLoc ) ;
2004-11-25 00:25:52 +00:00
char keyword [ ULOC_FULLNAME_CAPACITY ] ;
2003-05-15 22:31:16 +00:00
UnicodeString str ;
2004-11-25 00:25:52 +00:00
key . currentID ( str ) ;
getCalendarKeyword ( str , keyword , ( int32_t ) sizeof ( keyword ) ) ;
2004-05-18 21:35:50 +00:00
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC
2004-05-18 21:35:50 +00:00
fprintf ( stderr , " BasicCalendarFactory::create() - cur %s, can %s \n " , ( const char * ) curLoc . getName ( ) , ( const char * ) canLoc . getName ( ) ) ;
2003-05-15 22:31:16 +00:00
# endif
2004-11-25 00:25:52 +00:00
if ( ! isStandardSupportedKeyword ( keyword , status ) ) { // Do we handle this type?
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC
2004-05-18 21:35:50 +00:00
fprintf ( stderr , " BasicCalendarFactory - not handling %s.[%s] \n " , ( const char * ) curLoc . getName ( ) , tmp ) ;
2003-05-15 22:31:16 +00:00
# endif
return NULL ;
}
2004-11-25 00:25:52 +00:00
return createStandardCalendar ( keyword , canLoc , status ) ;
2003-05-15 22:31:16 +00:00
}
} ;
/**
* A factory which looks up the DefaultCalendar resource to determine which class of calendar to use
*/
class DefaultCalendarFactory : public ICUResourceBundleFactory {
2003-11-12 22:14:32 +00:00
public :
2003-05-15 22:31:16 +00:00
DefaultCalendarFactory ( ) : ICUResourceBundleFactory ( ) { }
2003-11-12 22:14:32 +00:00
protected :
2003-05-17 23:24:25 +00:00
virtual UObject * create ( const ICUServiceKey & key , const ICUService * /*service*/ , UErrorCode & status ) const {
2003-11-12 22:14:32 +00:00
LocaleKey & lkey = ( LocaleKey & ) key ;
Locale loc ;
lkey . currentLocale ( loc ) ;
UnicodeString myString ;
// attempt keyword lookup
char keyword [ 128 ] ;
2004-05-18 02:06:28 +00:00
if ( ! loc . getKeywordValue ( " calendar " , keyword , sizeof ( keyword ) - 1 , status ) ) {
// fetch default calendar id
2004-10-01 18:59:31 +00:00
char funcEquiv [ ULOC_FULLNAME_CAPACITY ] ;
2004-05-18 02:06:28 +00:00
ures_getFunctionalEquivalent ( funcEquiv , sizeof ( funcEquiv ) - 1 ,
NULL , " calendar " , " calendar " ,
loc . getName ( ) ,
NULL , FALSE , & status ) ;
uloc_getKeywordValue ( funcEquiv , " calendar " , keyword ,
sizeof ( keyword ) - 1 , & status ) ;
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC
2004-05-18 02:06:28 +00:00
fprintf ( stderr , " getFunctionalEquivalent calendar=%s [%s] \n " , keyword , u_errorName ( status ) ) ;
2003-05-15 22:31:16 +00:00
# endif
2004-05-18 02:06:28 +00:00
}
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC
2004-05-18 02:06:28 +00:00
else { fprintf ( stderr , " explicit calendar=%s \n " , keyword ) ; }
2003-05-15 22:31:16 +00:00
# endif
2004-05-18 02:06:28 +00:00
if ( U_FAILURE ( status ) ) {
return NULL ;
} else {
2004-08-27 17:57:36 +00:00
UnicodeString * ret = new UnicodeString ( ) ;
ret - > append ( ( UChar ) 0x40 ) ; // '@' is a variant character
ret - > append ( UNICODE_STRING ( " calendar= " , 9 ) ) ;
2004-09-16 06:22:05 +00:00
( * ret ) + = UnicodeString ( keyword , - 1 , US_INV ) ;
2004-05-18 02:06:28 +00:00
return ret ;
2003-11-12 22:14:32 +00:00
}
}
2003-05-15 22:31:16 +00:00
} ;
// -------------------------------------
class CalendarService : public ICULocaleService {
public :
CalendarService ( )
2004-10-01 18:59:31 +00:00
: ICULocaleService ( UNICODE_STRING_SIMPLE ( " Calendar " ) )
2003-05-15 22:31:16 +00:00
{
UErrorCode status = U_ZERO_ERROR ;
registerFactory ( new DefaultCalendarFactory ( ) , status ) ;
}
virtual UObject * cloneInstance ( UObject * instance ) const {
if ( instance - > getDynamicClassID ( ) = = UnicodeString : : getStaticClassID ( ) ) {
return ( ( UnicodeString * ) instance ) - > clone ( ) ;
} else {
# ifdef U_DEBUG_CALSVC_F
UErrorCode status2 = U_ZERO_ERROR ;
fprintf ( stderr , " Cloning a %s calendar with tz=%ld \n " , ( ( Calendar * ) instance ) - > getType ( ) , ( ( Calendar * ) instance ) - > get ( UCAL_ZONE_OFFSET , status2 ) ) ;
# endif
return ( ( Calendar * ) instance ) - > clone ( ) ;
}
}
2003-05-17 23:24:25 +00:00
virtual UObject * handleDefault ( const ICUServiceKey & key , UnicodeString * /*actualID*/ , UErrorCode & status ) const {
2004-11-11 23:34:58 +00:00
LocaleKey & lkey = ( LocaleKey & ) key ;
//int32_t kind = lkey.kind();
2003-05-15 22:31:16 +00:00
2004-11-11 23:34:58 +00:00
Locale loc ;
lkey . canonicalLocale ( loc ) ;
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC
Locale loc2 ;
lkey . currentLocale ( loc2 ) ;
fprintf ( stderr , " CalSvc:handleDefault for currentLoc %s, canloc %s \n " , ( const char * ) loc . getName ( ) , ( const char * ) loc2 . getName ( ) ) ;
# endif
2004-11-11 23:34:58 +00:00
Calendar * nc = new GregorianCalendar ( loc , status ) ;
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC
UErrorCode status2 = U_ZERO_ERROR ;
fprintf ( stderr , " New default calendar has tz=%d \n " , ( ( Calendar * ) nc ) - > get ( UCAL_ZONE_OFFSET , status2 ) ) ;
# endif
return nc ;
}
virtual UBool isDefault ( ) const {
return countFactories ( ) = = 1 ;
}
} ;
// -------------------------------------
2004-10-17 19:46:59 +00:00
static inline UBool
isCalendarServiceUsed ( ) {
Mutex mutex ;
return ( UBool ) ( gService ! = NULL ) ;
}
// -------------------------------------
2003-05-15 22:31:16 +00:00
static ICULocaleService *
2004-09-12 23:07:29 +00:00
getCalendarService ( UErrorCode & status )
2003-05-15 22:31:16 +00:00
{
2003-05-20 17:02:52 +00:00
UBool needInit ;
{
2003-06-06 22:01:28 +00:00
Mutex mutex ;
2003-05-20 17:02:52 +00:00
needInit = ( UBool ) ( gService = = NULL ) ;
}
if ( needInit ) {
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC
fprintf ( stderr , " Spinning up Calendar Service \n " ) ;
# endif
ICULocaleService * newservice = new CalendarService ( ) ;
# ifdef U_DEBUG_CALSVC
fprintf ( stderr , " Registering classes.. \n " ) ;
# endif
// Register all basic instances.
2004-05-18 21:35:50 +00:00
newservice - > registerFactory ( new BasicCalendarFactory ( ) , status ) ;
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC
fprintf ( stderr , " Done.. \n " ) ;
# endif
if ( U_FAILURE ( status ) ) {
# ifdef U_DEBUG_CALSVC
fprintf ( stderr , " err (%s) registering classes, deleting service..... \n " , u_errorName ( status ) ) ;
# endif
delete newservice ;
newservice = NULL ;
}
if ( newservice ) {
2003-06-06 22:01:28 +00:00
Mutex mutex ;
2003-05-15 22:31:16 +00:00
if ( gService = = NULL ) {
gService = newservice ;
newservice = NULL ;
}
}
if ( newservice ) {
delete newservice ;
2003-06-06 22:01:28 +00:00
} else {
// we won the contention - we can register the cleanup.
2004-09-12 23:07:29 +00:00
ucln_i18n_registerCleanup ( UCLN_I18N_CALENDAR , calendar_cleanup ) ;
2003-05-15 22:31:16 +00:00
}
}
return gService ;
}
2003-11-19 05:38:55 +00:00
URegistryKey Calendar : : registerFactory ( ICUServiceFactory * toAdopt , UErrorCode & status )
{
2004-09-12 23:07:29 +00:00
return getCalendarService ( status ) - > registerFactory ( toAdopt , status ) ;
2003-11-19 05:38:55 +00:00
}
UBool Calendar : : unregister ( URegistryKey key , UErrorCode & status ) {
2004-09-12 23:07:29 +00:00
return getCalendarService ( status ) - > unregister ( key , status ) ;
2003-11-19 05:38:55 +00:00
}
2004-07-18 01:37:13 +00:00
# endif /* UCONFIG_NO_SERVICE */
2003-11-19 05:38:55 +00:00
2003-05-15 22:31:16 +00:00
// -------------------------------------
2003-10-26 10:24:58 +00:00
static const int32_t kCalendarLimits [ UCAL_FIELD_COUNT ] [ 4 ] = {
// Minimum Greatest min Least max Greatest max
2003-10-28 01:52:29 +00:00
{ /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 } , // ERA
{ /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 } , // YEAR
{ /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 } , // MONTH
{ /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 } , // WEEK_OF_YEAR
{ /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 } , // WEEK_OF_MONTH
{ /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 } , // DAY_OF_MONTH
{ /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 } , // DAY_OF_YEAR
2003-10-26 10:24:58 +00:00
{ 1 , 1 , 7 , 7 } , // DAY_OF_WEEK
2003-10-28 01:52:29 +00:00
{ /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 } , // DAY_OF_WEEK_IN_MONTH
2003-10-26 10:24:58 +00:00
{ 0 , 0 , 1 , 1 } , // AM_PM
{ 0 , 0 , 11 , 11 } , // HOUR
{ 0 , 0 , 23 , 23 } , // HOUR_OF_DAY
{ 0 , 0 , 59 , 59 } , // MINUTE
{ 0 , 0 , 59 , 59 } , // SECOND
{ 0 , 0 , 999 , 999 } , // MILLISECOND
2004-05-25 19:27:38 +00:00
{ - 12 * kOneHour , - 12 * kOneHour , 12 * kOneHour , 15 * kOneHour } , // ZONE_OFFSET
2003-10-26 10:24:58 +00:00
{ 0 , 0 , 1 * kOneHour , 1 * kOneHour } , // DST_OFFSET
2003-10-28 01:52:29 +00:00
{ /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 } , // YEAR_WOY
2003-10-26 10:24:58 +00:00
{ 1 , 1 , 7 , 7 } , // DOW_LOCAL
2003-10-28 01:52:29 +00:00
{ /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 , /*N/A*/ - 1 } , // EXTENDED_YEAR
2003-10-26 10:24:58 +00:00
{ - 0x7F000000 , - 0x7F000000 , 0x7F000000 , 0x7F000000 } , // JULIAN_DAY
2003-10-28 01:52:29 +00:00
{ 0 , 0 , 24 * kOneHour - 1 , 24 * kOneHour - 1 } // MILLISECONDS_IN_DAY
2003-10-26 10:24:58 +00:00
} ;
2003-05-15 22:31:16 +00:00
1999-08-16 21:50:52 +00:00
// Resource bundle tags read by this class
2005-03-09 05:48:19 +00:00
static const char gDateTimeElements [ ] = " DateTimeElements " ;
1999-08-16 21:50:52 +00:00
// Data flow in Calendar
// ---------------------
// The current time is represented in two ways by Calendar: as UTC
// milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local
// fields such as MONTH, HOUR, AM_PM, etc. It is possible to compute the
// millis from the fields, and vice versa. The data needed to do this
// conversion is encapsulated by a TimeZone object owned by the Calendar.
// The data provided by the TimeZone object may also be overridden if the
// user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class
// keeps track of what information was most recently set by the caller, and
// uses that to compute any other information as needed.
// If the user sets the fields using set(), the data flow is as follows.
// This is implemented by the Calendar subclass's computeTime() method.
// During this process, certain fields may be ignored. The disambiguation
// algorithm for resolving which fields to pay attention to is described
// above.
// local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
// |
// | Using Calendar-specific algorithm
// V
// local standard millis
// |
// | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET
// V
// UTC millis (in time data member)
// If the user sets the UTC millis using setTime(), the data flow is as
// follows. This is implemented by the Calendar subclass's computeFields()
// method.
// UTC millis (in time data member)
// |
// | Using TimeZone getOffset()
// V
// local standard millis
// |
// | Using Calendar-specific algorithm
// V
// local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
// In general, a round trip from fields, through local and UTC millis, and
// back out to fields is made when necessary. This is implemented by the
// complete() method. Resolving a partial set of fields into a UTC millis
// value allows all remaining fields to be generated from that value. If
// the Calendar is lenient, the fields are also renormalized to standard
// ranges when they are regenerated.
// -------------------------------------
Calendar : : Calendar ( UErrorCode & success )
2002-07-02 23:58:34 +00:00
: UObject ( ) ,
fIsTimeSet ( FALSE ) ,
1999-08-16 21:50:52 +00:00
fAreFieldsSet ( FALSE ) ,
fAreAllFieldsSet ( FALSE ) ,
2004-03-04 23:24:43 +00:00
fAreFieldsVirtuallySet ( FALSE ) ,
2003-11-19 05:38:55 +00:00
fNextStamp ( ( int32_t ) kMinimumUserStamp ) ,
2000-04-07 23:10:09 +00:00
fTime ( 0 ) ,
1999-08-16 21:50:52 +00:00
fLenient ( TRUE ) ,
2000-04-07 23:10:09 +00:00
fZone ( 0 )
1999-08-16 21:50:52 +00:00
{
clear ( ) ;
fZone = TimeZone : : createDefault ( ) ;
2004-05-18 22:19:54 +00:00
setWeekCountData ( Locale : : getDefault ( ) , NULL , success ) ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
Calendar : : Calendar ( TimeZone * zone , const Locale & aLocale , UErrorCode & success )
2002-07-02 23:58:34 +00:00
: UObject ( ) ,
fIsTimeSet ( FALSE ) ,
1999-08-16 21:50:52 +00:00
fAreFieldsSet ( FALSE ) ,
fAreAllFieldsSet ( FALSE ) ,
2004-03-04 23:24:43 +00:00
fAreFieldsVirtuallySet ( FALSE ) ,
2003-11-19 05:38:55 +00:00
fNextStamp ( ( int32_t ) kMinimumUserStamp ) ,
2000-04-07 23:10:09 +00:00
fTime ( 0 ) ,
1999-08-16 21:50:52 +00:00
fLenient ( TRUE ) ,
2000-04-07 23:10:09 +00:00
fZone ( 0 )
1999-08-16 21:50:52 +00:00
{
if ( zone = = 0 ) {
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d: ILLEGAL ARG because timezone cannot be 0 \n " ,
__FILE__ , __LINE__ ) ;
# endif
success = U_ILLEGAL_ARGUMENT_ERROR ;
return ;
1999-08-16 21:50:52 +00:00
}
clear ( ) ;
fZone = zone ;
2004-05-18 22:19:54 +00:00
setWeekCountData ( aLocale , NULL , success ) ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
Calendar : : Calendar ( const TimeZone & zone , const Locale & aLocale , UErrorCode & success )
2002-07-02 23:58:34 +00:00
: UObject ( ) ,
fIsTimeSet ( FALSE ) ,
1999-08-16 21:50:52 +00:00
fAreFieldsSet ( FALSE ) ,
fAreAllFieldsSet ( FALSE ) ,
2004-03-04 23:24:43 +00:00
fAreFieldsVirtuallySet ( FALSE ) ,
2003-11-19 05:38:55 +00:00
fNextStamp ( ( int32_t ) kMinimumUserStamp ) ,
2000-04-07 23:10:09 +00:00
fTime ( 0 ) ,
1999-08-16 21:50:52 +00:00
fLenient ( TRUE ) ,
2000-04-07 23:10:09 +00:00
fZone ( 0 )
1999-08-16 21:50:52 +00:00
{
clear ( ) ;
fZone = zone . clone ( ) ;
2004-05-18 22:19:54 +00:00
setWeekCountData ( aLocale , NULL , success ) ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
Calendar : : ~ Calendar ( )
{
delete fZone ;
}
// -------------------------------------
Calendar : : Calendar ( const Calendar & source )
2002-07-02 23:58:34 +00:00
: UObject ( source )
1999-08-16 21:50:52 +00:00
{
fZone = 0 ;
* this = source ;
}
// -------------------------------------
Calendar &
Calendar : : operator = ( const Calendar & right )
{
2003-10-28 00:04:00 +00:00
if ( this ! = & right ) {
2003-02-21 08:32:22 +00:00
uprv_arrayCopy ( right . fFields , fFields , UCAL_FIELD_COUNT ) ;
uprv_arrayCopy ( right . fIsSet , fIsSet , UCAL_FIELD_COUNT ) ;
uprv_arrayCopy ( right . fStamp , fStamp , UCAL_FIELD_COUNT ) ;
2004-03-04 23:24:43 +00:00
fTime = right . fTime ;
fIsTimeSet = right . fIsTimeSet ;
1999-08-16 21:50:52 +00:00
fAreAllFieldsSet = right . fAreAllFieldsSet ;
2004-03-04 23:24:43 +00:00
fAreFieldsSet = right . fAreFieldsSet ;
fAreFieldsVirtuallySet = right . fAreFieldsVirtuallySet ;
1999-08-16 21:50:52 +00:00
fLenient = right . fLenient ;
delete fZone ;
fZone = right . fZone - > clone ( ) ;
2004-03-04 23:24:43 +00:00
fFirstDayOfWeek = right . fFirstDayOfWeek ;
fMinimalDaysInFirstWeek = right . fMinimalDaysInFirstWeek ;
fNextStamp = right . fNextStamp ;
1999-08-16 21:50:52 +00:00
}
return * this ;
}
// -------------------------------------
2004-08-24 17:38:33 +00:00
Calendar * U_EXPORT2
1999-08-16 21:50:52 +00:00
Calendar : : createInstance ( UErrorCode & success )
{
2003-05-15 22:31:16 +00:00
return createInstance ( TimeZone : : createDefault ( ) , Locale : : getDefault ( ) , success ) ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
2004-08-24 17:38:33 +00:00
Calendar * U_EXPORT2
1999-08-16 21:50:52 +00:00
Calendar : : createInstance ( const TimeZone & zone , UErrorCode & success )
{
2003-05-15 22:31:16 +00:00
return createInstance ( zone , Locale : : getDefault ( ) , success ) ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
2004-08-24 17:38:33 +00:00
Calendar * U_EXPORT2
1999-08-16 21:50:52 +00:00
Calendar : : createInstance ( const Locale & aLocale , UErrorCode & success )
{
2003-05-15 22:31:16 +00:00
return createInstance ( TimeZone : : createDefault ( ) , aLocale , success ) ;
1999-08-16 21:50:52 +00:00
}
2003-05-15 22:31:16 +00:00
// ------------------------------------- Adopting
// Note: this is the bottleneck that actually calls the service routines.
1999-08-16 21:50:52 +00:00
2004-08-24 17:38:33 +00:00
Calendar * U_EXPORT2
1999-08-16 21:50:52 +00:00
Calendar : : createInstance ( TimeZone * zone , const Locale & aLocale , UErrorCode & success )
{
2004-10-01 18:59:31 +00:00
Locale actualLoc ;
UObject * u ;
2004-10-17 19:46:59 +00:00
# if !UCONFIG_NO_SERVICE
if ( isCalendarServiceUsed ( ) ) {
u = getCalendarService ( success ) - > get ( aLocale , LocaleKey : : KIND_ANY , & actualLoc , success ) ;
}
else
# endif
2004-10-01 18:59:31 +00:00
{
2004-11-25 00:25:52 +00:00
char calLocaleType [ ULOC_FULLNAME_CAPACITY ] ;
calLocaleType [ 0 ] = 0 ; // NULL terminate
int32_t keywordCapacity = aLocale . getKeywordValue ( " calendar " , calLocaleType , sizeof ( calLocaleType ) - 1 , success ) ;
2004-10-01 18:59:31 +00:00
if ( keywordCapacity = = 0 ) {
char funcEquiv [ ULOC_FULLNAME_CAPACITY ] ;
// fetch default calendar id
ures_getFunctionalEquivalent ( funcEquiv , sizeof ( funcEquiv ) - 1 ,
NULL , " calendar " , " calendar " ,
aLocale . getName ( ) ,
NULL , FALSE , & success ) ;
2004-11-25 00:25:52 +00:00
keywordCapacity = uloc_getKeywordValue ( funcEquiv , " calendar " , calLocaleType ,
sizeof ( calLocaleType ) - 1 , & success ) ;
2004-10-01 18:59:31 +00:00
if ( keywordCapacity = = 0 | | U_FAILURE ( success ) ) {
// no calendar type. Default to nothing.
calLocaleType [ 0 ] = 0 ;
}
# ifdef U_DEBUG_CALSVC
fprintf ( stderr , " getFunctionalEquivalent calendar=%s [%s] \n " , keyword , u_errorName ( status ) ) ;
# endif
2004-09-12 23:07:29 +00:00
}
2004-10-01 18:59:31 +00:00
# ifdef U_DEBUG_CALSVC
else { fprintf ( stderr , " explicit calendar=%s \n " , keyword ) ; }
# endif
u = createStandardCalendar ( calLocaleType , aLocale , success ) ;
}
Calendar * c = NULL ;
2003-05-25 07:12:19 +00:00
2003-05-15 22:31:16 +00:00
if ( U_FAILURE ( success ) | | ! u ) {
delete zone ;
2003-05-25 07:12:19 +00:00
if ( U_SUCCESS ( success ) ) { // Propagate some kind of err
success = U_INTERNAL_PROGRAM_ERROR ;
}
2003-05-15 22:31:16 +00:00
return NULL ;
}
2004-01-22 00:15:12 +00:00
2004-07-18 01:37:13 +00:00
# if !UCONFIG_NO_SERVICE
2003-05-15 22:31:16 +00:00
if ( u - > getDynamicClassID ( ) = = UnicodeString : : getStaticClassID ( ) ) {
// It's a unicode string telling us what type of calendar to load ("gregorian", etc)
const UnicodeString & str = * ( UnicodeString * ) u ;
2003-05-25 07:12:19 +00:00
// Create a Locale over this string
2005-01-15 04:46:14 +00:00
Locale l ( " " ) ;
LocaleUtility : : initLocaleFromName ( str , l ) ;
2004-05-18 21:35:50 +00:00
# ifdef U_DEBUG_CALSVC
2005-01-15 04:46:14 +00:00
fprintf ( stderr , " Calendar::createInstance(%s), looking up [%s] \n " , aLocale . getName ( ) , l . getName ( ) ) ;
2004-05-18 21:35:50 +00:00
# endif
2003-05-15 22:31:16 +00:00
2004-05-18 21:35:50 +00:00
Locale actualLoc2 ;
2003-05-15 22:31:16 +00:00
delete u ;
u = NULL ;
2004-05-18 21:35:50 +00:00
2004-01-22 00:15:12 +00:00
// Don't overwrite actualLoc, since the actual loc from this call
// may be something like "@calendar=gregorian" -- TODO investigate
// further...
2004-09-12 23:07:29 +00:00
c = ( Calendar * ) getCalendarService ( success ) - > get ( l , LocaleKey : : KIND_ANY , & actualLoc2 , success ) ;
2003-05-25 07:12:19 +00:00
2003-05-15 22:31:16 +00:00
if ( U_FAILURE ( success ) | | ! c ) {
delete zone ;
2003-05-25 07:12:19 +00:00
if ( U_SUCCESS ( success ) ) {
success = U_INTERNAL_PROGRAM_ERROR ; // Propagate some err
}
2003-05-15 22:31:16 +00:00
return NULL ;
1999-10-14 19:13:11 +00:00
}
2003-05-15 22:31:16 +00:00
if ( c - > getDynamicClassID ( ) = = UnicodeString : : getStaticClassID ( ) ) {
2003-05-25 07:12:19 +00:00
// recursed! Second lookup returned a UnicodeString.
// Perhaps DefaultCalendar{} was set to another locale.
2003-11-19 05:38:55 +00:00
# ifdef U_DEBUG_CALSVC
2004-05-18 21:35:50 +00:00
char tmp [ 200 ] ;
const UnicodeString & str = * ( UnicodeString * ) c ;
// Extract a char* out of it..
int32_t len = str . length ( ) ;
int32_t actLen = sizeof ( tmp ) - 1 ;
if ( len > actLen ) {
len = actLen ;
}
str . extract ( 0 , len , tmp ) ;
tmp [ len ] = 0 ;
fprintf ( stderr , " err - recursed, 2nd lookup was unistring %s \n " , tmp ) ;
2003-11-19 05:38:55 +00:00
# endif
2003-05-15 22:31:16 +00:00
success = U_MISSING_RESOURCE_ERROR ; // requested a calendar type which could NOT be found.
delete c ;
delete zone ;
return NULL ;
}
# ifdef U_DEBUG_CALSVC
2004-05-18 22:19:54 +00:00
fprintf ( stderr , " %p: setting week count data to locale %s, actual locale %s \n " , c , ( const char * ) aLocale . getName ( ) , ( const char * ) actualLoc . getName ( ) ) ;
2003-05-15 22:31:16 +00:00
# endif
2004-05-18 22:19:54 +00:00
c - > setWeekCountData ( aLocale , c - > getType ( ) , success ) ; // set the correct locale (this was an indirected calendar)
2004-07-18 01:37:13 +00:00
}
else
# endif /* UCONFIG_NO_SERVICE */
{
2003-05-25 07:12:19 +00:00
// a calendar was returned - we assume the factory did the right thing.
2003-05-15 22:31:16 +00:00
c = ( Calendar * ) u ;
}
2003-06-10 03:58:41 +00:00
// Now, reset calendar to default state:
2003-05-15 22:31:16 +00:00
c - > adoptTimeZone ( zone ) ; // Set the correct time zone
2003-06-10 03:58:41 +00:00
c - > setTimeInMillis ( getNow ( ) , success ) ; // let the new calendar have the current time.
2004-01-22 00:15:12 +00:00
2003-05-15 22:31:16 +00:00
return c ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
2004-08-24 17:38:33 +00:00
Calendar * U_EXPORT2
1999-08-16 21:50:52 +00:00
Calendar : : createInstance ( const TimeZone & zone , const Locale & aLocale , UErrorCode & success )
{
2003-05-25 07:12:19 +00:00
Calendar * c = createInstance ( aLocale , success ) ;
if ( U_SUCCESS ( success ) & & c ) {
c - > setTimeZone ( zone ) ;
2003-05-15 22:31:16 +00:00
}
2003-05-25 07:12:19 +00:00
return c ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
2000-05-18 22:08:39 +00:00
UBool
1999-08-16 21:50:52 +00:00
Calendar : : operator = = ( const Calendar & that ) const
{
1999-10-07 00:07:53 +00:00
UErrorCode status = U_ZERO_ERROR ;
2002-08-21 18:33:38 +00:00
return isEquivalentTo ( that ) & &
1999-08-16 21:50:52 +00:00
getTimeInMillis ( status ) = = that . getTimeInMillis ( status ) & &
2002-08-21 18:33:38 +00:00
U_SUCCESS ( status ) ;
}
UBool
Calendar : : isEquivalentTo ( const Calendar & other ) const
{
return getDynamicClassID ( ) = = other . getDynamicClassID ( ) & &
fLenient = = other . fLenient & &
fFirstDayOfWeek = = other . fFirstDayOfWeek & &
fMinimalDaysInFirstWeek = = other . fMinimalDaysInFirstWeek & &
* fZone = = * other . fZone ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
2000-05-18 22:08:39 +00:00
UBool
1999-08-16 21:50:52 +00:00
Calendar : : equals ( const Calendar & when , UErrorCode & status ) const
{
return ( this = = & when | |
getTime ( status ) = = when . getTime ( status ) ) ;
}
// -------------------------------------
2000-05-18 22:08:39 +00:00
UBool
1999-08-16 21:50:52 +00:00
Calendar : : before ( const Calendar & when , UErrorCode & status ) const
{
return ( this ! = & when & &
getTimeInMillis ( status ) < when . getTimeInMillis ( status ) ) ;
}
// -------------------------------------
2000-05-18 22:08:39 +00:00
UBool
1999-08-16 21:50:52 +00:00
Calendar : : after ( const Calendar & when , UErrorCode & status ) const
{
return ( this ! = & when & &
getTimeInMillis ( status ) > when . getTimeInMillis ( status ) ) ;
}
// -------------------------------------
2004-08-24 17:38:33 +00:00
const Locale * U_EXPORT2
1999-08-16 21:50:52 +00:00
Calendar : : getAvailableLocales ( int32_t & count )
{
return Locale : : getAvailableLocales ( count ) ;
}
// -------------------------------------
2004-08-24 17:38:33 +00:00
UDate U_EXPORT2
1999-08-16 21:50:52 +00:00
Calendar : : getNow ( )
{
2004-08-13 01:20:26 +00:00
return uprv_getUTCtime ( ) ; // return as milliseconds
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
/**
* Gets this Calendar ' s current time as a long .
* @ return the current time as UTC milliseconds from the epoch .
*/
double
Calendar : : getTimeInMillis ( UErrorCode & status ) const
{
1999-10-18 22:48:32 +00:00
if ( U_FAILURE ( status ) )
1999-08-16 21:50:52 +00:00
return 0.0 ;
if ( ! fIsTimeSet )
( ( Calendar * ) this ) - > updateTime ( status ) ;
2002-07-02 15:10:30 +00:00
/* Test for buffer overflows */
if ( U_FAILURE ( status ) ) {
return 0.0 ;
}
1999-08-16 21:50:52 +00:00
return fTime ;
}
// -------------------------------------
/**
* Sets this Calendar ' s current time from the given long value .
* @ param date the new time in UTC milliseconds from the epoch .
*/
void
Calendar : : setTimeInMillis ( double millis , UErrorCode & status ) {
1999-10-18 22:48:32 +00:00
if ( U_FAILURE ( status ) )
1999-08-16 21:50:52 +00:00
return ;
2004-05-24 20:32:59 +00:00
if ( millis > MAX_MILLIS ) {
millis = MAX_MILLIS ;
} else if ( millis < MIN_MILLIS ) {
millis = MIN_MILLIS ;
}
1999-08-16 21:50:52 +00:00
fTime = millis ;
2004-03-04 23:24:43 +00:00
fAreFieldsSet = fAreAllFieldsSet = FALSE ;
fIsTimeSet = fAreFieldsVirtuallySet = TRUE ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
int32_t
2003-02-21 08:32:22 +00:00
Calendar : : get ( UCalendarDateFields field , UErrorCode & status ) const
1999-08-16 21:50:52 +00:00
{
// field values are only computed when actually requested; for more on when computation
// of various things happens, see the "data flow in Calendar" description at the top
// of this file
1999-10-18 22:48:32 +00:00
if ( U_SUCCESS ( status ) ) ( ( Calendar * ) this ) - > complete ( status ) ; // Cast away const
return U_SUCCESS ( status ) ? fFields [ field ] : 0 ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
void
2003-02-21 08:32:22 +00:00
Calendar : : set ( UCalendarDateFields field , int32_t value )
1999-08-16 21:50:52 +00:00
{
2004-03-04 23:24:43 +00:00
if ( fAreFieldsVirtuallySet ) {
UErrorCode ec = U_ZERO_ERROR ;
computeFields ( ec ) ;
}
1999-08-16 21:50:52 +00:00
fFields [ field ] = value ;
fStamp [ field ] = fNextStamp + + ;
fIsSet [ field ] = TRUE ; // Remove later
2004-03-04 23:24:43 +00:00
fIsTimeSet = fAreFieldsSet = fAreFieldsVirtuallySet = FALSE ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
void
Calendar : : set ( int32_t year , int32_t month , int32_t date )
{
2003-02-21 08:32:22 +00:00
set ( UCAL_YEAR , year ) ;
set ( UCAL_MONTH , month ) ;
set ( UCAL_DATE , date ) ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
void
Calendar : : set ( int32_t year , int32_t month , int32_t date , int32_t hour , int32_t minute )
{
2003-02-21 08:32:22 +00:00
set ( UCAL_YEAR , year ) ;
set ( UCAL_MONTH , month ) ;
set ( UCAL_DATE , date ) ;
set ( UCAL_HOUR_OF_DAY , hour ) ;
set ( UCAL_MINUTE , minute ) ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
void
Calendar : : set ( int32_t year , int32_t month , int32_t date , int32_t hour , int32_t minute , int32_t second )
{
2003-02-21 08:32:22 +00:00
set ( UCAL_YEAR , year ) ;
set ( UCAL_MONTH , month ) ;
set ( UCAL_DATE , date ) ;
set ( UCAL_HOUR_OF_DAY , hour ) ;
set ( UCAL_MINUTE , minute ) ;
set ( UCAL_SECOND , second ) ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
void
Calendar : : clear ( )
{
2003-02-21 08:32:22 +00:00
for ( int32_t i = 0 ; i < UCAL_FIELD_COUNT ; + + i ) {
1999-08-16 21:50:52 +00:00
fFields [ i ] = 0 ; // Must do this; other code depends on it
fStamp [ i ] = kUnset ;
2004-03-04 23:24:43 +00:00
fIsSet [ i ] = FALSE ; // Remove later
1999-08-16 21:50:52 +00:00
}
2004-03-04 23:24:43 +00:00
fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = FALSE ;
2005-06-29 16:33:06 +00:00
// fTime is not 'cleared' - may be used if no fields are set.
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
void
2003-02-21 08:32:22 +00:00
Calendar : : clear ( UCalendarDateFields field )
1999-08-16 21:50:52 +00:00
{
2004-03-04 23:24:43 +00:00
if ( fAreFieldsVirtuallySet ) {
UErrorCode ec = U_ZERO_ERROR ;
computeFields ( ec ) ;
}
1999-08-16 21:50:52 +00:00
fFields [ field ] = 0 ;
fStamp [ field ] = kUnset ;
fIsSet [ field ] = FALSE ; // Remove later
2004-03-04 23:24:43 +00:00
fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = FALSE ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
2000-05-18 22:08:39 +00:00
UBool
2003-02-21 08:32:22 +00:00
Calendar : : isSet ( UCalendarDateFields field ) const
1999-08-16 21:50:52 +00:00
{
2004-03-04 23:24:43 +00:00
return fAreFieldsVirtuallySet | | ( fStamp [ field ] ! = kUnset ) ;
1999-08-16 21:50:52 +00:00
}
2003-10-26 10:24:58 +00:00
int32_t Calendar : : newestStamp ( UCalendarDateFields first , UCalendarDateFields last , int32_t bestStampSoFar ) const
{
int32_t bestStamp = bestStampSoFar ;
for ( int32_t i = ( int32_t ) first ; i < = ( int32_t ) last ; + + i ) {
if ( fStamp [ i ] > bestStamp ) {
bestStamp = fStamp [ i ] ;
}
}
return bestStamp ;
}
1999-08-16 21:50:52 +00:00
// -------------------------------------
void
Calendar : : complete ( UErrorCode & status )
{
2002-07-02 15:10:30 +00:00
if ( ! fIsTimeSet ) {
1999-08-16 21:50:52 +00:00
updateTime ( status ) ;
2002-07-02 15:10:30 +00:00
/* Test for buffer overflows */
if ( U_FAILURE ( status ) ) {
return ;
}
}
1999-08-16 21:50:52 +00:00
if ( ! fAreFieldsSet ) {
computeFields ( status ) ; // fills in unset fields
2002-07-02 15:10:30 +00:00
/* Test for buffer overflows */
if ( U_FAILURE ( status ) ) {
return ;
}
1999-08-16 21:50:52 +00:00
fAreFieldsSet = TRUE ;
fAreAllFieldsSet = TRUE ;
}
}
2003-10-26 10:24:58 +00:00
//-------------------------------------------------------------------------
// Protected utility methods for use by subclasses. These are very handy
// for implementing add, roll, and computeFields.
//-------------------------------------------------------------------------
/**
* Adjust the specified field so that it is within
* the allowable range for the date to which this calendar is set .
* For example , in a Gregorian calendar pinning the { @ link # DAY_OF_MONTH DAY_OF_MONTH }
* field for a calendar set to April 31 would cause it to be set
* to April 30.
* < p >
* < b > Subclassing : < / b >
* < br >
* This utility method is intended for use by subclasses that need to implement
* their own overrides of { @ link # roll roll } and { @ link # add add } .
* < p >
* < b > Note : < / b >
* < code > pinField < / code > is implemented in terms of
* { @ link # getActualMinimum getActualMinimum }
* and { @ link # getActualMaximum getActualMaximum } . If either of those methods uses
* a slow , iterative algorithm for a particular field , it would be
* unwise to attempt to call < code > pinField < / code > for that field . If you
* really do need to do so , you should override this method to do
* something more efficient for that field .
* < p >
* @ param field The calendar field whose value should be pinned .
*
* @ see # getActualMinimum
* @ see # getActualMaximum
* @ stable ICU 2.0
*/
void Calendar : : pinField ( UCalendarDateFields field , UErrorCode & status ) {
int32_t max = getActualMaximum ( field , status ) ;
int32_t min = getActualMinimum ( field , status ) ;
if ( fFields [ field ] > max ) {
set ( field , max ) ;
} else if ( fFields [ field ] < min ) {
set ( field , min ) ;
}
}
void Calendar : : computeFields ( UErrorCode & ec )
{
if ( U_FAILURE ( ec ) ) {
return ;
}
// Compute local wall millis
double localMillis = internalGetTime ( ) ;
int32_t rawOffset , dstOffset ;
getTimeZone ( ) . getOffset ( localMillis , FALSE , rawOffset , dstOffset , ec ) ;
localMillis + = rawOffset ;
// Mark fields as set. Do this before calling handleComputeFields().
uint32_t mask = //fInternalSetMask;
( 1 < < ERA ) |
( 1 < < UCAL_YEAR ) |
( 1 < < UCAL_MONTH ) |
( 1 < < UCAL_DAY_OF_MONTH ) | // = UCAL_DATE
( 1 < < UCAL_DAY_OF_YEAR ) |
( 1 < < UCAL_EXTENDED_YEAR ) ;
for ( int32_t i = 0 ; i < UCAL_FIELD_COUNT ; + + i ) {
if ( ( mask & 1 ) = = 0 ) {
fStamp [ i ] = kInternallySet ;
fIsSet [ i ] = TRUE ; // Remove later
} else {
fStamp [ i ] = kUnset ;
2004-03-04 23:24:43 +00:00
fIsSet [ i ] = FALSE ; // Remove later
2003-10-26 10:24:58 +00:00
}
mask > > = 1 ;
}
// We used to check for and correct extreme millis values (near
// Long.MIN_VALUE or Long.MAX_VALUE) here. Such values would cause
// overflows from positive to negative (or vice versa) and had to
// be manually tweaked. We no longer need to do this because we
// have limited the range of supported dates to those that have a
// Julian day that fits into an int. This allows us to implement a
// JULIAN_DAY field and also removes some inelegant code. - Liu
// 11/6/00
2003-11-19 05:38:55 +00:00
int32_t days = ( int32_t ) Math : : floorDivide ( localMillis , ( double ) kOneDay ) ;
2003-10-26 10:24:58 +00:00
internalSet ( UCAL_JULIAN_DAY , days + kEpochStartAsJulianDay ) ;
# if defined (U_DEBUG_CAL)
//fprintf(stderr, "%s:%d- Hmm! Jules @ %d, as per %.0lf millis\n",
//__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis);
# endif
// In some cases we will have to call this method again below to
// adjust for DST pushing us into the next Julian day.
computeGregorianAndDOWFields ( fFields [ UCAL_JULIAN_DAY ] , ec ) ;
int32_t millisInDay = ( int32_t ) ( localMillis - ( days * kOneDay ) ) ;
if ( millisInDay < 0 ) millisInDay + = ( int32_t ) kOneDay ;
// Adjust our millisInDay for DST. dstOffset will be zero if DST
// is not in effect at this time of year, or if our zone does not
// use DST.
millisInDay + = dstOffset ;
// If DST has pushed us into the next day, we must call
// computeGregorianAndDOWFields() again. This happens in DST between
// 12:00 am and 1:00 am every day. The first call to
// computeGregorianAndDOWFields() will give the wrong day, since the
// Standard time is in the previous day.
if ( millisInDay > = ( int32_t ) kOneDay ) {
millisInDay - = ( int32_t ) kOneDay ; // ASSUME dstOffset < 24:00
// We don't worry about overflow of JULIAN_DAY because the
// allowable range of JULIAN_DAY has slop at the ends (that is,
// the max is less that 0x7FFFFFFF and the min is greater than
// -0x80000000).
computeGregorianAndDOWFields ( + + fFields [ UCAL_JULIAN_DAY ] , ec ) ;
}
// Call framework method to have subclass compute its fields.
// These must include, at a minimum, MONTH, DAY_OF_MONTH,
// EXTENDED_YEAR, YEAR, DAY_OF_YEAR. This method will call internalSet(),
// which will update stamp[].
handleComputeFields ( fFields [ UCAL_JULIAN_DAY ] , ec ) ;
// Compute week-related fields, based on the subclass-computed
// fields computed by handleComputeFields().
computeWeekFields ( ec ) ;
// Compute time-related fields. These are indepent of the date and
// of the subclass algorithm. They depend only on the local zone
// wall milliseconds in day.
fFields [ UCAL_MILLISECONDS_IN_DAY ] = millisInDay ;
fFields [ UCAL_MILLISECOND ] = millisInDay % 1000 ;
millisInDay / = 1000 ;
fFields [ UCAL_SECOND ] = millisInDay % 60 ;
millisInDay / = 60 ;
fFields [ UCAL_MINUTE ] = millisInDay % 60 ;
millisInDay / = 60 ;
fFields [ UCAL_HOUR_OF_DAY ] = millisInDay ;
fFields [ UCAL_AM_PM ] = millisInDay / 12 ; // Assume AM == 0
fFields [ UCAL_HOUR ] = millisInDay % 12 ;
fFields [ UCAL_ZONE_OFFSET ] = rawOffset ;
fFields [ UCAL_DST_OFFSET ] = dstOffset ;
}
2004-10-02 03:58:10 +00:00
uint8_t Calendar : : julianDayToDayOfWeek ( double julian )
{
// If julian is negative, then julian%7 will be negative, so we adjust
// accordingly. We add 1 because Julian day 0 is Monday.
int8_t dayOfWeek = ( int8_t ) uprv_fmod ( julian + 1 , 7 ) ;
uint8_t result = ( uint8_t ) ( dayOfWeek + ( ( dayOfWeek < 0 ) ? ( 7 + UCAL_SUNDAY ) : UCAL_SUNDAY ) ) ;
return result ;
}
2003-10-26 10:24:58 +00:00
/**
* Compute the Gregorian calendar year , month , and day of month from
* the given Julian day . These values are not stored in fields , but in
* member variables gregorianXxx . Also compute the DAY_OF_WEEK and
* DOW_LOCAL fields .
*/
void Calendar : : computeGregorianAndDOWFields ( int32_t julianDay , UErrorCode & ec )
{
computeGregorianFields ( julianDay , ec ) ;
// Compute day of week: JD 0 = Monday
2003-10-28 01:52:29 +00:00
int32_t dow = julianDayToDayOfWeek ( julianDay ) ;
2003-10-26 10:24:58 +00:00
internalSet ( UCAL_DAY_OF_WEEK , dow ) ;
// Calculate 1-based localized day of week
2003-10-28 01:52:29 +00:00
int32_t dowLocal = dow - getFirstDayOfWeek ( ) + 1 ;
2003-10-26 10:24:58 +00:00
if ( dowLocal < 1 ) {
dowLocal + = 7 ;
}
internalSet ( UCAL_DOW_LOCAL , dowLocal ) ;
fFields [ UCAL_DOW_LOCAL ] = dowLocal ;
}
/**
* Compute the Gregorian calendar year , month , and day of month from the
* Julian day . These values are not stored in fields , but in member
* variables gregorianXxx . They are used for time zone computations and by
* subclasses that are Gregorian derivatives . Subclasses may call this
* method to perform a Gregorian calendar millis - > fields computation .
* To perform a Gregorian calendar fields - > millis computation , call
* computeGregorianMonthStart ( ) .
* @ see # computeGregorianMonthStart
*/
void Calendar : : computeGregorianFields ( int32_t julianDay , UErrorCode & /* ec */ ) {
int32_t gregorianDayOfWeekUnused ;
Grego : : dayToFields ( julianDay - kEpochStartAsJulianDay , fGregorianYear , fGregorianMonth , fGregorianDayOfMonth , gregorianDayOfWeekUnused , fGregorianDayOfYear ) ;
}
/**
* Compute the fields WEEK_OF_YEAR , YEAR_WOY , WEEK_OF_MONTH ,
* DAY_OF_WEEK_IN_MONTH , and DOW_LOCAL from EXTENDED_YEAR , YEAR ,
* DAY_OF_WEEK , and DAY_OF_YEAR . The latter fields are computed by the
* subclass based on the calendar system .
*
* < p > The YEAR_WOY field is computed simplistically . It is equal to YEAR
* most of the time , but at the year boundary it may be adjusted to YEAR - 1
* or YEAR + 1 to reflect the overlap of a week into an adjacent year . In
* this case , a simple increment or decrement is performed on YEAR , even
* though this may yield an invalid YEAR value . For instance , if the YEAR
* is part of a calendar system with an N - year cycle field CYCLE , then
* incrementing the YEAR may involve incrementing CYCLE and setting YEAR
* back to 0 or 1. This is not handled by this code , and in fact cannot be
* simply handled without having subclasses define an entire parallel set of
* fields for fields larger than or equal to a year . This additional
* complexity is not warranted , since the intention of the YEAR_WOY field is
* to support ISO 8601 notation , so it will typically be used with a
* proleptic Gregorian calendar , which has no field larger than a year .
*/
void Calendar : : computeWeekFields ( UErrorCode & ec ) {
2003-10-28 01:52:29 +00:00
if ( U_FAILURE ( ec ) ) {
return ;
}
2003-10-26 10:24:58 +00:00
int32_t eyear = fFields [ UCAL_EXTENDED_YEAR ] ;
int32_t year = fFields [ UCAL_YEAR ] ;
int32_t dayOfWeek = fFields [ UCAL_DAY_OF_WEEK ] ;
int32_t dayOfYear = fFields [ UCAL_DAY_OF_YEAR ] ;
// WEEK_OF_YEAR start
// Compute the week of the year. For the Gregorian calendar, valid week
// numbers run from 1 to 52 or 53, depending on the year, the first day
// of the week, and the minimal days in the first week. For other
// calendars, the valid range may be different -- it depends on the year
// length. Days at the start of the year may fall into the last week of
// the previous year; days at the end of the year may fall into the
// first week of the next year. ASSUME that the year length is less than
// 7000 days.
int32_t yearOfWeekOfYear = year ;
int32_t relDow = ( dayOfWeek + 7 - getFirstDayOfWeek ( ) ) % 7 ; // 0..6
int32_t relDowJan1 = ( dayOfWeek - dayOfYear + 7001 - getFirstDayOfWeek ( ) ) % 7 ; // 0..6
int32_t woy = ( dayOfYear - 1 + relDowJan1 ) / 7 ; // 0..53
if ( ( 7 - relDowJan1 ) > = getMinimalDaysInFirstWeek ( ) ) {
+ + woy ;
}
// Adjust for weeks at the year end that overlap into the previous or
// next calendar year.
if ( woy = = 0 ) {
// We are the last week of the previous year.
// Check to see if we are in the last week; if so, we need
// to handle the case in which we are the first week of the
// next year.
2003-10-28 01:52:29 +00:00
int32_t prevDoy = dayOfYear + handleGetYearLength ( eyear - 1 ) ;
2003-10-26 10:24:58 +00:00
woy = weekNumber ( prevDoy , dayOfWeek ) ;
yearOfWeekOfYear - - ;
} else {
2003-10-28 01:52:29 +00:00
int32_t lastDoy = handleGetYearLength ( eyear ) ;
2003-10-26 10:24:58 +00:00
// Fast check: For it to be week 1 of the next year, the DOY
// must be on or after L-5, where L is yearLength(), then it
// cannot possibly be week 1 of the next year:
// L-5 L
// doy: 359 360 361 362 363 364 365 001
// dow: 1 2 3 4 5 6 7
if ( dayOfYear > = ( lastDoy - 5 ) ) {
2003-10-28 01:52:29 +00:00
int32_t lastRelDow = ( relDow + lastDoy - dayOfYear ) % 7 ;
2003-10-26 10:24:58 +00:00
if ( lastRelDow < 0 ) {
lastRelDow + = 7 ;
}
if ( ( ( 6 - lastRelDow ) > = getMinimalDaysInFirstWeek ( ) ) & &
( ( dayOfYear + 7 - relDow ) > lastDoy ) ) {
woy = 1 ;
yearOfWeekOfYear + + ;
}
}
}
fFields [ UCAL_WEEK_OF_YEAR ] = woy ;
fFields [ UCAL_YEAR_WOY ] = yearOfWeekOfYear ;
// WEEK_OF_YEAR end
2003-10-28 01:52:29 +00:00
int32_t dayOfMonth = fFields [ UCAL_DAY_OF_MONTH ] ;
2003-10-26 10:24:58 +00:00
fFields [ UCAL_WEEK_OF_MONTH ] = weekNumber ( dayOfMonth , dayOfWeek ) ;
fFields [ UCAL_DAY_OF_WEEK_IN_MONTH ] = ( dayOfMonth - 1 ) / 7 + 1 ;
# if defined (U_DEBUG_CAL)
if ( fFields [ UCAL_DAY_OF_WEEK_IN_MONTH ] = = 0 ) fprintf ( stderr , " %s:%d: DOWIM %d on %g \n " ,
__FILE__ , __LINE__ , fFields [ UCAL_DAY_OF_WEEK_IN_MONTH ] , fTime ) ;
# endif
}
int32_t Calendar : : weekNumber ( int32_t desiredDay , int32_t dayOfPeriod , int32_t dayOfWeek )
{
// Determine the day of the week of the first day of the period
// in question (either a year or a month). Zero represents the
// first day of the week on this calendar.
int32_t periodStartDayOfWeek = ( dayOfWeek - getFirstDayOfWeek ( ) - dayOfPeriod + 1 ) % 7 ;
if ( periodStartDayOfWeek < 0 ) periodStartDayOfWeek + = 7 ;
// Compute the week number. Initially, ignore the first week, which
// may be fractional (or may not be). We add periodStartDayOfWeek in
// order to fill out the first week, if it is fractional.
int32_t weekNo = ( desiredDay + periodStartDayOfWeek - 1 ) / 7 ;
// If the first week is long enough, then count it. If
// the minimal days in the first week is one, or if the period start
// is zero, we always increment weekNo.
if ( ( 7 - periodStartDayOfWeek ) > = getMinimalDaysInFirstWeek ( ) ) + + weekNo ;
return weekNo ;
}
2003-10-28 01:52:29 +00:00
void Calendar : : handleComputeFields ( int32_t /* julianDay */ , UErrorCode & /* status */ )
2003-10-26 10:24:58 +00:00
{
internalSet ( UCAL_MONTH , getGregorianMonth ( ) ) ;
internalSet ( UCAL_DAY_OF_MONTH , getGregorianDayOfMonth ( ) ) ;
internalSet ( UCAL_DAY_OF_YEAR , getGregorianDayOfYear ( ) ) ;
int32_t eyear = getGregorianYear ( ) ;
internalSet ( UCAL_EXTENDED_YEAR , eyear ) ;
2003-10-28 01:52:29 +00:00
int32_t era = GregorianCalendar : : AD ;
2003-10-26 10:24:58 +00:00
if ( eyear < 1 ) {
era = GregorianCalendar : : BC ;
eyear = 1 - eyear ;
}
internalSet ( UCAL_ERA , era ) ;
internalSet ( UCAL_YEAR , eyear ) ;
}
1999-08-16 21:50:52 +00:00
// -------------------------------------
2003-11-24 22:43:31 +00:00
void Calendar : : roll ( EDateFields field , int32_t amount , UErrorCode & status )
{
roll ( ( UCalendarDateFields ) field , amount , status ) ;
}
2003-10-26 10:24:58 +00:00
void Calendar : : roll ( UCalendarDateFields field , int32_t amount , UErrorCode & status )
{
if ( amount = = 0 ) {
return ; // Nothing to do
}
complete ( status ) ;
if ( U_FAILURE ( status ) ) {
return ;
}
switch ( field ) {
case UCAL_DAY_OF_MONTH :
case UCAL_AM_PM :
case UCAL_MINUTE :
case UCAL_SECOND :
case UCAL_MILLISECOND :
case UCAL_MILLISECONDS_IN_DAY :
case UCAL_ERA :
// These are the standard roll instructions. These work for all
// simple cases, that is, cases in which the limits are fixed, such
// as the hour, the day of the month, and the era.
{
int32_t min = getActualMinimum ( field , status ) ;
int32_t max = getActualMaximum ( field , status ) ;
int32_t gap = max - min + 1 ;
int32_t value = internalGet ( field ) + amount ;
value = ( value - min ) % gap ;
if ( value < 0 ) {
value + = gap ;
}
value + = min ;
set ( field , value ) ;
return ;
}
case UCAL_HOUR :
case UCAL_HOUR_OF_DAY :
// Rolling the hour is difficult on the ONSET and CEASE days of
// daylight savings. For example, if the change occurs at
// 2 AM, we have the following progression:
// ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst
// CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std
// To get around this problem we don't use fields; we manipulate
// the time in millis directly.
{
// Assume min == 0 in calculations below
double start = getTimeInMillis ( status ) ;
int32_t oldHour = internalGet ( field ) ;
int32_t max = getMaximum ( field ) ;
int32_t newHour = ( oldHour + amount ) % ( max + 1 ) ;
if ( newHour < 0 ) {
newHour + = max + 1 ;
}
setTimeInMillis ( start + kOneHour * ( newHour - oldHour ) , status ) ;
return ;
}
case UCAL_MONTH :
// Rolling the month involves both pinning the final value
// and adjusting the DAY_OF_MONTH if necessary. We only adjust the
// DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
// E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
{
int32_t max = getActualMaximum ( UCAL_MONTH , status ) ;
int32_t mon = ( internalGet ( UCAL_MONTH ) + amount ) % ( max + 1 ) ;
if ( mon < 0 ) {
mon + = ( max + 1 ) ;
}
set ( UCAL_MONTH , mon ) ;
2003-03-19 13:21:51 +00:00
2003-10-26 10:24:58 +00:00
// Keep the day of month in range. We don't want to spill over
// into the next month; e.g., we don't want jan31 + 1 mo -> feb31 ->
// mar3.
pinField ( UCAL_DAY_OF_MONTH , status ) ;
return ;
}
case UCAL_YEAR :
case UCAL_YEAR_WOY :
case UCAL_EXTENDED_YEAR :
// Rolling the year can involve pinning the DAY_OF_MONTH.
set ( field , internalGet ( field ) + amount ) ;
pinField ( UCAL_MONTH , status ) ;
pinField ( UCAL_DAY_OF_MONTH , status ) ;
return ;
case UCAL_WEEK_OF_MONTH :
{
// This is tricky, because during the roll we may have to shift
// to a different day of the week. For example:
// s m t w r f s
// 1 2 3 4 5
// 6 7 8 9 10 11 12
// When rolling from the 6th or 7th back one week, we go to the
// 1st (assuming that the first partial week counts). The same
// thing happens at the end of the month.
// The other tricky thing is that we have to figure out whether
// the first partial week actually counts or not, based on the
// minimal first days in the week. And we have to use the
// correct first day of the week to delineate the week
// boundaries.
// Here's our algorithm. First, we find the real boundaries of
// the month. Then we discard the first partial week if it
// doesn't count in this locale. Then we fill in the ends with
// phantom days, so that the first partial week and the last
// partial week are full weeks. We then have a nice square
// block of weeks. We do the usual rolling within this block,
// as is done elsewhere in this method. If we wind up on one of
// the phantom days that we added, we recognize this and pin to
// the first or the last day of the month. Easy, eh?
// Normalize the DAY_OF_WEEK so that 0 is the first day of the week
// in this locale. We have dow in 0..6.
int32_t dow = internalGet ( UCAL_DAY_OF_WEEK ) - getFirstDayOfWeek ( ) ;
if ( dow < 0 ) dow + = 7 ;
// Find the day of the week (normalized for locale) for the first
// of the month.
int32_t fdm = ( dow - internalGet ( UCAL_DAY_OF_MONTH ) + 1 ) % 7 ;
if ( fdm < 0 ) fdm + = 7 ;
// Get the first day of the first full week of the month,
// including phantom days, if any. Figure out if the first week
// counts or not; if it counts, then fill in phantom days. If
// not, advance to the first real full week (skip the partial week).
int32_t start ;
if ( ( 7 - fdm ) < getMinimalDaysInFirstWeek ( ) )
start = 8 - fdm ; // Skip the first partial week
else
start = 1 - fdm ; // This may be zero or negative
// Get the day of the week (normalized for locale) for the last
// day of the month.
int32_t monthLen = getActualMaximum ( UCAL_DAY_OF_MONTH , status ) ;
int32_t ldm = ( monthLen - internalGet ( UCAL_DAY_OF_MONTH ) + dow ) % 7 ;
// We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here.
// Get the limit day for the blocked-off rectangular month; that
// is, the day which is one past the last day of the month,
// after the month has already been filled in with phantom days
// to fill out the last week. This day has a normalized DOW of 0.
int32_t limit = monthLen + 7 - ldm ;
// Now roll between start and (limit - 1).
int32_t gap = limit - start ;
int32_t day_of_month = ( internalGet ( UCAL_DAY_OF_MONTH ) + amount * 7 -
start ) % gap ;
if ( day_of_month < 0 ) day_of_month + = gap ;
day_of_month + = start ;
// Finally, pin to the real start and end of the month.
if ( day_of_month < 1 ) day_of_month = 1 ;
if ( day_of_month > monthLen ) day_of_month = monthLen ;
// Set the DAY_OF_MONTH. We rely on the fact that this field
// takes precedence over everything else (since all other fields
// are also set at this point). If this fact changes (if the
// disambiguation algorithm changes) then we will have to unset
// the appropriate fields here so that DAY_OF_MONTH is attended
// to.
set ( UCAL_DAY_OF_MONTH , day_of_month ) ;
return ;
}
case UCAL_WEEK_OF_YEAR :
{
// This follows the outline of WEEK_OF_MONTH, except it applies
// to the whole year. Please see the comment for WEEK_OF_MONTH
// for general notes.
// Normalize the DAY_OF_WEEK so that 0 is the first day of the week
// in this locale. We have dow in 0..6.
int32_t dow = internalGet ( UCAL_DAY_OF_WEEK ) - getFirstDayOfWeek ( ) ;
if ( dow < 0 ) dow + = 7 ;
// Find the day of the week (normalized for locale) for the first
// of the year.
int32_t fdy = ( dow - internalGet ( UCAL_DAY_OF_YEAR ) + 1 ) % 7 ;
if ( fdy < 0 ) fdy + = 7 ;
// Get the first day of the first full week of the year,
// including phantom days, if any. Figure out if the first week
// counts or not; if it counts, then fill in phantom days. If
// not, advance to the first real full week (skip the partial week).
int32_t start ;
if ( ( 7 - fdy ) < getMinimalDaysInFirstWeek ( ) )
start = 8 - fdy ; // Skip the first partial week
else
start = 1 - fdy ; // This may be zero or negative
// Get the day of the week (normalized for locale) for the last
// day of the year.
int32_t yearLen = getActualMaximum ( UCAL_DAY_OF_YEAR , status ) ;
int32_t ldy = ( yearLen - internalGet ( UCAL_DAY_OF_YEAR ) + dow ) % 7 ;
// We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here.
// Get the limit day for the blocked-off rectangular year; that
// is, the day which is one past the last day of the year,
// after the year has already been filled in with phantom days
// to fill out the last week. This day has a normalized DOW of 0.
int32_t limit = yearLen + 7 - ldy ;
// Now roll between start and (limit - 1).
int32_t gap = limit - start ;
int32_t day_of_year = ( internalGet ( UCAL_DAY_OF_YEAR ) + amount * 7 -
start ) % gap ;
if ( day_of_year < 0 ) day_of_year + = gap ;
day_of_year + = start ;
// Finally, pin to the real start and end of the month.
if ( day_of_year < 1 ) day_of_year = 1 ;
if ( day_of_year > yearLen ) day_of_year = yearLen ;
// Make sure that the year and day of year are attended to by
// clearing other fields which would normally take precedence.
// If the disambiguation algorithm is changed, this section will
// have to be updated as well.
set ( UCAL_DAY_OF_YEAR , day_of_year ) ;
clear ( UCAL_MONTH ) ;
return ;
}
case UCAL_DAY_OF_YEAR :
{
// Roll the day of year using millis. Compute the millis for
// the start of the year, and get the length of the year.
double delta = amount * kOneDay ; // Scale up from days to millis
double min2 = internalGet ( UCAL_DAY_OF_YEAR ) - 1 ;
min2 * = kOneDay ;
min2 = internalGetTime ( ) - min2 ;
// double min2 = internalGetTime() - (internalGet(UCAL_DAY_OF_YEAR) - 1.0) * kOneDay;
double newtime ;
double yearLength = getActualMaximum ( UCAL_DAY_OF_YEAR , status ) ;
double oneYear = yearLength ;
oneYear * = kOneDay ;
newtime = uprv_fmod ( ( internalGetTime ( ) + delta - min2 ) , oneYear ) ;
if ( newtime < 0 ) newtime + = oneYear ;
setTimeInMillis ( newtime + min2 , status ) ;
return ;
}
case UCAL_DAY_OF_WEEK :
case UCAL_DOW_LOCAL :
{
// Roll the day of week using millis. Compute the millis for
// the start of the week, using the first day of week setting.
// Restrict the millis to [start, start+7days).
double delta = amount * kOneDay ; // Scale up from days to millis
// Compute the number of days before the current day in this
// week. This will be a value 0..6.
int32_t leadDays = internalGet ( field ) ;
leadDays - = ( field = = UCAL_DAY_OF_WEEK ) ? getFirstDayOfWeek ( ) : 1 ;
if ( leadDays < 0 ) leadDays + = 7 ;
double min2 = internalGetTime ( ) - leadDays * kOneDay ;
double newtime = uprv_fmod ( ( internalGetTime ( ) + delta - min2 ) , kOneWeek ) ;
if ( newtime < 0 ) newtime + = kOneWeek ;
setTimeInMillis ( newtime + min2 , status ) ;
return ;
}
case UCAL_DAY_OF_WEEK_IN_MONTH :
{
// Roll the day of week in the month using millis. Determine
// the first day of the week in the month, and then the last,
// and then roll within that range.
double delta = amount * kOneWeek ; // Scale up from weeks to millis
// Find the number of same days of the week before this one
// in this month.
int32_t preWeeks = ( internalGet ( UCAL_DAY_OF_MONTH ) - 1 ) / 7 ;
// Find the number of same days of the week after this one
// in this month.
int32_t postWeeks = ( getActualMaximum ( UCAL_DAY_OF_MONTH , status ) -
internalGet ( UCAL_DAY_OF_MONTH ) ) / 7 ;
// From these compute the min and gap millis for rolling.
double min2 = internalGetTime ( ) - preWeeks * kOneWeek ;
double gap2 = kOneWeek * ( preWeeks + postWeeks + 1 ) ; // Must add 1!
// Roll within this range
double newtime = uprv_fmod ( ( internalGetTime ( ) + delta - min2 ) , gap2 ) ;
if ( newtime < 0 ) newtime + = gap2 ;
setTimeInMillis ( newtime + min2 , status ) ;
return ;
}
case UCAL_JULIAN_DAY :
set ( field , internalGet ( field ) + amount ) ;
return ;
default :
// Other fields cannot be rolled by this method
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d: ILLEGAL ARG because of roll on non-rollable field %s \n " ,
__FILE__ , __LINE__ , fldName ( field ) ) ;
# endif
status = U_ILLEGAL_ARGUMENT_ERROR ;
}
}
void Calendar : : add ( EDateFields field , int32_t amount , UErrorCode & status )
{
Calendar : : add ( ( UCalendarDateFields ) field , amount , status ) ;
}
// -------------------------------------
void Calendar : : add ( UCalendarDateFields field , int32_t amount , UErrorCode & status )
{
if ( amount = = 0 ) {
return ; // Do nothing!
}
// We handle most fields in the same way. The algorithm is to add
// a computed amount of millis to the current millis. The only
// wrinkle is with DST -- for some fields, like the DAY_OF_MONTH,
// we don't want the HOUR to shift due to changes in DST. If the
// result of the add operation is to move from DST to Standard, or
// vice versa, we need to adjust by an hour forward or back,
// respectively. For such fields we set keepHourInvariant to TRUE.
// We only adjust the DST for fields larger than an hour. For
// fields smaller than an hour, we cannot adjust for DST without
// causing problems. for instance, if you add one hour to April 5,
// 1998, 1:00 AM, in PST, the time becomes "2:00 AM PDT" (an
// illegal value), but then the adjustment sees the change and
// compensates by subtracting an hour. As a result the time
// doesn't advance at all.
// For some fields larger than a day, such as a UCAL_MONTH, we pin the
// UCAL_DAY_OF_MONTH. This allows <March 31>.add(UCAL_MONTH, 1) to be
// <April 30>, rather than <April 31> => <May 1>.
double delta = amount ; // delta in ms
UBool keepHourInvariant = TRUE ;
switch ( field ) {
case UCAL_ERA :
set ( field , get ( field , status ) + amount ) ;
pinField ( UCAL_ERA , status ) ;
return ;
case UCAL_YEAR :
case UCAL_EXTENDED_YEAR :
case UCAL_YEAR_WOY :
case UCAL_MONTH :
set ( field , get ( field , status ) + amount ) ;
pinField ( UCAL_DAY_OF_MONTH , status ) ;
return ;
case UCAL_WEEK_OF_YEAR :
case UCAL_WEEK_OF_MONTH :
case UCAL_DAY_OF_WEEK_IN_MONTH :
delta * = kOneWeek ;
break ;
case UCAL_AM_PM :
delta * = 12 * kOneHour ;
break ;
case UCAL_DAY_OF_MONTH :
case UCAL_DAY_OF_YEAR :
case UCAL_DAY_OF_WEEK :
case UCAL_DOW_LOCAL :
case UCAL_JULIAN_DAY :
delta * = kOneDay ;
break ;
case UCAL_HOUR_OF_DAY :
case UCAL_HOUR :
delta * = kOneHour ;
keepHourInvariant = FALSE ;
break ;
case UCAL_MINUTE :
delta * = kOneMinute ;
keepHourInvariant = FALSE ;
break ;
case UCAL_SECOND :
delta * = kOneSecond ;
keepHourInvariant = FALSE ;
break ;
case UCAL_MILLISECOND :
case UCAL_MILLISECONDS_IN_DAY :
keepHourInvariant = FALSE ;
break ;
default :
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d: ILLEGAL ARG because field %s not addable " ,
__FILE__ , __LINE__ , fldName ( field ) ) ;
# endif
status = U_ILLEGAL_ARGUMENT_ERROR ;
return ;
// throw new IllegalArgumentException("Calendar.add(" + fieldName(field) +
// ") not supported");
}
// In order to keep the hour invariant (for fields where this is
// appropriate), record the DST_OFFSET before and after the add()
// operation. If it has changed, then adjust the millis to
// compensate.
int32_t dst = 0 ;
int32_t hour = 0 ;
if ( keepHourInvariant ) {
dst = get ( UCAL_DST_OFFSET , status ) ;
hour = internalGet ( UCAL_HOUR_OF_DAY ) ;
}
2003-03-19 13:21:51 +00:00
2003-10-26 10:24:58 +00:00
setTimeInMillis ( getTimeInMillis ( status ) + delta , status ) ;
if ( keepHourInvariant ) {
dst - = get ( UCAL_DST_OFFSET , status ) ;
if ( dst ! = 0 ) {
// We have done an hour-invariant adjustment but the
// DST offset has altered. We adjust millis to keep
// the hour constant. In cases such as midnight after
// a DST change which occurs at midnight, there is the
// danger of adjusting into a different day. To avoid
// this we make the adjustment only if it actually
// maintains the hour.
double t = internalGetTime ( ) ;
setTimeInMillis ( t + dst , status ) ;
if ( get ( UCAL_HOUR_OF_DAY , status ) ! = hour ) {
setTimeInMillis ( t , status ) ;
}
}
}
2003-02-28 13:21:23 +00:00
}
1999-08-16 21:50:52 +00:00
2003-10-26 10:24:58 +00:00
// -------------------------------------
int32_t Calendar : : fieldDifference ( UDate when , EDateFields field , UErrorCode & status ) {
return fieldDifference ( when , ( UCalendarDateFields ) field , status ) ;
}
2003-03-19 13:21:51 +00:00
2003-02-21 08:32:22 +00:00
int32_t Calendar : : fieldDifference ( UDate targetMs , UCalendarDateFields field , UErrorCode & ec ) {
2002-04-09 22:06:14 +00:00
if ( U_FAILURE ( ec ) ) return 0 ;
int32_t min = 0 ;
double startMs = getTimeInMillis ( ec ) ;
// Always add from the start millis. This accomodates
// operations like adding years from February 29, 2000 up to
// February 29, 2004. If 1, 1, 1, 1 is added to the year
// field, the DOM gets pinned to 28 and stays there, giving an
// incorrect DOM difference of 1. We have to add 1, reset, 2,
// reset, 3, reset, 4.
if ( startMs < targetMs ) {
int32_t max = 1 ;
// Find a value that is too large
while ( U_SUCCESS ( ec ) ) {
setTimeInMillis ( startMs , ec ) ;
add ( field , max , ec ) ;
double ms = getTimeInMillis ( ec ) ;
if ( ms = = targetMs ) {
return max ;
} else if ( ms > targetMs ) {
2000-06-15 23:36:25 +00:00
break ;
2002-04-09 22:06:14 +00:00
} else {
max < < = 1 ;
if ( max < 0 ) {
// Field difference too large to fit into int32_t
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d: ILLEGAL ARG because field %s's max too large for int32_t \n " ,
__FILE__ , __LINE__ , fldName ( field ) ) ;
# endif
2002-04-09 22:06:14 +00:00
ec = U_ILLEGAL_ARGUMENT_ERROR ;
}
2000-06-15 23:36:25 +00:00
}
}
2002-04-09 22:06:14 +00:00
// Do a binary search
while ( ( max - min ) > 1 & & U_SUCCESS ( ec ) ) {
int32_t t = ( min + max ) / 2 ;
setTimeInMillis ( startMs , ec ) ;
add ( field , t , ec ) ;
double ms = getTimeInMillis ( ec ) ;
if ( ms = = targetMs ) {
return t ;
} else if ( ms > targetMs ) {
max = t ;
} else {
min = t ;
}
}
} else if ( startMs > targetMs ) {
int32_t max = - 1 ;
// Find a value that is too small
while ( U_SUCCESS ( ec ) ) {
setTimeInMillis ( startMs , ec ) ;
add ( field , max , ec ) ;
double ms = getTimeInMillis ( ec ) ;
if ( ms = = targetMs ) {
return max ;
} else if ( ms < targetMs ) {
2000-06-15 23:36:25 +00:00
break ;
2002-04-09 22:06:14 +00:00
} else {
max < < = 1 ;
if ( max = = 0 ) {
// Field difference too large to fit into int32_t
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d: ILLEGAL ARG because field %s's max too large for int32_t \n " ,
__FILE__ , __LINE__ , fldName ( field ) ) ;
# endif
2002-04-09 22:06:14 +00:00
ec = U_ILLEGAL_ARGUMENT_ERROR ;
}
}
}
// Do a binary search
while ( ( min - max ) > 1 & & U_SUCCESS ( ec ) ) {
int32_t t = ( min + max ) / 2 ;
setTimeInMillis ( startMs , ec ) ;
add ( field , t , ec ) ;
double ms = getTimeInMillis ( ec ) ;
if ( ms = = targetMs ) {
return t ;
} else if ( ms < targetMs ) {
max = t ;
} else {
min = t ;
2000-06-15 23:36:25 +00:00
}
}
}
2002-04-09 22:06:14 +00:00
// Set calendar to end point
setTimeInMillis ( startMs , ec ) ;
add ( field , min , ec ) ;
2002-07-02 15:10:30 +00:00
/* Test for buffer overflows */
if ( U_FAILURE ( ec ) ) {
return 0 ;
}
2002-04-09 22:06:14 +00:00
return min ;
2000-06-15 23:36:25 +00:00
}
// -------------------------------------
1999-08-16 21:50:52 +00:00
void
Calendar : : adoptTimeZone ( TimeZone * zone )
{
// Do nothing if passed-in zone is NULL
2002-07-02 15:10:30 +00:00
if ( zone = = NULL ) return ;
1999-08-16 21:50:52 +00:00
// fZone should always be non-null
2002-07-02 15:10:30 +00:00
if ( fZone ! = NULL ) delete fZone ;
1999-08-16 21:50:52 +00:00
fZone = zone ;
// if the zone changes, we need to recompute the time fields
fAreFieldsSet = FALSE ;
}
// -------------------------------------
void
Calendar : : setTimeZone ( const TimeZone & zone )
{
adoptTimeZone ( zone . clone ( ) ) ;
}
// -------------------------------------
const TimeZone &
Calendar : : getTimeZone ( ) const
{
return * fZone ;
}
// -------------------------------------
TimeZone *
Calendar : : orphanTimeZone ( )
{
TimeZone * z = fZone ;
// we let go of the time zone; the new time zone is the system default time zone
fZone = TimeZone : : createDefault ( ) ;
return z ;
}
// -------------------------------------
void
2000-05-18 22:08:39 +00:00
Calendar : : setLenient ( UBool lenient )
1999-08-16 21:50:52 +00:00
{
fLenient = lenient ;
}
// -------------------------------------
2000-05-18 22:08:39 +00:00
UBool
1999-08-16 21:50:52 +00:00
Calendar : : isLenient ( ) const
{
return fLenient ;
}
// -------------------------------------
void
2003-02-21 08:32:22 +00:00
Calendar : : setFirstDayOfWeek ( UCalendarDaysOfWeek value )
1999-08-16 21:50:52 +00:00
{
2003-05-15 22:31:16 +00:00
if ( fFirstDayOfWeek ! = value & &
value > = UCAL_SUNDAY & & value < = UCAL_SATURDAY ) {
2003-03-18 18:17:24 +00:00
fFirstDayOfWeek = value ;
fAreFieldsSet = FALSE ;
}
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
2003-03-19 13:21:51 +00:00
Calendar : : EDaysOfWeek
1999-08-16 21:50:52 +00:00
Calendar : : getFirstDayOfWeek ( ) const
{
2003-03-19 13:21:51 +00:00
return ( Calendar : : EDaysOfWeek ) fFirstDayOfWeek ;
1999-08-16 21:50:52 +00:00
}
2003-05-01 17:43:01 +00:00
UCalendarDaysOfWeek
2003-05-17 23:24:25 +00:00
Calendar : : getFirstDayOfWeek ( UErrorCode & /*status*/ ) const
2003-05-01 17:43:01 +00:00
{
return fFirstDayOfWeek ;
}
1999-08-16 21:50:52 +00:00
// -------------------------------------
void
Calendar : : setMinimalDaysInFirstWeek ( uint8_t value )
{
2003-03-18 18:17:24 +00:00
// Values less than 1 have the same effect as 1; values greater
// than 7 have the same effect as 7. However, we normalize values
// so operator== and so forth work.
if ( value < 1 ) {
value = 1 ;
} else if ( value > 7 ) {
value = 7 ;
}
if ( fMinimalDaysInFirstWeek ! = value ) {
fMinimalDaysInFirstWeek = value ;
fAreFieldsSet = FALSE ;
}
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
uint8_t
Calendar : : getMinimalDaysInFirstWeek ( ) const
{
return fMinimalDaysInFirstWeek ;
}
2003-10-26 10:24:58 +00:00
// ------------------------------------- limits
int32_t
Calendar : : getMinimum ( EDateFields field ) const {
2003-11-19 05:38:55 +00:00
return getLimit ( ( UCalendarDateFields ) field , UCAL_LIMIT_MINIMUM ) ;
2003-10-26 10:24:58 +00:00
}
int32_t
Calendar : : getMinimum ( UCalendarDateFields field ) const
{
2003-11-19 05:38:55 +00:00
return getLimit ( field , UCAL_LIMIT_MINIMUM ) ;
2003-10-26 10:24:58 +00:00
}
// -------------------------------------
int32_t
Calendar : : getMaximum ( EDateFields field ) const
{
2003-11-19 05:38:55 +00:00
return getLimit ( ( UCalendarDateFields ) field , UCAL_LIMIT_MAXIMUM ) ;
2003-10-26 10:24:58 +00:00
}
int32_t
Calendar : : getMaximum ( UCalendarDateFields field ) const
{
2003-11-19 05:38:55 +00:00
return getLimit ( field , UCAL_LIMIT_MAXIMUM ) ;
2003-10-26 10:24:58 +00:00
}
1999-08-16 21:50:52 +00:00
// -------------------------------------
2003-10-26 10:24:58 +00:00
int32_t
Calendar : : getGreatestMinimum ( EDateFields field ) const
{
2003-11-19 05:38:55 +00:00
return getLimit ( ( UCalendarDateFields ) field , UCAL_LIMIT_GREATEST_MINIMUM ) ;
2003-10-26 10:24:58 +00:00
}
int32_t
Calendar : : getGreatestMinimum ( UCalendarDateFields field ) const
{
2003-11-19 05:38:55 +00:00
return getLimit ( field , UCAL_LIMIT_GREATEST_MINIMUM ) ;
2003-10-26 10:24:58 +00:00
}
// -------------------------------------
int32_t
Calendar : : getLeastMaximum ( EDateFields field ) const
{
2003-11-19 05:38:55 +00:00
return getLimit ( ( UCalendarDateFields ) field , UCAL_LIMIT_LEAST_MAXIMUM ) ;
2003-10-26 10:24:58 +00:00
}
int32_t
Calendar : : getLeastMaximum ( UCalendarDateFields field ) const
{
2003-11-19 05:38:55 +00:00
return getLimit ( field , UCAL_LIMIT_LEAST_MAXIMUM ) ;
2003-10-26 10:24:58 +00:00
}
// -------------------------------------
int32_t
Calendar : : getActualMinimum ( EDateFields field , UErrorCode & status ) const
{
return getActualMinimum ( ( UCalendarDateFields ) field , status ) ;
}
int32_t Calendar : : getLimit ( UCalendarDateFields field , ELimitType limitType ) const {
switch ( field ) {
case UCAL_DAY_OF_WEEK :
case UCAL_AM_PM :
case UCAL_HOUR :
case UCAL_HOUR_OF_DAY :
case UCAL_MINUTE :
case UCAL_SECOND :
case UCAL_MILLISECOND :
case UCAL_ZONE_OFFSET :
case UCAL_DST_OFFSET :
case UCAL_DOW_LOCAL :
case UCAL_JULIAN_DAY :
case UCAL_MILLISECONDS_IN_DAY :
return kCalendarLimits [ field ] [ limitType ] ;
2003-10-28 00:04:00 +00:00
default :
return handleGetLimit ( field , limitType ) ;
2003-10-26 10:24:58 +00:00
}
}
1999-08-16 21:50:52 +00:00
int32_t
2003-02-21 08:32:22 +00:00
Calendar : : getActualMinimum ( UCalendarDateFields field , UErrorCode & status ) const
1999-08-16 21:50:52 +00:00
{
int32_t fieldValue = getGreatestMinimum ( field ) ;
int32_t endValue = getMinimum ( field ) ;
// if we know that the minimum value is always the same, just return it
if ( fieldValue = = endValue ) {
return fieldValue ;
}
// clone the calendar so we don't mess with the real one, and set it to
// accept anything for the field values
Calendar * work = ( Calendar * ) this - > clone ( ) ;
work - > setLenient ( TRUE ) ;
// now try each value from getLeastMaximum() to getMaximum() one by one until
// we get a value that normalizes to another value. The last value that
// normalizes to itself is the actual minimum for the current date
int32_t result = fieldValue ;
do {
work - > set ( field , fieldValue ) ;
if ( work - > get ( field , status ) ! = fieldValue ) {
break ;
}
else {
result = fieldValue ;
fieldValue - - ;
}
} while ( fieldValue > = endValue ) ;
delete work ;
2002-07-02 15:10:30 +00:00
/* Test for buffer overflows */
if ( U_FAILURE ( status ) ) {
return 0 ;
}
1999-08-16 21:50:52 +00:00
return result ;
}
// -------------------------------------
2003-10-26 10:24:58 +00:00
/**
* Ensure that each field is within its valid range by calling { @ link
* # validateField ( int ) } on each field that has been set . This method
* should only be called if this calendar is not lenient .
* @ see # isLenient
* @ see # validateField ( int )
*/
void Calendar : : validateFields ( UErrorCode & status ) {
for ( int32_t field = 0 ; U_SUCCESS ( status ) & & ( field < UCAL_FIELD_COUNT ) ; field + + ) {
if ( isSet ( ( UCalendarDateFields ) field ) ) {
validateField ( ( UCalendarDateFields ) field , status ) ;
}
}
}
/**
* Validate a single field of this calendar . Subclasses should
* override this method to validate any calendar - specific fields .
* Generic fields can be handled by
* < code > Calendar . validateField ( ) < / code > .
* @ see # validateField ( int , int , int )
*/
void Calendar : : validateField ( UCalendarDateFields field , UErrorCode & status ) {
int32_t y ;
switch ( field ) {
case UCAL_DAY_OF_MONTH :
y = handleGetExtendedYear ( ) ;
validateField ( field , 1 , handleGetMonthLength ( y , internalGet ( UCAL_MONTH ) ) , status ) ;
break ;
case UCAL_DAY_OF_YEAR :
y = handleGetExtendedYear ( ) ;
validateField ( field , 1 , handleGetYearLength ( y ) , status ) ;
break ;
case UCAL_DAY_OF_WEEK_IN_MONTH :
if ( internalGet ( field ) = = 0 ) {
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d: ILLEGAL ARG because DOW in month cannot be 0 \n " ,
__FILE__ , __LINE__ ) ;
# endif
status = U_ILLEGAL_ARGUMENT_ERROR ; // "DAY_OF_WEEK_IN_MONTH cannot be zero"
return ;
}
validateField ( field , getMinimum ( field ) , getMaximum ( field ) , status ) ;
break ;
default :
validateField ( field , getMinimum ( field ) , getMaximum ( field ) , status ) ;
break ;
}
}
/**
* Validate a single field of this calendar given its minimum and
* maximum allowed value . If the field is out of range , throw a
* descriptive < code > IllegalArgumentException < / code > . Subclasses may
* use this method in their implementation of { @ link
* # validateField ( int ) } .
*/
void Calendar : : validateField ( UCalendarDateFields field , int32_t min , int32_t max , UErrorCode & status )
{
int32_t value = fFields [ field ] ;
if ( value < min | | value > max ) {
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d: ILLEGAL ARG because of field %s out of range %d..%d at %d \n " ,
__FILE__ , __LINE__ , fldName ( field ) , min , max , value ) ;
# endif
status = U_ILLEGAL_ARGUMENT_ERROR ;
return ;
}
}
// -------------------------
2003-10-28 02:30:49 +00:00
const UFieldResolutionTable * Calendar : : getFieldResolutionTable ( ) const {
2003-10-26 10:24:58 +00:00
return kDatePrecedence ;
}
UCalendarDateFields Calendar : : newerField ( UCalendarDateFields defaultField , UCalendarDateFields alternateField ) const
{
if ( fStamp [ alternateField ] > fStamp [ defaultField ] ) {
return alternateField ;
}
return defaultField ;
}
UCalendarDateFields Calendar : : resolveFields ( const UFieldResolutionTable * precedenceTable ) {
2003-12-03 00:11:52 +00:00
int32_t bestField = UCAL_FIELD_COUNT ;
for ( int32_t g = 0 ; precedenceTable [ g ] [ 0 ] [ 0 ] ! = - 1 & & ( bestField = = UCAL_FIELD_COUNT ) ; + + g ) {
2003-10-26 10:24:58 +00:00
int32_t bestStamp = kUnset ;
for ( int32_t l = 0 ; precedenceTable [ g ] [ l ] [ 0 ] ! = - 1 ; + + l ) {
int32_t lineStamp = kUnset ;
// Skip over first entry if it is negative
for ( int32_t i = ( ( precedenceTable [ g ] [ l ] [ 0 ] > = kResolveRemap ) ? 1 : 0 ) ; precedenceTable [ g ] [ l ] [ i ] ! = - 1 ; + + i ) {
int32_t s = fStamp [ precedenceTable [ g ] [ l ] [ i ] ] ;
// If any field is unset then don't use this line
if ( s = = kUnset ) {
goto linesInGroup ;
} else if ( s > lineStamp ) {
lineStamp = s ;
}
}
// Record new maximum stamp & field no.
if ( lineStamp > bestStamp ) {
bestStamp = lineStamp ;
bestField = precedenceTable [ g ] [ l ] [ 0 ] ; // First field refers to entire line
}
linesInGroup :
;
}
}
return ( UCalendarDateFields ) ( ( bestField > = kResolveRemap ) ? ( bestField & ( kResolveRemap - 1 ) ) : bestField ) ;
}
2003-10-28 02:30:49 +00:00
const UFieldResolutionTable Calendar : : kDatePrecedence [ ] =
2003-10-26 10:24:58 +00:00
{
{
2003-11-19 05:38:55 +00:00
{ UCAL_DAY_OF_MONTH , kResolveSTOP } ,
{ UCAL_WEEK_OF_YEAR , UCAL_DAY_OF_WEEK , kResolveSTOP } ,
{ UCAL_WEEK_OF_MONTH , UCAL_DAY_OF_WEEK , kResolveSTOP } ,
{ UCAL_DAY_OF_WEEK_IN_MONTH , UCAL_DAY_OF_WEEK , kResolveSTOP } ,
{ UCAL_WEEK_OF_YEAR , UCAL_DOW_LOCAL , kResolveSTOP } ,
{ UCAL_WEEK_OF_MONTH , UCAL_DOW_LOCAL , kResolveSTOP } ,
{ UCAL_DAY_OF_WEEK_IN_MONTH , UCAL_DOW_LOCAL , kResolveSTOP } ,
{ UCAL_DAY_OF_YEAR , kResolveSTOP } ,
{ kResolveRemap | UCAL_DAY_OF_MONTH , UCAL_YEAR , kResolveSTOP } , // if YEAR is set over YEAR_WOY use DAY_OF_MONTH
{ kResolveRemap | UCAL_WEEK_OF_YEAR , UCAL_YEAR_WOY , kResolveSTOP } , // if YEAR_WOY is set, calc based on WEEK_OF_YEAR
{ kResolveSTOP }
2003-10-26 10:24:58 +00:00
} ,
{
2003-11-19 05:38:55 +00:00
{ UCAL_WEEK_OF_YEAR , kResolveSTOP } ,
{ UCAL_WEEK_OF_MONTH , kResolveSTOP } ,
{ UCAL_DAY_OF_WEEK_IN_MONTH , kResolveSTOP } ,
{ kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH , UCAL_DAY_OF_WEEK , kResolveSTOP } ,
{ kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH , UCAL_DOW_LOCAL , kResolveSTOP } ,
{ kResolveSTOP }
2003-10-26 10:24:58 +00:00
} ,
2003-11-19 05:38:55 +00:00
{ { kResolveSTOP } }
2003-10-26 10:24:58 +00:00
} ;
2003-10-28 02:30:49 +00:00
const UFieldResolutionTable Calendar : : kDOWPrecedence [ ] =
2003-10-26 10:24:58 +00:00
{
{
2003-11-19 05:38:55 +00:00
{ UCAL_DAY_OF_WEEK , kResolveSTOP , kResolveSTOP } ,
{ UCAL_DOW_LOCAL , kResolveSTOP , kResolveSTOP } ,
{ kResolveSTOP }
2003-10-26 10:24:58 +00:00
} ,
2003-11-19 05:38:55 +00:00
{ { kResolveSTOP } }
2003-10-26 10:24:58 +00:00
} ;
// precedence for calculating a year
2003-10-28 02:30:49 +00:00
const UFieldResolutionTable Calendar : : kYearPrecedence [ ] =
2003-10-26 10:24:58 +00:00
{
{
2003-11-19 05:38:55 +00:00
{ UCAL_YEAR , kResolveSTOP } ,
{ UCAL_EXTENDED_YEAR , kResolveSTOP } ,
{ UCAL_YEAR_WOY , UCAL_WEEK_OF_YEAR , kResolveSTOP } , // YEAR_WOY is useless without WEEK_OF_YEAR
{ kResolveSTOP }
2003-10-26 10:24:58 +00:00
} ,
2003-11-19 05:38:55 +00:00
{ { kResolveSTOP } }
2003-10-26 10:24:58 +00:00
} ;
// -------------------------
void Calendar : : computeTime ( UErrorCode & status ) {
if ( ! isLenient ( ) ) {
validateFields ( status ) ;
}
// Compute the Julian day
int32_t julianDay = computeJulianDay ( ) ;
double millis = Grego : : julianDayToMillis ( julianDay ) ;
# if defined (U_DEBUG_CAL)
// int32_t julianInsanityCheck = (int32_t)Math::floorDivide(millis, kOneDay);
// julianInsanityCheck += kEpochStartAsJulianDay;
// if(1 || julianInsanityCheck != julianDay) {
// fprintf(stderr, "%s:%d- D'oh- computed jules %d, to mills (%s)%.lf, recomputed %d\n",
// __FILE__, __LINE__, julianDay, millis<0.0?"NEG":"", millis, julianInsanityCheck);
// }
# endif
int32_t millisInDay ;
// We only use MILLISECONDS_IN_DAY if it has been set by the user.
// This makes it possible for the caller to set the calendar to a
// time and call clear(MONTH) to reset the MONTH to January. This
// is legacy behavior. Without this, clear(MONTH) has no effect,
// since the internally set JULIAN_DAY is used.
2003-11-19 05:38:55 +00:00
if ( fStamp [ UCAL_MILLISECONDS_IN_DAY ] > = ( ( int32_t ) kMinimumUserStamp ) & &
2003-10-26 10:24:58 +00:00
newestStamp ( UCAL_AM_PM , UCAL_MILLISECOND , kUnset ) < = fStamp [ UCAL_MILLISECONDS_IN_DAY ] ) {
millisInDay = internalGet ( UCAL_MILLISECONDS_IN_DAY ) ;
} else {
millisInDay = computeMillisInDay ( ) ;
}
// Compute the time zone offset and DST offset. There are two potential
// ambiguities here. We'll assume a 2:00 am (wall time) switchover time
// for discussion purposes here.
// 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am
// can be in standard or in DST depending. However, 2:00 am is an invalid
// representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
// We assume standard time.
// 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am
// can be in standard or DST. Both are valid representations (the rep
// jumps from 1:59:59 DST to 1:00:00 Std).
// Again, we assume standard time.
// We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
// or DST_OFFSET fields; then we use those fields.
2003-11-19 05:38:55 +00:00
if ( fStamp [ UCAL_ZONE_OFFSET ] > = ( ( int32_t ) kMinimumUserStamp ) | |
fStamp [ UCAL_DST_OFFSET ] > = ( ( int32_t ) kMinimumUserStamp ) ) {
2003-10-26 10:24:58 +00:00
millisInDay - = internalGet ( UCAL_ZONE_OFFSET ) + internalGet ( UCAL_DST_OFFSET ) ;
} else {
millisInDay - = computeZoneOffset ( millis , millisInDay , status ) ;
}
internalSetTime ( millis + millisInDay ) ;
}
/**
* Compute the milliseconds in the day from the fields . This is a
* value from 0 to 23 : 59 : 59.999 inclusive , unless fields are out of
* range , in which case it can be an arbitrary value . This value
* reflects local zone wall time .
* @ stable ICU 2.0
*/
int32_t Calendar : : computeMillisInDay ( ) {
// Do the time portion of the conversion.
int32_t millisInDay = 0 ;
// Find the best set of fields specifying the time of day. There
// are only two possibilities here; the HOUR_OF_DAY or the
// AM_PM and the HOUR.
int32_t hourOfDayStamp = fStamp [ UCAL_HOUR_OF_DAY ] ;
int32_t hourStamp = ( fStamp [ UCAL_HOUR ] > fStamp [ UCAL_AM_PM ] ) ? fStamp [ UCAL_HOUR ] : fStamp [ UCAL_AM_PM ] ;
int32_t bestStamp = ( hourStamp > hourOfDayStamp ) ? hourStamp : hourOfDayStamp ;
// Hours
if ( bestStamp ! = kUnset ) {
if ( bestStamp = = hourOfDayStamp ) {
// Don't normalize here; let overflow bump into the next period.
// This is consistent with how we handle other fields.
millisInDay + = internalGet ( UCAL_HOUR_OF_DAY ) ;
} else {
// Don't normalize here; let overflow bump into the next period.
// This is consistent with how we handle other fields.
millisInDay + = internalGet ( UCAL_HOUR ) ;
millisInDay + = 12 * internalGet ( UCAL_AM_PM ) ; // Default works for unset AM_PM
}
}
// We use the fact that unset == 0; we start with millisInDay
// == HOUR_OF_DAY.
millisInDay * = 60 ;
millisInDay + = internalGet ( UCAL_MINUTE ) ; // now have minutes
millisInDay * = 60 ;
millisInDay + = internalGet ( UCAL_SECOND ) ; // now have seconds
millisInDay * = 1000 ;
millisInDay + = internalGet ( MILLISECOND ) ; // now have millis
return millisInDay ;
}
/**
* This method can assume EXTENDED_YEAR has been set .
* @ param millis milliseconds of the date fields
* @ param millisInDay milliseconds of the time fields ; may be out
* or range .
* @ stable ICU 2.0
*/
int32_t Calendar : : computeZoneOffset ( double millis , int32_t millisInDay , UErrorCode & ec ) {
int32_t rawOffset , dstOffset ;
getTimeZone ( ) . getOffset ( millis + millisInDay , TRUE , rawOffset , dstOffset , ec ) ;
return rawOffset + dstOffset ;
// Note: Because we pass in wall millisInDay, rather than
// standard millisInDay, we interpret "1:00 am" on the day
// of cessation of DST as "1:00 am Std" (assuming the time
// of cessation is 2:00 am).
}
int32_t Calendar : : computeJulianDay ( )
{
// We want to see if any of the date fields is newer than the
// JULIAN_DAY. If not, then we use JULIAN_DAY. If so, then we do
// the normal resolution. We only use JULIAN_DAY if it has been
// set by the user. This makes it possible for the caller to set
// the calendar to a time and call clear(MONTH) to reset the MONTH
// to January. This is legacy behavior. Without this,
// clear(MONTH) has no effect, since the internally set JULIAN_DAY
// is used.
2003-11-19 05:38:55 +00:00
if ( fStamp [ UCAL_JULIAN_DAY ] > = ( int32_t ) kMinimumUserStamp ) {
2003-10-26 10:24:58 +00:00
int32_t bestStamp = newestStamp ( UCAL_ERA , UCAL_DAY_OF_WEEK_IN_MONTH , kUnset ) ;
bestStamp = newestStamp ( UCAL_YEAR_WOY , UCAL_EXTENDED_YEAR , bestStamp ) ;
if ( bestStamp < = fStamp [ UCAL_JULIAN_DAY ] ) {
return internalGet ( UCAL_JULIAN_DAY ) ;
}
}
UCalendarDateFields bestField = resolveFields ( getFieldResolutionTable ( ) ) ;
2003-12-03 00:11:52 +00:00
if ( bestField = = UCAL_FIELD_COUNT ) {
2003-10-26 10:24:58 +00:00
bestField = UCAL_DAY_OF_MONTH ;
}
return handleComputeJulianDay ( bestField ) ;
}
// -------------------------------------------
int32_t Calendar : : handleComputeJulianDay ( UCalendarDateFields bestField ) {
UBool useMonth = ( bestField = = UCAL_DAY_OF_MONTH | |
bestField = = UCAL_WEEK_OF_MONTH | |
bestField = = UCAL_DAY_OF_WEEK_IN_MONTH ) ;
int32_t year ;
if ( bestField = = UCAL_WEEK_OF_YEAR ) {
year = internalGet ( UCAL_YEAR_WOY , handleGetExtendedYear ( ) ) ;
internalSet ( UCAL_EXTENDED_YEAR , year ) ;
} else {
year = handleGetExtendedYear ( ) ;
internalSet ( UCAL_EXTENDED_YEAR , year ) ;
}
# if defined (U_DEBUG_CAL)
2005-06-29 16:33:06 +00:00
fprintf ( stderr , " %s:%d: bestField= %s - y=%d \n " , __FILE__ , __LINE__ , fldName ( bestField ) , year ) ;
2003-10-26 10:24:58 +00:00
# endif
// Get the Julian day of the day BEFORE the start of this year.
// If useMonth is true, get the day before the start of the month.
// give calendar subclass a chance to have a default 'first' month
int8_t month ;
if ( isSet ( UCAL_MONTH ) ) {
2003-10-28 01:52:29 +00:00
month = ( int8_t ) internalGet ( UCAL_MONTH ) ;
2003-10-26 10:24:58 +00:00
} else {
2003-10-28 01:52:29 +00:00
month = ( int8_t ) getDefaultMonthInYear ( ) ;
2003-10-26 10:24:58 +00:00
}
int32_t julianDay = handleComputeMonthStart ( year , useMonth ? month : 0 , useMonth ) ;
if ( bestField = = UCAL_DAY_OF_MONTH ) {
// give calendar subclass a chance to have a default 'first' dom
int32_t dayOfMonth ;
if ( isSet ( UCAL_DAY_OF_MONTH ) ) {
dayOfMonth = internalGet ( UCAL_DAY_OF_MONTH , 1 ) ;
} else {
dayOfMonth = getDefaultDayInMonth ( month ) ;
}
return julianDay + dayOfMonth ;
}
if ( bestField = = UCAL_DAY_OF_YEAR ) {
return julianDay + internalGet ( UCAL_DAY_OF_YEAR ) ;
}
int32_t firstDayOfWeek = getFirstDayOfWeek ( ) ; // Localized fdw
// At this point julianDay is the 0-based day BEFORE the first day of
// January 1, year 1 of the given calendar. If julianDay == 0, it
// specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
// or Gregorian). (or it is before the month we are in, if useMonth is True)
// At this point we need to process the WEEK_OF_MONTH or
// WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
// First, perform initial shared computations. These locate the
// first week of the period.
// Get the 0-based localized DOW of day one of the month or year.
// Valid range 0..6.
int32_t first = julianDayToDayOfWeek ( julianDay + 1 ) - firstDayOfWeek ;
if ( first < 0 ) {
first + = 7 ;
}
int32_t dowLocal = getLocalDOW ( ) ;
// Find the first target DOW (dowLocal) in the month or year.
// Actually, it may be just before the first of the month or year.
// It will be an integer from -5..7.
int32_t date = 1 - first + dowLocal ;
if ( bestField = = UCAL_DAY_OF_WEEK_IN_MONTH ) {
// Adjust the target DOW to be in the month or year.
if ( date < 1 ) {
date + = 7 ;
}
// The only trickiness occurs if the day-of-week-in-month is
// negative.
int32_t dim = internalGet ( UCAL_DAY_OF_WEEK_IN_MONTH , 1 ) ;
if ( dim > = 0 ) {
date + = 7 * ( dim - 1 ) ;
} else {
// Move date to the last of this day-of-week in this month,
// then back up as needed. If dim==-1, we don't back up at
// all. If dim==-2, we back up once, etc. Don't back up
// past the first of the given day-of-week in this month.
// Note that we handle -2, -3, etc. correctly, even though
// values < -1 are technically disallowed.
int32_t m = internalGet ( UCAL_MONTH , UCAL_JANUARY ) ;
int32_t monthLength = handleGetMonthLength ( year , m ) ;
date + = ( ( monthLength - date ) / 7 + dim + 1 ) * 7 ;
}
} else {
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d - bf= %s \n " , __FILE__ , __LINE__ , fldName ( bestField ) ) ;
# endif
if ( bestField = = UCAL_WEEK_OF_YEAR ) { // ------------------------------------- WOY -------------
if ( ! isSet ( UCAL_YEAR_WOY ) | | // YWOY not set at all or
( ( resolveFields ( kYearPrecedence ) ! = UCAL_YEAR_WOY ) // YWOY doesn't have precedence
& & ( fStamp [ UCAL_YEAR_WOY ] ! = kInternallySet ) ) ) { // (excluding where all fields are internally set - then YWOY is used)
// need to be sure to stay in 'real' year.
int32_t woy = internalGet ( bestField ) ;
int32_t nextJulianDay = handleComputeMonthStart ( year + 1 , 0 , FALSE ) ; // jd of day before jan 1
int32_t nextFirst = julianDayToDayOfWeek ( nextJulianDay + 1 ) - firstDayOfWeek ;
if ( nextFirst < 0 ) { // 0..6 ldow of Jan 1
nextFirst + = 7 ;
}
if ( woy = = 1 ) { // FIRST WEEK ---------------------------------
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d - woy=%d, yp=%d, nj(%d)=%d, nf=%d " , __FILE__ , __LINE__ ,
internalGet ( bestField ) , resolveFields ( kYearPrecedence ) , year + 1 ,
nextJulianDay , nextFirst ) ;
fprintf ( stderr , " next: %d DFW, min=%d \n " , ( 7 - nextFirst ) , getMinimalDaysInFirstWeek ( ) ) ;
# endif
// nextFirst is now the localized DOW of Jan 1 of y-woy+1
if ( ( nextFirst > 0 ) & & // Jan 1 starts on FDOW
( 7 - nextFirst ) > = getMinimalDaysInFirstWeek ( ) ) { // or enough days in the week
// Jan 1 of (yearWoy+1) is in yearWoy+1 - recalculate JD to next year
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d - was going to move JD from %d to %d [d%d] \n " , __FILE__ , __LINE__ ,
julianDay , nextJulianDay , ( nextJulianDay - julianDay ) ) ;
# endif
julianDay = nextJulianDay ;
// recalculate 'first' [0-based local dow of jan 1]
first = julianDayToDayOfWeek ( julianDay + 1 ) - firstDayOfWeek ;
if ( first < 0 ) {
first + = 7 ;
}
// recalculate date.
date = 1 - first + dowLocal ;
}
} else if ( woy > = getLeastMaximum ( bestField ) ) {
// could be in the last week- find out if this JD would overstep
int32_t testDate = date ;
if ( ( 7 - first ) < getMinimalDaysInFirstWeek ( ) ) {
testDate + = 7 ;
}
// Now adjust for the week number.
testDate + = 7 * ( woy - 1 ) ;
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d - y=%d, y-1=%d doy%d, njd%d (C.F. %d) \n " ,
__FILE__ , __LINE__ , year , year - 1 , testDate , julianDay + testDate , nextJulianDay ) ;
# endif
if ( julianDay + testDate > nextJulianDay ) { // is it past Dec 31? (nextJulianDay is day BEFORE year+1's Jan 1)
// Fire up the calculating engines.. retry YWOY = (year-1)
julianDay = handleComputeMonthStart ( year - 1 , 0 , FALSE ) ; // jd before Jan 1 of previous year
first = julianDayToDayOfWeek ( julianDay + 1 ) - firstDayOfWeek ; // 0 based local dow of first week
if ( first < 0 ) { // 0..6
first + = 7 ;
}
date = 1 - first + dowLocal ;
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d - date now %d, jd%d, ywoy%d \n " ,
__FILE__ , __LINE__ , date , julianDay , year - 1 ) ;
# endif
} /* correction needed */
} /* leastmaximum */
} /* resolvefields(year) != year_woy */
} /* bestfield != week_of_year */
// assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR)
// Adjust for minimal days in first week
if ( ( 7 - first ) < getMinimalDaysInFirstWeek ( ) ) {
date + = 7 ;
}
// Now adjust for the week number.
date + = 7 * ( internalGet ( bestField ) - 1 ) ;
}
return julianDay + date ;
}
int32_t
Calendar : : getDefaultMonthInYear ( )
{
return 0 ;
}
int32_t
Calendar : : getDefaultDayInMonth ( int32_t /*month*/ )
{
return 1 ;
}
int32_t Calendar : : getLocalDOW ( )
{
// Get zero-based localized DOW, valid range 0..6. This is the DOW
// we are looking for.
int32_t dowLocal = 0 ;
switch ( resolveFields ( kDOWPrecedence ) ) {
case DAY_OF_WEEK :
dowLocal = internalGet ( UCAL_DAY_OF_WEEK ) - fFirstDayOfWeek ;
break ;
case DOW_LOCAL :
dowLocal = internalGet ( UCAL_DOW_LOCAL ) - 1 ;
break ;
2003-10-28 00:04:00 +00:00
default :
break ;
2003-10-26 10:24:58 +00:00
}
dowLocal = dowLocal % 7 ;
if ( dowLocal < 0 ) {
dowLocal + = 7 ;
}
return dowLocal ;
}
int32_t Calendar : : handleGetExtendedYearFromWeekFields ( int32_t yearWoy , int32_t woy )
{
// We have UCAL_YEAR_WOY and UCAL_WEEK_OF_YEAR - from those, determine
// what year we fall in, so that other code can set it properly.
// (code borrowed from computeWeekFields and handleComputeJulianDay)
//return yearWoy;
// First, we need a reliable DOW.
UCalendarDateFields bestField = resolveFields ( kDatePrecedence ) ; // !! Note: if subclasses have a different table, they should override handleGetExtendedYearFromWeekFields
// Now, a local DOW
int32_t dowLocal = getLocalDOW ( ) ; // 0..6
int32_t firstDayOfWeek = getFirstDayOfWeek ( ) ; // Localized fdw
int32_t jan1Start = handleComputeMonthStart ( yearWoy , 0 , FALSE ) ;
int32_t nextJan1Start = handleComputeMonthStart ( yearWoy + 1 , 0 , FALSE ) ; // next year's Jan1 start
// At this point julianDay is the 0-based day BEFORE the first day of
// January 1, year 1 of the given calendar. If julianDay == 0, it
// specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
// or Gregorian). (or it is before the month we are in, if useMonth is True)
// At this point we need to process the WEEK_OF_MONTH or
// WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
// First, perform initial shared computations. These locate the
// first week of the period.
// Get the 0-based localized DOW of day one of the month or year.
// Valid range 0..6.
int32_t first = julianDayToDayOfWeek ( jan1Start + 1 ) - firstDayOfWeek ;
if ( first < 0 ) {
first + = 7 ;
}
int32_t nextFirst = julianDayToDayOfWeek ( nextJan1Start + 1 ) - firstDayOfWeek ;
if ( nextFirst < 0 ) {
nextFirst + = 7 ;
}
int32_t minDays = getMinimalDaysInFirstWeek ( ) ;
UBool jan1InPrevYear = FALSE ; // January 1st in the year of WOY is the 1st week? (i.e. first week is < minimal )
UBool nextJan1InPrevYear = FALSE ; // January 1st of Year of WOY + 1 is in the first week?
if ( ( 7 - first ) < minDays ) {
jan1InPrevYear = TRUE ;
}
if ( ( 7 - nextFirst ) < minDays ) {
nextJan1InPrevYear = TRUE ;
}
switch ( bestField ) {
case UCAL_WEEK_OF_YEAR :
if ( woy = = 1 ) {
if ( jan1InPrevYear = = TRUE ) {
// the first week of January is in the previous year
// therefore WOY1 is always solidly within yearWoy
return yearWoy ;
} else {
// First WOY is split between two years
if ( dowLocal < first ) { // we are prior to Jan 1
return yearWoy - 1 ; // previous year
} else {
return yearWoy ; // in this year
}
}
} else if ( woy > = getLeastMaximum ( bestField ) ) {
// we _might_ be in the last week..
int32_t jd = // Calculate JD of our target day:
jan1Start + // JD of Jan 1
( 7 - first ) + // days in the first week (Jan 1.. )
( woy - 1 ) * 7 + // add the weeks of the year
dowLocal ; // the local dow (0..6) of last week
if ( jan1InPrevYear = = FALSE ) {
jd - = 7 ; // woy already includes Jan 1's week.
}
if ( ( jd + 1 ) > = nextJan1Start ) {
// we are in week 52 or 53 etc. - actual year is yearWoy+1
return yearWoy + 1 ;
} else {
// still in yearWoy;
return yearWoy ;
}
} else {
// we're not possibly in the last week -must be ywoy
return yearWoy ;
}
break ;
case UCAL_DATE :
if ( ( internalGet ( UCAL_MONTH ) = = 0 ) & &
( woy > = getLeastMaximum ( UCAL_WEEK_OF_YEAR ) ) ) {
return yearWoy + 1 ; // month 0, late woy = in the next year
} else if ( woy = = 1 ) {
//if(nextJan1InPrevYear) {
if ( internalGet ( UCAL_MONTH ) = = 0 ) {
return yearWoy ;
} else {
return yearWoy - 1 ;
}
//}
}
//(internalGet(UCAL_DATE) <= (7-first)) /* && in minDow */ ) {
//within 1st week and in this month..
//return yearWoy+1;
return yearWoy ;
break ;
default : // assume the year is appropriate
return yearWoy ;
break ;
}
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d - forgot a return on field %s \n " , __FILE__ , __LINE__ , fldName ( bestField ) ) ;
# endif
return yearWoy ;
}
int32_t Calendar : : handleGetMonthLength ( int32_t extendedYear , int32_t month ) const
{
return handleComputeMonthStart ( extendedYear , month + 1 , TRUE ) -
handleComputeMonthStart ( extendedYear , month , TRUE ) ;
}
int32_t Calendar : : handleGetYearLength ( int32_t eyear ) const {
2003-10-28 09:49:43 +00:00
return handleComputeMonthStart ( eyear + 1 , 0 , FALSE ) -
handleComputeMonthStart ( eyear , 0 , FALSE ) ;
2003-10-26 10:24:58 +00:00
}
1999-08-16 21:50:52 +00:00
int32_t
2003-02-21 08:32:22 +00:00
Calendar : : getActualMaximum ( UCalendarDateFields field , UErrorCode & status ) const
1999-08-16 21:50:52 +00:00
{
2003-10-26 10:24:58 +00:00
int32_t result ;
switch ( field ) {
case UCAL_DATE :
{
if ( U_FAILURE ( status ) ) return 0 ;
Calendar * cal = clone ( ) ;
if ( ! cal ) { status = U_MEMORY_ALLOCATION_ERROR ; return 0 ; }
cal - > prepareGetActual ( field , FALSE , status ) ;
result = handleGetMonthLength ( cal - > get ( UCAL_EXTENDED_YEAR , status ) , cal - > get ( UCAL_MONTH , status ) ) ;
delete cal ;
}
break ;
1999-08-16 21:50:52 +00:00
2003-10-26 10:24:58 +00:00
case UCAL_DAY_OF_YEAR :
{
if ( U_FAILURE ( status ) ) return 0 ;
Calendar * cal = clone ( ) ;
if ( ! cal ) { status = U_MEMORY_ALLOCATION_ERROR ; return 0 ; }
cal - > prepareGetActual ( field , FALSE , status ) ;
result = handleGetYearLength ( cal - > get ( UCAL_EXTENDED_YEAR , status ) ) ;
delete cal ;
}
break ;
case DAY_OF_WEEK :
case AM_PM :
case HOUR :
case HOUR_OF_DAY :
case MINUTE :
case SECOND :
case MILLISECOND :
case ZONE_OFFSET :
case DST_OFFSET :
case DOW_LOCAL :
2003-11-12 09:01:41 +00:00
case UCAL_JULIAN_DAY :
case UCAL_MILLISECONDS_IN_DAY :
2003-10-26 10:24:58 +00:00
// These fields all have fixed minima/maxima
result = getMaximum ( field ) ;
break ;
default :
// For all other fields, do it the hard way....
result = getActualHelper ( field , getLeastMaximum ( field ) , getMaximum ( field ) , status ) ;
break ;
1999-08-16 21:50:52 +00:00
}
2003-10-26 10:24:58 +00:00
return result ;
}
/**
* Prepare this calendar for computing the actual minimum or maximum .
* This method modifies this calendar ' s fields ; it is called on a
* temporary calendar .
*
* < p > Rationale : The semantics of getActualXxx ( ) is to return the
* maximum or minimum value that the given field can take , taking into
* account other relevant fields . In general these other fields are
* larger fields . For example , when computing the actual maximum
* DATE , the current value of DATE itself is ignored ,
* as is the value of any field smaller .
*
* < p > The time fields all have fixed minima and maxima , so we don ' t
* need to worry about them . This also lets us set the
* MILLISECONDS_IN_DAY to zero to erase any effects the time fields
* might have when computing date fields .
*
* < p > DAY_OF_WEEK is adjusted specially for the WEEK_OF_MONTH and
* WEEK_OF_YEAR fields to ensure that they are computed correctly .
* @ internal
*/
void Calendar : : prepareGetActual ( UCalendarDateFields field , UBool isMinimum , UErrorCode & status )
{
set ( UCAL_MILLISECONDS_IN_DAY , 0 ) ;
switch ( field ) {
case UCAL_YEAR :
case UCAL_YEAR_WOY :
case UCAL_EXTENDED_YEAR :
set ( UCAL_DAY_OF_YEAR , getGreatestMinimum ( UCAL_DAY_OF_YEAR ) ) ;
break ;
case UCAL_MONTH :
set ( UCAL_DATE , getGreatestMinimum ( UCAL_DATE ) ) ;
break ;
case UCAL_DAY_OF_WEEK_IN_MONTH :
// For dowim, the maximum occurs for the DOW of the first of the
// month.
set ( UCAL_DATE , 1 ) ;
set ( UCAL_DAY_OF_WEEK , get ( UCAL_DAY_OF_WEEK , status ) ) ; // Make this user set
break ;
case UCAL_WEEK_OF_MONTH :
case UCAL_WEEK_OF_YEAR :
// If we're counting weeks, set the day of the week to either the
// first or last localized DOW. We know the last week of a month
// or year will contain the first day of the week, and that the
// first week will contain the last DOW.
{
int32_t dow = fFirstDayOfWeek ;
if ( isMinimum ) {
dow = ( dow + 6 ) % 7 ; // set to last DOW
if ( dow < UCAL_SUNDAY ) {
dow + = 7 ;
}
}
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " prepareGetActualHelper(WOM/WOY) - dow=%d \n " , dow ) ;
# endif
set ( UCAL_DAY_OF_WEEK , dow ) ;
}
break ;
2003-10-28 00:04:00 +00:00
default :
2004-11-11 23:34:58 +00:00
;
2003-10-26 10:24:58 +00:00
}
// Do this last to give it the newest time stamp
set ( field , getGreatestMinimum ( field ) ) ;
}
int32_t Calendar : : getActualHelper ( UCalendarDateFields field , int32_t startValue , int32_t endValue , UErrorCode & status ) const
{
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " getActualHelper(%d,%d .. %d, %s) \n " , field , startValue , endValue , u_errorName ( status ) ) ;
# endif
if ( startValue = = endValue ) {
// if we know that the maximum value is always the same, just return it
return startValue ;
}
int32_t delta = ( endValue > startValue ) ? 1 : - 1 ;
1999-08-16 21:50:52 +00:00
// clone the calendar so we don't mess with the real one, and set it to
// accept anything for the field values
2003-10-26 10:24:58 +00:00
if ( U_FAILURE ( status ) ) return startValue ;
Calendar * work = clone ( ) ;
if ( ! work ) { status = U_MEMORY_ALLOCATION_ERROR ; return startValue ; }
1999-08-16 21:50:52 +00:00
work - > setLenient ( TRUE ) ;
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d - getActualHelper - %s \n " , __FILE__ , __LINE__ , u_errorName ( status ) ) ;
# endif
work - > prepareGetActual ( field , delta < 0 , status ) ;
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d - getActualHelper - %s \n " , __FILE__ , __LINE__ , u_errorName ( status ) ) ;
# endif
1999-08-16 21:50:52 +00:00
2003-10-26 10:24:58 +00:00
// now try each value from the start to the end one by one until
1999-08-16 21:50:52 +00:00
// we get a value that normalizes to another value. The last value that
// normalizes to itself is the actual maximum for the current date
2003-10-26 10:24:58 +00:00
int32_t result = startValue ;
1999-08-16 21:50:52 +00:00
do {
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d - getActualHelper - %s \n " , __FILE__ , __LINE__ , u_errorName ( status ) ) ;
# endif
work - > set ( field , startValue ) ;
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " %s:%d - getActualHelper - %s (set to %d) \n " , __FILE__ , __LINE__ , u_errorName ( status ) , startValue ) ;
# endif
if ( work - > get ( field , status ) ! = startValue ) {
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " getActualHelper(fld %d) - got %d (not %d), BREAK - %s \n " , field , work - > get ( field , status ) , startValue , u_errorName ( status ) ) ;
# endif
1999-08-16 21:50:52 +00:00
break ;
2003-10-26 10:24:58 +00:00
} else {
result = startValue ;
startValue + = delta ;
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " getActualHelper(%d) result=%d (start), start += %d to %d \n " , field , result , delta , startValue ) ;
# endif
1999-08-16 21:50:52 +00:00
}
2003-10-26 10:24:58 +00:00
} while ( result ! = endValue & & U_SUCCESS ( status ) ) ;
1999-08-16 21:50:52 +00:00
delete work ;
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " getActualHelper(%d) = %d \n " , field , result ) ;
# endif
1999-08-16 21:50:52 +00:00
return result ;
}
2003-10-26 10:24:58 +00:00
1999-08-16 21:50:52 +00:00
// -------------------------------------
void
2004-05-18 22:19:54 +00:00
Calendar : : setWeekCountData ( const Locale & desiredLocale , const char * type , UErrorCode & status )
1999-08-16 21:50:52 +00:00
{
// Read the week count data from the resource bundle. This should
// have the form:
//
2002-02-08 02:48:01 +00:00
// DateTimeElements:intvector {
// 1, // first day of week
// 1 // min days in week
1999-08-16 21:50:52 +00:00
// }
2002-02-08 02:48:01 +00:00
// Both have a range of 1..7
1999-08-16 21:50:52 +00:00
1999-10-18 22:48:32 +00:00
if ( U_FAILURE ( status ) ) return ;
2002-02-08 02:48:01 +00:00
2003-02-21 08:32:22 +00:00
fFirstDayOfWeek = UCAL_SUNDAY ;
2002-02-08 02:48:01 +00:00
fMinimalDaysInFirstWeek = 1 ;
2004-05-18 22:19:54 +00:00
CalendarData calData ( desiredLocale , type , status ) ;
1999-08-16 21:50:52 +00:00
// If the resource data doesn't seem to be present at all, then use last-resort
// hard-coded data.
2005-03-09 05:48:19 +00:00
UResourceBundle * dateTimeElements = calData . getByKey ( gDateTimeElements , status ) ;
2004-05-18 02:06:28 +00:00
1999-10-18 22:48:32 +00:00
if ( U_FAILURE ( status ) )
1999-08-16 21:50:52 +00:00
{
2004-05-18 22:19:54 +00:00
# if defined (U_DEBUG_CALDATA)
fprintf ( stderr , " Failure loading dateTimeElements = %s \n " , u_errorName ( status ) ) ;
# endif
2002-08-21 19:12:24 +00:00
status = U_USING_FALLBACK_WARNING ;
1999-08-16 21:50:52 +00:00
return ;
}
2004-01-20 23:06:38 +00:00
U_LOCALE_BASED ( locBased , * this ) ;
2004-05-18 02:06:28 +00:00
locBased . setLocaleIDs ( ures_getLocaleByType ( dateTimeElements , ULOC_VALID_LOCALE , & status ) ,
ures_getLocaleByType ( dateTimeElements , ULOC_ACTUAL_LOCALE , & status ) ) ;
2002-02-08 02:48:01 +00:00
if ( U_SUCCESS ( status ) ) {
2004-05-18 22:19:54 +00:00
# if defined (U_DEBUG_CAL)
fprintf ( stderr , " Valid=%s, Actual=%s \n " , validLocale , actualLocale ) ;
# endif
2002-02-08 02:48:01 +00:00
int32_t arrLen ;
const int32_t * dateTimeElementsArr = ures_getIntVector ( dateTimeElements , & arrLen , & status ) ;
if ( U_SUCCESS ( status ) & & arrLen = = 2
& & 1 < = dateTimeElementsArr [ 0 ] & & dateTimeElementsArr [ 0 ] < = 7
& & 1 < = dateTimeElementsArr [ 1 ] & & dateTimeElementsArr [ 1 ] < = 7 )
{
2003-02-21 08:32:22 +00:00
fFirstDayOfWeek = ( UCalendarDaysOfWeek ) dateTimeElementsArr [ 0 ] ;
2002-02-08 02:48:01 +00:00
fMinimalDaysInFirstWeek = ( uint8_t ) dateTimeElementsArr [ 1 ] ;
}
else {
status = U_INVALID_FORMAT_ERROR ;
}
1999-08-16 21:50:52 +00:00
}
2004-05-18 02:06:28 +00:00
// do NOT close dateTimeElements
1999-08-16 21:50:52 +00:00
}
/**
* Recompute the time and update the status fields isTimeSet
* and areFieldsSet . Callers should check isTimeSet and only
* call this method if isTimeSet is false .
*/
void
Calendar : : updateTime ( UErrorCode & status )
{
computeTime ( status ) ;
1999-10-18 22:48:32 +00:00
if ( U_FAILURE ( status ) )
1999-08-16 21:50:52 +00:00
return ;
// If we are lenient, we need to recompute the fields to normalize
// the values. Also, if we haven't set all the fields yet (i.e.,
// in a newly-created object), we need to fill in the fields. [LIU]
if ( isLenient ( ) | | ! fAreAllFieldsSet )
fAreFieldsSet = FALSE ;
fIsTimeSet = TRUE ;
2004-03-04 23:24:43 +00:00
fAreFieldsVirtuallySet = FALSE ;
1999-08-16 21:50:52 +00:00
}
2003-11-14 21:08:16 +00:00
Locale
2004-01-20 23:06:38 +00:00
Calendar : : getLocale ( ULocDataLocaleType type , UErrorCode & status ) const {
U_LOCALE_BASED ( locBased , * this ) ;
return locBased . getLocale ( type , status ) ;
2003-11-14 21:08:16 +00:00
}
2004-01-20 23:06:38 +00:00
const char *
Calendar : : getLocaleID ( ULocDataLocaleType type , UErrorCode & status ) const {
U_LOCALE_BASED ( locBased , * this ) ;
return locBased . getLocaleID ( type , status ) ;
2003-11-14 21:08:16 +00:00
}
2003-05-15 22:31:16 +00:00
2001-10-08 23:26:58 +00:00
U_NAMESPACE_END
2002-09-20 01:54:48 +00:00
# endif /* #if !UCONFIG_NO_FORMATTING */
2004-05-18 21:35:50 +00:00
1999-08-16 21:50:52 +00:00
//eof