I would like to propose the following API for: ICU 78
Please provide feedback by: next Wednesday, 2025-04-02
Designated API reviewer: Shane
diff --git a/icu4c/source/i18n/unicode/calendar.h b/icu4c/source/i18n/unicode/calendar.h
index 4499e281f9c5..20363fcc2ae5 100644
--- a/icu4c/source/i18n/unicode/calendar.h
+++ b/icu4c/source/i18n/unicode/calendar.h
@@ -56,6 +56,271 @@ typedef int32_t UFieldResolutionTable[12][8];
class BasicTimeZone;
class CharString;
+
+/**
+ * The WeekRules interface in ICU defines the logic for week-related
+ * calculations in different calendar systems. It manages parameters like the
+ * first day of the week and the minimum days in the first week, supporting
+ * various regional and international week numbering conventions, including the
+ * ISO 8601 standard. This class works with the Calendar class, enabling
+ * customization and adherence to specific week-related rules.
+ */
+class U_I18N_API WeekRules {
+ public:
+ /**
+ * Gets what the first day of the week is; e.g., Sunday in US, Monday in France.
+ *
+ * @param status error code
+ * @return The first day of the week.
+ */
+ virtual UCalendarDaysOfWeek getFirstDayOfWeek(UErrorCode &status) const = 0;
+
+ /**
+ * Gets what the minimal days required in the first week of the year are; e.g., if
+ * the first week is defined as one that contains the first day of the first month
+ * of a year, getMinimalDaysInFirstWeek returns 1. If the minimal days required must
+ * be a full week, getMinimalDaysInFirstWeek returns 7.
+ *
+ * @return The minimal days required in the first week of the year.
+ */
+ virtual uint8_t getMinimalDaysInFirstWeek() const = 0;
+
+ /**
+ * Returns whether the given day of the week is a weekday, a weekend day,
+ * or a day that transitions from one to the other, for the locale and
+ * calendar system associated with this Calendar (the locale's region is
+ * often the most determinant factor). If a transition occurs at midnight,
+ * then the days before and after the transition will have the
+ * type UCAL_WEEKDAY or UCAL_WEEKEND. If a transition occurs at a time
+ * other than midnight, then the day of the transition will have
+ * the type UCAL_WEEKEND_ONSET or UCAL_WEEKEND_CEASE. In this case, the
+ * method getWeekendTransition() will return the point of
+ * transition.
+ * @param dayOfWeek The day of the week whose type is desired (UCAL_SUNDAY..UCAL_SATURDAY).
+ * @param status The error code for the operation.
+ * @return The UCalendarWeekdayType for the day of the week.
+ */
+ virtual UCalendarWeekdayType getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const = 0;
+
+ /**
+ * Returns the time during the day at which the weekend begins or ends in
+ * this calendar system. If getDayOfWeekType() returns UCAL_WEEKEND_ONSET
+ * for the specified dayOfWeek, return the time at which the weekend begins.
+ * If getDayOfWeekType() returns UCAL_WEEKEND_CEASE for the specified dayOfWeek,
+ * return the time at which the weekend ends. If getDayOfWeekType() returns
+ * some other UCalendarWeekdayType for the specified dayOfWeek, is it an error condition
+ * (U_ILLEGAL_ARGUMENT_ERROR).
+ * @param dayOfWeek The day of the week for which the weekend transition time is
+ * desired (UCAL_SUNDAY..UCAL_SATURDAY).
+ * @param status The error code for the operation.
+ * @return The milliseconds after midnight at which the weekend begins or ends.
+ */
+ virtual int32_t getWeekendTransition(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const = 0;
+
+ /**
+ * Returns true if the given UDate is in the weekend in
+ * this calendar system.
+ * @param date The UDate in question.
+ * @param status The error code for the operation.
+ * @return true if the given UDate is in the weekend in
+ * this calendar system, false otherwise.
+ */
+ virtual UBool isWeekend(UDate date, UErrorCode &status) const = 0;
+};
+
+/**
+ * DateFieldRange interface defines permissible boundaries for date/time
+ * components (e.g., month: 1-12). This ensures data integrity within the ICU
+ * library by preventing invalid dates/times during formatting/parsing. It's
+ * also useful for developers when iterating through date/time ranges (e.g.,
+ * generating schedules). Associated with constants like DAY_OF_MONTH, it
+ * provides a structured way to manage date/time component constraints.
+ */
+class U_I18N_API DateFieldRange {
+ public:
+ /**
+ * Gets the minimum value for the given time field. e.g., for Gregorian
+ * DAY_OF_MONTH, 1.
+ *
+ * @param field The given time field.
+ * @return The minimum value for the given time field.
+ */
+ virtual int32_t getMinimum(UCalendarDateFields field) const = 0;
+
+ /**
+ * Gets the maximum value for the given time field. e.g. for Gregorian DAY_OF_MONTH,
+ * 31.
+ *
+ * @param field The given time field.
+ * @return The maximum value for the given time field.
+ */
+ virtual int32_t getMaximum(UCalendarDateFields field) const = 0;
+
+ /**
+ * Gets the highest minimum value for the given field if varies. Otherwise same as
+ * getMinimum(). For Gregorian, no difference.
+ *
+ * @param field The given time field.
+ * @return The highest minimum value for the given time field.
+ */
+ virtual int32_t getGreatestMinimum(UCalendarDateFields field) const = 0;
+
+ /**
+ * Gets the lowest maximum value for the given field if varies. Otherwise same as
+ * getMaximum(). e.g., for Gregorian DAY_OF_MONTH, 28.
+ *
+ * @param field The given time field.
+ * @return The lowest maximum value for the given time field.
+ */
+ virtual int32_t getLeastMaximum(UCalendarDateFields field) const = 0;
+
+ /**
+ * Return the minimum value that this field could have, given the current date.
+ * For the Gregorian calendar, this is the same as getMinimum() and getGreatestMinimum().
+ *
+ * The version of this function on Calendar uses an iterative algorithm to determine the
+ * actual minimum value for the field. There is almost always a more efficient way to
+ * accomplish this (in most cases, you can simply return getMinimum()). GregorianCalendar
+ * overrides this function with a more efficient implementation.
+ *
+ * @param field the field to determine the minimum of
+ * @param status Fill-in parameter which receives the status of this operation.
+ * @return the minimum of the given field for the current date of this Calendar
+ */
+ virtual int32_t getActualMinimum(UCalendarDateFields field, UErrorCode& status) const = 0;
+
+ /**
+ * Return the maximum value that this field could have, given the current date.
+ * For example, with the date "Feb 3, 1997" and the DAY_OF_MONTH field, the actual
+ * maximum would be 28; for "Feb 3, 1996" it s 29. Similarly for a Hebrew calendar,
+ * for some years the actual maximum for MONTH is 12, and for others 13.
+ *
+ * The version of this function on Calendar uses an iterative algorithm to determine the
+ * actual maximum value for the field. There is almost always a more efficient way to
+ * accomplish this (in most cases, you can simply return getMaximum()). GregorianCalendar
+ * overrides this function with a more efficient implementation.
+ *
+ * @param field the field to determine the maximum of
+ * @param status Fill-in parameter which receives the status of this operation.
+ * @return the maximum of the given field for the current date of this Calendar
+ */
+ virtual int32_t getActualMaximum(UCalendarDateFields field, UErrorCode& status) const = 0;
+
+};
+
+/**
+ * The CalendarFieldAccessor class provides an interface to get individual
+ * components (year, month, day, etc.) of a Calendar object. This improves code
+ * maintainability and flexibility.
+ */
+class U_I18N_API CalendarFieldAccessor {
+ public:
+ /**
+ * Gets the value for a given time field.
+ *
+ * @param field The given time field.
+ * @param status Fill-in parameter which receives the status of the operation.
+ * @return The value for the given time field, or zero if the field is unset,
+ * and set() has been called for any other field.
+ */
+ virtual int32_t get(UCalendarDateFields field, UErrorCode& status) const = 0;
+
+ /**
+ * Returns true if this Calendar's current date-time is in the weekend in
+ * this calendar system.
+ * @return true if this Calendar's current date-time is in the weekend in
+ * this calendar system, false otherwise.
+ */
+ virtual UBool isWeekend() const = 0;
+
+ /**
+ * Returns true if the date is in a leap year. Recalculate the current time
+ * field values if the time value has been changed by a call to * setTime().
+ * This method is semantically const, but may alter the object in memory.
+ * A "leap year" is a year that contains more days than other years (for
+ * solar or lunar calendars) or more months than other years (for lunisolar
+ * calendars like Hebrew or Chinese), as defined in the ECMAScript Temporal
+ * proposal.
+ *
+ * @param status ICU Error Code
+ * @return True if the date in the fields is in a Temporal proposal
+ * defined leap year. False otherwise.
+ */
+ virtual bool inTemporalLeapYear(UErrorCode& status) const = 0;
+
+ /**
+ * Gets The Temporal monthCode value corresponding to the month for the date.
+ * The value is a string identifier that starts with the literal grapheme
+ * "M" followed by two graphemes representing the zero-padded month number
+ * of the current month in a normal (non-leap) year and suffixed by an
+ * optional literal grapheme "L" if this is a leap month in a lunisolar
+ * calendar. The 25 possible values are "M01" .. "M13" and "M01L" .. "M12L".
+ * For the Hebrew calendar, the values are "M01" .. "M12" for non-leap year, and
+ * "M01" .. "M05", "M05L", "M06" .. "M12" for leap year.
+ * For the Chinese calendar, the values are "M01" .. "M12" for non-leap year and
+ * in leap year with another monthCode in "M01L" .. "M12L".
+ * For Coptic and Ethiopian calendar, the Temporal monthCode values for any
+ * years are "M01" to "M13".
+ *
+ * @param status ICU Error Code
+ * @return One of 25 possible strings in {"M01".."M13", "M01L".."M12L"}.
+ */
+ virtual const char* getTemporalMonthCode(UErrorCode& status) const = 0;
+
+ /**
+ * Queries if the current date for this Calendar is in Daylight Savings Time.
+ *
+ * @param status Fill-in parameter which receives the status of this operation.
+ * @return True if the current date for this Calendar is in Daylight Savings Time,
+ * false, otherwise.
+ */
+ virtual UBool inDaylightTime(UErrorCode& status) const = 0;
+
+ /**
+ * Gets this Calendar's time as milliseconds. May involve recalculation of time due
+ * to previous calls to set time field values. The time specified is non-local UTC
+ * (GMT) time. Although this method is const, this object may actually be changed
+ * (semantically const).
+ *
+ * @param status Output param set to success/failure code on exit. If any value
+ * previously set in the time field is invalid or restricted by
+ * leniency, this will be set to an error status.
+ * @return The current time in UTC (GMT) time, or zero if the operation
+ * failed.
+ * @stable ICU 2.0
+ */
+ virtual UDate getTime(UErrorCode& status) const = 0;
+};
+
+/**
+ * The CenturyContext class provides a framework for interpreting year values
+ * that are not fully specified with a century, such as a two-digit year. This
+ * class addresses the ambiguity of two-digit years by providing context, such
+ * as a default century or a range of years for interpretation. It is utilized
+ * during date parsing and formatting to ensure accurate conversion between
+ * textual representations of dates and the internal Calendar representation,
+ * particularly when dealing with formats where the century might be omitted.
+ */
+class U_I18N_API CenturyContext {
+ public:
+ /**
+ * @return true if this calendar has a default century (i.e. 03 -> 2003)
+ * @internal
+ */
+ virtual UBool haveDefaultCentury() const = 0;
+
+ /**
+ * @return the start of the default century, as a UDate
+ * @internal
+ */
+ virtual UDate defaultCenturyStart() const = 0;
+ /**
+ * @return the beginning year of the default century, as a year
+ * @internal
+ */
+ virtual int32_t defaultCenturyStartYear() const = 0;
+};
+
/**
* `Calendar` is an abstract base class for converting between
* a `UDate` object and a set of integer fields such as
@@ -187,7 +452,11 @@ class CharString;
*
* @stable ICU 2.0
*/
-class U_I18N_API Calendar : public UObject {
+class U_I18N_API Calendar : public UObject,
+ public CenturyContext,
+ public WeekRules,
+ public DateFieldRange,
+ public CalendarFieldAccessor {
public:
#ifndef U_FORCE_HIDE_DEPRECATED_API
/**
@@ -2413,23 +2682,6 @@ class U_I18N_API Calendar : public UObject {
friend class DefaultCalendarFactory;
#endif /* !UCONFIG_NO_SERVICE */
- /**
- * @return true if this calendar has a default century (i.e. 03 -> 2003)
- * @internal
- */
- virtual UBool haveDefaultCentury() const = 0;
-
- /**
- * @return the start of the default century, as a UDate
- * @internal
- */
- virtual UDate defaultCenturyStart() const = 0;
- /**
- * @return the beginning year of the default century, as a year
- * @internal
- */
- virtual int32_t defaultCenturyStartYear() const = 0;
-
/** Get the locale for this calendar object. You can choose between valid and actual locale.
* @param type type of the locale we're looking for (valid or actual)
* @param status error code for the operation
@@ -2509,6 +2761,214 @@ class U_I18N_API Calendar : public UObject {
#endif /* U_HIDE_INTERNAL_API */
};
+/**
+ * Provides a builder pattern for constructing instances of
+ * CalendarFieldAccessor,simplifying the creation and configuration of field
+ * accessors for Calendar objects.
+ */
+class U_I18N_API FieldAccessorBuilder : public UObject {
+ public:
+ FieldAccessorBuilder(const Locale& locale, UErrorCode &status);
+ virtual ~FieldAccessorBuilder();
+
+ FieldAccessorBuilder& adoptCalendar (Calendar *value, UErrorCode &status);
+ FieldAccessorBuilder& setTimeZone(const TimeZone& value, UErrorCode &status);
+ FieldAccessorBuilder& adoptTimeZone (TimeZone *value, UErrorCode &status);
+
+ /**
+ * Sets this Calendar's current time with the given UDate. The time specified should
+ * be in non-local UTC (GMT) time.
+ *
+ * @param date The given UDate in UTC (GMT) time.
+ * @param status Output param set to success/failure code on exit. If any value
+ * set in the time field is invalid or restricted by
+ * leniency, this will be set to an error status.
+ */
+ FieldAccessorBuilder& setTime(UDate value, UErrorCode &status);
+
+ /**
+ * UDate Arithmetic function. Adds the specified (signed) amount of time to the given
+ * time field, based on the calendar's rules. For example, to subtract 5 days from
+ * the current time of the calendar, call add(Calendar::DATE, -5). When adding on
+ * the month or Calendar::MONTH field, other fields like date might conflict and
+ * need to be changed. For instance, adding 1 month on the date 01/31/96 will result
+ * in 02/29/96.
+ * Adding a positive value always means moving forward in time, so for the Gregorian calendar,
+ * starting with 100 BC and adding +1 to year results in 99 BC (even though this actually reduces
+ * the numeric value of the field itself).
+ *
+ * @param field Specifies which date field to modify.
+ * @param amount The amount of time to be added to the field, in the natural unit
+ * for that field (e.g., days for the day fields, hours for the hour
+ * field.)
+ * @param status Output param set to success/failure code on exit. If any value
+ * previously set in the time field is invalid or restricted by
+ * leniency, this will be set to an error status.
+ */
+ FieldAccessorBuilder& add(UCalendarDateFields field, int32_t amount, UErrorCode& status);
+
+ /**
+ * Time Field Rolling function. Rolls by the given amount on the given
+ * time field. For example, to roll the current date up by one day, call
+ * roll(Calendar::DATE, +1, status). When rolling on the month or
+ * Calendar::MONTH field, other fields like date might conflict and, need to be
+ * changed. For instance, rolling the month up on the date 01/31/96 will result in
+ * 02/29/96. Rolling by a positive value always means rolling forward in time (unless
+ * the limit of the field is reached, in which case it may pin or wrap), so for
+ * Gregorian calendar, starting with 100 BC and rolling the year by + 1 results in 99 BC.
+ * When eras have a definite beginning and end (as in the Chinese calendar, or as in
+ * most eras in the Japanese calendar) then rolling the year past either limit of the
+ * era will cause the year to wrap around. When eras only have a limit at one end,
+ * then attempting to roll the year past that limit will result in pinning the year
+ * at that limit. Note that for most calendars in which era 0 years move forward in
+ * time (such as Buddhist, Hebrew, or Islamic), it is possible for add or roll to
+ * result in negative years for era 0 (that is the only way to represent years before
+ * the calendar epoch).
+ * When rolling on the hour-in-day or Calendar::HOUR_OF_DAY field, it will roll the
+ * hour value in the range between 0 and 23, which is zero-based.
+ * <P>
+ * The only difference between roll() and add() is that roll() does not change
+ * the value of more significant fields when it reaches the minimum or maximum
+ * of its range, whereas add() does.
+ *
+ * @param field The time field.
+ * @param amount Indicates amount to roll.
+ * @param status Output param set to success/failure code on exit. If any value
+ * previously set in the time field is invalid, this will be set to
+ * an error status.
+ */
+ FieldAccessorBuilder& roll(UCalendarDateFields field, int32_t amount, UErrorCode& status);
+
+ /**
+ * Specifies whether or not date/time interpretation is to be lenient. With lenient
+ * interpretation, a date such as "February 942, 1996" will be treated as being
+ * equivalent to the 941st day after February 1, 1996. With strict interpretation,
+ * such dates will cause an error when computing time from the time field values
+ * representing the dates.
+ *
+ * @param lenient True specifies date/time interpretation to be lenient.
+ */
+ FieldAccessorBuilder& setLenient(UBool lenient, UErrorCode& status);
+
+ /**
+ * Sets the behavior for handling wall time repeating multiple times
+ * at negative time zone offset transitions. For example, 1:30 AM on
+ * November 6, 2011 in US Eastern time (America/New_York) occurs twice;
+ * 1:30 AM EDT, then 1:30 AM EST one hour later. When <code>UCAL_WALLTIME_FIRST</code>
+ * is used, the wall time 1:30AM in this example will be interpreted as 1:30 AM EDT
+ * (first occurrence). When <code>UCAL_WALLTIME_LAST</code> is used, it will be
+ * interpreted as 1:30 AM EST (last occurrence). The default value is
+ * <code>UCAL_WALLTIME_LAST</code>.
+ * <p>
+ * <b>Note:</b>When <code>UCAL_WALLTIME_NEXT_VALID</code> is not a valid
+ * option for this. When the argument is neither <code>UCAL_WALLTIME_FIRST</code>
+ * nor <code>UCAL_WALLTIME_LAST</code>, this method has no effect and will keep
+ * the current setting.
+ *
+ * @param option the behavior for handling repeating wall time, either
+ * <code>UCAL_WALLTIME_FIRST</code> or <code>UCAL_WALLTIME_LAST</code>.
+ * @see #getRepeatedWallTimeOption
+ */
+ FieldAccessorBuilder& setRepeatedWallTimeOption(UCalendarWallTimeOption option, UErrorCode& status);
+
+ /**
+ * Sets the behavior for handling skipped wall time at positive time zone offset
+ * transitions. For example, 2:30 AM on March 13, 2011 in US Eastern time (America/New_York)
+ * does not exist because the wall time jump from 1:59 AM EST to 3:00 AM EDT. When
+ * <code>UCAL_WALLTIME_FIRST</code> is used, 2:30 AM is interpreted as 30 minutes before 3:00 AM
+ * EDT, therefore, it will be resolved as 1:30 AM EST. When <code>UCAL_WALLTIME_LAST</code>
+ * is used, 2:30 AM is interpreted as 31 minutes after 1:59 AM EST, therefore, it will be
+ * resolved as 3:30 AM EDT. When <code>UCAL_WALLTIME_NEXT_VALID</code> is used, 2:30 AM will
+ * be resolved as next valid wall time, that is 3:00 AM EDT. The default value is
+ * <code>UCAL_WALLTIME_LAST</code>.
+ * <p>
+ * <b>Note:</b>This option is effective only when this calendar is lenient.
+ * When the calendar is strict, such non-existing wall time will cause an error.
+ *
+ * @param option the behavior for handling skipped wall time at positive time zone
+ * offset transitions, one of <code>UCAL_WALLTIME_FIRST</code>, <code>UCAL_WALLTIME_LAST</code> and
+ * <code>UCAL_WALLTIME_NEXT_VALID</code>.
+ * @see #getSkippedWallTimeOption
+ */
+ FieldAccessorBuilder& setSkippedWallTimeOption(UCalendarWallTimeOption option, UErrorCode& status);
+
+ /**
+ * Sets what the first day of the week is; e.g., Sunday in US, Monday in France.
+ *
+ * @param value The given first day of the week.
+ */
+ FieldAccessorBuilder& setFirstDayOfWeek(UCalendarDaysOfWeek value, UErrorCode& status);
+
+ /**
+ * Sets what the minimal days required in the first week of the year are; For
+ * example, if the first week is defined as one that contains the first day of the
+ * first month of a year, call the method with value 1. If it must be a full week,
+ * use value 7.
+ *
+ * @param value The given minimal days required in the first week of the year.
+ */
+ FieldAccessorBuilder& setMinimalDaysInFirstWeek(uint8_t value, UErrorCode& status);
+
+ /**
+ * Sets the given time field with the given value.
+ *
+ * @param field The given time field.
+ * @param value The value to be set for the given time field.
+ */
+ FieldAccessorBuilder& set(UCalendarDateFields field, int32_t value, UErrorCode& status);
+
+ /**
+ * Clears the values of all the time fields, making them both unset and assigning
+ * them a value of zero. The field values will be determined during the next
+ * resolving of time into time fields.
+ */
+ FieldAccessorBuilder& clear(UErrorCode& status);
+
+ /**
+ * Clears the value in the given time field, both making it unset and assigning it a
+ * value of zero. This field value will be determined during the next resolving of
+ * time into time fields. Clearing UCAL_ORDINAL_MONTH or UCAL_MONTH will
+ * clear both fields.
+ *
+ * @param field The time field to be cleared.
+ */
+ FieldAccessorBuilder& clear(UCalendarDateFields field, UErrorCode& status);
+
+ /**
+ * Sets The Temporal monthCode which is a string identifier that starts
+ * with the literal grapheme "M" followed by two graphemes representing
+ * the zero-padded month number of the current month in a normal
+ * (non-leap) year and suffixed by an optional literal grapheme "L" if this
+ * is a leap month in a lunisolar calendar. The 25 possible values are
+ * "M01" .. "M13" and "M01L" .. "M12L". For Hebrew calendar, the values are
+ * "M01" .. "M12" for non-leap years, and "M01" .. "M05", "M05L", "M06"
+ * .. "M12" for leap year.
+ * For the Chinese calendar, the values are "M01" .. "M12" for non-leap year and
+ * in leap year with another monthCode in "M01L" .. "M12L".
+ * For Coptic and Ethiopian calendar, the Temporal monthCode values for any
+ * years are "M01" to "M13".
+ *
+ * @param temporalMonth The value to be set for temporal monthCode.
+ * @param status ICU Error Code
+ */
+ FieldAccessorBuilder& setTemporalMonthCode(const char* temporalMonth, UErrorCode& status);
+
+ /**
+ * Sets the GregorianCalendar change date. This is the point when the switch from
+ * Julian dates to Gregorian dates occurred. Default is 00:00:00 local time, October
+ * 15, 1582. Previous to this time and date will be Julian dates.
+ *
+ * @param date The given Gregorian cutover date.
+ * @param status Output param set to success/failure code on exit.
+ */
+ FieldAccessorBuilder& setGregorianChange(UDate date, UErrorCode& status);
+
+ CalendarFieldAccessor* buildFieldAccessor(UErrorCode& status) const;
+
+ private:
+ LocalPointer<Calendar> fCalendar;
+};
+
// -------------------------------------
inline Calendar*--
You received this message because you are subscribed to the Google Groups "icu-design" group.
To unsubscribe from this group and stop receiving emails from it, send an email to icu-design+...@unicode.org.
To view this discussion visit https://groups.google.com/a/unicode.org/d/msgid/icu-design/CA%2B7fzPGQXv4UpVBsoqqROk7wsws6MW%2B%3DCR4t37C-OTJoDAezoQ%40mail.gmail.com.
For more options, visit https://groups.google.com/a/unicode.org/d/optout.