2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.astro.internal.util;
15 import java.util.Calendar;
16 import java.util.Date;
17 import java.util.regex.Pattern;
19 import org.apache.commons.lang3.time.DateUtils;
20 import org.openhab.binding.astro.internal.config.AstroChannelConfig;
21 import org.openhab.binding.astro.internal.model.Range;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
26 * Common used DateTime functions.
28 * @author Gerhard Riegler - Initial contribution
30 public class DateTimeUtils {
31 private static final Logger LOGGER = LoggerFactory.getLogger(DateTimeUtils.class);
32 private static final Pattern HHMM_PATTERN = Pattern.compile("^([0-1][0-9]|2[0-3])(:[0-5][0-9])$");
34 public static final double J1970 = 2440588.0;
35 public static final double MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24;
38 private DateTimeUtils() {
39 throw new IllegalAccessError("Non-instantiable");
43 * Truncates the time from the calendar object.
45 public static Calendar truncateToMidnight(Calendar calendar) {
46 return DateUtils.truncate(calendar, Calendar.DAY_OF_MONTH);
50 * Creates a Range object within the specified months and days. The start
51 * time is midnight, the end time is end of the day.
53 public static Range getRange(int startYear, int startMonth, int startDay, int endYear, int endMonth, int endDay) {
54 Calendar start = Calendar.getInstance();
55 start.set(Calendar.YEAR, startYear);
56 start.set(Calendar.MONTH, startMonth);
57 start.set(Calendar.DAY_OF_MONTH, startDay);
58 start = truncateToMidnight(start);
60 Calendar end = Calendar.getInstance();
61 end.set(Calendar.YEAR, endYear);
62 end.set(Calendar.MONTH, endMonth);
63 end.set(Calendar.DAY_OF_MONTH, endDay);
64 end.set(Calendar.HOUR_OF_DAY, 23);
65 end.set(Calendar.MINUTE, 59);
66 end.set(Calendar.SECOND, 59);
67 end.set(Calendar.MILLISECOND, 999);
69 return new Range(start, end);
73 * Returns a calendar object from a julian date.
75 public static Calendar toCalendar(double julianDate) {
76 if (Double.compare(julianDate, Double.NaN) == 0 || julianDate == 0) {
79 long millis = (long) ((julianDate + 0.5 - J1970) * MILLISECONDS_PER_DAY);
80 Calendar cal = Calendar.getInstance();
81 cal.setTimeInMillis(millis);
82 return DateUtils.round(cal, Calendar.MINUTE);
86 * Returns the julian date from the calendar object.
88 public static double dateToJulianDate(Calendar calendar) {
89 return calendar.getTimeInMillis() / MILLISECONDS_PER_DAY - 0.5 + J1970;
93 * Returns the midnight julian date from the calendar object.
95 public static double midnightDateToJulianDate(Calendar calendar) {
96 return dateToJulianDate(truncateToMidnight(calendar));
100 * Returns the end of day from the calendar object.
102 public static Calendar endOfDayDate(Calendar calendar) {
103 Calendar cal = (Calendar) calendar.clone();
104 cal = DateUtils.ceiling(cal, Calendar.DATE);
105 cal.add(Calendar.MILLISECOND, -1);
110 * Returns the end of day julian date from the calendar object.
112 public static double endOfDayDateToJulianDate(Calendar calendar) {
113 return dateToJulianDate(endOfDayDate(calendar));
117 * Returns the year of the calendar object as a decimal value.
119 public static double getDecimalYear(Calendar calendar) {
120 return calendar.get(Calendar.YEAR)
121 + (double) calendar.get(Calendar.DAY_OF_YEAR) / calendar.getActualMaximum(Calendar.DAY_OF_YEAR);
125 * Converts the time (hour.minute) to a calendar object.
127 public static Calendar timeToCalendar(Calendar calendar, double time) {
131 Calendar cal = (Calendar) calendar.clone();
135 cal.add(Calendar.DAY_OF_MONTH, 1);
138 minute = (int) ((time * 100) - (hour * 100));
140 cal.set(Calendar.HOUR_OF_DAY, hour);
141 cal.set(Calendar.MINUTE, minute);
142 return DateUtils.truncate(cal, Calendar.MINUTE);
146 * Returns true, if two calendar objects are on the same day ignoring time.
148 public static boolean isSameDay(Calendar cal1, Calendar cal2) {
149 return cal1 != null && cal2 != null && DateUtils.isSameDay(cal1, cal2);
153 * Returns a date object from a calendar.
155 public static Date getDate(Calendar calendar) {
156 return calendar == null ? null : calendar.getTime();
160 * Returns the next Calendar from today.
162 public static Calendar getNextFromToday(Calendar... calendars) {
163 return getNext(Calendar.getInstance(), calendars);
166 static Calendar getNext(Calendar now, Calendar... calendars) {
167 Calendar next = null;
168 Calendar firstSeasonOfYear = null;
169 for (Calendar calendar : calendars) {
170 if (firstSeasonOfYear == null || calendar.before(firstSeasonOfYear)) {
171 firstSeasonOfYear = calendar;
173 if (calendar.after(now) && (next == null || calendar.before(next))) {
177 return next == null ? firstSeasonOfYear : next;
181 * Returns true, if cal1 is greater or equal than cal2, ignoring seconds.
183 public static boolean isTimeGreaterEquals(Calendar cal1, Calendar cal2) {
184 Calendar truncCal1 = DateUtils.truncate(cal1, Calendar.MINUTE);
185 Calendar truncCal2 = DateUtils.truncate(cal2, Calendar.MINUTE);
186 return truncCal1.getTimeInMillis() >= truncCal2.getTimeInMillis();
190 * Applies the config to the given calendar.
192 public static Calendar applyConfig(Calendar cal, AstroChannelConfig config) {
194 if (config.offset != 0) {
195 Calendar cOffset = Calendar.getInstance();
196 cOffset.setTime(cCal.getTime());
197 cOffset.add(Calendar.MINUTE, config.offset);
201 Calendar cEarliest = adjustTime(cCal, getMinutesFromTime(config.earliest));
202 if (cCal.before(cEarliest)) {
205 Calendar cLatest = adjustTime(cCal, getMinutesFromTime(config.latest));
206 if (cCal.after(cLatest)) {
213 private static Calendar adjustTime(Calendar cal, int minutes) {
215 Calendar cTime = Calendar.getInstance();
216 cTime = DateUtils.truncate(cal, Calendar.DAY_OF_MONTH);
217 cTime.add(Calendar.MINUTE, minutes);
224 * Parses a HH:MM string and returns the minutes.
226 private static int getMinutesFromTime(String configTime) {
227 if (configTime != null) {
228 String time = configTime.trim();
229 if (!time.isEmpty()) {
231 if (!HHMM_PATTERN.matcher(time).matches()) {
232 throw new NumberFormatException();
234 String[] elements = time.split(":");
235 int hour = Integer.parseInt(elements[0]);
236 int minutes = Integer.parseInt(elements[1]);
237 return (hour * 60) + minutes;
239 } catch (NumberFormatException ex) {
241 "Can not parse astro channel configuration '{}' to hour and minutes, use pattern hh:mm, ignoring!",