2 ******************************************************************************
3 * Copyright (C) 2007-2011, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 ******************************************************************************
8 package com.ibm.icu.impl.duration;
10 import java.util.TimeZone;
12 import com.ibm.icu.impl.duration.impl.DataRecord;
13 import com.ibm.icu.impl.duration.impl.PeriodFormatterData;
14 import com.ibm.icu.impl.duration.impl.PeriodFormatterDataService;
17 * Default implementation of PeriodBuilderFactory. This creates builders that
18 * use approximate durations.
20 class BasicPeriodBuilderFactory implements PeriodBuilderFactory {
21 private PeriodFormatterDataService ds;
22 private Settings settings;
24 private static final short allBits = 0xff;
26 BasicPeriodBuilderFactory(PeriodFormatterDataService ds) {
28 this.settings = new Settings();
31 static long approximateDurationOf(TimeUnit unit) {
32 return TimeUnit.approxDurations[unit.ordinal];
38 TimeUnit maxUnit = TimeUnit.YEAR;
39 TimeUnit minUnit = TimeUnit.MILLISECOND;
42 boolean allowZero = true;
43 boolean weeksAloneOnly;
44 boolean allowMillis = true;
46 Settings setUnits(int uset) {
47 if (this.uset == uset) {
50 Settings result = inUse ? copy() : this;
52 result.uset = (short)uset;
54 if ((uset & allBits) == allBits) {
55 result.uset = allBits;
56 result.maxUnit = TimeUnit.YEAR;
57 result.minUnit = TimeUnit.MILLISECOND;
60 for (int i = 0; i < TimeUnit.units.length; ++i) {
61 if (0 != (uset & (1 << i))) {
63 result.maxUnit = TimeUnit.units[i];
69 // currently empty, but this might be transient so no fail
70 result.minUnit = result.maxUnit = null;
72 result.minUnit = TimeUnit.units[lastUnit];
79 short effectiveSet() {
83 return (short)(uset & ~(1 << TimeUnit.MILLISECOND.ordinal));
86 TimeUnit effectiveMinUnit() {
87 if (allowMillis || minUnit != TimeUnit.MILLISECOND) {
90 // -1 to skip millisecond
91 for (int i = TimeUnit.units.length - 1; --i >= 0;) {
92 if (0 != (uset & (1 << i))) {
93 return TimeUnit.units[i];
96 return TimeUnit.SECOND; // default for pathological case
99 Settings setMaxLimit(float maxLimit) {
100 int val = maxLimit <= 0 ? 0 : (int)(maxLimit*1000);
101 if (maxLimit == val) {
104 Settings result = inUse ? copy() : this;
105 result.maxLimit = val;
109 Settings setMinLimit(float minLimit) {
110 int val = minLimit <= 0 ? 0 : (int)(minLimit*1000);
111 if (minLimit == val) {
114 Settings result = inUse ? copy() : this;
115 result.minLimit = val;
119 Settings setAllowZero(boolean allow) {
120 if (this.allowZero == allow) {
123 Settings result = inUse ? copy() : this;
124 result.allowZero = allow;
128 Settings setWeeksAloneOnly(boolean weeksAlone) {
129 if (this.weeksAloneOnly == weeksAlone) {
132 Settings result = inUse ? copy() : this;
133 result.weeksAloneOnly = weeksAlone;
137 Settings setAllowMilliseconds(boolean allowMillis) {
138 if (this.allowMillis == allowMillis) {
141 Settings result = inUse ? copy() : this;
142 result.allowMillis = allowMillis;
146 Settings setLocale(String localeName) {
147 PeriodFormatterData data = ds.get(localeName);
149 .setAllowZero(data.allowZero())
150 .setWeeksAloneOnly(data.weeksAloneOnly())
151 .setAllowMilliseconds(data.useMilliseconds() != DataRecord.EMilliSupport.NO);
154 Settings setInUse() {
159 Period createLimited(long duration, boolean inPast) {
161 long maxUnitDuration = approximateDurationOf(maxUnit);
162 if (duration * 1000 > maxLimit * maxUnitDuration) {
163 return Period.moreThan(maxLimit/1000f, maxUnit).inPast(inPast);
168 TimeUnit emu = effectiveMinUnit();
169 long emud = approximateDurationOf(emu);
170 long eml = (emu == minUnit) ? minLimit :
171 Math.max(1000, (approximateDurationOf(minUnit) * minLimit) / emud);
172 if (duration * 1000 < eml * emud) {
173 return Period.lessThan(eml/1000f, emu).inPast(inPast);
179 public Settings copy() {
180 Settings result = new Settings();
181 result.inUse = inUse;
183 result.maxUnit = maxUnit;
184 result.minUnit = minUnit;
185 result.maxLimit = maxLimit;
186 result.minLimit = minLimit;
187 result.allowZero = allowZero;
188 result.weeksAloneOnly = weeksAloneOnly;
189 result.allowMillis = allowMillis;
194 public PeriodBuilderFactory setAvailableUnitRange(TimeUnit minUnit,
197 for (int i = maxUnit.ordinal; i <= minUnit.ordinal; ++i) {
201 throw new IllegalArgumentException("range " + minUnit + " to " + maxUnit + " is empty");
203 settings = settings.setUnits(uset);
207 public PeriodBuilderFactory setUnitIsAvailable(TimeUnit unit,
209 int uset = settings.uset;
211 uset |= 1 << unit.ordinal;
213 uset &= ~(1 << unit.ordinal);
215 settings = settings.setUnits(uset);
219 public PeriodBuilderFactory setMaxLimit(float maxLimit) {
220 settings = settings.setMaxLimit(maxLimit);
224 public PeriodBuilderFactory setMinLimit(float minLimit) {
225 settings = settings.setMinLimit(minLimit);
229 public PeriodBuilderFactory setAllowZero(boolean allow) {
230 settings = settings.setAllowZero(allow);
234 public PeriodBuilderFactory setWeeksAloneOnly(boolean aloneOnly) {
235 settings = settings.setWeeksAloneOnly(aloneOnly);
239 public PeriodBuilderFactory setAllowMilliseconds(boolean allow) {
240 settings = settings.setAllowMilliseconds(allow);
244 public PeriodBuilderFactory setLocale(String localeName) {
245 settings = settings.setLocale(localeName);
249 public PeriodBuilderFactory setTimeZone(TimeZone timeZone) {
254 private Settings getSettings() {
255 if (settings.effectiveSet() == 0) {
258 return settings.setInUse();
262 * Return a builder that represents relative time in terms of the single
265 * @param unit the single TimeUnit with which to represent times
268 public PeriodBuilder getFixedUnitBuilder(TimeUnit unit) {
269 return FixedUnitBuilder.get(unit, getSettings());
273 * Return a builder that represents relative time in terms of the
274 * largest period less than or equal to the duration.
278 public PeriodBuilder getSingleUnitBuilder() {
279 return SingleUnitBuilder.get(getSettings());
283 * Return a builder that formats the largest one or two periods,
284 * Starting with the largest period less than or equal to the duration.
285 * It formats two periods if the first period has a count < 2
286 * and the next period has a count >= 1.
290 public PeriodBuilder getOneOrTwoUnitBuilder() {
291 return OneOrTwoUnitBuilder.get(getSettings());
295 * Return a builder that formats the given number of periods,
296 * starting with the largest period less than or equal to the
301 public PeriodBuilder getMultiUnitBuilder(int periodCount) {
302 return MultiUnitBuilder.get(periodCount, getSettings());
306 abstract class PeriodBuilderImpl implements PeriodBuilder {
308 protected BasicPeriodBuilderFactory.Settings settings;
310 public Period create(long duration) {
311 return createWithReferenceDate(duration, System.currentTimeMillis());
314 public long approximateDurationOf(TimeUnit unit) {
315 return BasicPeriodBuilderFactory.approximateDurationOf(unit);
318 public Period createWithReferenceDate(long duration, long referenceDate) {
319 boolean inPast = duration < 0;
321 duration = -duration;
323 Period ts = settings.createLimited(duration, inPast);
325 ts = handleCreate(duration, referenceDate, inPast);
327 ts = Period.lessThan(1, settings.effectiveMinUnit()).inPast(inPast);
333 public PeriodBuilder withTimeZone(TimeZone timeZone) {
334 // ignore the time zone
338 public PeriodBuilder withLocale(String localeName) {
339 BasicPeriodBuilderFactory.Settings newSettings = settings.setLocale(localeName);
340 if (newSettings != settings) {
341 return withSettings(newSettings);
346 protected abstract PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse);
348 protected abstract Period handleCreate(long duration, long referenceDate,
351 protected PeriodBuilderImpl(BasicPeriodBuilderFactory.Settings settings) {
352 this.settings = settings;
356 class FixedUnitBuilder extends PeriodBuilderImpl {
357 private TimeUnit unit;
359 public static FixedUnitBuilder get(TimeUnit unit, BasicPeriodBuilderFactory.Settings settingsToUse) {
360 if (settingsToUse != null && (settingsToUse.effectiveSet() & (1 << unit.ordinal)) != 0) {
361 return new FixedUnitBuilder(unit, settingsToUse);
366 FixedUnitBuilder(TimeUnit unit, BasicPeriodBuilderFactory.Settings settings) {
371 protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
372 return get(unit, settingsToUse);
375 protected Period handleCreate(long duration, long referenceDate,
380 long unitDuration = approximateDurationOf(unit);
381 return Period.at((float)((double)duration/unitDuration), unit)
386 class SingleUnitBuilder extends PeriodBuilderImpl {
387 SingleUnitBuilder(BasicPeriodBuilderFactory.Settings settings) {
391 public static SingleUnitBuilder get(BasicPeriodBuilderFactory.Settings settings) {
392 if (settings == null) {
395 return new SingleUnitBuilder(settings);
398 protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
399 return SingleUnitBuilder.get(settingsToUse);
402 protected Period handleCreate(long duration, long referenceDate,
404 short uset = settings.effectiveSet();
405 for (int i = 0; i < TimeUnit.units.length; ++i) {
406 if (0 != (uset & (1 << i))) {
407 TimeUnit unit = TimeUnit.units[i];
408 long unitDuration = approximateDurationOf(unit);
409 if (duration >= unitDuration) {
410 return Period.at((float)((double)duration/unitDuration), unit)
419 class OneOrTwoUnitBuilder extends PeriodBuilderImpl {
420 OneOrTwoUnitBuilder(BasicPeriodBuilderFactory.Settings settings) {
424 public static OneOrTwoUnitBuilder get(BasicPeriodBuilderFactory.Settings settings) {
425 if (settings == null) {
428 return new OneOrTwoUnitBuilder(settings);
431 protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
432 return OneOrTwoUnitBuilder.get(settingsToUse);
435 protected Period handleCreate(long duration, long referenceDate,
437 Period period = null;
438 short uset = settings.effectiveSet();
439 for (int i = 0; i < TimeUnit.units.length; ++i) {
440 if (0 != (uset & (1 << i))) {
441 TimeUnit unit = TimeUnit.units[i];
442 long unitDuration = approximateDurationOf(unit);
443 if (duration >= unitDuration || period != null) {
444 double count = (double)duration/unitDuration;
445 if (period == null) {
447 period = Period.at((float)count, unit);
450 period = Period.at(1, unit).inPast(inPast);
451 duration -= unitDuration;
454 period = period.and((float)count, unit);
465 class MultiUnitBuilder extends PeriodBuilderImpl {
466 private int nPeriods;
468 MultiUnitBuilder(int nPeriods, BasicPeriodBuilderFactory.Settings settings) {
470 this.nPeriods = nPeriods;
473 public static MultiUnitBuilder get(int nPeriods, BasicPeriodBuilderFactory.Settings settings) {
474 if (nPeriods > 0 && settings != null) {
475 return new MultiUnitBuilder(nPeriods, settings);
480 protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
481 return MultiUnitBuilder.get(nPeriods, settingsToUse);
484 protected Period handleCreate(long duration, long referenceDate,
486 Period period = null;
488 short uset = settings.effectiveSet();
489 for (int i = 0; i < TimeUnit.units.length; ++i) {
490 if (0 != (uset & (1 << i))) {
491 TimeUnit unit = TimeUnit.units[i];
495 long unitDuration = approximateDurationOf(unit);
496 if (duration >= unitDuration || n > 0) {
498 double count = (double)duration / unitDuration;
500 count = Math.floor(count);
501 duration -= (long)(count * unitDuration);
503 if (period == null) {
504 period = Period.at((float)count, unit).inPast(inPast);
506 period = period.and((float)count, unit);