/** * Parses a datetime from the given text, at the given position, saving the result into the fields * of the given ReadWritableInstant. If the parse succeeds, the return value is the new text * position. Note that the parse may succeed without fully reading the text and in this case those * fields that were read will be set. * * <p>Only those fields present in the string will be changed in the specified instant. All other * fields will remain unaltered. Thus if the string only contains a year and a month, then the day * and time will be retained from the input instant. If this is not the behaviour you want, then * reset the fields before calling this method, or use {@link #parseDateTime(String)} or {@link * #parseMutableDateTime(String)}. * * <p>If it fails, the return value is negative, but the instant may still be modified. To * determine the position where the parse failed, apply the one's complement operator (~) on the * return value. * * <p>This parse method ignores the {@link #getDefaultYear() default year} and parses using the * year from the supplied instant based on the chronology and time-zone of the supplied instant. * * <p>The parse will use the chronology of the instant. * * @param instant an instant that will be modified, not null * @param text the text to parse * @param position position to start parsing from * @return new position, negative value means parse failed - apply complement operator (~) to get * position of failure * @throws UnsupportedOperationException if parsing is not supported * @throws IllegalArgumentException if the instant is null * @throws IllegalArgumentException if any field is out of range */ public int parseInto(ReadWritableInstant instant, String text, int position) { InternalParser parser = requireParser(); if (instant == null) { throw new IllegalArgumentException("Instant must not be null"); } long instantMillis = instant.getMillis(); Chronology chrono = instant.getChronology(); int defaultYear = DateTimeUtils.getChronology(chrono).year().get(instantMillis); long instantLocal = instantMillis + chrono.getZone().getOffset(instantMillis); chrono = selectChronology(chrono); DateTimeParserBucket bucket = new DateTimeParserBucket(instantLocal, chrono, iLocale, iPivotYear, defaultYear); int newPos = parser.parseInto(bucket, text, position); instant.setMillis(bucket.computeMillis(false, text)); if (iOffsetParsed && bucket.getOffsetInteger() != null) { int parsedOffset = bucket.getOffsetInteger(); DateTimeZone parsedZone = DateTimeZone.forOffsetMillis(parsedOffset); chrono = chrono.withZone(parsedZone); } else if (bucket.getZone() != null) { chrono = chrono.withZone(bucket.getZone()); } instant.setChronology(chrono); if (iZone != null) { instant.setZone(iZone); } return newPos; }
/** * Parses a date-time from the given text, returning a new MutableDateTime. * * <p>The parse will use the zone and chronology specified on this formatter. * * <p>If the text contains a time zone string then that will be taken into account in adjusting * the time of day as follows. If the {@link #withOffsetParsed()} has been called, then the * resulting DateTime will have a fixed offset based on the parsed time zone. Otherwise the * resulting DateTime will have the zone of this formatter, but the parsed zone may have caused * the time to be adjusted. * * @param text the text to parse, not null * @return the parsed date-time, never null * @throws UnsupportedOperationException if parsing is not supported * @throws IllegalArgumentException if the text to parse is invalid */ public MutableDateTime parseMutableDateTime(String text) { InternalParser parser = requireParser(); Chronology chrono = selectChronology(null); DateTimeParserBucket bucket = new DateTimeParserBucket(0, chrono, iLocale, iPivotYear, iDefaultYear); int newPos = parser.parseInto(bucket, text, 0); if (newPos >= 0) { if (newPos >= text.length()) { long millis = bucket.computeMillis(true, text); if (iOffsetParsed && bucket.getOffsetInteger() != null) { int parsedOffset = bucket.getOffsetInteger(); DateTimeZone parsedZone = DateTimeZone.forOffsetMillis(parsedOffset); chrono = chrono.withZone(parsedZone); } else if (bucket.getZone() != null) { chrono = chrono.withZone(bucket.getZone()); } MutableDateTime dt = new MutableDateTime(millis, chrono); if (iZone != null) { dt.setZone(iZone); } return dt; } } else { newPos = ~newPos; } throw new IllegalArgumentException(FormatUtils.createErrorMessage(text, newPos)); }
@Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PeriodGranularity that = (PeriodGranularity) o; if (hasOrigin != that.hasOrigin) { return false; } if (origin != that.origin) { return false; } if (!chronology.equals(that.chronology)) { return false; } if (!period.equals(that.period)) { return false; } return true; }
private long truncateMillisPeriod(final long t) { // toStandardDuration assumes days are always 24h, and hours are always 60 minutes, // which may not always be the case, e.g if there are daylight saving changes. if (chronology.days().isPrecise() && chronology.hours().isPrecise()) { final long millis = period.toStandardDuration().getMillis(); long offset = t % millis - origin % millis; if (offset < 0) { offset += millis; } return t - offset; } else { throw new UnsupportedOperationException( "Period cannot be converted to milliseconds as some fields mays vary in length with chronology " + chronology.toString()); } }
private long truncateCompoundPeriod(long t) { long current; if (t >= origin) { long next = origin; do { current = next; next = chronology.add(period, current, 1); } while (t >= next); } else { current = origin; do { current = chronology.add(period, current, -1); } while (t < current); } return current; }
private void printTo(Appendable appendable, long instant, Chronology chrono) throws IOException { InternalPrinter printer = requirePrinter(); chrono = selectChronology(chrono); // Shift instant into local time (UTC) to avoid excessive offset // calculations when printing multiple fields in a composite printer. DateTimeZone zone = chrono.getZone(); int offset = zone.getOffset(instant); long adjustedInstant = instant + offset; if ((instant ^ adjustedInstant) < 0 && (instant ^ offset) >= 0) { // Time zone offset overflow, so revert to UTC. zone = DateTimeZone.UTC; offset = 0; adjustedInstant = instant; } printer.printTo(appendable, adjustedInstant, chrono.withUTC(), offset, zone, iLocale); }
@Override public AggregateCount getCounts(String name, Interval interval, DateTimeField resolution) { DateTime end = interval.getEnd(); Chronology c = interval.getChronology(); DurationField resolutionDuration = resolution.getDurationField(); long[] counts; if (resolutionDuration.getUnitMillis() == DateTimeConstants.MILLIS_PER_MINUTE) { // Iterate through each hour in the interval and load the minutes for it MutableDateTime dt = new MutableDateTime(interval.getStart()); dt.setRounding(c.hourOfDay()); Duration step = Duration.standardHours(1); List<long[]> hours = new ArrayList<long[]>(); while (dt.isBefore(end)) { hours.add(getMinCountsForHour(name, dt)); dt.add(step); } counts = MetricUtils.concatArrays( hours, interval.getStart().getMinuteOfHour(), interval.toPeriod().toStandardMinutes().getMinutes() + 1, 60); } else if (resolutionDuration.getUnitMillis() == DateTimeConstants.MILLIS_PER_HOUR) { DateTime cursor = new DateTime(c.dayOfMonth().roundFloor(interval.getStart().getMillis())); List<long[]> days = new ArrayList<long[]>(); Duration step = Duration.standardHours(24); while (cursor.isBefore(end)) { days.add(getHourCountsForDay(name, cursor)); cursor = cursor.plus(step); } counts = MetricUtils.concatArrays( days, interval.getStart().getHourOfDay(), interval.toPeriod().toStandardHours().getHours() + 1, 24); } else { throw new IllegalArgumentException("Only minute or hour resolution is currently supported"); } return new AggregateCount(name, interval, counts, resolution); }
@Override public int hashCode() { int result = period.hashCode(); result = 31 * result + chronology.hashCode(); result = 31 * result + (int) (origin ^ (origin >>> 32)); result = 31 * result + (hasOrigin ? 1 : 0); return result; }
/** * Determines the correct chronology to use. * * @param chrono the proposed chronology * @return the actual chronology */ private Chronology selectChronology(Chronology chrono) { chrono = DateTimeUtils.getChronology(chrono); if (iChrono != null) { chrono = iChrono; } if (iZone != null) { chrono = chrono.withZone(iZone); } return chrono; }
@Override public String toString() { return "PeriodGranularity{" + "period=" + period + ", timeZone=" + chronology.getZone() + ", origin=" + (hasOrigin ? origin : "null") + '}'; }
/** * Create a ZonedChronology for any chronology, overriding any time zone it may already have. * * @param base base chronology to wrap * @param zone the time zone * @throws IllegalArgumentException if chronology or time zone is null */ public static ZonedChronology getInstance(Chronology base, DateTimeZone zone) { if (base == null) { throw new IllegalArgumentException("Must supply a chronology"); } base = base.withUTC(); if (base == null) { throw new IllegalArgumentException("UTC chronology must not be null"); } if (zone == null) { throw new IllegalArgumentException("DateTimeZone must not be null"); } return new ZonedChronology(base, zone); }
/** * Parses only the local date-time from the given text, returning a new LocalDateTime. * * <p>This will parse the text fully according to the formatter, using the UTC zone. Once parsed, * only the local date-time will be used. This means that any parsed time-zone or offset field is * completely ignored. It also means that the zone and offset-parsed settings are ignored. * * @param text the text to parse, not null * @return the parsed date-time, never null * @throws UnsupportedOperationException if parsing is not supported * @throws IllegalArgumentException if the text to parse is invalid * @since 2.0 */ public LocalDateTime parseLocalDateTime(String text) { InternalParser parser = requireParser(); Chronology chrono = selectChronology(null).withUTC(); // always use UTC, avoiding DST gaps DateTimeParserBucket bucket = new DateTimeParserBucket(0, chrono, iLocale, iPivotYear, iDefaultYear); int newPos = parser.parseInto(bucket, text, 0); if (newPos >= 0) { if (newPos >= text.length()) { long millis = bucket.computeMillis(true, text); if (bucket.getOffsetInteger() != null) { // treat withOffsetParsed() as being true int parsedOffset = bucket.getOffsetInteger(); DateTimeZone parsedZone = DateTimeZone.forOffsetMillis(parsedOffset); chrono = chrono.withZone(parsedZone); } else if (bucket.getZone() != null) { chrono = chrono.withZone(bucket.getZone()); } return new LocalDateTime(millis, chrono); } } else { newPos = ~newPos; } throw new IllegalArgumentException(FormatUtils.createErrorMessage(text, newPos)); }
public PeriodGranularity(Period period, DateTime origin, DateTimeZone tz) { this.period = period; this.chronology = tz == null ? ISOChronology.getInstanceUTC() : ISOChronology.getInstance(tz); if (origin == null) { // default to origin in given time zone when aligning multi-period granularities this.origin = new DateTime(0, DateTimeZone.UTC) .withZoneRetainFields(chronology.getZone()) .getMillis(); this.hasOrigin = false; } else { this.origin = origin.getMillis(); this.hasOrigin = true; } this.isCompound = isCompoundPeriod(period); }
@Override public DateTime toDateTime(long t) { return new DateTime(t, chronology.getZone()); }
@Override public long next(long t) { return chronology.add(period, t, 1); }
@Override public long truncate(long t) { if (isCompound) { try { return truncateMillisPeriod(t); } catch (UnsupportedOperationException e) { return truncateCompoundPeriod(t); } } final int years = period.getYears(); if (years > 0) { if (years > 1 || hasOrigin) { int y = chronology.years().getDifference(t, origin); y -= y % years; long tt = chronology.years().add(origin, y); // always round down to the previous period (for timestamps prior to origin) if (t < tt) t = chronology.years().add(tt, -years); else t = tt; return t; } else { return chronology.year().roundFloor(t); } } final int months = period.getMonths(); if (months > 0) { if (months > 1 || hasOrigin) { int m = chronology.months().getDifference(t, origin); m -= m % months; long tt = chronology.months().add(origin, m); // always round down to the previous period (for timestamps prior to origin) if (t < tt) t = chronology.months().add(tt, -months); else t = tt; return t; } else { return chronology.monthOfYear().roundFloor(t); } } final int weeks = period.getWeeks(); if (weeks > 0) { if (weeks > 1 || hasOrigin) { // align on multiples from origin int w = chronology.weeks().getDifference(t, origin); w -= w % weeks; long tt = chronology.weeks().add(origin, w); // always round down to the previous period (for timestamps prior to origin) if (t < tt) t = chronology.weeks().add(tt, -weeks); else t = tt; return t; } else { t = chronology.dayOfWeek().roundFloor(t); // default to Monday as beginning of the week return chronology.dayOfWeek().set(t, 1); } } final int days = period.getDays(); if (days > 0) { if (days > 1 || hasOrigin) { // align on multiples from origin int d = chronology.days().getDifference(t, origin); d -= d % days; long tt = chronology.days().add(origin, d); // always round down to the previous period (for timestamps prior to origin) if (t < tt) t = chronology.days().add(tt, -days); else t = tt; return t; } else { t = chronology.hourOfDay().roundFloor(t); return chronology.hourOfDay().set(t, 0); } } final int hours = period.getHours(); if (hours > 0) { if (hours > 1 || hasOrigin) { // align on multiples from origin long h = chronology.hours().getDifferenceAsLong(t, origin); h -= h % hours; long tt = chronology.hours().add(origin, h); // always round down to the previous period (for timestamps prior to origin) if (t < tt) t = chronology.hours().add(tt, -hours); else t = tt; return t; } else { t = chronology.minuteOfHour().roundFloor(t); return chronology.minuteOfHour().set(t, 0); } } final int minutes = period.getMinutes(); if (minutes > 0) { // align on multiples from origin if (minutes > 1 || hasOrigin) { long m = chronology.minutes().getDifferenceAsLong(t, origin); m -= m % minutes; long tt = chronology.minutes().add(origin, m); // always round down to the previous period (for timestamps prior to origin) if (t < tt) t = chronology.minutes().add(tt, -minutes); else t = tt; return t; } else { t = chronology.secondOfMinute().roundFloor(t); return chronology.secondOfMinute().set(t, 0); } } final int seconds = period.getSeconds(); if (seconds > 0) { // align on multiples from origin if (seconds > 1 || hasOrigin) { long s = chronology.seconds().getDifferenceAsLong(t, origin); s -= s % seconds; long tt = chronology.seconds().add(origin, s); // always round down to the previous period (for timestamps prior to origin) if (t < tt) t = chronology.seconds().add(tt, -seconds); else t = tt; return t; } else { return chronology.millisOfSecond().set(t, 0); } } final int millis = period.getMillis(); if (millis > 0) { if (millis > 1) { long ms = chronology.millis().getDifferenceAsLong(t, origin); ms -= ms % millis; long tt = chronology.millis().add(origin, ms); // always round down to the previous period (for timestamps prior to origin) if (t < tt) t = chronology.millis().add(tt, -millis); else t = tt; return t; } else { return t; } } return t; }
/** Serialization singleton. */ private Object readResolve() { Chronology base = getBase(); return base == null ? getInstanceUTC() : getInstance(base.getZone()); }
/** * Get this object as a MutableDateTime using the same chronology but a different zone. * * @param zone time zone to apply, or default if null * @return a MutableDateTime using the same millis */ public MutableDateTime toMutableDateTime(DateTimeZone zone) { Chronology chrono = DateTimeUtils.getChronology(getChronology()); chrono = chrono.withZone(zone); return new MutableDateTime(getMillis(), chrono); }
/** * Parses a UDUNITS time string (of the form "1992-10-8 15:15:42.5 -6:00") and returns a DateTime * in the given Chronology. * * @param baseDateTimeString UDUNITS-formatted time string * @param chronology The Chronology (calendar system) in which the time string is to be * interpreted. This must have a time zone of UTC, otherwise an IllegalArgumentException will * be thrown. If this is null, the ISOChronology (in the UTC time zone) will be assumed. * @return a DateTime in the given Chronology, with the time zone set to UTC. */ public static DateTime parseUdunitsTimeString(String baseDateTimeString, Chronology chronology) { if (chronology == null) chronology = ISOChronology.getInstanceUTC(); if (!chronology.getZone().equals(DateTimeZone.UTC)) { throw new IllegalArgumentException("The time zone of the Chronology must be UTC"); } // Set the defaults for any values that are not specified int year = 0; int month = 1; int day = 1; int hour = 0; int minute = 0; double second = 0.0; // We parse the string using a tokenizer to allow for partial strings // (e.g. those that contain only the date and not the time) StringTokenizer tokenizer = new StringTokenizer(baseDateTimeString, " "); try { // Parse the date if present if (tokenizer.hasMoreTokens()) { StringTokenizer dateTokenizer = new StringTokenizer(tokenizer.nextToken(), "-"); if (dateTokenizer.hasMoreTokens()) year = Integer.parseInt(dateTokenizer.nextToken()); if (baseDateTimeString.startsWith("-")) { /* * A '-' was used to denote a negative year. */ year *= -1; } if (dateTokenizer.hasMoreTokens()) month = Integer.parseInt(dateTokenizer.nextToken()); if (dateTokenizer.hasMoreTokens()) day = Integer.parseInt(dateTokenizer.nextToken()); } // Parse the time if present if (tokenizer.hasMoreTokens()) { StringTokenizer timeTokenizer = new StringTokenizer(tokenizer.nextToken(), ":"); if (timeTokenizer.hasMoreTokens()) hour = Integer.parseInt(timeTokenizer.nextToken()); if (timeTokenizer.hasMoreTokens()) minute = Integer.parseInt(timeTokenizer.nextToken()); if (timeTokenizer.hasMoreTokens()) second = Double.parseDouble(timeTokenizer.nextToken()); } // Get a DateTime object in this Chronology DateTime dt = new DateTime(year, month, day, hour, minute, 0, 0, chronology); // Add the seconds dt = dt.plus((long) (1000 * second)); // Parse the time zone if present if (tokenizer.hasMoreTokens()) { StringTokenizer zoneTokenizer = new StringTokenizer(tokenizer.nextToken(), ":"); int hourOffset = zoneTokenizer.hasMoreTokens() ? Integer.parseInt(zoneTokenizer.nextToken()) : 0; int minuteOffset = zoneTokenizer.hasMoreTokens() ? Integer.parseInt(zoneTokenizer.nextToken()) : 0; DateTimeZone dtz = DateTimeZone.forOffsetHoursMinutes(hourOffset, minuteOffset); // Apply the time zone offset, retaining the field values. This // manipulates the millisecond instance. dt = dt.withZoneRetainFields(dtz); // Now convert to the UTC time zone, retaining the millisecond instant dt = dt.withZone(DateTimeZone.UTC); } return dt; } catch (NumberFormatException nfe) { throw new IllegalArgumentException("Illegal base time specification " + baseDateTimeString); } }
/** Tests the dayOfMonth DateTimeField */ @Test public void testDayOfMonthField() { DateTimeField dayOfMonthField = CHRON_NOLEAP.dayOfMonth(); assertEquals(1, dayOfMonthField.getMinimumValue()); assertEquals(31, dayOfMonthField.getMaximumValue()); }