/* * Copyright 2011-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package kr.debop4j.timeperiod.tools; import com.google.common.base.Objects; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import kr.debop4j.core.Function1; import kr.debop4j.core.NotSupportException; import kr.debop4j.core.Pair; import kr.debop4j.core.parallelism.Parallels; import kr.debop4j.core.tools.ArrayTool; import kr.debop4j.timeperiod.*; import kr.debop4j.timeperiod.timerange.*; import lombok.Getter; import org.joda.time.DateTime; import org.joda.time.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Comparator; import java.util.Iterator; import java.util.List; import static kr.debop4j.core.Guard.shouldBe; import static kr.debop4j.core.Guard.shouldNotBeNull; /** * {@link DateTime} 관련 Utility Class 입니다. * * @author 배성혁 sunghyouk.bae@gmail.com * @since 13. 5. 11. 오후 2:44 */ public abstract class Times { private static final Logger log = LoggerFactory.getLogger(Times.class); private static final boolean isTraceEnabled = log.isTraceEnabled(); private static final boolean isDebugEnabled = log.isDebugEnabled(); private Times() { } public static final String NullString = "<null>"; public static final DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0); /** * Current system time * * @return the date time */ public static DateTime now() { return DateTime.now(); } /** * Today * * @return the date time */ public static DateTime today() { return DateTime.now().withTimeAtStartOfDay(); } /** * Datepart datepart. * * @param moment the moment * @return the datepart */ public static Datepart datepart(DateTime moment) { return new Datepart(moment); } /** * Timepart timepart. * * @param moment the moment * @return the timepart */ public static Timepart timepart(DateTime moment) { return new Timepart(moment); } /** * 날짜 부분만 가진 {@link DateTime} 을 만듭니다. * * @param year 년도 * @param monthOfYear 월 * @param dayOfMonth 일 * @return 년 /월/일을 가진 DateTime */ public static DateTime asDate(int year, int monthOfYear, int dayOfMonth) { return new DateTime(year, monthOfYear, dayOfMonth, 0, 0); } /** * As string. * * @param period the period * @return the string */ public static String asString(ITimePeriod period) { return (period == null) ? NullString : period.toString(); } /** * To date time. * * @param value the value * @return the date time */ public static DateTime toDateTime(String value) { return toDateTime(value, new DateTime(0)); } /** * To date time. * * @param value the value * @param defaultValue the default value * @return the date time */ public static DateTime toDateTime(String value, DateTime defaultValue) { try { return DateTime.parse(value); } catch (Exception e) { return defaultValue; } } /** * To time period collection. * * @param sequence the sequence * @return the i time period collection */ public static <T extends ITimePeriod> ITimePeriodCollection toTimePeriodCollection(Iterable<T> sequence) { return new TimePeriodCollection(sequence); } // region << Calendar >> /** * Gets year of. * * @param moment the moment * @return the year of */ public static int getYearOf(DateTime moment) { return getYearOf(moment.getYear(), moment.getMonthOfYear()); } /** * Gets year of. * * @param moment the moment * @param calendar the calendar * @return the year of */ public static int getYearOf(DateTime moment, ITimeCalendar calendar) { return getYearOf(calendar.getYear(moment), calendar.getMonthOfYear(moment)); } /** * Gets year of. * * @param year the year * @param monthOfYear the month of year * @return the year of */ public static int getYearOf(int year, int monthOfYear) { return monthOfYear >= 1 ? year : year - 1; } /** * Gets days of year. * * @param year the year * @return the days of year */ public static int getDaysOfYear(int year) { return startTimeOfYear(year + 1).minusMillis(1).getDayOfYear(); } /** * Next halfyear. * * @param startYear the start year * @param startHalfyear the start halfyear * @return the year and halfyear */ public static YearAndHalfyear nextHalfyear(int startYear, Halfyear startHalfyear) { return addHalfyear(startYear, startHalfyear, 1); } /** * Previous halfyear. * * @param startYear the start year * @param startHalfyear the start halfyear * @return the year and halfyear */ public static YearAndHalfyear previousHalfyear(int startYear, Halfyear startHalfyear) { return addHalfyear(startYear, startHalfyear, -1); } /** * Add halfyear. * * @param startYear the start year * @param startHalfyear the start halfyear * @param halfyearCount the halfyear count * @return the year and halfyear */ public static YearAndHalfyear addHalfyear(int startYear, Halfyear startHalfyear, int halfyearCount) { int offsetYear = (Math.abs(halfyearCount) / TimeSpec.HalfyearsPerYear) + 1; int startHalfyearCount = ((startYear + offsetYear) * TimeSpec.HalfyearsPerYear) + (startHalfyear.getValue() - 1); int targetHalfyearCount = startHalfyearCount + halfyearCount; int year = (targetHalfyearCount / TimeSpec.HalfyearsPerYear) - offsetYear; Halfyear halfyear = Halfyear.valueOf((targetHalfyearCount % TimeSpec.HalfyearsPerYear) + 1); if (isTraceEnabled) log.trace("addHalfyear. startYear=[{}], startHalfyear=[{}], halfyearCount=[{}], year=[{}], halfyear=[{}]", startYear, startHalfyear, halfyearCount, year, halfyear); return new YearAndHalfyear(year, halfyear); } /** * Gets halfyear of month. * * @param monthOfYear the month of year * @return the halfyear of month */ public static Halfyear getHalfyearOfMonth(int monthOfYear) { assert monthOfYear >= 1 && monthOfYear <= 12; return (monthOfYear <= TimeSpec.MonthsPerHalfyear) ? Halfyear.First : Halfyear.Second; } /** * Get months of halfyear. * * @param halfyear the halfyear * @return the int [ ] */ public static int[] getMonthsOfHalfyear(Halfyear halfyear) { return (halfyear == Halfyear.First) ? TimeSpec.FirstHalfyearMonths : TimeSpec.SecondHalfyearMonths; } /** * Next quarter. * * @param year the year * @param quarter the quarter * @return the year and quarter */ public static YearAndQuarter nextQuarter(int year, Quarter quarter) { return addQuarter(year, quarter, 1); } /** * Previous quarter. * * @param year the year * @param quarter the quarter * @return the year and quarter */ public static YearAndQuarter previousQuarter(int year, Quarter quarter) { return addQuarter(year, quarter, -1); } /** * Add quarter. * * @param year the year * @param quarter the quarter * @param count the count * @return the year and quarter */ public static YearAndQuarter addQuarter(int year, Quarter quarter, int count) { int offsetYear = Math.abs(count) / TimeSpec.QuartersPerYear + 1; int startQuarters = (year + offsetYear) * TimeSpec.QuartersPerYear + quarter.getValue() - 1; int targetQuarters = startQuarters + count; int y = targetQuarters / TimeSpec.QuartersPerYear - offsetYear; int q = (targetQuarters % TimeSpec.QuartersPerYear) + 1; return new YearAndQuarter(y, q); } /** * Gets quarter of month. * * @param monthOfYear the month of year * @return the quarter of month */ public static Quarter getQuarterOfMonth(int monthOfYear) { int quarter = (monthOfYear - 1) / TimeSpec.MonthsPerQuarter + 1; return Quarter.valueOf(quarter); } /** * Get months of quarter. * * @param quarter the quarter * @return the int [ ] */ public static int[] getMonthsOfQuarter(Quarter quarter) { switch (quarter) { case First: return TimeSpec.FirstQuarterMonths; case Second: return TimeSpec.SecondQuarterMonths; case Third: return TimeSpec.ThirdQuarterMonths; case Fourth: return TimeSpec.FourthQuarterMonths; default: throw new IllegalArgumentException("invalid quarter. quarter=" + quarter); } } /** * Next month. * * @param year the year * @param monthOfYear the month of year * @return the year and month */ public static YearAndMonth nextMonth(int year, int monthOfYear) { return addMonth(year, monthOfYear, 1); } /** * Previous month. * * @param year the year * @param monthOfYear the month of year * @return the year and month */ public static YearAndMonth previousMonth(int year, int monthOfYear) { return addMonth(year, monthOfYear, -1); } /** * Add month. * * @param year the year * @param monthOfYear the month of year * @param count the count * @return the year and month */ public static YearAndMonth addMonth(int year, int monthOfYear, int count) { int offset = Math.abs(count) / TimeSpec.MonthsPerYear + 1; int startMonths = (year + offset) * TimeSpec.MonthsPerYear + monthOfYear - 1; int endMonths = startMonths + count; int y = endMonths / TimeSpec.MonthsPerYear - offset; int m = (endMonths % TimeSpec.MonthsPerYear) + 1; return new YearAndMonth(y, m); } /** * Gets days in month. * * @param year the year * @param month the month * @return the days in month */ public static int getDaysInMonth(int year, int month) { DateTime firstDay = new DateTime(year, month, 1, 0, 0); return firstDay.plusMonths(1).plusDays(-1).getDayOfMonth(); } /** * Gets start of week. * * @param time the time * @return the start of week */ public static DateTime getStartOfWeek(DateTime time) { return getStartOfWeek(time, TimeSpec.FirstDayOfWeek); } /** * Gets start of week. * * @param time the time * @param firstDayOfWeek the first day of week * @return the start of week */ public static DateTime getStartOfWeek(DateTime time, DayOfWeek firstDayOfWeek) { DateTime current = time.withTimeAtStartOfDay(); while (current.getDayOfWeek() != firstDayOfWeek.getValue()) { current = current.minusDays(1); } return current; } /** * Gets week of year. * * @param moment the moment * @return the week of year */ public static YearAndWeek getWeekOfYear(DateTime moment) { return getWeekOfYear(moment, TimeCalendar.getDefault()); } /** * Gets week of year. * * @param moment the moment * @param timeCalendar the time calendar * @return the week of year */ public static YearAndWeek getWeekOfYear(DateTime moment, ITimeCalendar timeCalendar) { return new YearAndWeek(moment.getWeekyear(), moment.getWeekOfWeekyear()); } /** * Gets weeks of year. * * @param year the year * @return the weeks of year */ public static int getWeeksOfYear(int year) { return getWeeksOfYear(year, TimeCalendar.getDefault()); } /** * Gets weeks of year. * * @param year the year * @param timeCalendar the time calendar * @return the weeks of year */ public static int getWeeksOfYear(int year, ITimeCalendar timeCalendar) { return asDate(year, 12, 28).getWeekOfWeekyear(); } /** * Gets start of year week. * * @param year the year * @param weekOfYear the week of year * @return the start of year week */ public static DateTime getStartOfYearWeek(int year, int weekOfYear) { return getStartOfYearWeek(year, weekOfYear, null); } /** * Gets start of year week. * * @param year the year * @param weekOfYear the week of year * @param timeCalendar the time calendar * @return the start of year week */ public static DateTime getStartOfYearWeek(int year, int weekOfYear, ITimeCalendar timeCalendar) { return new DateTime().withYear(year).withWeekOfWeekyear(weekOfYear); } /** * Day start. * * @param moment the moment * @return the date time */ public static DateTime dayStart(DateTime moment) { return moment.withTimeAtStartOfDay(); } /** * Next day of week. * * @param day the day * @return the day of week */ public static DayOfWeek nextDayOfWeek(DayOfWeek day) { return addDayOfWeek(day, 1); } /** * Previous day of week. * * @param day the day * @return the day of week */ public static DayOfWeek previousDayOfWeek(DayOfWeek day) { return addDayOfWeek(day, -1); } /** * Add day of week. * * @param day the day * @param days the days * @return the day of week */ public static DayOfWeek addDayOfWeek(DayOfWeek day, int days) { if (days == 0) return day; int weeks = Math.abs(days) / TimeSpec.DaysPerWeek + 1; int offset = weeks * TimeSpec.DaysPerWeek + day.getValue() - 1 + days; return DayOfWeek.valueOf((offset % TimeSpec.DaysPerWeek) + 1); } // endregion << Calendar >> // region << Compare >> /** * 두 일자의 값이 {@link kr.debop4j.timeperiod.PeriodUnit} 단위까지 같은지 비교합니다. (상위값들도 같아야 합니다.) * * @param left the left * @param right the right * @param periodUnit the period unit * @return the boolean */ public static boolean isSameTime(DateTime left, DateTime right, PeriodUnit periodUnit) { if (isTraceEnabled) log.trace("두 일자가 값은지 비교합니다. left=[{}], right=[{}], periodUnit=[{}]", left, right, periodUnit); switch (periodUnit) { case Year: return isSameYear(left, right); case Halfyear: return isSameHalfyear(left, right); case Quarter: return isSameQuarter(left, right); case Month: return isSameMonth(left, right); case Week: return isSameWeek(left, right); case Day: return isSameDay(left, right); case Hour: return isSameHour(left, right); case Minute: return isSameMinute(left, right); case Second: return isSameSecond(left, right); case Millisecond: default: return isSameDateTime(left, right); } } /** * 두 일자의 년(Year) 단위까지 같은지 비교합니다. * * @param left the left * @param right the right * @return the boolean */ public static boolean isSameYear(DateTime left, DateTime right) { return left.getYear() == right.getYear(); } /** * 두 일자가 반기(halfyear) 단위까지 같으면 true를 아니면 false를 반환합니다. * * @param left the left * @param right the right * @return the boolean */ public static boolean isSameHalfyear(DateTime left, DateTime right) { return isSameYear(left, right) && getHalfyearOfMonth(left.getMonthOfYear()) == getHalfyearOfMonth(right.getMonthOfYear()); } /** * 두 일자가 분기(quarter) 단위까지 같으면 true를 아니면 false를 반환합니다. * * @param left the left * @param right the right * @return the boolean */ public static boolean isSameQuarter(DateTime left, DateTime right) { return isSameYear(left, right) && getQuarterOfMonth(left.getMonthOfYear()) == getQuarterOfMonth(right.getMonthOfYear()); } /** * 두 일자가 월(Month) 단위까지 같으면 true를 아니면 false를 반환합니다. * * @param left the left * @param right the right * @return the boolean */ public static boolean isSameMonth(DateTime left, DateTime right) { return isSameYear(left, right) && left.getMonthOfYear() == right.getMonthOfYear(); } /** * 두 일자가 주(Week) 단위까지 같으면 true를 아니면 false를 반환합니다. * * @param left the left * @param right the right * @return the boolean */ public static boolean isSameWeek(DateTime left, DateTime right) { return isSameYear(left, right) && left.getWeekOfWeekyear() == right.getWeekOfWeekyear(); } /** * 두 일자가 일(Day) 단위까지 같으면 true를 아니면 false를 반환합니다. * * @param left the left * @param right the right * @return the boolean */ public static boolean isSameDay(DateTime left, DateTime right) { return isSameMonth(left, right) && left.getDayOfMonth() == right.getDayOfMonth(); } /** * 두 일자가 시(Hour) 단위까지 같으면 true를 아니면 false를 반환합니다. * * @param left the left * @param right the right * @return the boolean */ public static boolean isSameHour(DateTime left, DateTime right) { return isSameDay(left, right) && left.getHourOfDay() == right.getHourOfDay(); } /** * 두 일자가 분 단위까지 같으면 true를 아니면 false를 반환합니다. * * @param left the left * @param right the right * @return the boolean */ public static boolean isSameMinute(DateTime left, DateTime right) { return isSameDay(left, right) && left.getMinuteOfDay() == right.getMinuteOfDay(); } /** * 두 일자가 초 단위까지 같으면 true를 아니면 false를 반환합니다. * * @param left the left * @param right the right * @return the boolean */ public static boolean isSameSecond(DateTime left, DateTime right) { return isSameDay(left, right) && left.getSecondOfDay() == right.getSecondOfDay(); } /** * 두 일자가 같으면 true를 아니면 false를 반환합니다. * * @param left the left * @param right the right * @return the boolean */ public static boolean isSameDateTime(DateTime left, DateTime right) { return (left != null) && left.equals(right); } // endregion << Compare >> // region << Current >> /** 현재 시각이 속한 년도의 시작일 */ public static DateTime currentYear() { return asDate(now().getYear(), 1, 1); } /** 현재 시각이 속한 반기의 시작일 */ public static DateTime currentHalfyear() { DateTime now = now(); Halfyear halfyear = getHalfyearOfMonth(now.getMonthOfYear()); int month = getMonthsOfHalfyear(halfyear)[0]; return asDate(now.getYear(), month, 1); } /** 현재 시각이 속한 분기의 시작일 */ public static DateTime currentQuarter() { DateTime now = now(); Quarter quarter = getQuarterOfMonth(now.getMonthOfYear()); int month = getMonthsOfQuarter(quarter)[0]; return asDate(now.getYear(), month, 1); } /** 현재 시각이 속한 월의 시작일 */ public static DateTime currentMonth() { return trimToDay(now()); } /** 현재 시각이 속한 주의 시작일 */ public static DateTime currentWeek() { return currentWeek(TimeSpec.FirstDayOfWeek); } /** 현재 시각이 속한 주의 시작일 */ public static DateTime currentWeek(DayOfWeek firstDayOfWeek) { return getStartOfWeek(now(), firstDayOfWeek); } /** 오늘 */ public static DateTime currentDay() { return today(); } /** 현재 시각이 속한 시각의 처음 */ public static DateTime currentHour() { return trimToMinute(now()); } /** 현재 시각이 속한 분의 처음 */ public static DateTime currentMinute() { return trimToSecond(now()); } /** 현재 시각이 속한 초단위의 처음 */ public static DateTime currentSecond() { return trimToMillis(now()); } // endregion << Current >> // region << DateTime >> /** moment 가 속한 년도의 시작 시각 */ public static DateTime startTimeOfYear(DateTime moment) { return new DateTime(moment.getYear(), 1, 1, 0, 0); } /** 지정한 년도의 시작 시각 */ public static DateTime startTimeOfYear(int year) { return new DateTime(year, 1, 1, 0, 0); } /** moment가 속한 년도의 마지막 시각 */ public static DateTime endTimeOfYear(DateTime moment) { return startTimeOfYear(moment.getYear() + 1).plusMillis(-1); } /** 지정한 년도의 마지막 시각 */ public static DateTime endTimeOfYear(int year) { return startTimeOfYear(year + 1).plusMillis(-1); } /** 작년 시작 시각 */ public static DateTime startTimeOfLastYear(DateTime moment) { return startTimeOfYear(moment.getYear() - 1); } /** 작년 마지막 시각 */ public static DateTime endTimeOfLastYear(DateTime moment) { return endTimeOfYear(moment.getYear() - 1); } /** * 해당 일자가 속한 반기의 시작 시각 * * @param moment 일자 * @return 반기의 시작 시각 */ public static DateTime startTimeOfHalfyear(DateTime moment) { return startTimeOfHalfyear(moment.getYear(), moment.getMonthOfYear()); } /** * 해당 년/월이 속한 반기의 시작 시각 * * @param year 년 * @param monthOfYear 월 * @return 반기의 시작 시각 */ public static DateTime startTimeOfHalfyear(int year, int monthOfYear) { return startTimeOfHalfyear(year, getHalfyearOfMonth(monthOfYear)); } /** * 해당 년 / 반기의 시작 시각 * * @param year year * @param halfyear halfyear * @return start time of half year */ public static DateTime startTimeOfHalfyear(int year, Halfyear halfyear) { return new DateTime(year, getMonthsOfHalfyear(halfyear)[0], 1, 0, 0); } /** * End time of halfyear. * * @param moment the moment * @return the date time */ public static DateTime endTimeOfHalfyear(DateTime moment) { return endTimeOfHalfyear(moment.getYear(), moment.getMonthOfYear()); } /** * End time of halfyear. * * @param year the year * @param monthOfYear the month of year * @return the date time */ public static DateTime endTimeOfHalfyear(int year, int monthOfYear) { return startTimeOfHalfyear(year, monthOfYear) .plusMonths(TimeSpec.MonthsPerHalfyear) .minus(TimeSpec.MinPositiveDuration); } /** * Start time of quarter. * * @param moment the moment * @return the date time */ public static DateTime startTimeOfQuarter(DateTime moment) { return startTimeOfQuarter(moment.getYear(), moment.getMonthOfYear()); } /** * Start time of quarter. * * @param year the year * @param monthOfYear the month of year * @return the date time */ public static DateTime startTimeOfQuarter(int year, int monthOfYear) { return startTimeOfQuarter(year, getQuarterOfMonth(monthOfYear)); } /** * Start time of quarter. * * @param year the year * @param quarter the quarter * @return the date time */ public static DateTime startTimeOfQuarter(int year, Quarter quarter) { return new DateTime(year, getMonthsOfQuarter(quarter)[0], 1, 0, 0); } /** * End time of quarter. * * @param moment the moment * @return the date time */ public static DateTime endTimeOfQuarter(DateTime moment) { return endTimeOfQuarter(moment.getYear(), moment.getMonthOfYear()); } /** * End time of quarter. * * @param year the year * @param monthOfYear the month of year * @return the date time */ public static DateTime endTimeOfQuarter(int year, int monthOfYear) { return startTimeOfQuarter(year, monthOfYear) .plusMonths(TimeSpec.MonthsPerQuarter) .minus(TimeSpec.MinPositiveDuration); } /** * Start time of last quarter. * * @param moment the moment * @return the date time */ public static DateTime startTimeOfLastQuarter(DateTime moment) { return startTimeOfQuarter(moment.minusMonths(TimeSpec.MonthsPerQuarter)); } /** * End time of last quarter. * * @param moment the moment * @return the date time */ public static DateTime endTimeOfLastQuarter(DateTime moment) { return endTimeOfQuarter(moment.minusMonths(TimeSpec.MonthsPerQuarter)); } /** * Start time of month. * * @param moment the moment * @return the date time */ public static DateTime startTimeOfMonth(DateTime moment) { return new DateTime(moment.getYear(), moment.getMonthOfYear(), 1, 0, 0); } /** * Start time of month. * * @param year the year * @param month the month * @return the date time */ public static DateTime startTimeOfMonth(int year, Month month) { return new DateTime(year, month.getValue(), 1, 0, 0); } /** * Start time of month. * * @param year the year * @param month the month * @return the date time */ public static DateTime startTimeOfMonth(int year, int month) { return new DateTime(year, month, 1, 0, 0); } /** * End time of month. * * @param moment the moment * @return the date time */ public static DateTime endTimeOfMonth(DateTime moment) { return startTimeOfMonth(moment).plusMonths(1).minus(TimeSpec.MinPositiveDuration); } /** * End time of month. * * @param year the year * @param month the month * @return the date time */ public static DateTime endTimeOfMonth(int year, Month month) { return startTimeOfMonth(year, month).plusMonths(1).minus(TimeSpec.MinPositiveDuration); } /** * End time of month. * * @param year the year * @param month the month * @return the date time */ public static DateTime endTimeOfMonth(int year, int month) { return startTimeOfMonth(year, month).plusMonths(1).minus(TimeSpec.MinPositiveDuration); } /** * Start time of last month. * * @param moment the moment * @return the date time */ public static DateTime startTimeOfLastMonth(DateTime moment) { return startTimeOfMonth(moment.minusMonths(1)); } /** * End time of last month. * * @param moment the moment * @return the date time */ public static DateTime endTimeOfLastMonth(DateTime moment) { return endTimeOfMonth(moment.minusMonths(1)); } /** * Start time of week. * * @param moment the moment * @return the date time */ public static DateTime startTimeOfWeek(DateTime moment) { return startTimeOfWeek(moment, TimeSpec.FirstDayOfWeek); } /** * Start time of week. * * @param moment the moment * @param firstDayOfWeek the first day of week * @return the date time */ public static DateTime startTimeOfWeek(DateTime moment, DayOfWeek firstDayOfWeek) { return getStartOfWeek(moment, firstDayOfWeek); } /** * Start time of week. * * @param year the year * @param weekOfYear the week of year * @return the date time */ public static DateTime startTimeOfWeek(int year, int weekOfYear) { return startTimeOfWeek(year, weekOfYear, TimeCalendar.getDefault()); } /** * Start time of week. * * @param year the year * @param weekOfYear the week of year * @param timeCalendar the time calendar * @return the date time */ public static DateTime startTimeOfWeek(int year, int weekOfYear, ITimeCalendar timeCalendar) { DateTime current = startTimeOfYear(year).minusWeeks(1); while (current.getYear() < year + 2) { if (current.getWeekyear() == year && current.getWeekOfWeekyear() == weekOfYear) break; current = current.plusDays(1); } return current; } /** * End time of week. * * @param moment the moment * @return the date time */ public static DateTime endTimeOfWeek(DateTime moment) { return endTimeOfWeek(moment, TimeSpec.FirstDayOfWeek); } /** * End time of week. * * @param moment the moment * @param firstDayOfWeek the first day of week * @return the date time */ public static DateTime endTimeOfWeek(DateTime moment, DayOfWeek firstDayOfWeek) { return startTimeOfWeek(moment, firstDayOfWeek).plusWeeks(1).minus(TimeSpec.MinPositiveDuration); } /** * End time of week. * * @param year the year * @param weekOfYear the week of year * @return the date time */ public static DateTime endTimeOfWeek(int year, int weekOfYear) { return endTimeOfWeek(year, weekOfYear, TimeCalendar.getDefault()); } /** * End time of week. * * @param year the year * @param weekOfYear the week of year * @param timeCalendar the time calendar * @return the date time */ public static DateTime endTimeOfWeek(int year, int weekOfYear, ITimeCalendar timeCalendar) { return startTimeOfWeek(year, weekOfYear, timeCalendar).plusWeeks(1).minus(TimeSpec.MinPositiveDuration); } /** * Start time of last week. * * @param moment the moment * @return the date time */ public static DateTime startTimeOfLastWeek(DateTime moment) { return startTimeOfLastWeek(moment, TimeSpec.FirstDayOfWeek); } /** * Start time of last week. * * @param moment the moment * @param firstDayOfWeek the first day of week * @return the date time */ public static DateTime startTimeOfLastWeek(DateTime moment, DayOfWeek firstDayOfWeek) { return startTimeOfWeek(moment, firstDayOfWeek).minusWeeks(1); } /** * End time of last week. * * @param moment the moment * @return the date time */ public static DateTime endTimeOfLastWeek(DateTime moment) { return endTimeOfLastWeek(moment, TimeSpec.FirstDayOfWeek); } /** * End time of last week. * * @param moment the moment * @param firstDayOfWeek the first day of week * @return the date time */ public static DateTime endTimeOfLastWeek(DateTime moment, DayOfWeek firstDayOfWeek) { return endTimeOfWeek(moment, firstDayOfWeek).minusWeeks(1); } /** * Start time of day. * * @param moment the moment * @return the date time */ public static DateTime startTimeOfDay(DateTime moment) { return moment.withTimeAtStartOfDay(); } /** * End time of day. * * @param moment the moment * @return the date time */ public static DateTime endTimeOfDay(DateTime moment) { return startTimeOfDay(moment).plusDays(1).minus(TimeSpec.MinPositiveDuration); } /** * Start time of hour. * * @param moment the moment * @return the date time */ public static DateTime startTimeOfHour(DateTime moment) { return trimToMinute(moment); } /** * End time of hour. * * @param moment the moment * @return the date time */ public static DateTime endTimeOfHour(DateTime moment) { return startTimeOfHour(moment).plusHours(1).minus(TimeSpec.MinPositiveDuration); } /** * Start time of minute. * * @param moment the moment * @return the date time */ public static DateTime startTimeOfMinute(DateTime moment) { return trimToSecond(moment); } /** * End time of minute. * * @param moment the moment * @return the date time */ public static DateTime endTimeOfMinute(DateTime moment) { return startTimeOfMinute(moment).plusMinutes(1).minus(TimeSpec.MinPositiveDuration); } /** * Start time of second. * * @param moment the moment * @return the date time */ public static DateTime startTimeOfSecond(DateTime moment) { return trimToMillis(moment); } /** * End time of second. * * @param moment the moment * @return the date time */ public static DateTime endTimeOfSecond(DateTime moment) { return startTimeOfSecond(moment).plusSeconds(1).minus(TimeSpec.MinPositiveDuration); } /** * Halfyear of. * * @param monthOfYear the month of year * @return the halfyear */ public static Halfyear halfyearOf(int monthOfYear) { return (monthOfYear < 7) ? Halfyear.First : Halfyear.Second; } /** * Halfyear of. * * @param moment the moment * @return the halfyear */ public static Halfyear halfyearOf(DateTime moment) { return halfyearOf(moment.getMonthOfYear()); } /** * Start month of quarter. * * @param quarter the quarter * @return the int */ public static int startMonthOfQuarter(Quarter quarter) { return (quarter.getValue() - 1) * TimeSpec.MonthsPerQuarter + 1; } /** * End month of quarter. * * @param quarter the quarter * @return the int */ public static int endMonthOfQuarter(Quarter quarter) { return quarter.getValue() * TimeSpec.MonthsPerQuarter; } /** * Quarter of. * * @param monthOfYear the month of year * @return the quarter */ public static Quarter quarterOf(int monthOfYear) { return Quarter.valueOf((monthOfYear - 1) / TimeSpec.MonthsPerQuarter + 1); } /** * Quarter of. * * @param moment the moment * @return the quarter */ public static Quarter quarterOf(DateTime moment) { return quarterOf(moment.getMonthOfYear()); } /** * Previous quarter of. * * @param moment the moment * @return the quarter */ public static Quarter previousQuarterOf(DateTime moment) { return previousQuarter(moment.getYear(), quarterOf(moment)).getQuarter(); } /** * 지정한 일자의 다음 주 같은 요일을 반환합니다. * * @param moment 기준 일자 */ public static DateTime nextDayOfWeek(DateTime moment) { return nextDayOfWeek(moment, DayOfWeek.valueOf(moment.getDayOfWeek())); } /** * 기준일 이후로 지정한 요일에 해당하는 일자를 반환합니다. * * @param moment 기준 일자 * @param dayOfWeek 원하는 요일 */ public static DateTime nextDayOfWeek(final DateTime moment, final DayOfWeek dayOfWeek) { final int dow = dayOfWeek.getValue(); DateTime next = moment.plusDays(1); while (next.getDayOfWeek() != dow) { next = next.plusDays(1); } return next; } /** * 지정한 일자의 전주의 같은 요일을 반환합니다. * * @param moment 기준 일자 */ public static DateTime previousDayOfWeek(DateTime moment) { return previousDayOfWeek(moment, DayOfWeek.valueOf(moment.getDayOfWeek())); } /** * 지정한 일자 이전에 지정한 요일에 해당하는 일자를 반환한다. * * @param moment 기준 일자 * @param dayOfWeek 원하는 요일 */ public static DateTime previousDayOfWeek(DateTime moment, DayOfWeek dayOfWeek) { int dow = dayOfWeek.getValue(); DateTime prev = moment.minusDays(1); while (prev.getDayOfWeek() != dow) { prev = prev.minusDays(1); } return prev; } /** 시간부분을 제외한 날짜 부분만 반환한다 */ public static DateTime getDate(DateTime moment) { return moment.withTimeAtStartOfDay(); } /** 일자부분이 존재하는지 */ public static boolean hasDate(DateTime moment) { return moment.withTimeAtStartOfDay().getMillis() > 0; } /** 지정한 날짜에 알자(년/월/일) 부분을 지정한 datePart로 설정합니다. */ public static DateTime setDate(DateTime moment, DateTime datepart) { return new Datepart(datepart).getDateTime(new Timepart(moment)); } /** 지정한 날짜의 년, 월, 일을 수정합니다. */ public static DateTime setDate(DateTime moment, int year, int month, int day) { return setDate(moment, new DateTime(year, month, day, 0, 0)); } /** 지정한 일자의 년도만 수정합니다. */ public static DateTime setYear(DateTime moment, int year) { return setDate(moment, year, moment.getMonthOfYear(), moment.getDayOfMonth()); } /** 지정한 일자의 월만 수정합니다. */ public static DateTime setMonth(DateTime moment, int monthOfYear) { return setDate(moment, moment.getYear(), monthOfYear, moment.getDayOfMonth()); } /** 지정한 일자의 일만 수정합니다. */ public static DateTime setDay(DateTime moment, int dayOfMonth) { return setDate(moment, moment.getYear(), moment.getMonthOfYear(), dayOfMonth); } /** 일자 부분과 시간 부분을 조합합니다. */ public static DateTime combine(DateTime datepart, DateTime timepart) { return setTime(datepart, timepart); } /** 일자의 시간 부분만을 반환합니다. */ public static Duration getTime(DateTime moment) { return new Duration(moment.getMillisOfDay()); } /** 시각에 시간부분의 값이 존재하는지 여부 */ public static boolean hasTime(DateTime moment) { return moment.getMillisOfDay() > 0; } /** * 해당 일자의 시간 부분을 지정한 값으로 설정합니다. * * @param moment 기준 일자 * @param timepart 지정할 시간 부분 * @return 계산한 일자 */ public static DateTime setTime(DateTime moment, DateTime timepart) { return setTime(moment, timepart.getMillisOfDay()); } /** * Sets time. * * @param moment the moment * @param millis the millis * @return the time */ public static DateTime setTime(DateTime moment, int millis) { return moment.withTimeAtStartOfDay().plusMillis(millis); } /** * Sets time. * * @param moment the moment * @param hourOfDay the hour of day * @param minuteOfHour the minute of hour * @return the time */ public static DateTime setTime(DateTime moment, int hourOfDay, int minuteOfHour) { return moment.withTimeAtStartOfDay().withTime(hourOfDay, minuteOfHour, 0, 0); } /** * Sets time. * * @param moment the moment * @param hourOfDay the hour of day * @param minuteOfHour the minute of hour * @param secondOfMinute the second of minute * @return the time */ public static DateTime setTime(DateTime moment, int hourOfDay, int minuteOfHour, int secondOfMinute) { return moment.withTimeAtStartOfDay().withTime(hourOfDay, minuteOfHour, secondOfMinute, 0); } /** * Sets time. * * @param moment the moment * @param hourOfDay the hour of day * @param minuteOfHour the minute of hour * @param secondOfMinute the second of minute * @param millisOfSecond the millis of second * @return the time */ public static DateTime setTime(DateTime moment, int hourOfDay, int minuteOfHour, int secondOfMinute, int millisOfSecond) { return moment.withTimeAtStartOfDay().withTime(hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond); } /** * Sets hour. * * @param moment the moment * @param hourOfDay the hour of day * @return the hour */ public static DateTime setHour(DateTime moment, int hourOfDay) { return setTime(moment, hourOfDay, moment.getMinuteOfHour(), moment.getSecondOfMinute(), moment.getMillisOfSecond()); } /** * Sets minute. * * @param moment the moment * @param minuteOfHour the minute of hour * @return the minute */ public static DateTime setMinute(DateTime moment, int minuteOfHour) { return setTime(moment, moment.getHourOfDay(), minuteOfHour, moment.getSecondOfMinute(), moment.getMillisOfSecond()); } /** * Sets second. * * @param moment the moment * @param secondOfMinute the second of minute * @return the second */ public static DateTime setSecond(DateTime moment, int secondOfMinute) { return setTime(moment, moment.getHourOfDay(), moment.getMinuteOfHour(), secondOfMinute, moment.getMillisOfSecond()); } /** * Sets millisecond. * * @param moment the moment * @param millisOfSecond the millis of second * @return the millisecond */ public static DateTime setMillisecond(DateTime moment, int millisOfSecond) { return setTime(moment, moment.getHourOfDay(), moment.getMinuteOfHour(), moment.getSecondOfMinute(), millisOfSecond); } /** 정오 */ public static DateTime noon(DateTime moment) { return moment.withTimeAtStartOfDay().plusHours(12); } /** 지정한 시각에서 지정한 기간 이전의 시각 */ public static DateTime ago(DateTime moment, Duration duration) { return moment.minus(duration); } /** 지정한 시각에서 지정한 기간 이후의 시각 */ public static DateTime from(DateTime moment, Duration duration) { return moment.plus(duration); } public static DateTime fromNow(Duration duration) { return from(DateTime.now(), duration); } /** 지정한 시각에서 지정한 기간 이후의 시각 */ public static DateTime since(DateTime moment, Duration duration) { return moment.plus(duration); } // endregion << DateTime >> // region << Math >> /** * Min date time. * * @param a the a * @param b the b * @return the date time */ public static DateTime min(DateTime a, DateTime b) { if (a == null && b == null) return null; if (a == null) return b; if (b == null) return a; return a.compareTo(b) < 0 ? a : b; } /** * Max date time. * * @param a the a * @param b the b * @return the date time */ public static DateTime max(DateTime a, DateTime b) { if (a == null && b == null) return null; if (a == null) return b; if (b == null) return a; return a.compareTo(b) > 0 ? a : b; } /** * Min duration. * * @param a the a * @param b the b * @return the duration */ public static Duration min(Duration a, Duration b) { if (a == null && b == null) return null; if (a == null) return b; if (b == null) return a; return a.compareTo(b) < 0 ? a : b; } /** * Max duration. * * @param a the a * @param b the b * @return the duration */ public static Duration max(Duration a, Duration b) { if (a == null && b == null) return null; if (a == null) return b; if (b == null) return a; return a.compareTo(b) > 0 ? a : b; } /** * Adjust period. * * @param start the start * @param end the end * @return the pair */ public static Pair<DateTime, DateTime> adjustPeriod(final DateTime start, final DateTime end) { return Pair.create(min(start, end), max(start, end)); } /** * Adjust period. * * @param start the start * @param duration the duration * @return the pair */ public static Pair<DateTime, Duration> adjustPeriod(DateTime start, Duration duration) { shouldNotBeNull(start, "start"); shouldNotBeNull(duration, "duration"); return (duration.getMillis() < 0) ? Pair.create(start.plus(duration), new Duration(-duration.getMillis())) : Pair.create(start, duration); } // endregion << Math >> // region << Period >> /** * Gets time block. * * @param start the start * @param duration the duration * @return the time block */ public static TimeBlock getTimeBlock(DateTime start, Duration duration) { return new TimeBlock(start, duration, false); } /** * Gets time block. * * @param start the start * @param end the end * @return the time block */ public static TimeBlock getTimeBlock(DateTime start, DateTime end) { return new TimeBlock(start, end, false); } /** * Gets time range. * * @param start the start * @param duration the duration * @return the time range */ public static TimeRange getTimeRange(DateTime start, Duration duration) { return new TimeRange(start, duration); } /** * Gets time range. * * @param start the start * @param end the end * @return the time range */ public static TimeRange getTimeRange(DateTime start, DateTime end) { return new TimeRange(start, end); } /** 시작 시각으로부터 지정한 년도(years) 이후의 기간 */ public static TimeRange getRelativeYearPeriod(DateTime start, int years) { return getTimeRange(start, start.plusYears(years)); } /** 시작 시각으로부터 지정한 개월(months) 이후의 기간 */ public static TimeRange getRelativeMonthPeriod(DateTime start, int months) { return getTimeRange(start, start.plusMonths(months)); } /** 시작 시각으로부터 지정한 주(weeks) 이후의 기간 */ public static TimeRange getRelativeWeekPeriod(DateTime start, int weeks) { return getTimeRange(start, start.plusWeeks(weeks)); } /** 시작 시각으로부터 지정한 일(days) 이후의 기간 */ public static TimeRange getRelativeDayPeriod(DateTime start, int days) { return getTimeRange(start, start.plusDays(days)); } /** 시작 시각으로부터 지정한 시간(hours) 이후의 기간 */ public static TimeRange getRelativeHourPeriod(DateTime start, int hours) { return getTimeRange(start, start.plusHours(hours)); } /** 시작 시각으로부터 지정한 분(minutes) 이후의 기간 */ public static TimeRange getRelativeMinutePeriod(DateTime start, int minutes) { return getTimeRange(start, start.plusMinutes(minutes)); } /** 시작 시각으로부터 지정한 초(seconds) 이후의 기간 */ public static TimeRange getRelativeSecondPeriod(DateTime start, int seconds) { return getTimeRange(start, start.plusSeconds(seconds)); } /** moment가 속한 특정 종류의 기간 */ public static ITimePeriod getPeriodOf(DateTime moment, PeriodUnit periodUnit) { return getPeriodOf(moment, periodUnit, TimeCalendar.getDefault()); } /** moment가 속한 특정 종류의 기간 */ public static ITimePeriod getPeriodOf(DateTime moment, PeriodUnit periodUnit, ITimeCalendar timeCalendar) { if (isTraceEnabled) log.trace("일자[{}]가 속한 기간 종류[{}]의 기간을 구합니다.", moment, periodUnit); if (timeCalendar == null) timeCalendar = TimeCalendar.getDefault(); switch (periodUnit) { case Year: return getYearRange(moment, timeCalendar); case Halfyear: return getHalfyearRange(moment, timeCalendar); case Quarter: return getQuarterRange(moment, timeCalendar); case Month: return getMonthRange(moment, timeCalendar); case Week: return getWeekRange(moment, timeCalendar); case Day: return getDayRange(moment, timeCalendar); case Hour: return getHourRange(moment, timeCalendar); case Minute: return getMinuteRange(moment, timeCalendar); case Second: return new TimeRange(trimToMillis(moment), Durations.Second); default: throw new NotSupportException("지원하지 않는 Period 종류입니다. periodUnit=" + periodUnit); } } /** moment 가 속한 특정 종류의 기간에 대해 periodCount 갯수만큼의 기간 정보를 컬렉션으로 반환한다. */ public static ICalendarTimeRange getPeriodsOf(DateTime moment, PeriodUnit periodUnit, int periodCount) { return getPeriodsOf(moment, periodUnit, periodCount, TimeCalendar.getDefault()); } /** moment 가 속한 특정 종류의 기간에 대해 periodCount 갯수만큼의 기간 정보를 컬렉션으로 반환한다. */ public static ICalendarTimeRange getPeriodsOf(DateTime moment, PeriodUnit periodUnit, int periodCount, ITimeCalendar timeCalendar) { if (isTraceEnabled) log.trace("일자[{}]가 속한 기간 종류[{}]의 기간을 구합니다.", moment, periodUnit); if (timeCalendar == null) timeCalendar = TimeCalendar.getDefault(); switch (periodUnit) { case Year: return getYearRanges(moment, periodCount, timeCalendar); case Halfyear: return getHalfyearRanges(moment, periodCount, timeCalendar); case Quarter: return getQuarterRanges(moment, periodCount, timeCalendar); case Month: return getMonthRanges(moment, periodCount, timeCalendar); case Week: return getWeekRanges(moment, periodCount, timeCalendar); case Day: return getDayRanges(moment, periodCount, timeCalendar); case Hour: return getHourRanges(moment, periodCount, timeCalendar); case Minute: return getMinuteRanges(moment, periodCount, timeCalendar); case Second: return new CalendarTimeRange(trimToMillis(moment), trimToMillis(moment).plusSeconds(periodCount), timeCalendar); default: throw new NotSupportException("지원하지 않는 Period 종류입니다. periodUnit=" + periodUnit); } } /** * Gets year range. * * @param moment the moment * @param timeCalendar the time calendar * @return the year range */ public static YearRange getYearRange(DateTime moment, ITimeCalendar timeCalendar) { return new YearRange(moment, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } /** * Gets year ranges. * * @param moment the moment * @param years the years * @param timeCalendar the time calendar * @return the year ranges */ public static YearRangeCollection getYearRanges(DateTime moment, int years, ITimeCalendar timeCalendar) { return new YearRangeCollection(moment, years, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } /** * Gets halfyear range. * * @param moment the moment * @param timeCalendar the time calendar * @return the halfyear range */ public static HalfyearRange getHalfyearRange(DateTime moment, ITimeCalendar timeCalendar) { return new HalfyearRange(moment, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } /** * Gets halfyear ranges. * * @param moment the moment * @param halfyears the halfyears * @param timeCalendar the time calendar * @return the halfyear ranges */ public static HalfyearRangeCollection getHalfyearRanges(DateTime moment, int halfyears, ITimeCalendar timeCalendar) { return new HalfyearRangeCollection(moment, halfyears, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } /** * Gets quarter range. * * @param moment the moment * @param timeCalendar the time calendar * @return the quarter range */ public static QuarterRange getQuarterRange(DateTime moment, ITimeCalendar timeCalendar) { return new QuarterRange(moment, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } /** * Gets quarter ranges. * * @param moment the moment * @param quarters the quarters * @param timeCalendar the time calendar * @return the quarter ranges */ public static QuarterRangeCollection getQuarterRanges(DateTime moment, int quarters, ITimeCalendar timeCalendar) { return new QuarterRangeCollection(moment, quarters, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } /** * Gets month range. * * @param moment the moment * @param timeCalendar the time calendar * @return the month range */ public static MonthRange getMonthRange(DateTime moment, ITimeCalendar timeCalendar) { return new MonthRange(moment, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } /** * Gets month ranges. * * @param moment the moment * @param months the months * @param timeCalendar the time calendar * @return the month ranges */ public static MonthRangeCollection getMonthRanges(DateTime moment, int months, ITimeCalendar timeCalendar) { return new MonthRangeCollection(moment, months, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } /** * Gets week range. * * @param moment the moment * @param timeCalendar the time calendar * @return the week range */ public static WeekRange getWeekRange(DateTime moment, ITimeCalendar timeCalendar) { return new WeekRange(moment, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } /** * Gets week ranges. * * @param moment the moment * @param weeks the weeks * @param timeCalendar the time calendar * @return the week ranges */ public static WeekRangeCollection getWeekRanges(DateTime moment, int weeks, ITimeCalendar timeCalendar) { return new WeekRangeCollection(moment, weeks, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } /** * Gets day range. * * @param moment the moment * @param timeCalendar the time calendar * @return the day range */ public static DayRange getDayRange(DateTime moment, ITimeCalendar timeCalendar) { return new DayRange(moment, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } /** * Gets day ranges. * * @param moment the moment * @param days the days * @param timeCalendar the time calendar * @return the day ranges */ public static DayRangeCollection getDayRanges(DateTime moment, int days, ITimeCalendar timeCalendar) { return new DayRangeCollection(moment, days, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } /** * Gets hour range. * * @param moment the moment * @param timeCalendar the time calendar * @return the hour range */ public static HourRange getHourRange(DateTime moment, ITimeCalendar timeCalendar) { return new HourRange(moment, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } /** * Gets hour ranges. * * @param moment the moment * @param hours the hours * @param timeCalendar the time calendar * @return the hour ranges */ public static HourRangeCollection getHourRanges(DateTime moment, int hours, ITimeCalendar timeCalendar) { return new HourRangeCollection(moment, hours, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } /** * Gets minute range. * * @param moment the moment * @param timeCalendar the time calendar * @return the minute range */ public static MinuteRange getMinuteRange(DateTime moment, ITimeCalendar timeCalendar) { return new MinuteRange(moment, timeCalendar); } /** * Gets minute ranges. * * @param moment the moment * @param minutes the minutes * @param timeCalendar the time calendar * @return the minute ranges */ public static MinuteRangeCollection getMinuteRanges(DateTime moment, int minutes, ITimeCalendar timeCalendar) { return new MinuteRangeCollection(moment, minutes, (timeCalendar != null) ? timeCalendar : TimeCalendar.getDefault()); } // endregion << Period >> // region << Relation >> /** 지정된 기간 안에 일자(target)이 있는지 여부 */ public static boolean hasInside(ITimePeriod period, DateTime target) { shouldNotBeNull(period, "period"); boolean isInside = target.compareTo(period.getStart()) >= 0 && target.compareTo(period.getEnd()) <= 0; if (isTraceEnabled) log.trace("기간 [{}] 안에 target[{}]이 포함되는가? [{}]", period, target, isInside); return isInside; } /** 지정된 기간 안에 대상 기간이 있는지 여부 */ public static boolean hasInside(ITimePeriod period, ITimePeriod target) { shouldNotBeNull(period, "period"); shouldNotBeNull(target, "target"); boolean isInside = hasInside(period, target.getStart()) && hasInside(period, target.getEnd()); if (isTraceEnabled) log.trace("기간 [{}] 안에 target[{}]이 포함되는가? [{}]", period, target, isInside); return isInside; } /** * Has pure inside. * * @param period the period * @param target the target * @return the boolean */ public static boolean hasPureInside(ITimePeriod period, DateTime target) { shouldNotBeNull(period, "period"); boolean isInside = target.compareTo(period.getStart()) > 0 && target.compareTo(period.getEnd()) < 0; if (isTraceEnabled) log.trace("기간 [{}] 안에 target[{}]이 경계를 제외하고, 포함되는가? [{}]", period, target, isInside); return isInside; } /** * Has pure inside. * * @param period the period * @param target the target * @return the boolean */ public static boolean hasPureInside(ITimePeriod period, ITimePeriod target) { shouldNotBeNull(period, "period"); shouldNotBeNull(target, "target"); boolean isInside = hasPureInside(period, target.getStart()) && hasPureInside(period, target.getEnd()); if (isTraceEnabled) log.trace("기간 [{}] 안에 target[{}]이 경계를 제외하고, 포함되는가? [{}]", period, target, isInside); return isInside; } /** * Is anytime. * * @param period the period * @return the boolean */ public static boolean isAnytime(ITimePeriod period) { return period != null && period.isAnytime(); } /** * Is not anytime. * * @param period the period * @return the boolean */ public static boolean isNotAnytime(ITimePeriod period) { return period != null && !period.isAnytime(); } /** 기준 기간과 대상 기간의 관계를 파악합니다. */ public static PeriodRelation getRelation(ITimePeriod period, ITimePeriod target) { shouldNotBeNull(period, "period"); shouldNotBeNull(target, "target"); PeriodRelation relation = PeriodRelation.NoRelation; if (period.getStart().compareTo(target.getEnd()) > 0) { relation = PeriodRelation.After; } else if (period.getEnd().compareTo(target.getStart()) < 0) { relation = PeriodRelation.Before; } else if (period.getStart().equals(target.getStart()) && period.getEnd().equals(target.getEnd())) { relation = PeriodRelation.ExactMatch; } else if (period.getStart().equals(target.getEnd())) { relation = PeriodRelation.StartTouching; } else if (period.getEnd().equals(target.getStart())) { relation = PeriodRelation.EndTouching; } else if (hasInside(period, target)) { if (Objects.equal(period.getStart(), target.getStart())) relation = PeriodRelation.EnclosingStartTouching; else relation = Objects.equal(period.getEnd(), target.getEnd()) ? PeriodRelation.EnclosingEndTouching : PeriodRelation.Enclosing; } else { // 기간이 대상 내부에 속할 때 boolean insideStart = hasInside(target, period.getStart()); boolean insideEnd = hasInside(target, period.getEnd()); if (insideStart && insideEnd) { relation = Objects.equal(period.getStart(), target.getStart()) ? PeriodRelation.InsideStartTouching : (Objects.equal(period.getEnd(), target.getEnd()) ? PeriodRelation.InsideEndTouching : PeriodRelation.Inside); } else if (insideStart) { relation = PeriodRelation.StartInside; } else if (insideEnd) { relation = PeriodRelation.EndInside; } } if (isDebugEnabled) log.debug("period [{}], target [{}]의 관계는 [{}]입니다.", period, target, relation); return relation; } /** 두 기간이 교차하거나, period가 target의 내부 구간이면 true를 반환합니다. */ public static boolean intersectsWith(ITimePeriod period, ITimePeriod target) { shouldNotBeNull(period, "period"); shouldNotBeNull(target, "target"); boolean isIntersected = hasInside(period, target.getStart()) || hasInside(period, target.getEnd()) || hasPureInside(target, period); if (isTraceEnabled) log.trace("period[{}]와 target[{}]이 교차 구간인가? result=[{}]", period, target, isIntersected); return isIntersected; } /** * 두 구간이 겹치는 구간이 있으면 true를 반환합니다. * * @param period 기준 기간 * @param target 대상 기간 * @return 겹치는지 여부 */ public static boolean overlapsWith(ITimePeriod period, ITimePeriod target) { shouldNotBeNull(period, "period"); shouldNotBeNull(target, "target"); PeriodRelation relation = getRelation(period, target); boolean isOverlaps = relation != PeriodRelation.After && relation != PeriodRelation.StartTouching && relation != PeriodRelation.EndTouching && relation != PeriodRelation.Before; if (isTraceEnabled) log.trace("period[{}]와 target[{}]이 overlap 되는가? [{}]", period, target, isOverlaps); return isOverlaps; } /** 두 기간의 교집합 (교차 구간)을 구합니다. */ public static TimeBlock getIntersectionBlock(ITimePeriod period, ITimePeriod target) { shouldNotBeNull(period, "period"); shouldNotBeNull(target, "target"); TimeBlock intersection = null; if (intersectsWith(period, target)) { DateTime start = max(period.getStart(), target.getStart()); DateTime end = min(period.getEnd(), target.getEnd()); intersection = new TimeBlock(start, end, period.isReadonly()); } if (isTraceEnabled) log.trace("period[{}], target[{}]의 교집합 [{}]", period, target, intersection); return intersection; } /** 두 기간의 합집합 구간을 구합니다. */ public static TimeBlock getUnionBlock(ITimePeriod period, ITimePeriod target) { shouldNotBeNull(period, "period"); shouldNotBeNull(target, "target"); DateTime start = min(period.getStart(), target.getStart()); DateTime end = max(period.getEnd(), target.getEnd()); TimeBlock union = new TimeBlock(start, end, period.isReadonly()); if (isTraceEnabled) log.trace("period[{}], target[{}]의 합집합 [{}]", period, target, union); return union; } /** 두 기간의 교집합 (교차 구간)을 구합니다. */ public static TimeRange getIntersectionRange(ITimePeriod period, ITimePeriod target) { shouldNotBeNull(period, "period"); shouldNotBeNull(target, "target"); TimeRange intersection = null; if (intersectsWith(period, target)) { DateTime start = max(period.getStart(), target.getStart()); DateTime end = min(period.getEnd(), target.getEnd()); intersection = new TimeRange(start, end, period.isReadonly()); } if (isTraceEnabled) log.trace("period[{}], target[{}]의 교집합 [{}]", period, target, intersection); return intersection; } /** 두 기간의 합집합 구간을 구합니다. */ public static TimeRange getUnionRange(ITimePeriod period, ITimePeriod target) { shouldNotBeNull(period, "period"); shouldNotBeNull(target, "target"); DateTime start = min(period.getStart(), target.getStart()); DateTime end = max(period.getEnd(), target.getEnd()); TimeRange union = new TimeRange(start, end, period.isReadonly()); if (isTraceEnabled) log.trace("period[{}], target[{}]의 합집합 [{}]", period, target, union); return union; } // endregion << Relation >> // region << Trim >> /** * Trim to year. (해당일자의 년도의 시작 시각을 반환한다) * * @param moment the moment * @return the date time */ public static DateTime trimToYear(DateTime moment) { return new DateTime(moment.getYear(), 1, 1, 0, 0); } /** * 해당 일자의 년도의 시작 시각을 반환한다 * * @param moment the moment * @return the date time */ public static DateTime trimToMonth(DateTime moment) { return trimToMonth(moment, 1); } /** * 해당 일자의 년도의 시작 시각을 반환한다 * * @param moment the moment * @param monthOfYear the month of year * @return the date time */ public static DateTime trimToMonth(DateTime moment, int monthOfYear) { return asDate(moment.getYear(), monthOfYear, 1); } /** * @param moment the moment * @return the date time */ public static DateTime trimToDay(DateTime moment) { return trimToDay(moment, 1); } /** * 해당 일자를 지정한 년/월/일의 시작 시각을 반환합니다. * * @param moment the moment * @param dayOfMonth the day of month * @return the date time */ public static DateTime trimToDay(DateTime moment, int dayOfMonth) { return asDate(moment.getYear(), moment.getMonthOfYear(), dayOfMonth); } /** * Trim to hour. * * @param moment the moment * @return the date time */ public static DateTime trimToHour(DateTime moment) { return trimToHour(moment, 0); } /** * Trim to hour. * * @param moment the moment * @param hourOfDay the hour of day * @return the date time */ public static DateTime trimToHour(DateTime moment, int hourOfDay) { return getDate(moment).withHourOfDay(hourOfDay); } /** * Trim to minute. * * @param moment the moment * @return the date time */ public static DateTime trimToMinute(DateTime moment) { return trimToMinute(moment, 0); } /** * Trim to minute. * * @param moment the moment * @param minuteOfHour the minute of hour * @return the date time */ public static DateTime trimToMinute(DateTime moment, int minuteOfHour) { return trimToHour(moment, moment.getHourOfDay()).withMinuteOfHour(minuteOfHour); } /** * Trim to second. * * @param moment the moment * @return the date time */ public static DateTime trimToSecond(DateTime moment) { return trimToSecond(moment, 0); } /** * Trim to second. * * @param moment the moment * @param secondOfMinute the second of minute * @return the date time */ public static DateTime trimToSecond(DateTime moment, int secondOfMinute) { return trimToMinute(moment, moment.getMinuteOfHour()).withSecondOfMinute(secondOfMinute); } /** * Trim to millis. * * @param moment the moment * @return the date time */ public static DateTime trimToMillis(DateTime moment) { return trimToMillis(moment, 0); } /** * Trim to millis. * * @param moment the moment * @param millisOfSecond the millis of second * @return the date time */ public static DateTime trimToMillis(DateTime moment, int millisOfSecond) { return moment.withMillisOfSecond(millisOfSecond); } // endregion << Trim >> // region << Validation >> /** * Assert valid period. * * @param start the start * @param end the end */ public static void assertValidPeriod(DateTime start, DateTime end) { if (start != null && end != null) assert start.compareTo(end) <= 0 : String.format("시작시각이 완료시각보다 이전이어야 합니다. start=[%s], end=[%s]", start, end); } /** * Assert mutable. * * @param period the period */ public static void assertMutable(ITimePeriod period) { shouldNotBeNull(period, "period"); assert !period.isReadonly() : "TimePeriod가 읽기전용입니다. period=" + period; } /** * All items are equal. * * @param left the left * @param right the right * @return the boolean */ public static boolean allItemsAreEqual(Iterable<ITimePeriod> left, Iterable<ITimePeriod> right) { shouldNotBeNull(left, "left"); shouldNotBeNull(right, "right"); if (Iterables.size(left) != Iterables.size(right)) return false; Iterator<ITimePeriod> leftIter = left.iterator(); Iterator<ITimePeriod> rightIter = right.iterator(); while (leftIter.hasNext() && rightIter.hasNext()) { if (!Objects.equal(leftIter.next(), rightIter.next())) return false; } return true; } /** * Is weekday. * * @param dayOfWeek the day of week * @return the boolean */ public static boolean isWeekday(DayOfWeek dayOfWeek) { return ArrayTool.contains(TimeSpec.Weekdays, dayOfWeek); } /** * Is weekend. * * @param dayOfWeek the day of week * @return the boolean */ public static boolean isWeekend(DayOfWeek dayOfWeek) { return ArrayTool.contains(TimeSpec.Weekends, dayOfWeek); } // endregion << Validation >> // region << Comparator >> @Getter(lazy = true) private static final StartComparator startComparator = new StartComparator(); @Getter(lazy = true) private static final StartDescComparator startDescComparator = new StartDescComparator(); @Getter(lazy = true) private static final EndComparator endComparator = new EndComparator(); @Getter(lazy = true) private static final EndDescComparator endDescComparator = new EndDescComparator(); @Getter(lazy = true) private static final DurationComparator durationComparator = new DurationComparator(); @Getter(lazy = true) private static final DurationDescComparator durationDescComparator = new DurationDescComparator(); /** The type Start comparator. */ public static class StartComparator implements Comparator<ITimePeriod> { @Override public int compare(ITimePeriod o1, ITimePeriod o2) { return o1.getStart().compareTo(o2.getStart()); } } /** The type Start desc comparator. */ public static class StartDescComparator implements Comparator<ITimePeriod> { @Override public int compare(ITimePeriod o1, ITimePeriod o2) { return o2.getStart().compareTo(o1.getStart()); } } /** The type End comparator. */ public static class EndComparator implements Comparator<ITimePeriod> { @Override public int compare(ITimePeriod o1, ITimePeriod o2) { return o1.getEnd().compareTo(o2.getEnd()); } } /** The type End desc comparator. */ public static class EndDescComparator implements Comparator<ITimePeriod> { @Override public int compare(ITimePeriod o1, ITimePeriod o2) { return o2.getEnd().compareTo(o1.getEnd()); } } /** The type Duration comparator. */ public static class DurationComparator implements Comparator<ITimePeriod> { @Override public int compare(ITimePeriod o1, ITimePeriod o2) { return o1.getDuration().compareTo(o2.getDuration()); } } /** The type Duration desc comparator. */ public static class DurationDescComparator implements Comparator<ITimePeriod> { @Override public int compare(ITimePeriod o1, ITimePeriod o2) { return o2.getDuration().compareTo(o1.getDuration()); } } // endregion // region << ForEach >> /** 지정된 기간을 기간 단위별로 세분하여 컬렉션을 빌드합니다. */ public static List<ITimePeriod> foreachPeriods(ITimePeriod period, PeriodUnit periodUnit) { switch (periodUnit) { case Year: return foreachYears(period); case Halfyear: return foreachHalfyears(period); case Quarter: return foreachQuarters(period); case Month: return foreachMonths(period); case Week: return foreachWeeks(period); case Day: return foreachDays(period); case Hour: return foreachHours(period); case Minute: return foreachMinutes(period); default: throw new IllegalArgumentException("지원하지 않는 PeriodKind입니다. PeriodUnit=" + periodUnit); } } /** 지정된 기간을 년단위로 컬렉션을 만듭니다. */ public static List<ITimePeriod> foreachYears(ITimePeriod period) { assert period != null; if (isTraceEnabled) log.trace("기간[{}]에 대해 Year 단위로 열거합니다...", period); List<ITimePeriod> years = Lists.newArrayList(); if (period.isAnytime()) return years; assertHasPeriod(period); if (Times.isSameYear(period.getStart(), period.getEnd())) { years.add(new TimeRange(period)); return years; } years.add(new TimeRange(period.getStart(), Times.endTimeOfYear(period.getStart()))); DateTime current = Times.startTimeOfYear(period.getStart()).plusYears(1); int endYear = period.getEnd().getYear(); ITimeCalendar calendar = TimeCalendar.getDefault(); while (current.getYear() < endYear) { years.add(Times.getYearRange(current, calendar)); current = current.plusYears(1); } if (current.compareTo(period.getEnd()) < 0) { years.add(new TimeRange(Times.startTimeOfYear(current), period.getEnd())); } return years; } /** 지정된 기간을 반기 단위로 열거합니다. */ public static List<ITimePeriod> foreachHalfyears(ITimePeriod period) { assert period != null; if (isTraceEnabled) log.trace("기간[{}]에 대해 Halfyear 단위로 열거합니다...", period); List<ITimePeriod> halfyears = Lists.newArrayList(); if (period.isAnytime()) return halfyears; assertHasPeriod(period); if (Times.isSameHalfyear(period.getStart(), period.getEnd())) { halfyears.add(new TimeRange(period)); return halfyears; } DateTime current = Times.endTimeOfHalfyear(period.getStart()); halfyears.add(new TimeRange(period.getStart(), current)); int endHashCode = period.getEnd().getYear() * 10 + Times.halfyearOf(period.getEnd()).getValue(); current = current.plusDays(1); ITimeCalendar calendar = TimeCalendar.getDefault(); while (current.getYear() * 10 + Times.halfyearOf(current).getValue() < endHashCode) { halfyears.add(Times.getHalfyearRange(current, calendar)); current = current.plusMonths(TimeSpec.MonthsPerHalfyear); } if (current.compareTo(period.getEnd()) < 0) { halfyears.add(new TimeRange(Times.startTimeOfHalfyear(current), period.getEnd())); } return halfyears; } /** 지정된 기간을 분기단위로 열거합니다. */ public static List<ITimePeriod> foreachQuarters(ITimePeriod period) { assert period != null; if (isTraceEnabled) log.trace("기간[{}]에 대해 Quarter 단위로 열거합니다...", period); List<ITimePeriod> quarters = Lists.newArrayList(); if (period.isAnytime()) return quarters; assertHasPeriod(period); if (Times.isSameQuarter(period.getStart(), period.getEnd())) { quarters.add(new TimeRange(period)); return quarters; } DateTime current = Times.endTimeOfQuarter(period.getStart()); quarters.add(new TimeRange(period.getStart(), current)); int endHashCode = period.getEnd().getYear() * 10 + Times.quarterOf(period.getEnd()).getValue(); current = current.plusDays(1); ITimeCalendar calendar = TimeCalendar.getDefault(); while (current.getYear() * 10 + Times.quarterOf(current).getValue() < endHashCode) { quarters.add(Times.getQuarterRange(current, calendar)); current = current.plusMonths(TimeSpec.MonthsPerQuarter); } if (current.compareTo(period.getEnd()) < 0) quarters.add(new TimeRange(Times.startTimeOfQuarter(current), period.getEnd())); return quarters; } /** 지정된 기간을 월(Month) 단위로 열거합니다. */ public static List<ITimePeriod> foreachMonths(ITimePeriod period) { assert period != null; if (isTraceEnabled) log.trace("기간[{}]에 대해 월(Month) 단위로 열거합니다...", period); List<ITimePeriod> months = Lists.newArrayList(); if (period.isAnytime()) return months; assertHasPeriod(period); if (Times.isSameMonth(period.getStart(), period.getEnd())) { months.add(new TimeRange(period)); return months; } DateTime current = Times.endTimeOfMonth(period.getStart()); months.add(new TimeRange(period.getStart(), current)); DateTime monthEnd = Times.startTimeOfMonth(period.getEnd()); current = current.plusDays(1); ITimeCalendar calendar = TimeCalendar.getDefault(); while (current.compareTo(monthEnd) < 0) { months.add(Times.getMonthRange(current, calendar)); current = current.plusMonths(1); } current = Times.startTimeOfMonth(current); if (current.compareTo(period.getEnd()) < 0) months.add(new TimeRange(current, period.getEnd())); return months; } /** 지정된 기간을 주(Week) 단위로 열거합니다. */ public static List<ITimePeriod> foreachWeeks(ITimePeriod period) { assert period != null; if (isTraceEnabled) log.trace("기간[{}]에 대해 주(Week) 단위로 열거합니다...", period); List<ITimePeriod> weeks = Lists.newArrayList(); if (period.isAnytime()) return weeks; assertHasPeriod(period); if (Times.isSameWeek(period.getStart(), period.getEnd())) { weeks.add(new TimeRange(period)); return weeks; } DateTime current = period.getStart(); DateTime weekEnd = Times.endTimeOfWeek(current); if (weekEnd.compareTo(period.getEnd()) >= 0) { weeks.add(new TimeRange(current, period.getEnd())); return weeks; } weeks.add(new TimeRange(current, weekEnd)); current = weekEnd.plusWeeks(1); ITimeCalendar calendar = TimeCalendar.getDefault(); while (current.compareTo(period.getEnd()) < 0) { weeks.add(Times.getWeekRange(current, calendar)); current = current.plusWeeks(1); } current = Times.startTimeOfWeek(current); if (current.compareTo(period.getEnd()) < 0) { weeks.add(new TimeRange(current, period.getEnd())); } return weeks; } /** 지정한 기간을 일(Day)단위로 열거합니다. */ public static List<ITimePeriod> foreachDays(ITimePeriod period) { shouldNotBeNull(period, "period"); if (isTraceEnabled) log.trace("기간[{}]에 대해 일(Day) 단위로 열거합니다...", period); List<ITimePeriod> days = Lists.newArrayList(); if (period.isAnytime()) return days; assertHasPeriod(period); if (Times.isSameDay(period.getStart(), period.getEnd())) { days.add(new TimeRange(period)); return days; } days.add(new TimeRange(period.getStart(), Times.endTimeOfDay(period.getStart()))); DateTime endDay = period.getEnd().withTimeAtStartOfDay(); DateTime current = period.getStart().withTimeAtStartOfDay().plusDays(1); ITimeCalendar calendar = TimeCalendar.getDefault(); while (current.compareTo(endDay) < 0) { days.add(Times.getDayRange(current, calendar)); current = current.plusDays(1); } if (period.getEnd().getMillisOfDay() > 0) days.add(new TimeRange(endDay.withTimeAtStartOfDay(), period.getEnd())); return days; } /** 지정한 기간을 시(Hour) 단위로 열거합니다. */ public static List<ITimePeriod> foreachHours(ITimePeriod period) { shouldNotBeNull(period, "period"); if (isTraceEnabled) log.trace("기간[{}]에 대해 시간(Hour) 단위로 열거합니다...", period); List<ITimePeriod> hours = Lists.newArrayList(); if (period.isAnytime()) return hours; assertHasPeriod(period); if (Times.isSameHour(period.getStart(), period.getEnd())) { hours.add(new TimeRange(period)); return hours; } hours.add(new TimeRange(period.getStart(), Times.endTimeOfHour(period.getStart()))); DateTime endHour = period.getEnd(); DateTime current = Times.trimToHour(period.getStart(), period.getStart().getHourOfDay() + 1); ITimeCalendar calendar = TimeCalendar.getDefault(); DateTime maxHour = endHour.minusHours(1); while (current.compareTo(maxHour) <= 0) { hours.add(Times.getHourRange(current, calendar)); current = current.plusHours(1); } if (endHour.minusHours(endHour.getHourOfDay()).getMillisOfDay() > 0) { hours.add(new TimeRange(Times.startTimeOfHour(endHour), endHour)); } return hours; } /** 지정한 기간을 분(Minute) 단위로 열거합니다. */ public static List<ITimePeriod> foreachMinutes(ITimePeriod period) { shouldNotBeNull(period, "period"); if (isTraceEnabled) log.trace("기간[{}]에 대해 분(Minute) 단위로 열거합니다...", period); List<ITimePeriod> minutes = Lists.newArrayList(); if (period.isAnytime()) return minutes; assertHasPeriod(period); if (Times.isSameMinute(period.getStart(), period.getEnd())) { minutes.add(new TimeRange(period)); return minutes; } minutes.add(new TimeRange(period.getStart(), Times.endTimeOfMinute(period.getStart()))); DateTime endMinute = period.getEnd(); DateTime current = Times.trimToMinute(period.getStart(), period.getStart().getMinuteOfHour() + 1); ITimeCalendar calendar = TimeCalendar.getDefault(); DateTime maxMinute = endMinute.minusMinutes(1); while (current.compareTo(maxMinute) <= 0) { minutes.add(Times.getMinuteRange(current, calendar)); current = current.plusMinutes(1); } if (endMinute.minusMinutes(endMinute.getMinuteOfHour()).getMillisOfDay() > 0) { minutes.add(new TimeRange(Times.startTimeOfMinute(endMinute), endMinute)); } return minutes; } private static void assertHasPeriod(ITimePeriod period) { assert period != null && period.hasPeriod() : "기간이 설정되지 않았습니다. period=" + period; } /** 기간을 특정 단위로 열거한 값을 이용하여 특정 코드를 수행하여 결과값을 반환합니다. */ public static <T> List<T> runPeriods(ITimePeriod period, PeriodUnit periodUnit, Function1<ITimePeriod, T> runner) { shouldNotBeNull(period, "period"); shouldNotBeNull(runner, "runner"); shouldBe(period.hasPeriod(), "period는 기간을 가져야합니다. period=%s", period); if (isDebugEnabled) log.debug("기간[{}]을 [{}] 단위로 열거하여, 메소드르 실행시켜 결과를 반환합니다.", period, periodUnit); List<T> results = Lists.newArrayList(); for (ITimePeriod item : foreachPeriods(period, periodUnit)) { results.add(runner.execute(item)); } return results; } /** 기간을 특정 단위로 열거한 값을 이용하여 특정 코드를 병렬로 수행하여 결과값을 반환합니다. */ public static <T> List<T> runPeriodsAsParallel(ITimePeriod period, PeriodUnit periodUnit, Function1<ITimePeriod, T> runner) { shouldNotBeNull(period, "period"); shouldNotBeNull(runner, "runner"); shouldBe(period.hasPeriod(), "period는 기간을 가져야합니다. period=%s", period); if (log.isDebugEnabled()) log.debug("기간[{}]을 [{}] 단위로 열거하여, 병렬로 메소드르 실행시켜 결과를 반환합니다.", period, periodUnit); return Parallels.runEach(foreachPeriods(period, periodUnit), runner); } // endregion << ForEach >> }