1999-08-16 21:50:52 +00:00
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2013-02-18 23:36:57 +00:00
* Copyright ( C ) 1997 - 2013 , 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
*
2013-08-16 17:09:51 +00:00
* Modification History :
1999-08-16 21:50:52 +00:00
*
* 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 .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
2012-06-28 23:28:00 +00:00
# include "utypeinfo.h" // for 'typeid' to work
2010-05-19 17:29:33 +00:00
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"
2012-02-17 23:53:05 +00:00
# include "unicode/basictz.h"
# include "unicode/simpletz.h"
# include "unicode/rbtz.h"
# include "unicode/vtzone.h"
2003-10-26 10:24:58 +00:00
# include "gregoimp.h"
2003-05-16 22:05:41 +00:00
# include "buddhcal.h"
2007-06-29 16:56:34 +00:00
# include "taiwncal.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"
2007-03-19 17:33:20 +00:00
# include "persncal.h"
2007-07-17 21:15:22 +00:00
# include "indiancal.h"
2007-10-25 20:26:34 +00:00
# include "chnsecal.h"
2008-03-22 02:58:07 +00:00
# include "coptccal.h"
2013-02-18 23:36:57 +00:00
# include "dangical.h"
2008-03-22 02:58:07 +00:00
# include "ethpccal.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"
2009-02-14 02:32:41 +00:00
# include "ustrenum.h"
2011-11-14 19:32:51 +00:00
# include "uassert.h"
2012-02-17 23:53:05 +00:00
# include "olsontz.h"
1999-08-16 21:50:52 +00:00
2004-09-12 23:07:29 +00:00
# if !UCONFIG_NO_SERVICE
2011-07-06 04:03:35 +00:00
static icu : : ICULocaleService * gService = NULL ;
2013-09-10 00:13:41 +00:00
static icu : : UInitOnce gServiceInitOnce = U_INITONCE_INITIALIZER ;
2004-09-12 23:07:29 +00:00
# endif
// INTERNAL - for cleanup
U_CDECL_BEGIN
static UBool calendar_cleanup ( void ) {
# if !UCONFIG_NO_SERVICE
2006-09-08 04:02:23 +00:00
if ( gService ) {
delete gService ;
gService = NULL ;
}
2013-05-31 23:50:15 +00:00
gServiceInitOnce . reset ( ) ;
2004-09-12 23:07:29 +00:00
# endif
2006-09-08 04:02:23 +00:00
return TRUE ;
2004-09-12 23:07:29 +00:00
}
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)
2008-01-04 01:21:37 +00:00
/**
* fldName was removed as a duplicate implementation .
* use udbg_ services instead ,
2012-02-21 01:21:01 +00:00
* which depend on include files and library from . . / tools / toolutil , the following circular link :
* CPPFLAGS + = - I $ ( top_srcdir ) / tools / toolutil
* LIBS + = $ ( LIBICUTOOLUTIL )
2008-01-04 01:21:37 +00:00
*/
2012-02-21 01:21:01 +00:00
# include "udbgutil.h"
2008-01-04 01:21:37 +00:00
# include <stdio.h>
2003-10-26 10:24:58 +00:00
/**
2006-09-08 04:02:23 +00:00
* convert a UCalendarDateFields into a string - for debugging
* @ param f field enum
* @ return static string to the field name
* @ internal
*/
2007-07-12 21:53:20 +00:00
2008-01-04 01:21:37 +00:00
const char * fldName ( UCalendarDateFields f ) {
return udbg_enumName ( UDBG_UCalendarDateFields , ( int32_t ) f ) ;
2003-10-26 10:24:58 +00:00
}
2006-07-21 21:59:06 +00:00
# if UCAL_DEBUG_DUMP
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 {
2006-09-08 04:02:23 +00:00
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 ' ,
2005-06-29 16:33:06 +00:00
fAreFieldsVirtuallySet ? ' y ' : ' n ' ,
fTime ) ;
2006-09-08 04:02:23 +00:00
// can add more things here: DST, zone, etc.
2005-06-29 16:33:06 +00:00
fprintf ( stderr , " \n " ) ;
2006-09-08 04:02:23 +00:00
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 " ) ;
}
2005-06-29 16:33:06 +00:00
}
U_CFUNC void ucal_dump ( UCalendar * cal ) {
ucal_dump ( * ( ( Calendar * ) cal ) ) ;
}
2006-07-21 21:59:06 +00:00
# endif
2005-06-29 16:33:06 +00:00
2003-05-15 22:31:16 +00:00
# endif
2010-12-07 23:46:13 +00:00
/* Max value for stamp allowable before recalculation */
# define STAMP_MAX 10000
2010-12-07 17:27:42 +00:00
2009-03-29 05:13:44 +00:00
static const char * const gCalTypes [ ] = {
2004-11-25 00:25:52 +00:00
" gregorian " ,
2009-03-29 05:13:44 +00:00
" japanese " ,
" buddhist " ,
" roc " ,
" persian " ,
" islamic-civil " ,
" islamic " ,
" hebrew " ,
" chinese " ,
" indian " ,
" coptic " ,
" ethiopic " ,
" ethiopic-amete-alem " ,
2011-04-23 16:35:30 +00:00
" iso8601 " ,
2013-02-18 23:36:57 +00:00
" dangi " ,
2013-07-08 06:51:38 +00:00
" islamic-umalqura " ,
" islamic-tbla " ,
" islamic-rgsa " ,
2009-03-29 05:13:44 +00:00
NULL
2004-11-25 00:25:52 +00:00
} ;
2004-05-18 21:35:50 +00:00
2009-03-29 05:13:44 +00:00
// Must be in the order of gCalTypes above
typedef enum ECalType {
CALTYPE_UNKNOWN = - 1 ,
CALTYPE_GREGORIAN = 0 ,
CALTYPE_JAPANESE ,
CALTYPE_BUDDHIST ,
CALTYPE_ROC ,
CALTYPE_PERSIAN ,
CALTYPE_ISLAMIC_CIVIL ,
CALTYPE_ISLAMIC ,
CALTYPE_HEBREW ,
CALTYPE_CHINESE ,
CALTYPE_INDIAN ,
CALTYPE_COPTIC ,
CALTYPE_ETHIOPIC ,
2011-04-23 16:35:30 +00:00
CALTYPE_ETHIOPIC_AMETE_ALEM ,
2013-02-18 23:36:57 +00:00
CALTYPE_ISO8601 ,
2013-07-08 06:51:38 +00:00
CALTYPE_DANGI ,
CALTYPE_ISLAMIC_UMALQURA ,
CALTYPE_ISLAMIC_TBLA ,
CALTYPE_ISLAMIC_RGSA
2009-03-29 05:13:44 +00:00
} ECalType ;
2004-09-12 23:07:29 +00:00
U_NAMESPACE_BEGIN
2009-03-29 05:13:44 +00:00
static ECalType getCalendarType ( const char * s ) {
for ( int i = 0 ; gCalTypes [ i ] ! = NULL ; i + + ) {
if ( uprv_stricmp ( s , gCalTypes [ i ] ) = = 0 ) {
return ( ECalType ) i ;
}
}
return CALTYPE_UNKNOWN ;
}
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 ;
}
2009-03-29 05:13:44 +00:00
ECalType calType = getCalendarType ( keyword ) ;
return ( calType ! = CALTYPE_UNKNOWN ) ;
2004-07-18 01:37:13 +00:00
}
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 ;
}
2009-03-29 05:13:44 +00:00
static ECalType getCalendarTypeForLocale ( const char * locid ) {
UErrorCode status = U_ZERO_ERROR ;
ECalType calType = CALTYPE_UNKNOWN ;
2004-07-18 01:37:13 +00:00
2009-03-29 05:13:44 +00:00
//TODO: ULOC_FULL_NAME is out of date and too small..
char canonicalName [ 256 ] ;
// canonicalize, so grandfathered variant will be transformed to keywords
// e.g ja_JP_TRADITIONAL -> ja_JP@calendar=japanese
int32_t canonicalLen = uloc_canonicalize ( locid , canonicalName , sizeof ( canonicalName ) - 1 , & status ) ;
if ( U_FAILURE ( status ) ) {
return CALTYPE_GREGORIAN ;
}
canonicalName [ canonicalLen ] = 0 ; // terminate
char calTypeBuf [ 32 ] ;
int32_t calTypeBufLen ;
calTypeBufLen = uloc_getKeywordValue ( canonicalName , " calendar " , calTypeBuf , sizeof ( calTypeBuf ) - 1 , & status ) ;
if ( U_SUCCESS ( status ) ) {
calTypeBuf [ calTypeBufLen ] = 0 ;
calType = getCalendarType ( calTypeBuf ) ;
if ( calType ! = CALTYPE_UNKNOWN ) {
return calType ;
}
}
status = U_ZERO_ERROR ;
// when calendar keyword is not available or not supported, read supplementalData
// to get the default calendar type for the locale's region
char region [ ULOC_COUNTRY_CAPACITY ] ;
int32_t regionLen = 0 ;
regionLen = uloc_getCountry ( canonicalName , region , sizeof ( region ) - 1 , & status ) ;
if ( regionLen = = 0 ) {
char fullLoc [ 256 ] ;
2012-02-06 19:57:08 +00:00
uloc_addLikelySubtags ( locid , fullLoc , sizeof ( fullLoc ) - 1 , & status ) ;
2009-03-29 05:13:44 +00:00
regionLen = uloc_getCountry ( fullLoc , region , sizeof ( region ) - 1 , & status ) ;
}
if ( U_FAILURE ( status ) ) {
return CALTYPE_GREGORIAN ;
}
region [ regionLen ] = 0 ;
// Read preferred calendar values from supplementalData calendarPreference
UResourceBundle * rb = ures_openDirect ( NULL , " supplementalData " , & status ) ;
ures_getByKey ( rb , " calendarPreferenceData " , rb , & status ) ;
UResourceBundle * order = ures_getByKey ( rb , region , NULL , & status ) ;
if ( status = = U_MISSING_RESOURCE_ERROR & & rb ! = NULL ) {
status = U_ZERO_ERROR ;
order = ures_getByKey ( rb , " 001 " , NULL , & status ) ;
}
calTypeBuf [ 0 ] = 0 ;
if ( U_SUCCESS ( status ) & & order ! = NULL ) {
// the first calender type is the default for the region
int32_t len = 0 ;
const UChar * uCalType = ures_getStringByIndex ( order , 0 , & len , & status ) ;
2009-04-15 01:57:02 +00:00
if ( len < ( int32_t ) sizeof ( calTypeBuf ) ) {
2009-03-29 05:13:44 +00:00
u_UCharsToChars ( uCalType , calTypeBuf , len ) ;
* ( calTypeBuf + len ) = 0 ; // terminate;
calType = getCalendarType ( calTypeBuf ) ;
}
2006-09-08 04:02:23 +00:00
}
2009-03-29 05:13:44 +00:00
ures_close ( order ) ;
ures_close ( rb ) ;
if ( calType = = CALTYPE_UNKNOWN ) {
// final fallback
calType = CALTYPE_GREGORIAN ;
}
return calType ;
2004-07-18 01:37:13 +00:00
}
2009-03-29 05:13:44 +00:00
static Calendar * createStandardCalendar ( ECalType calType , const Locale & loc , UErrorCode & status ) {
Calendar * cal = NULL ;
switch ( calType ) {
case CALTYPE_GREGORIAN :
cal = new GregorianCalendar ( loc , status ) ;
break ;
case CALTYPE_JAPANESE :
cal = new JapaneseCalendar ( loc , status ) ;
break ;
case CALTYPE_BUDDHIST :
cal = new BuddhistCalendar ( loc , status ) ;
break ;
case CALTYPE_ROC :
cal = new TaiwanCalendar ( loc , status ) ;
break ;
case CALTYPE_PERSIAN :
cal = new PersianCalendar ( loc , status ) ;
break ;
2013-09-17 16:55:02 +00:00
case CALTYPE_ISLAMIC_TBLA :
cal = new IslamicCalendar ( loc , status , IslamicCalendar : : TBLA ) ;
break ;
2009-03-29 05:13:44 +00:00
case CALTYPE_ISLAMIC_CIVIL :
cal = new IslamicCalendar ( loc , status , IslamicCalendar : : CIVIL ) ;
break ;
2013-09-17 16:55:02 +00:00
case CALTYPE_ISLAMIC_RGSA :
// default any region specific not handled individually to islamic
2009-03-29 05:13:44 +00:00
case CALTYPE_ISLAMIC :
cal = new IslamicCalendar ( loc , status , IslamicCalendar : : ASTRONOMICAL ) ;
break ;
2013-08-16 17:09:51 +00:00
case CALTYPE_ISLAMIC_UMALQURA :
cal = new IslamicCalendar ( loc , status , IslamicCalendar : : UMALQURA ) ;
break ;
2009-03-29 05:13:44 +00:00
case CALTYPE_HEBREW :
cal = new HebrewCalendar ( loc , status ) ;
break ;
case CALTYPE_CHINESE :
cal = new ChineseCalendar ( loc , status ) ;
break ;
case CALTYPE_INDIAN :
cal = new IndianCalendar ( loc , status ) ;
break ;
case CALTYPE_COPTIC :
cal = new CopticCalendar ( loc , status ) ;
break ;
case CALTYPE_ETHIOPIC :
cal = new EthiopicCalendar ( loc , status , EthiopicCalendar : : AMETE_MIHRET_ERA ) ;
break ;
case CALTYPE_ETHIOPIC_AMETE_ALEM :
cal = new EthiopicCalendar ( loc , status , EthiopicCalendar : : AMETE_ALEM_ERA ) ;
break ;
2011-04-23 16:35:30 +00:00
case CALTYPE_ISO8601 :
cal = new GregorianCalendar ( loc , status ) ;
cal - > setFirstDayOfWeek ( UCAL_MONDAY ) ;
cal - > setMinimalDaysInFirstWeek ( 4 ) ;
break ;
2013-02-18 23:36:57 +00:00
case CALTYPE_DANGI :
cal = new DangiCalendar ( loc , status ) ;
break ;
2009-03-29 05:13:44 +00:00
default :
status = U_UNSUPPORTED_ERROR ;
}
return cal ;
}
2004-07-18 01:37:13 +00:00
# if !UCONFIG_NO_SERVICE
// -------------------------------------
2003-05-15 22:31:16 +00:00
/**
2006-09-08 04:02:23 +00:00
* a Calendar Factory which creates the " basic " calendar types , that is , those
* shipped with ICU .
*/
2003-05-15 22:31:16 +00:00
class BasicCalendarFactory : public LocaleKeyFactory {
public :
2006-09-08 04:02:23 +00:00
/**
* @ param calendarType static const string ( caller owns storage - will be aliased ) to calendar type
*/
BasicCalendarFactory ( )
: LocaleKeyFactory ( LocaleKeyFactory : : INVISIBLE ) { }
2011-07-26 05:32:25 +00:00
virtual ~ BasicCalendarFactory ( ) ;
2003-05-15 22:31:16 +00:00
protected :
2006-09-08 04:02:23 +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);
//}
virtual void updateVisibleIDs ( Hashtable & result , UErrorCode & status ) const
{
if ( U_SUCCESS ( status ) ) {
2009-03-29 05:13:44 +00:00
for ( int32_t i = 0 ; gCalTypes [ i ] ! = NULL ; i + + ) {
2006-09-08 04:02:23 +00:00
UnicodeString id ( ( UChar ) 0x40 ) ; /* '@' a variant character */
id . append ( UNICODE_STRING_SIMPLE ( " calendar= " ) ) ;
2009-03-29 05:13:44 +00:00
id . append ( UnicodeString ( gCalTypes [ i ] , - 1 , US_INV ) ) ;
2006-09-08 04:02:23 +00:00
result . put ( id , ( void * ) this , status ) ;
}
}
2003-05-15 22:31:16 +00:00
}
2006-09-08 04:02:23 +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
2010-05-19 17:29:33 +00:00
if ( dynamic_cast < const LocaleKey * > ( & key ) = = NULL ) {
2006-09-08 04:02:23 +00:00
fprintf ( stderr , " ::create - not a LocaleKey! \n " ) ;
}
2003-11-24 20:02:11 +00:00
# endif
2006-09-08 04:02:23 +00:00
const LocaleKey & lkey = ( LocaleKey & ) key ;
Locale curLoc ; // current locale
Locale canLoc ; // Canonical locale
2003-05-15 22:31:16 +00:00
2006-09-08 04:02:23 +00:00
lkey . currentLocale ( curLoc ) ;
lkey . canonicalLocale ( canLoc ) ;
2003-05-15 22:31:16 +00:00
2006-09-08 04:02:23 +00:00
char keyword [ ULOC_FULLNAME_CAPACITY ] ;
UnicodeString str ;
2003-05-15 22:31:16 +00:00
2006-09-08 04:02:23 +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
2006-09-08 04:02:23 +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
2006-09-08 04:02:23 +00:00
if ( ! isStandardSupportedKeyword ( keyword , status ) ) { // Do we handle this type?
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC
2006-09-08 04:02:23 +00:00
fprintf ( stderr , " BasicCalendarFactory - not handling %s.[%s] \n " , ( const char * ) curLoc . getName ( ) , tmp ) ;
2003-05-15 22:31:16 +00:00
# endif
2006-09-08 04:02:23 +00:00
return NULL ;
}
2003-05-15 22:31:16 +00:00
2009-03-29 05:13:44 +00:00
return createStandardCalendar ( getCalendarType ( keyword ) , canLoc , status ) ;
2006-09-08 04:02:23 +00:00
}
2003-05-15 22:31:16 +00:00
} ;
2011-07-26 05:32:25 +00:00
BasicCalendarFactory : : ~ BasicCalendarFactory ( ) { }
2003-05-15 22:31:16 +00:00
/**
2006-09-08 04:02:23 +00:00
* A factory which looks up the DefaultCalendar resource to determine which class of calendar to use
*/
2003-05-15 22:31:16 +00:00
class DefaultCalendarFactory : public ICUResourceBundleFactory {
2006-09-08 04:02:23 +00:00
public :
2011-07-26 05:32:25 +00:00
DefaultCalendarFactory ( ) : ICUResourceBundleFactory ( ) { }
virtual ~ DefaultCalendarFactory ( ) ;
2006-09-08 04:02:23 +00:00
protected :
virtual UObject * create ( const ICUServiceKey & key , const ICUService * /*service*/ , UErrorCode & status ) const {
LocaleKey & lkey = ( LocaleKey & ) key ;
Locale loc ;
lkey . currentLocale ( loc ) ;
2009-03-29 05:13:44 +00:00
UnicodeString * ret = new UnicodeString ( ) ;
if ( ret = = NULL ) {
status = U_MEMORY_ALLOCATION_ERROR ;
2006-09-08 04:02:23 +00:00
} else {
2009-03-29 05:13:44 +00:00
ret - > append ( ( UChar ) 0x40 ) ; // '@' is a variant character
ret - > append ( UNICODE_STRING ( " calendar= " , 9 ) ) ;
2011-06-03 05:23:57 +00:00
ret - > append ( UnicodeString ( gCalTypes [ getCalendarTypeForLocale ( loc . getName ( ) ) ] , - 1 , US_INV ) ) ;
2006-09-08 04:02:23 +00:00
}
2009-03-29 05:13:44 +00:00
return ret ;
2003-11-12 22:14:32 +00:00
}
2003-05-15 22:31:16 +00:00
} ;
2011-07-26 05:32:25 +00:00
DefaultCalendarFactory : : ~ DefaultCalendarFactory ( ) { }
2003-05-15 22:31:16 +00:00
// -------------------------------------
class CalendarService : public ICULocaleService {
public :
2006-09-08 04:02:23 +00:00
CalendarService ( )
: ICULocaleService ( UNICODE_STRING_SIMPLE ( " Calendar " ) )
{
UErrorCode status = U_ZERO_ERROR ;
registerFactory ( new DefaultCalendarFactory ( ) , status ) ;
}
2003-05-15 22:31:16 +00:00
2011-07-26 05:32:25 +00:00
virtual ~ CalendarService ( ) ;
2006-09-08 04:02:23 +00:00
virtual UObject * cloneInstance ( UObject * instance ) const {
2010-05-19 17:29:33 +00:00
UnicodeString * s = dynamic_cast < UnicodeString * > ( instance ) ;
if ( s ! = NULL ) {
return s - > clone ( ) ;
2006-09-08 04:02:23 +00:00
} else {
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC_F
2006-09-08 04:02:23 +00:00
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 ) ) ;
2003-05-15 22:31:16 +00:00
# endif
2006-09-08 04:02:23 +00:00
return ( ( Calendar * ) instance ) - > clone ( ) ;
}
2003-05-15 22:31:16 +00:00
}
2006-09-08 04:02:23 +00:00
virtual UObject * handleDefault ( const ICUServiceKey & key , UnicodeString * /*actualID*/ , UErrorCode & status ) const {
LocaleKey & lkey = ( LocaleKey & ) key ;
//int32_t kind = lkey.kind();
2003-05-15 22:31:16 +00:00
2006-09-08 04:02:23 +00:00
Locale loc ;
lkey . canonicalLocale ( loc ) ;
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC
Locale loc2 ;
lkey . currentLocale ( loc2 ) ;
2006-09-08 04:02:23 +00:00
fprintf ( stderr , " CalSvc:handleDefault for currentLoc %s, canloc %s \n " , ( const char * ) loc . getName ( ) , ( const char * ) loc2 . getName ( ) ) ;
2003-05-15 22:31:16 +00:00
# endif
2006-09-08 04:02:23 +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 ;
2006-09-08 04:02:23 +00:00
}
2003-05-15 22:31:16 +00:00
2006-09-08 04:02:23 +00:00
virtual UBool isDefault ( ) const {
return countFactories ( ) = = 1 ;
}
2003-05-15 22:31:16 +00:00
} ;
2011-07-26 05:32:25 +00:00
CalendarService : : ~ CalendarService ( ) { }
2003-05-15 22:31:16 +00:00
// -------------------------------------
2004-10-17 19:46:59 +00:00
static inline UBool
isCalendarServiceUsed ( ) {
2013-05-31 23:50:15 +00:00
return ! gServiceInitOnce . isReset ( ) ;
2004-10-17 19:46:59 +00:00
}
// -------------------------------------
2013-05-31 23:50:15 +00:00
static void U_CALLCONV
initCalendarService ( UErrorCode & status )
2003-05-15 22:31:16 +00:00
{
# ifdef U_DEBUG_CALSVC
2013-08-16 17:09:51 +00:00
fprintf ( stderr , " Spinning up Calendar Service \n " ) ;
2003-05-15 22:31:16 +00:00
# endif
2013-05-31 23:50:15 +00:00
ucln_i18n_registerCleanup ( UCLN_I18N_CALENDAR , calendar_cleanup ) ;
gService = new CalendarService ( ) ;
if ( gService = = NULL ) {
2013-08-16 17:09:51 +00:00
status = U_MEMORY_ALLOCATION_ERROR ;
2013-05-31 23:50:15 +00:00
return ;
2013-08-16 17:09:51 +00:00
}
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC
2013-08-16 17:09:51 +00:00
fprintf ( stderr , " Registering classes.. \n " ) ;
2003-05-15 22:31:16 +00:00
# endif
2013-08-16 17:09:51 +00:00
// Register all basic instances.
2013-05-31 23:50:15 +00:00
gService - > registerFactory ( new BasicCalendarFactory ( ) , status ) ;
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC
2013-08-16 17:09:51 +00:00
fprintf ( stderr , " Done.. \n " ) ;
2003-05-15 22:31:16 +00:00
# endif
2013-08-16 17:09:51 +00:00
if ( U_FAILURE ( status ) ) {
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC
2013-08-16 17:09:51 +00:00
fprintf ( stderr , " err (%s) registering classes, deleting service..... \n " , u_errorName ( status ) ) ;
2003-05-15 22:31:16 +00:00
# endif
2013-05-31 23:50:15 +00:00
delete gService ;
gService = NULL ;
2003-05-15 22:31:16 +00:00
}
2013-08-16 17:09:51 +00:00
}
2013-05-31 23:50:15 +00:00
static ICULocaleService *
getCalendarService ( UErrorCode & status )
{
umtx_initOnce ( gServiceInitOnce , & initCalendarService , status ) ;
2006-09-08 04:02:23 +00:00
return gService ;
2003-05-15 22:31:16 +00:00
}
2003-11-19 05:38:55 +00:00
URegistryKey Calendar : : registerFactory ( ICUServiceFactory * toAdopt , UErrorCode & status )
{
2006-09-08 04:02:23 +00:00
return getCalendarService ( status ) - > registerFactory ( toAdopt , status ) ;
2003-11-19 05:38:55 +00:00
}
UBool Calendar : : unregister ( URegistryKey key , UErrorCode & status ) {
2006-09-08 04:02:23 +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
2007-10-25 20:26:34 +00:00
{ 0 , 0 , 24 * kOneHour - 1 , 24 * kOneHour - 1 } , // MILLISECONDS_IN_DAY
{ 0 , 0 , 1 , 1 } , // IS_LEAP_MONTH
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
2011-01-17 15:27:25 +00:00
static const char gMonthNames [ ] = " monthNames " ;
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 ( ) ,
2006-09-08 04:02:23 +00:00
fIsTimeSet ( FALSE ) ,
fAreFieldsSet ( FALSE ) ,
fAreAllFieldsSet ( FALSE ) ,
fAreFieldsVirtuallySet ( FALSE ) ,
fNextStamp ( ( int32_t ) kMinimumUserStamp ) ,
fTime ( 0 ) ,
fLenient ( TRUE ) ,
2012-02-17 23:53:05 +00:00
fZone ( 0 ) ,
fRepeatedWallTime ( UCAL_WALLTIME_LAST ) ,
fSkippedWallTime ( UCAL_WALLTIME_LAST )
1999-08-16 21:50:52 +00:00
{
clear ( ) ;
fZone = TimeZone : : createDefault ( ) ;
2008-01-09 20:25:21 +00:00
if ( fZone = = NULL ) {
2008-02-23 19:15:18 +00:00
success = U_MEMORY_ALLOCATION_ERROR ;
2008-01-09 20:25:21 +00:00
}
2010-01-25 20:12:19 +00:00
setWeekData ( 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 ( ) ,
2006-09-08 04:02:23 +00:00
fIsTimeSet ( FALSE ) ,
fAreFieldsSet ( FALSE ) ,
fAreAllFieldsSet ( FALSE ) ,
fAreFieldsVirtuallySet ( FALSE ) ,
fNextStamp ( ( int32_t ) kMinimumUserStamp ) ,
fTime ( 0 ) ,
fLenient ( TRUE ) ,
2012-02-17 23:53:05 +00:00
fZone ( 0 ) ,
fRepeatedWallTime ( UCAL_WALLTIME_LAST ) ,
fSkippedWallTime ( UCAL_WALLTIME_LAST )
1999-08-16 21:50:52 +00:00
{
if ( zone = = 0 ) {
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2006-09-08 04:02:23 +00:00
fprintf ( stderr , " %s:%d: ILLEGAL ARG because timezone cannot be 0 \n " ,
__FILE__ , __LINE__ ) ;
2003-10-26 10:24:58 +00:00
# endif
2006-09-08 04:02:23 +00:00
success = U_ILLEGAL_ARGUMENT_ERROR ;
return ;
1999-08-16 21:50:52 +00:00
}
clear ( ) ;
fZone = zone ;
2006-09-08 04:02:23 +00:00
2010-01-25 20:12:19 +00:00
setWeekData ( 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 ( ) ,
2006-09-08 04:02:23 +00:00
fIsTimeSet ( FALSE ) ,
fAreFieldsSet ( FALSE ) ,
fAreAllFieldsSet ( FALSE ) ,
fAreFieldsVirtuallySet ( FALSE ) ,
fNextStamp ( ( int32_t ) kMinimumUserStamp ) ,
fTime ( 0 ) ,
fLenient ( TRUE ) ,
2012-02-17 23:53:05 +00:00
fZone ( 0 ) ,
fRepeatedWallTime ( UCAL_WALLTIME_LAST ) ,
fSkippedWallTime ( UCAL_WALLTIME_LAST )
1999-08-16 21:50:52 +00:00
{
clear ( ) ;
fZone = zone . clone ( ) ;
2008-01-09 20:25:21 +00:00
if ( fZone = = NULL ) {
success = U_MEMORY_ALLOCATION_ERROR ;
}
2010-01-25 20:12:19 +00:00
setWeekData ( 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 ;
2012-02-17 23:53:05 +00:00
fRepeatedWallTime = right . fRepeatedWallTime ;
fSkippedWallTime = right . fSkippedWallTime ;
2008-01-09 20:25:21 +00:00
if ( fZone ! = NULL ) {
2008-02-23 19:15:18 +00:00
delete fZone ;
}
if ( right . fZone ! = NULL ) {
fZone = right . fZone - > clone ( ) ;
2008-01-09 20:25:21 +00:00
}
2004-03-04 23:24:43 +00:00
fFirstDayOfWeek = right . fFirstDayOfWeek ;
fMinimalDaysInFirstWeek = right . fMinimalDaysInFirstWeek ;
2010-01-25 20:12:19 +00:00
fWeekendOnset = right . fWeekendOnset ;
fWeekendOnsetMillis = right . fWeekendOnsetMillis ;
fWeekendCease = right . fWeekendCease ;
fWeekendCeaseMillis = right . fWeekendCeaseMillis ;
2004-03-04 23:24:43 +00:00
fNextStamp = right . fNextStamp ;
2012-03-08 19:29:24 +00:00
uprv_strcpy ( validLocale , right . validLocale ) ;
uprv_strcpy ( actualLocale , right . actualLocale ) ;
1999-08-16 21:50:52 +00:00
}
2006-09-08 04:02:23 +00:00
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 )
{
2006-09-08 04:02:23 +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 )
{
2006-09-08 04:02:23 +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 )
{
2006-09-08 04:02:23 +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 )
{
2009-03-29 05:13:44 +00:00
if ( U_FAILURE ( success ) ) {
return NULL ;
}
2004-10-01 18:59:31 +00:00
Locale actualLoc ;
2009-03-29 05:13:44 +00:00
UObject * u = NULL ;
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
{
2009-03-29 05:13:44 +00:00
u = createStandardCalendar ( getCalendarTypeForLocale ( aLocale . getName ( ) ) , aLocale , success ) ;
2004-10-01 18:59:31 +00:00
}
Calendar * c = NULL ;
2003-05-25 07:12:19 +00:00
2006-09-08 04:02:23 +00:00
if ( U_FAILURE ( success ) | | ! u ) {
delete zone ;
if ( U_SUCCESS ( success ) ) { // Propagate some kind of err
success = U_INTERNAL_PROGRAM_ERROR ;
}
return NULL ;
2003-05-25 07:12:19 +00:00
}
2004-01-22 00:15:12 +00:00
2004-07-18 01:37:13 +00:00
# if !UCONFIG_NO_SERVICE
2010-05-19 17:29:33 +00:00
const UnicodeString * str = dynamic_cast < const UnicodeString * > ( u ) ;
if ( str ! = NULL ) {
2006-09-08 04:02:23 +00:00
// It's a unicode string telling us what type of calendar to load ("gregorian", etc)
// Create a Locale over this string
Locale l ( " " ) ;
2010-05-19 17:29:33 +00:00
LocaleUtility : : initLocaleFromName ( * str , l ) ;
2004-05-18 21:35:50 +00:00
# ifdef U_DEBUG_CALSVC
2006-09-08 04:02:23 +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
2006-09-08 04:02:23 +00:00
Locale actualLoc2 ;
delete u ;
u = NULL ;
// Don't overwrite actualLoc, since the actual loc from this call
// may be something like "@calendar=gregorian" -- TODO investigate
// further...
c = ( Calendar * ) getCalendarService ( success ) - > get ( l , LocaleKey : : KIND_ANY , & actualLoc2 , success ) ;
if ( U_FAILURE ( success ) | | ! c ) {
delete zone ;
if ( U_SUCCESS ( success ) ) {
success = U_INTERNAL_PROGRAM_ERROR ; // Propagate some err
}
return NULL ;
}
2010-05-19 17:29:33 +00:00
str = dynamic_cast < const UnicodeString * > ( c ) ;
if ( str ! = NULL ) {
2006-09-08 04:02:23 +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
2006-09-08 04:02:23 +00:00
char tmp [ 200 ] ;
// Extract a char* out of it..
2010-05-19 17:29:33 +00:00
int32_t len = str - > length ( ) ;
2006-09-08 04:02:23 +00:00
int32_t actLen = sizeof ( tmp ) - 1 ;
if ( len > actLen ) {
len = actLen ;
}
2010-05-19 17:29:33 +00:00
str - > extract ( 0 , len , tmp ) ;
2006-09-08 04:02:23 +00:00
tmp [ len ] = 0 ;
fprintf ( stderr , " err - recursed, 2nd lookup was unistring %s \n " , tmp ) ;
2003-11-19 05:38:55 +00:00
# endif
2006-09-08 04:02:23 +00:00
success = U_MISSING_RESOURCE_ERROR ; // requested a calendar type which could NOT be found.
delete c ;
delete zone ;
return NULL ;
}
2003-05-15 22:31:16 +00:00
# ifdef U_DEBUG_CALSVC
2006-09-08 04:02:23 +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
2010-01-25 20:12:19 +00:00
c - > setWeekData ( aLocale , c - > getType ( ) , success ) ; // set the correct locale (this was an indirected calendar)
2011-04-23 16:35:30 +00:00
char keyword [ ULOC_FULLNAME_CAPACITY ] ;
UErrorCode tmpStatus = U_ZERO_ERROR ;
l . getKeywordValue ( " calendar " , keyword , ULOC_FULLNAME_CAPACITY , tmpStatus ) ;
if ( U_SUCCESS ( tmpStatus ) & & uprv_strcmp ( keyword , " iso8601 " ) = = 0 ) {
c - > setFirstDayOfWeek ( UCAL_MONDAY ) ;
c - > setMinimalDaysInFirstWeek ( 4 ) ;
}
2006-09-08 04:02:23 +00:00
}
else
2004-07-18 01:37:13 +00:00
# endif /* UCONFIG_NO_SERVICE */
2006-09-08 04:02:23 +00:00
{
// a calendar was returned - we assume the factory did the right thing.
c = ( Calendar * ) u ;
}
// Now, reset calendar to default state:
c - > adoptTimeZone ( zone ) ; // Set the correct time zone
c - > setTimeInMillis ( getNow ( ) , success ) ; // let the new calendar have the current time.
2003-06-10 03:58:41 +00:00
2006-09-08 04:02:23 +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 )
{
2006-09-08 04:02:23 +00:00
Calendar * c = createInstance ( aLocale , success ) ;
if ( U_SUCCESS ( success ) & & c ) {
c - > setTimeZone ( zone ) ;
}
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
{
2010-05-19 17:29:33 +00:00
return typeid ( * this ) = = typeid ( other ) & &
2002-08-21 18:33:38 +00:00
fLenient = = other . fLenient & &
2012-02-17 23:53:05 +00:00
fRepeatedWallTime = = other . fRepeatedWallTime & &
fSkippedWallTime = = other . fSkippedWallTime & &
2002-08-21 18:33:38 +00:00
fFirstDayOfWeek = = other . fFirstDayOfWeek & &
fMinimalDaysInFirstWeek = = other . fMinimalDaysInFirstWeek & &
2010-01-25 20:12:19 +00:00
fWeekendOnset = = other . fWeekendOnset & &
fWeekendOnsetMillis = = other . fWeekendOnsetMillis & &
fWeekendCease = = other . fWeekendCease & &
fWeekendCeaseMillis = = other . fWeekendCeaseMillis & &
2002-08-21 18:33:38 +00:00
* 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 | |
2006-09-08 04:02:23 +00:00
getTime ( status ) = = when . getTime ( status ) ) ;
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 : : before ( const Calendar & when , UErrorCode & status ) const
{
return ( this ! = & when & &
2006-09-08 04:02:23 +00:00
getTimeInMillis ( status ) < when . getTimeInMillis ( status ) ) ;
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 : : after ( const Calendar & when , UErrorCode & status ) const
{
return ( this ! = & when & &
2006-09-08 04:02:23 +00:00
getTimeInMillis ( status ) > when . getTimeInMillis ( status ) ) ;
1999-08-16 21:50:52 +00:00
}
// -------------------------------------
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 ) ;
}
// -------------------------------------
2009-09-16 20:55:05 +00:00
StringEnumeration * U_EXPORT2
Calendar : : getKeywordValuesForLocale ( const char * key ,
2009-02-14 02:32:41 +00:00
const Locale & locale , UBool commonlyUsed , UErrorCode & status )
{
// This is a wrapper over ucal_getKeywordValuesForLocale
UEnumeration * uenum = ucal_getKeywordValuesForLocale ( key , locale . getName ( ) ,
commonlyUsed , & status ) ;
if ( U_FAILURE ( status ) ) {
uenum_close ( uenum ) ;
return NULL ;
}
return new UStringEnumeration ( uenum ) ;
}
// -------------------------------------
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
}
// -------------------------------------
/**
2006-09-08 04:02:23 +00:00
* Gets this Calendar ' s current time as a long .
* @ return the current time as UTC milliseconds from the epoch .
*/
1999-08-16 21:50:52 +00:00
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 ;
}
// -------------------------------------
/**
2006-09-08 04:02:23 +00:00
* Sets this Calendar ' s current time from the given long value .
2012-10-08 20:24:50 +00:00
* A status of U_ILLEGAL_ARGUMENT_ERROR is set when millis is
* outside the range permitted by a Calendar object when not in lenient mode .
* when in lenient mode the out of range values are pinned to their respective min / max .
2006-09-08 04:02:23 +00:00
* @ param date the new time in UTC milliseconds from the epoch .
*/
1999-08-16 21:50:52 +00:00
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 ) {
2012-10-08 20:24:50 +00:00
if ( isLenient ( ) ) {
millis = MAX_MILLIS ;
} else {
status = U_ILLEGAL_ARGUMENT_ERROR ;
return ;
}
2004-05-24 20:32:59 +00:00
} else if ( millis < MIN_MILLIS ) {
2012-10-08 20:24:50 +00:00
if ( isLenient ( ) ) {
millis = MIN_MILLIS ;
} else {
status = U_ILLEGAL_ARGUMENT_ERROR ;
return ;
}
2004-05-24 20:32:59 +00:00
}
1999-08-16 21:50:52 +00:00
fTime = millis ;
2004-03-04 23:24:43 +00:00
fAreFieldsSet = fAreAllFieldsSet = FALSE ;
fIsTimeSet = fAreFieldsVirtuallySet = TRUE ;
2010-03-02 16:23:13 +00:00
for ( int32_t i = 0 ; i < UCAL_FIELD_COUNT ; + + i ) {
fFields [ i ] = 0 ;
fStamp [ i ] = kUnset ;
fIsSet [ i ] = FALSE ;
}
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 ;
2010-12-03 19:31:10 +00:00
/* Ensure that the fNextStamp value doesn't go pass max value for int32_t */
2010-12-07 17:27:42 +00:00
if ( fNextStamp = = STAMP_MAX ) {
recalculateStamp ( ) ;
}
fStamp [ field ] = fNextStamp + + ;
1999-08-16 21:50:52 +00:00
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
{
2006-09-08 04:02:23 +00:00
int32_t bestStamp = bestStampSoFar ;
for ( int32_t i = ( int32_t ) first ; i < = ( int32_t ) last ; + + i ) {
if ( fStamp [ i ] > bestStamp ) {
bestStamp = fStamp [ i ] ;
}
2003-10-26 10:24:58 +00:00
}
2006-09-08 04:02:23 +00:00
return bestStamp ;
2003-10-26 10:24:58 +00:00
}
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 ;
}
}
2006-09-08 04:02:23 +00:00
//-------------------------------------------------------------------------
// Protected utility methods for use by subclasses. These are very handy
// for implementing add, roll, and computeFields.
//-------------------------------------------------------------------------
2003-10-26 10:24:58 +00:00
2006-09-08 04:02:23 +00:00
/**
* 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
*/
2003-10-26 10:24:58 +00:00
void Calendar : : pinField ( UCalendarDateFields field , UErrorCode & status ) {
2006-09-08 04:02:23 +00:00
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 ) ;
}
2003-10-26 10:24:58 +00:00
}
void Calendar : : computeFields ( UErrorCode & ec )
{
2006-09-21 01:40:29 +00:00
if ( U_FAILURE ( ec ) ) {
2006-09-08 04:02:23 +00:00
return ;
}
// Compute local wall millis
double localMillis = internalGetTime ( ) ;
int32_t rawOffset , dstOffset ;
getTimeZone ( ) . getOffset ( localMillis , FALSE , rawOffset , dstOffset , ec ) ;
2007-11-28 20:49:58 +00:00
localMillis + = ( rawOffset + dstOffset ) ;
2006-09-08 04:02:23 +00:00
// Mark fields as set. Do this before calling handleComputeFields().
uint32_t mask = //fInternalSetMask;
2006-09-21 01:40:29 +00:00
( 1 < < UCAL_ERA ) |
2006-09-08 04:02:23 +00:00
( 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 ;
fIsSet [ i ] = FALSE ; // Remove later
}
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
2008-09-25 20:24:14 +00:00
int32_t days = ( int32_t ) ClockMath : : floorDivide ( localMillis , ( double ) kOneDay ) ;
2006-09-08 04:02:23 +00:00
internalSet ( UCAL_JULIAN_DAY , days + kEpochStartAsJulianDay ) ;
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2006-09-08 04:02:23 +00:00
//fprintf(stderr, "%s:%d- Hmm! Jules @ %d, as per %.0lf millis\n",
//__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis);
2003-10-26 10:24:58 +00:00
# endif
2006-09-08 04:02:23 +00:00
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.
2007-11-28 20:49:58 +00:00
int32_t millisInDay = ( int32_t ) ( localMillis - ( days * kOneDay ) ) ;
2006-09-08 04:02:23 +00:00
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 ;
2003-10-26 10:24:58 +00:00
}
2004-10-02 03:58:10 +00:00
uint8_t Calendar : : julianDayToDayOfWeek ( double julian )
{
2006-09-08 04:02:23 +00:00
// 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 ) ;
2004-10-02 03:58:10 +00:00
2006-09-08 04:02:23 +00:00
uint8_t result = ( uint8_t ) ( dayOfWeek + ( ( dayOfWeek < 0 ) ? ( 7 + UCAL_SUNDAY ) : UCAL_SUNDAY ) ) ;
return result ;
2004-10-02 03:58:10 +00:00
}
2003-10-26 10:24:58 +00:00
/**
2006-09-08 04:02:23 +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 .
*/
2003-10-26 10:24:58 +00:00
void Calendar : : computeGregorianAndDOWFields ( int32_t julianDay , UErrorCode & ec )
{
2006-09-08 04:02:23 +00:00
computeGregorianFields ( julianDay , ec ) ;
// Compute day of week: JD 0 = Monday
int32_t dow = julianDayToDayOfWeek ( julianDay ) ;
internalSet ( UCAL_DAY_OF_WEEK , dow ) ;
2003-10-26 10:24:58 +00:00
2006-09-08 04:02:23 +00:00
// Calculate 1-based localized day of week
int32_t dowLocal = dow - getFirstDayOfWeek ( ) + 1 ;
if ( dowLocal < 1 ) {
dowLocal + = 7 ;
}
internalSet ( UCAL_DOW_LOCAL , dowLocal ) ;
fFields [ UCAL_DOW_LOCAL ] = dowLocal ;
2003-10-26 10:24:58 +00:00
}
/**
2006-09-08 04:02:23 +00:00
* 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 .
*/
2003-10-26 10:24:58 +00:00
void Calendar : : computeGregorianFields ( int32_t julianDay , UErrorCode & /* ec */ ) {
2006-09-08 04:02:23 +00:00
int32_t gregorianDayOfWeekUnused ;
Grego : : dayToFields ( julianDay - kEpochStartAsJulianDay , fGregorianYear , fGregorianMonth , fGregorianDayOfMonth , gregorianDayOfWeekUnused , fGregorianDayOfYear ) ;
2003-10-26 10:24:58 +00:00
}
/**
2006-09-08 04:02:23 +00:00
* 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 .
*/
2003-10-26 10:24:58 +00:00
void Calendar : : computeWeekFields ( UErrorCode & ec ) {
2006-09-08 04:02:23 +00:00
if ( U_FAILURE ( ec ) ) {
return ;
}
int32_t eyear = fFields [ UCAL_EXTENDED_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.
2008-04-09 18:26:42 +00:00
int32_t yearOfWeekOfYear = eyear ;
2006-09-08 04:02:23 +00:00
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.
int32_t prevDoy = dayOfYear + handleGetYearLength ( eyear - 1 ) ;
woy = weekNumber ( prevDoy , dayOfWeek ) ;
yearOfWeekOfYear - - ;
} else {
int32_t lastDoy = handleGetYearLength ( eyear ) ;
// 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 ) ) {
int32_t lastRelDow = ( relDow + lastDoy - dayOfYear ) % 7 ;
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
int32_t dayOfMonth = fFields [ UCAL_DAY_OF_MONTH ] ;
fFields [ UCAL_WEEK_OF_MONTH ] = weekNumber ( dayOfMonth , dayOfWeek ) ;
fFields [ UCAL_DAY_OF_WEEK_IN_MONTH ] = ( dayOfMonth - 1 ) / 7 + 1 ;
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2006-09-08 04:02:23 +00:00
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 ) ;
2003-10-26 10:24:58 +00:00
# endif
}
int32_t Calendar : : weekNumber ( int32_t desiredDay , int32_t dayOfPeriod , int32_t dayOfWeek )
{
2006-09-08 04:02:23 +00:00
// 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-26 10:24:58 +00:00
}
2003-10-28 01:52:29 +00:00
void Calendar : : handleComputeFields ( int32_t /* julianDay */ , UErrorCode & /* status */ )
2003-10-26 10:24:58 +00:00
{
2006-09-08 04:02:23 +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 ) ;
int32_t era = GregorianCalendar : : AD ;
if ( eyear < 1 ) {
era = GregorianCalendar : : BC ;
eyear = 1 - eyear ;
}
internalSet ( UCAL_ERA , era ) ;
internalSet ( UCAL_YEAR , eyear ) ;
2003-10-26 10:24:58 +00:00
}
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 )
{
2006-09-08 04:02:23 +00:00
roll ( ( UCalendarDateFields ) field , amount , status ) ;
2003-11-24 22:43:31 +00:00
}
2003-10-26 10:24:58 +00:00
void Calendar : : roll ( UCalendarDateFields field , int32_t amount , UErrorCode & status )
{
2006-09-08 04:02:23 +00:00
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 ) ;
// 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 :
2012-06-19 05:42:52 +00:00
{
// * If era==0 and years go backwards in time, change sign of amount.
// * Until we have new API per #9393, we temporarily hardcode knowledge of
// which calendars have era 0 years that go backwards.
UBool era0WithYearsThatGoBackwards = FALSE ;
int32_t era = get ( UCAL_ERA , status ) ;
if ( era = = 0 ) {
const char * calType = getType ( ) ;
if ( uprv_strcmp ( calType , " gregorian " ) = = 0 | | uprv_strcmp ( calType , " roc " ) = = 0 | | uprv_strcmp ( calType , " coptic " ) = = 0 ) {
amount = - amount ;
era0WithYearsThatGoBackwards = TRUE ;
}
}
int32_t newYear = internalGet ( field ) + amount ;
if ( era > 0 | | newYear > = 1 ) {
int32_t maxYear = getActualMaximum ( field , status ) ;
if ( maxYear < 32768 ) {
// this era has real bounds, roll should wrap years
if ( newYear < 1 ) {
newYear = maxYear - ( ( - newYear ) % maxYear ) ;
} else if ( newYear > maxYear ) {
newYear = ( ( newYear - 1 ) % maxYear ) + 1 ;
}
// else era is unbounded, just pin low year instead of wrapping
} else if ( newYear < 1 ) {
newYear = 1 ;
}
// else we are in era 0 with newYear < 1;
// calendars with years that go backwards must pin the year value at 0,
// other calendars can have years < 0 in era 0
} else if ( era0WithYearsThatGoBackwards ) {
newYear = 1 ;
}
set ( field , newYear ) ;
pinField ( UCAL_MONTH , status ) ;
pinField ( UCAL_DAY_OF_MONTH , status ) ;
return ;
}
2006-09-08 04:02:23 +00:00
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
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2006-09-08 04:02:23 +00:00
fprintf ( stderr , " %s:%d: ILLEGAL ARG because of roll on non-rollable field %s \n " ,
__FILE__ , __LINE__ , fldName ( field ) ) ;
2003-10-26 10:24:58 +00:00
# endif
2006-09-08 04:02:23 +00:00
status = U_ILLEGAL_ARGUMENT_ERROR ;
}
2003-10-26 10:24:58 +00:00
}
void Calendar : : add ( EDateFields field , int32_t amount , UErrorCode & status )
{
2006-09-08 04:02:23 +00:00
Calendar : : add ( ( UCalendarDateFields ) field , amount , status ) ;
2003-10-26 10:24:58 +00:00
}
// -------------------------------------
void Calendar : : add ( UCalendarDateFields field , int32_t amount , UErrorCode & status )
{
2006-09-08 04:02:23 +00:00
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
2011-09-14 22:33:22 +00:00
// wrinkle is with DST (and/or a change to the zone's UTC offset, which
// we'll include with DST) -- for some fields, like the DAY_OF_MONTH,
2006-09-08 04:02:23 +00:00
// 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_YEAR_WOY :
2012-06-19 05:42:52 +00:00
{
// * If era=0 and years go backwards in time, change sign of amount.
// * Until we have new API per #9393, we temporarily hardcode knowledge of
// which calendars have era 0 years that go backwards.
// * Note that for UCAL_YEAR (but not UCAL_YEAR_WOY) we could instead handle
// this by applying the amount to the UCAL_EXTENDED_YEAR field; but since
// we would still need to handle UCAL_YEAR_WOY as below, might as well
// also handle UCAL_YEAR the same way.
int32_t era = get ( UCAL_ERA , status ) ;
if ( era = = 0 ) {
const char * calType = getType ( ) ;
if ( uprv_strcmp ( calType , " gregorian " ) = = 0 | | uprv_strcmp ( calType , " roc " ) = = 0 | | uprv_strcmp ( calType , " coptic " ) = = 0 ) {
amount = - amount ;
}
}
}
// Fall through into normal handling
case UCAL_EXTENDED_YEAR :
2006-09-08 04:02:23 +00:00
case UCAL_MONTH :
2012-02-21 01:21:01 +00:00
{
UBool oldLenient = isLenient ( ) ;
setLenient ( TRUE ) ;
2006-09-08 04:02:23 +00:00
set ( field , get ( field , status ) + amount ) ;
pinField ( UCAL_DAY_OF_MONTH , status ) ;
2012-02-21 01:21:01 +00:00
if ( oldLenient = = FALSE ) {
complete ( status ) ; /* force recalculate */
setLenient ( oldLenient ) ;
}
}
return ;
2006-09-08 04:02:23 +00:00
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 :
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2006-09-08 04:02:23 +00:00
fprintf ( stderr , " %s:%d: ILLEGAL ARG because field %s not addable " ,
2003-10-26 10:24:58 +00:00
__FILE__ , __LINE__ , fldName ( field ) ) ;
# endif
2006-09-08 04:02:23 +00:00
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
2011-09-30 21:34:41 +00:00
// appropriate), check the combined DST & ZONE offset before and
// after the add() operation. If it changes, then adjust the millis
// to compensate.
2011-09-30 21:14:46 +00:00
int32_t prevOffset = 0 ;
2006-09-08 04:02:23 +00:00
int32_t hour = 0 ;
if ( keepHourInvariant ) {
2011-09-30 21:14:46 +00:00
prevOffset = get ( UCAL_DST_OFFSET , status ) + get ( UCAL_ZONE_OFFSET , status ) ;
2006-09-08 04:02:23 +00:00
hour = internalGet ( UCAL_HOUR_OF_DAY ) ;
}
setTimeInMillis ( getTimeInMillis ( status ) + delta , status ) ;
if ( keepHourInvariant ) {
2011-09-30 21:14:46 +00:00
int32_t newOffset = get ( UCAL_DST_OFFSET , status ) + get ( UCAL_ZONE_OFFSET , status ) ;
if ( newOffset ! = prevOffset ) {
2006-09-08 04:02:23 +00:00
// We have done an hour-invariant adjustment but the
2011-09-30 21:34:41 +00:00
// combined offset has changed. We adjust millis to keep
// the hour constant. In cases such as midnight after
2006-09-08 04:02:23 +00:00
// a DST change which occurs at midnight, there is the
2011-09-30 21:34:41 +00:00
// danger of adjusting into a different day. To avoid
2006-09-08 04:02:23 +00:00
// this we make the adjustment only if it actually
// maintains the hour.
2012-08-09 21:29:53 +00:00
// When the difference of the previous UTC offset and
// the new UTC offset exceeds 1 full day, we do not want
// to roll over/back the date. For now, this only happens
// in Samoa (Pacific/Apia) on Dec 30, 2011. See ticket:9452.
int32_t adjAmount = prevOffset - newOffset ;
adjAmount = adjAmount > = 0 ? adjAmount % ( int32_t ) kOneDay : - ( - adjAmount % ( int32_t ) kOneDay ) ;
if ( adjAmount ! = 0 ) {
double t = internalGetTime ( ) ;
setTimeInMillis ( t + adjAmount , status ) ;
if ( get ( UCAL_HOUR_OF_DAY , status ) ! = hour ) {
setTimeInMillis ( t , status ) ;
}
2006-09-08 04:02:23 +00:00
}
}
}
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 ) {
2006-09-08 04:02:23 +00:00
return fieldDifference ( when , ( UCalendarDateFields ) field , status ) ;
2003-10-26 10:24:58 +00:00
}
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 ;
2012-04-09 23:54:14 +00:00
} else if ( max < INT32_MAX ) {
2011-04-05 05:26:13 +00:00
min = max ;
2002-04-09 22:06:14 +00:00
max < < = 1 ;
if ( max < 0 ) {
2012-04-09 23:54:14 +00:00
max = INT32_MAX ;
}
} else {
// Field difference too large to fit into int32_t
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2012-04-09 23:54:14 +00:00
fprintf ( stderr , " %s:%d: ILLEGAL ARG because field %s's max too large for int32_t \n " ,
__FILE__ , __LINE__ , fldName ( field ) ) ;
2003-10-26 10:24:58 +00:00
# endif
2012-04-09 23:54: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 ) ) {
2012-04-09 23:54:14 +00:00
int32_t t = min + ( max - min ) / 2 ; // make sure intermediate values don't exceed INT32_MAX
2002-04-09 22:06:14 +00:00
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 {
2011-04-05 05:26:13 +00:00
min = max ;
2002-04-09 22:06:14 +00:00
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)
2006-09-08 04:02:23 +00:00
fprintf ( stderr , " %s:%d: ILLEGAL ARG because field %s's max too large for int32_t \n " ,
__FILE__ , __LINE__ , fldName ( field ) ) ;
2003-10-26 10:24:58 +00:00
# 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 ) ) {
2012-04-09 23:54:14 +00:00
int32_t t = min + ( max - min ) / 2 ; // make sure intermediate values don't exceed INT32_MAX
2002-04-09 22:06:14 +00:00
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 ) ;
2006-09-08 04:02:23 +00:00
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 ;
}
// -------------------------------------
2012-02-17 23:53:05 +00:00
void
Calendar : : setRepeatedWallTimeOption ( UCalendarWallTimeOption option )
{
if ( option = = UCAL_WALLTIME_LAST | | option = = UCAL_WALLTIME_FIRST ) {
fRepeatedWallTime = option ;
}
}
// -------------------------------------
UCalendarWallTimeOption
Calendar : : getRepeatedWallTimeOption ( void ) const
{
return fRepeatedWallTime ;
}
// -------------------------------------
void
Calendar : : setSkippedWallTimeOption ( UCalendarWallTimeOption option )
{
fSkippedWallTime = option ;
}
// -------------------------------------
UCalendarWallTimeOption
Calendar : : getSkippedWallTimeOption ( void ) const
{
return fSkippedWallTime ;
}
// -------------------------------------
1999-08-16 21:50:52 +00:00
void
2003-02-21 08:32:22 +00:00
Calendar : : setFirstDayOfWeek ( UCalendarDaysOfWeek value )
1999-08-16 21:50:52 +00:00
{
2006-09-08 04:02:23 +00:00
if ( fFirstDayOfWeek ! = value & &
2003-05-15 22:31:16 +00:00
value > = UCAL_SUNDAY & & value < = UCAL_SATURDAY ) {
2006-09-08 04:02:23 +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 ;
}
2010-01-15 23:38:12 +00:00
// -------------------------------------
// weekend functions, just dummy implementations for now (for API freeze)
UCalendarWeekdayType
Calendar : : getDayOfWeekType ( UCalendarDaysOfWeek dayOfWeek , UErrorCode & status ) const
{
if ( U_FAILURE ( status ) ) {
return UCAL_WEEKDAY ;
}
2010-01-25 20:12:19 +00:00
if ( dayOfWeek < UCAL_SUNDAY | | dayOfWeek > UCAL_SATURDAY ) {
status = U_ILLEGAL_ARGUMENT_ERROR ;
return UCAL_WEEKDAY ;
}
2013-03-27 22:44:22 +00:00
if ( fWeekendOnset = = fWeekendCease ) {
if ( dayOfWeek ! = fWeekendOnset )
return UCAL_WEEKDAY ;
return ( fWeekendOnsetMillis = = 0 ) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET ;
}
2010-01-25 20:12:19 +00:00
if ( fWeekendOnset < fWeekendCease ) {
if ( dayOfWeek < fWeekendOnset | | dayOfWeek > fWeekendCease ) {
return UCAL_WEEKDAY ;
}
} else {
if ( dayOfWeek > fWeekendCease & & dayOfWeek < fWeekendOnset ) {
return UCAL_WEEKDAY ;
}
}
if ( dayOfWeek = = fWeekendOnset ) {
return ( fWeekendOnsetMillis = = 0 ) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET ;
}
if ( dayOfWeek = = fWeekendCease ) {
2013-03-27 22:44:22 +00:00
return ( fWeekendCeaseMillis > = 86400000 ) ? UCAL_WEEKEND : UCAL_WEEKEND_CEASE ;
2010-01-25 20:12:19 +00:00
}
return UCAL_WEEKEND ;
2010-01-15 23:38:12 +00:00
}
int32_t
Calendar : : getWeekendTransition ( UCalendarDaysOfWeek dayOfWeek , UErrorCode & status ) const
{
if ( U_FAILURE ( status ) ) {
return 0 ;
}
2010-01-25 20:12:19 +00:00
if ( dayOfWeek = = fWeekendOnset ) {
return fWeekendOnsetMillis ;
} else if ( dayOfWeek = = fWeekendCease ) {
return fWeekendCeaseMillis ;
2010-01-15 23:38:12 +00:00
}
2010-01-25 20:12:19 +00:00
status = U_ILLEGAL_ARGUMENT_ERROR ;
2010-01-15 23:38:12 +00:00
return 0 ;
}
UBool
Calendar : : isWeekend ( UDate date , UErrorCode & status ) const
{
if ( U_FAILURE ( status ) ) {
return FALSE ;
}
2010-01-25 20:12:19 +00:00
// clone the calendar so we don't mess with the real one.
Calendar * work = ( Calendar * ) this - > clone ( ) ;
if ( work = = NULL ) {
status = U_MEMORY_ALLOCATION_ERROR ;
return FALSE ;
}
UBool result = FALSE ;
work - > setTime ( date , status ) ;
if ( U_SUCCESS ( status ) ) {
result = work - > isWeekend ( ) ;
}
delete work ;
return result ;
2010-01-15 23:38:12 +00:00
}
UBool
Calendar : : isWeekend ( void ) const
{
2010-01-25 20:12:19 +00:00
UErrorCode status = U_ZERO_ERROR ;
UCalendarDaysOfWeek dayOfWeek = ( UCalendarDaysOfWeek ) get ( UCAL_DAY_OF_WEEK , status ) ;
UCalendarWeekdayType dayType = getDayOfWeekType ( dayOfWeek , status ) ;
if ( U_SUCCESS ( status ) ) {
switch ( dayType ) {
case UCAL_WEEKDAY :
return FALSE ;
case UCAL_WEEKEND :
return TRUE ;
case UCAL_WEEKEND_ONSET :
case UCAL_WEEKEND_CEASE :
// Use internalGet() because the above call to get() populated all fields.
{
int32_t millisInDay = internalGet ( UCAL_MILLISECONDS_IN_DAY ) ;
int32_t transitionMillis = getWeekendTransition ( dayOfWeek , status ) ;
if ( U_SUCCESS ( status ) ) {
return ( dayType = = UCAL_WEEKEND_ONSET ) ?
( millisInDay > = transitionMillis ) :
( millisInDay < transitionMillis ) ;
}
// else fall through, return FALSE
}
default :
break ;
}
}
return FALSE ;
2010-01-15 23:38:12 +00:00
}
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 :
2007-10-25 20:26:34 +00:00
case UCAL_IS_LEAP_MONTH :
2003-10-26 10:24:58 +00:00
return kCalendarLimits [ field ] [ limitType ] ;
2008-04-09 18:26:42 +00:00
case UCAL_WEEK_OF_MONTH :
{
int32_t limit ;
if ( limitType = = UCAL_LIMIT_MINIMUM ) {
limit = getMinimalDaysInFirstWeek ( ) = = 1 ? 1 : 0 ;
} else if ( limitType = = UCAL_LIMIT_GREATEST_MINIMUM ) {
limit = 1 ;
} else {
int32_t minDaysInFirst = getMinimalDaysInFirstWeek ( ) ;
int32_t daysInMonth = handleGetLimit ( UCAL_DAY_OF_MONTH , limitType ) ;
if ( limitType = = UCAL_LIMIT_LEAST_MAXIMUM ) {
limit = ( daysInMonth + ( 7 - minDaysInFirst ) ) / 7 ;
} else { // limitType == UCAL_LIMIT_MAXIMUM
limit = ( daysInMonth + 6 + ( 7 - minDaysInFirst ) ) / 7 ;
}
}
return limit ;
}
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 ( ) ;
2008-01-09 20:25:21 +00:00
if ( work = = NULL ) {
2008-02-23 19:15:18 +00:00
status = U_MEMORY_ALLOCATION_ERROR ;
return 0 ;
2008-01-09 20:25:21 +00:00
}
1999-08-16 21:50:52 +00:00
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
/**
2006-09-08 04:02:23 +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 )
*/
2003-10-26 10:24:58 +00:00
void Calendar : : validateFields ( UErrorCode & status ) {
2006-09-08 04:02:23 +00:00
for ( int32_t field = 0 ; U_SUCCESS ( status ) & & ( field < UCAL_FIELD_COUNT ) ; field + + ) {
2011-01-19 23:31:36 +00:00
if ( fStamp [ field ] > = kMinimumUserStamp ) {
2006-09-08 04:02:23 +00:00
validateField ( ( UCalendarDateFields ) field , status ) ;
}
2003-10-26 10:24:58 +00:00
}
}
/**
2006-09-08 04:02:23 +00:00
* 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 )
*/
2003-10-26 10:24:58 +00:00
void Calendar : : validateField ( UCalendarDateFields field , UErrorCode & status ) {
2006-09-08 04:02:23 +00:00
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 ) {
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2006-09-08 04:02:23 +00:00
fprintf ( stderr , " %s:%d: ILLEGAL ARG because DOW in month cannot be 0 \n " ,
__FILE__ , __LINE__ ) ;
2003-10-26 10:24:58 +00:00
# endif
2006-09-08 04:02:23 +00:00
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 ;
2003-10-26 10:24:58 +00:00
}
}
/**
2006-09-08 04:02:23 +00:00
* 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 ) } .
*/
2003-10-26 10:24:58 +00:00
void Calendar : : validateField ( UCalendarDateFields field , int32_t min , int32_t max , UErrorCode & status )
{
2006-09-08 04:02:23 +00:00
int32_t value = fFields [ field ] ;
if ( value < min | | value > max ) {
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2006-09-08 04:02:23 +00:00
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 ) ;
2003-10-26 10:24:58 +00:00
# endif
2006-09-08 04:02:23 +00:00
status = U_ILLEGAL_ARGUMENT_ERROR ;
return ;
}
2003-10-26 10:24:58 +00:00
}
// -------------------------
2003-10-28 02:30:49 +00:00
const UFieldResolutionTable * Calendar : : getFieldResolutionTable ( ) const {
2006-09-08 04:02:23 +00:00
return kDatePrecedence ;
2003-10-26 10:24:58 +00:00
}
UCalendarDateFields Calendar : : newerField ( UCalendarDateFields defaultField , UCalendarDateFields alternateField ) const
{
2006-09-08 04:02:23 +00:00
if ( fStamp [ alternateField ] > fStamp [ defaultField ] ) {
return alternateField ;
}
return defaultField ;
2003-10-26 10:24:58 +00:00
}
UCalendarDateFields Calendar : : resolveFields ( const UFieldResolutionTable * precedenceTable ) {
2006-09-08 04:02:23 +00:00
int32_t bestField = UCAL_FIELD_COUNT ;
2012-05-09 21:00:40 +00:00
int32_t tempBestField ;
2006-09-08 04:02:23 +00:00
for ( int32_t g = 0 ; precedenceTable [ g ] [ 0 ] [ 0 ] ! = - 1 & & ( bestField = = UCAL_FIELD_COUNT ) ; + + g ) {
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 ) {
2011-11-14 19:32:51 +00:00
U_ASSERT ( precedenceTable [ g ] [ l ] [ i ] < UCAL_FIELD_COUNT ) ;
2006-09-08 04:02:23 +00:00
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 ) {
2012-05-09 21:00:40 +00:00
tempBestField = precedenceTable [ g ] [ l ] [ 0 ] ; // First field refers to entire line
if ( tempBestField > = kResolveRemap ) {
tempBestField & = ( kResolveRemap - 1 ) ;
// This check is needed to resolve some issues with UCAL_YEAR precedence mapping
if ( tempBestField ! = UCAL_DATE | | ( fStamp [ UCAL_WEEK_OF_MONTH ] < fStamp [ tempBestField ] ) ) {
bestField = tempBestField ;
}
} else {
bestField = tempBestField ;
}
if ( bestField = = tempBestField ) {
bestStamp = lineStamp ;
}
2006-09-08 04:02:23 +00:00
}
linesInGroup :
;
2003-10-26 10:24:58 +00:00
}
}
2012-05-09 21:00:40 +00:00
return ( UCalendarDateFields ) bestField ;
2003-10-26 10:24:58 +00:00
}
2003-10-28 02:30:49 +00:00
const UFieldResolutionTable Calendar : : kDatePrecedence [ ] =
2006-09-08 04:02:23 +00:00
{
2003-10-26 10:24:58 +00:00
{
2006-09-08 04:02:23 +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
} ,
{
2006-09-08 04:02:23 +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 } }
2006-09-08 04:02:23 +00:00
} ;
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
{
2006-09-08 04:02:23 +00:00
{
{ UCAL_DAY_OF_WEEK , kResolveSTOP , kResolveSTOP } ,
{ UCAL_DOW_LOCAL , kResolveSTOP , kResolveSTOP } ,
{ kResolveSTOP }
} ,
{ { 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
{
2006-09-08 04:02:23 +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 }
} ,
{ { kResolveSTOP } }
2003-10-26 10:24:58 +00:00
} ;
// -------------------------
void Calendar : : computeTime ( UErrorCode & status ) {
2006-09-08 04:02:23 +00:00
if ( ! isLenient ( ) ) {
validateFields ( status ) ;
2006-10-12 04:42:02 +00:00
if ( U_FAILURE ( status ) ) {
return ;
}
2006-09-08 04:02:23 +00:00
}
2003-10-26 10:24:58 +00:00
2006-09-08 04:02:23 +00:00
// Compute the Julian day
int32_t julianDay = computeJulianDay ( ) ;
2003-10-26 10:24:58 +00:00
2006-09-08 04:02:23 +00:00
double millis = Grego : : julianDayToMillis ( julianDay ) ;
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2008-09-25 20:24:14 +00:00
// int32_t julianInsanityCheck = (int32_t)ClockMath::floorDivide(millis, kOneDay);
2006-09-08 04:02:23 +00:00
// 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);
// }
2003-10-26 10:24:58 +00:00
# endif
2006-09-08 04:02:23 +00:00
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.
if ( fStamp [ UCAL_MILLISECONDS_IN_DAY ] > = ( ( int32_t ) kMinimumUserStamp ) & &
2012-02-17 23:53:05 +00:00
newestStamp ( UCAL_AM_PM , UCAL_MILLISECOND , kUnset ) < = fStamp [ UCAL_MILLISECONDS_IN_DAY ] ) {
millisInDay = internalGet ( UCAL_MILLISECONDS_IN_DAY ) ;
} else {
millisInDay = computeMillisInDay ( ) ;
}
2006-09-08 04:02:23 +00:00
2012-02-17 23:53:05 +00:00
UDate t = 0 ;
if ( fStamp [ UCAL_ZONE_OFFSET ] > = ( ( int32_t ) kMinimumUserStamp ) | | fStamp [ UCAL_DST_OFFSET ] > = ( ( int32_t ) kMinimumUserStamp ) ) {
t = millis + millisInDay - ( internalGet ( UCAL_ZONE_OFFSET ) + internalGet ( UCAL_DST_OFFSET ) ) ;
} else {
2006-09-08 04:02:23 +00:00
// 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.
2012-02-17 23:53:05 +00:00
//
// 1. The positive offset change such as transition into DST.
// Here, a designated time of 2:00 am - 2:59 am does not actually exist.
// For this case, skippedWallTime option specifies the behavior.
// For example, 2:30 am is interpreted as;
// - WALLTIME_LAST(default): 3:30 am (DST) (interpreting 2:30 am as 31 minutes after 1:59 am (STD))
// - WALLTIME_FIRST: 1:30 am (STD) (interpreting 2:30 am as 30 minutes before 3:00 am (DST))
// - WALLTIME_NEXT_VALID: 3:00 am (DST) (next valid time after 2:30 am on a wall clock)
// 2. The negative offset change such as 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).
// For this case, repeatedWallTime option specifies the behavior.
// For example, 1:30 am is interpreted as;
// - WALLTIME_LAST(default): 1:30 am (STD) - latter occurrence
// - WALLTIME_FIRST: 1:30 am (DST) - former occurrence
//
// In addition to above, when calendar is strict (not default), wall time falls into
// the skipped time range will be processed as an error case.
//
// These special cases are mostly handled in #computeZoneOffset(long), except WALLTIME_NEXT_VALID
// at positive offset change. The protected method computeZoneOffset(long) is exposed to Calendar
// subclass implementations and marked as @stable. Strictly speaking, WALLTIME_NEXT_VALID
// should be also handled in the same place, but we cannot change the code flow without deprecating
// the protected method.
//
2006-09-08 04:02:23 +00:00
// We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
// or DST_OFFSET fields; then we use those fields.
2012-02-17 23:53:05 +00:00
if ( ! isLenient ( ) | | fSkippedWallTime = = UCAL_WALLTIME_NEXT_VALID ) {
// When strict, invalidate a wall time falls into a skipped wall time range.
// When lenient and skipped wall time option is WALLTIME_NEXT_VALID,
// the result time will be adjusted to the next valid time (on wall clock).
int32_t zoneOffset = computeZoneOffset ( millis , millisInDay , status ) ;
UDate tmpTime = millis + millisInDay - zoneOffset ;
int32_t raw , dst ;
fZone - > getOffset ( tmpTime , FALSE , raw , dst , status ) ;
if ( U_SUCCESS ( status ) ) {
// zoneOffset != (raw + dst) only when the given wall time fall into
// a skipped wall time range caused by positive zone offset transition.
if ( zoneOffset ! = ( raw + dst ) ) {
if ( ! isLenient ( ) ) {
status = U_ILLEGAL_ARGUMENT_ERROR ;
} else {
U_ASSERT ( fSkippedWallTime = = UCAL_WALLTIME_NEXT_VALID ) ;
// Adjust time to the next valid wall clock time.
// At this point, tmpTime is on or after the zone offset transition causing
// the skipped time range.
BasicTimeZone * btz = getBasicTimeZone ( ) ;
if ( btz ) {
TimeZoneTransition transition ;
UBool hasTransition = btz - > getPreviousTransition ( tmpTime , TRUE , transition ) ;
if ( hasTransition ) {
t = transition . getTime ( ) ;
} else {
// Could not find any transitions.
// Note: This should never happen.
status = U_INTERNAL_PROGRAM_ERROR ;
}
} else {
// If not BasicTimeZone, return unsupported error for now.
// TODO: We may support non-BasicTimeZone in future.
status = U_UNSUPPORTED_ERROR ;
}
}
} else {
t = tmpTime ;
}
}
} else {
t = millis + millisInDay - computeZoneOffset ( millis , millisInDay , status ) ;
}
}
if ( U_SUCCESS ( status ) ) {
internalSetTime ( t ) ;
}
2003-10-26 10:24:58 +00:00
}
/**
2006-09-08 04:02:23 +00:00
* 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
*/
2003-10-26 10:24:58 +00:00
int32_t Calendar : : computeMillisInDay ( ) {
2006-09-21 01:40:29 +00:00
// Do the time portion of the conversion.
2006-09-08 04:02:23 +00:00
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 ;
2006-09-21 01:40:29 +00:00
millisInDay + = internalGet ( UCAL_MILLISECOND ) ; // now have millis
2006-09-08 04:02:23 +00:00
return millisInDay ;
2003-10-26 10:24:58 +00:00
}
/**
2006-09-08 04:02:23 +00:00
* 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
*/
2003-10-26 10:24:58 +00:00
int32_t Calendar : : computeZoneOffset ( double millis , int32_t millisInDay , UErrorCode & ec ) {
2006-09-08 04:02:23 +00:00
int32_t rawOffset , dstOffset ;
2012-02-17 23:53:05 +00:00
UDate wall = millis + millisInDay ;
BasicTimeZone * btz = getBasicTimeZone ( ) ;
if ( btz ) {
int duplicatedTimeOpt = ( fRepeatedWallTime = = UCAL_WALLTIME_FIRST ) ? BasicTimeZone : : kFormer : BasicTimeZone : : kLatter ;
int nonExistingTimeOpt = ( fSkippedWallTime = = UCAL_WALLTIME_FIRST ) ? BasicTimeZone : : kLatter : BasicTimeZone : : kFormer ;
btz - > getOffsetFromLocal ( wall , nonExistingTimeOpt , duplicatedTimeOpt , rawOffset , dstOffset , ec ) ;
} else {
const TimeZone & tz = getTimeZone ( ) ;
// By default, TimeZone::getOffset behaves UCAL_WALLTIME_LAST for both.
tz . getOffset ( wall , TRUE , rawOffset , dstOffset , ec ) ;
UBool sawRecentNegativeShift = FALSE ;
if ( fRepeatedWallTime = = UCAL_WALLTIME_FIRST ) {
// Check if the given wall time falls into repeated time range
UDate tgmt = wall - ( rawOffset + dstOffset ) ;
// Any negative zone transition within last 6 hours?
// Note: The maximum historic negative zone transition is -3 hours in the tz database.
// 6 hour window would be sufficient for this purpose.
int32_t tmpRaw , tmpDst ;
tz . getOffset ( tgmt - 6 * 60 * 60 * 1000 , FALSE , tmpRaw , tmpDst , ec ) ;
int32_t offsetDelta = ( rawOffset + dstOffset ) - ( tmpRaw + tmpDst ) ;
U_ASSERT ( offsetDelta < - 6 * 60 * 60 * 1000 ) ;
if ( offsetDelta < 0 ) {
sawRecentNegativeShift = TRUE ;
// Negative shift within last 6 hours. When UCAL_WALLTIME_FIRST is used and the given wall time falls
// into the repeated time range, use offsets before the transition.
// Note: If it does not fall into the repeated time range, offsets remain unchanged below.
tz . getOffset ( wall + offsetDelta , TRUE , rawOffset , dstOffset , ec ) ;
}
}
if ( ! sawRecentNegativeShift & & fSkippedWallTime = = UCAL_WALLTIME_FIRST ) {
// When skipped wall time option is WALLTIME_FIRST,
// recalculate offsets from the resolved time (non-wall).
// When the given wall time falls into skipped wall time,
// the offsets will be based on the zone offsets AFTER
// the transition (which means, earliest possibe interpretation).
UDate tgmt = wall - ( rawOffset + dstOffset ) ;
tz . getOffset ( tgmt , FALSE , rawOffset , dstOffset , ec ) ;
}
}
2006-09-08 04:02:23 +00:00
return rawOffset + dstOffset ;
2003-10-26 10:24:58 +00:00
}
int32_t Calendar : : computeJulianDay ( )
{
2006-09-08 04:02:23 +00:00
// 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.
if ( fStamp [ UCAL_JULIAN_DAY ] > = ( int32_t ) kMinimumUserStamp ) {
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 ) ;
}
2003-10-26 10:24:58 +00:00
}
2006-09-08 04:02:23 +00:00
UCalendarDateFields bestField = resolveFields ( getFieldResolutionTable ( ) ) ;
if ( bestField = = UCAL_FIELD_COUNT ) {
bestField = UCAL_DAY_OF_MONTH ;
}
2003-10-26 10:24:58 +00:00
2006-09-08 04:02:23 +00:00
return handleComputeJulianDay ( bestField ) ;
2003-10-26 10:24:58 +00:00
}
// -------------------------------------------
int32_t Calendar : : handleComputeJulianDay ( UCalendarDateFields bestField ) {
2006-09-08 04:02:23 +00:00
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 ) ;
}
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2006-09-08 04:02:23 +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
2006-09-08 04:02:23 +00:00
// 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
2007-04-02 20:57:34 +00:00
int32_t month ;
2003-10-26 10:24:58 +00:00
2006-09-08 04:02:23 +00:00
if ( isSet ( UCAL_MONTH ) ) {
2007-04-02 20:57:34 +00:00
month = internalGet ( UCAL_MONTH ) ;
2006-09-08 04:02:23 +00:00
} else {
2008-09-15 21:24:11 +00:00
month = getDefaultMonthInYear ( year ) ;
2006-09-08 04:02:23 +00:00
}
2003-10-26 10:24:58 +00:00
2006-09-08 04:02:23 +00:00
int32_t julianDay = handleComputeMonthStart ( year , useMonth ? month : 0 , useMonth ) ;
2003-10-26 10:24:58 +00:00
2006-09-08 04:02:23 +00:00
if ( bestField = = UCAL_DAY_OF_MONTH ) {
2003-10-26 10:24:58 +00:00
2006-09-08 04:02:23 +00:00
// 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 {
2008-09-15 21:24:11 +00:00
dayOfMonth = getDefaultDayInMonth ( year , month ) ;
2006-09-08 04:02:23 +00:00
}
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 ;
}
2003-10-26 10:24:58 +00:00
} else {
# if defined (U_DEBUG_CAL)
2006-09-08 04:02:23 +00:00
fprintf ( stderr , " %s:%d - bf= %s \n " , __FILE__ , __LINE__ , fldName ( bestField ) ) ;
2003-10-26 10:24:58 +00:00
# endif
2006-09-08 04:02:23 +00:00
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 ;
2003-10-26 10:24:58 +00:00
2006-09-08 04:02:23 +00:00
if ( nextFirst < 0 ) { // 0..6 ldow of Jan 1
nextFirst + = 7 ;
}
if ( woy = = 1 ) { // FIRST WEEK ---------------------------------
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2006-09-08 04:02:23 +00:00
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 ( ) ) ;
2003-10-26 10:24:58 +00:00
# endif
2006-09-08 04:02:23 +00:00
// 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
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2006-09-08 04:02:23 +00:00
fprintf ( stderr , " %s:%d - was going to move JD from %d to %d [d%d] \n " , __FILE__ , __LINE__ ,
julianDay , nextJulianDay , ( nextJulianDay - julianDay ) ) ;
2003-10-26 10:24:58 +00:00
# endif
2006-09-08 04:02:23 +00:00
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 ) ;
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2006-09-08 04:02:23 +00:00
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 ) ;
2003-10-26 10:24:58 +00:00
# endif
2006-09-08 04:02:23 +00:00
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 ;
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2006-09-08 04:02:23 +00:00
fprintf ( stderr , " %s:%d - date now %d, jd%d, ywoy%d \n " ,
__FILE__ , __LINE__ , date , julianDay , year - 1 ) ;
2003-10-26 10:24:58 +00:00
# endif
2006-09-08 04:02:23 +00:00
} /* 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 ;
2003-10-26 10:24:58 +00:00
}
int32_t
2008-09-15 21:24:11 +00:00
Calendar : : getDefaultMonthInYear ( int32_t /*eyear*/ )
2003-10-26 10:24:58 +00:00
{
return 0 ;
}
int32_t
2008-09-15 21:24:11 +00:00
Calendar : : getDefaultDayInMonth ( int32_t /*eyear*/ , int32_t /*month*/ )
2003-10-26 10:24:58 +00:00
{
return 1 ;
}
int32_t Calendar : : getLocalDOW ( )
{
2006-09-21 01:40:29 +00:00
// Get zero-based localized DOW, valid range 0..6. This is the DOW
2006-09-08 04:02:23 +00:00
// we are looking for.
int32_t dowLocal = 0 ;
switch ( resolveFields ( kDOWPrecedence ) ) {
2006-09-21 01:40:29 +00:00
case UCAL_DAY_OF_WEEK :
2006-09-08 04:02:23 +00:00
dowLocal = internalGet ( UCAL_DAY_OF_WEEK ) - fFirstDayOfWeek ;
break ;
2006-09-21 01:40:29 +00:00
case UCAL_DOW_LOCAL :
2006-09-08 04:02:23 +00:00
dowLocal = internalGet ( UCAL_DOW_LOCAL ) - 1 ;
break ;
default :
break ;
}
dowLocal = dowLocal % 7 ;
if ( dowLocal < 0 ) {
dowLocal + = 7 ;
}
return dowLocal ;
2003-10-26 10:24:58 +00:00
}
int32_t Calendar : : handleGetExtendedYearFromWeekFields ( int32_t yearWoy , int32_t woy )
{
2006-09-08 04:02:23 +00:00
// 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 ;
}
2003-10-26 10:24:58 +00:00
case UCAL_DATE :
2006-09-08 04:02:23 +00:00
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 ;
default : // assume the year is appropriate
return yearWoy ;
}
2003-10-26 10:24:58 +00:00
}
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 ) -
2006-09-08 04:02:23 +00:00
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 ; }
2011-01-19 23:31:36 +00:00
cal - > setLenient ( TRUE ) ;
2003-10-26 10:24:58 +00:00
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 ; }
2011-07-05 22:42:14 +00:00
cal - > setLenient ( TRUE ) ;
2003-10-26 10:24:58 +00:00
cal - > prepareGetActual ( field , FALSE , status ) ;
result = handleGetYearLength ( cal - > get ( UCAL_EXTENDED_YEAR , status ) ) ;
delete cal ;
}
break ;
2006-09-21 01:40:29 +00:00
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 :
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 )
2006-09-08 04:02:23 +00:00
{
2003-10-26 10:24:58 +00:00
set ( UCAL_MILLISECONDS_IN_DAY , 0 ) ;
switch ( field ) {
case UCAL_YEAR :
case UCAL_EXTENDED_YEAR :
set ( UCAL_DAY_OF_YEAR , getGreatestMinimum ( UCAL_DAY_OF_YEAR ) ) ;
break ;
2008-04-09 18:26:42 +00:00
case UCAL_YEAR_WOY :
set ( UCAL_WEEK_OF_YEAR , getGreatestMinimum ( UCAL_WEEK_OF_YEAR ) ) ;
2003-10-26 10:24:58 +00:00
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 :
2008-04-09 18:26:42 +00:00
break ;
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)
2006-09-08 04:02:23 +00:00
fprintf ( stderr , " getActualHelper(%d,%d .. %d, %s) \n " , field , startValue , endValue , u_errorName ( status ) ) ;
2003-10-26 10:24:58 +00:00
# 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 ; }
2011-07-05 22:42:14 +00:00
// need to resolve time here, otherwise, fields set for actual limit
// may cause conflict with fields previously set (but not yet resolved).
work - > complete ( status ) ;
1999-08-16 21:50:52 +00:00
work - > setLenient ( TRUE ) ;
2006-09-08 04:02:23 +00:00
work - > prepareGetActual ( field , delta < 0 , status ) ;
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
2008-04-09 18:26:42 +00:00
work - > set ( field , startValue ) ;
// prepareGetActual sets the first day of week in the same week with
// the first day of a month. Unlike WEEK_OF_YEAR, week number for the
// week which contains days from both previous and current month is
// not unique. For example, last several days in the previous month
// is week 5, and the rest of week is week 1.
2003-10-26 10:24:58 +00:00
int32_t result = startValue ;
2010-07-12 18:03:29 +00:00
if ( ( work - > get ( field , status ) ! = startValue
& & field ! = UCAL_WEEK_OF_MONTH & & delta > 0 ) | | U_FAILURE ( status ) ) {
2003-10-26 10:24:58 +00:00
# if defined (U_DEBUG_CAL)
2008-04-09 18:26:42 +00:00
fprintf ( stderr , " getActualHelper(fld %d) - got %d (not %d) - %s \n " , field , work - > get ( field , status ) , startValue , u_errorName ( status ) ) ;
2003-10-26 10:24:58 +00:00
# endif
2008-04-09 18:26:42 +00:00
} else {
do {
2003-10-26 10:24:58 +00:00
startValue + = delta ;
2008-04-09 18:26:42 +00:00
work - > add ( field , delta , status ) ;
if ( work - > get ( field , status ) ! = startValue | | U_FAILURE ( status ) ) {
# 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 ) ) ;
2003-10-26 10:24:58 +00:00
# endif
2008-04-09 18:26:42 +00:00
break ;
}
result = startValue ;
} while ( startValue ! = endValue ) ;
}
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
2010-01-25 20:12:19 +00:00
Calendar : : setWeekData ( const Locale & desiredLocale , const char * type , UErrorCode & status )
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 ;
2010-01-25 20:12:19 +00:00
fWeekendOnset = UCAL_SATURDAY ;
fWeekendOnsetMillis = 0 ;
fWeekendCease = UCAL_SUNDAY ;
fWeekendCeaseMillis = 86400000 ; // 24*60*60*1000
2002-02-08 02:48:01 +00:00
2010-09-20 21:22:31 +00:00
// Since week and weekend data is territory based instead of language based,
// we may need to tweak the locale that we are using to try to get the appropriate
// values, using the following logic:
// 1). If the locale has a language but no territory, use the territory as defined by
// the likely subtags.
// 2). If the locale has a script designation then we ignore it,
// then remove it ( i.e. "en_Latn_US" becomes "en_US" )
2010-09-30 05:44:33 +00:00
char minLocaleID [ ULOC_FULLNAME_CAPACITY ] = { 0 } ;
2010-09-20 21:22:31 +00:00
UErrorCode myStatus = U_ZERO_ERROR ;
2010-09-30 05:44:33 +00:00
2010-09-20 21:22:31 +00:00
uloc_minimizeSubtags ( desiredLocale . getName ( ) , minLocaleID , ULOC_FULLNAME_CAPACITY , & myStatus ) ;
Locale min = Locale : : createFromName ( minLocaleID ) ;
Locale useLocale ;
if ( uprv_strlen ( desiredLocale . getCountry ( ) ) = = 0 | |
2011-05-04 22:34:13 +00:00
( uprv_strlen ( desiredLocale . getScript ( ) ) > 0 & & uprv_strlen ( min . getScript ( ) ) = = 0 ) ) {
2010-09-30 05:44:33 +00:00
char maxLocaleID [ ULOC_FULLNAME_CAPACITY ] = { 0 } ;
2010-09-20 21:22:31 +00:00
myStatus = U_ZERO_ERROR ;
uloc_addLikelySubtags ( desiredLocale . getName ( ) , maxLocaleID , ULOC_FULLNAME_CAPACITY , & myStatus ) ;
Locale max = Locale : : createFromName ( maxLocaleID ) ;
useLocale = Locale ( max . getLanguage ( ) , max . getCountry ( ) ) ;
} else {
useLocale = Locale ( desiredLocale ) ;
}
2011-01-17 15:27:25 +00:00
/* The code here is somewhat of a hack, since week data and weekend data aren't really tied to
a specific calendar , they aren ' t truly locale data . But this is the only place where valid and
actual locale can be set , so we take a shot at it here by loading a representative resource
from the calendar data . The code used to use the dateTimeElements resource to get first day
of week data , but this was moved to supplemental data under ticket 7755. ( JCE ) */
CalendarData calData ( useLocale , type , status ) ;
UResourceBundle * monthNames = calData . getByKey ( gMonthNames , status ) ;
if ( U_SUCCESS ( status ) ) {
U_LOCALE_BASED ( locBased , * this ) ;
locBased . setLocaleIDs ( ures_getLocaleByType ( monthNames , ULOC_VALID_LOCALE , & status ) ,
ures_getLocaleByType ( monthNames , ULOC_ACTUAL_LOCALE , & status ) ) ;
} else {
status = U_USING_FALLBACK_WARNING ;
return ;
}
// Read week data values from supplementalData week data
UResourceBundle * rb = ures_openDirect ( NULL , " supplementalData " , & status ) ;
ures_getByKey ( rb , " weekData " , rb , & status ) ;
UResourceBundle * weekData = ures_getByKey ( rb , useLocale . getCountry ( ) , NULL , & status ) ;
if ( status = = U_MISSING_RESOURCE_ERROR & & rb ! = NULL ) {
status = U_ZERO_ERROR ;
weekData = ures_getByKey ( rb , " 001 " , NULL , & status ) ;
}
2004-05-18 02:06:28 +00:00
2010-01-25 20:12:19 +00:00
if ( U_FAILURE ( status ) ) {
2004-05-18 22:19:54 +00:00
# if defined (U_DEBUG_CALDATA)
2011-01-17 15:27:25 +00:00
fprintf ( stderr , " Failure loading weekData from supplemental = %s \n " , u_errorName ( status ) ) ;
2004-05-18 22:19:54 +00:00
# endif
2002-08-21 19:12:24 +00:00
status = U_USING_FALLBACK_WARNING ;
2010-01-25 20:12:19 +00:00
} else {
2011-01-17 15:27:25 +00:00
int32_t arrLen ;
const int32_t * weekDataArr = ures_getIntVector ( weekData , & arrLen , & status ) ;
if ( U_SUCCESS ( status ) & & arrLen = = 6
& & 1 < = weekDataArr [ 0 ] & & weekDataArr [ 0 ] < = 7
& & 1 < = weekDataArr [ 1 ] & & weekDataArr [ 1 ] < = 7
& & 1 < = weekDataArr [ 2 ] & & weekDataArr [ 2 ] < = 7
& & 1 < = weekDataArr [ 4 ] & & weekDataArr [ 4 ] < = 7 ) {
fFirstDayOfWeek = ( UCalendarDaysOfWeek ) weekDataArr [ 0 ] ;
fMinimalDaysInFirstWeek = ( uint8_t ) weekDataArr [ 1 ] ;
fWeekendOnset = ( UCalendarDaysOfWeek ) weekDataArr [ 2 ] ;
fWeekendOnsetMillis = weekDataArr [ 3 ] ;
fWeekendCease = ( UCalendarDaysOfWeek ) weekDataArr [ 4 ] ;
fWeekendCeaseMillis = weekDataArr [ 5 ] ;
2010-01-25 20:12:19 +00:00
} else {
2011-01-17 15:27:25 +00:00
status = U_INVALID_FORMAT_ERROR ;
2010-01-25 20:12:19 +00:00
}
}
2011-01-17 15:27:25 +00:00
ures_close ( weekData ) ;
ures_close ( rb ) ;
1999-08-16 21:50:52 +00:00
}
/**
2006-09-08 04:02:23 +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 .
*/
1999-08-16 21:50:52 +00:00
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 ;
2006-09-08 04:02:23 +00:00
1999-08-16 21:50:52 +00:00
// 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 ;
2006-09-08 04:02:23 +00:00
1999-08-16 21:50:52 +00:00
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
2010-12-07 17:27:42 +00:00
void
Calendar : : recalculateStamp ( ) {
int32_t index ;
int32_t currentValue ;
int32_t j , i ;
fNextStamp = 1 ;
for ( j = 0 ; j < UCAL_FIELD_COUNT ; j + + ) {
currentValue = STAMP_MAX ;
index = - 1 ;
for ( i = 0 ; i < UCAL_FIELD_COUNT ; i + + ) {
if ( fStamp [ i ] > fNextStamp & & fStamp [ i ] < currentValue ) {
currentValue = fStamp [ i ] ;
index = i ;
}
}
if ( index > = 0 ) {
fStamp [ index ] = + + fNextStamp ;
} else {
break ;
}
}
fNextStamp + + ;
}
2006-09-21 01:40:29 +00:00
// Deprecated function. This doesn't need to be inline.
void
Calendar : : internalSet ( EDateFields field , int32_t value )
{
internalSet ( ( UCalendarDateFields ) field , value ) ;
}
2012-02-17 23:53:05 +00:00
BasicTimeZone *
Calendar : : getBasicTimeZone ( void ) const {
if ( dynamic_cast < const OlsonTimeZone * > ( fZone ) ! = NULL
| | dynamic_cast < const SimpleTimeZone * > ( fZone ) ! = NULL
| | dynamic_cast < const RuleBasedTimeZone * > ( fZone ) ! = NULL
| | dynamic_cast < const VTimeZone * > ( fZone ) ! = NULL ) {
return ( BasicTimeZone * ) fZone ;
}
return NULL ;
}
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
2013-08-16 17:09:51 +00:00