]> git.basschouten.com Git - openhab-addons.git/blob
05eb5bffea20772c6721e714895d9c5f5b326482
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.astro.internal.util;
14
15 import java.util.Calendar;
16 import java.util.regex.Pattern;
17
18 import org.openhab.binding.astro.internal.config.AstroChannelConfig;
19 import org.openhab.binding.astro.internal.model.Range;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 /**
24  * Common used DateTime functions.
25  *
26  * @author Gerhard Riegler - Initial contribution
27  */
28 public class DateTimeUtils {
29     private static final Logger LOGGER = LoggerFactory.getLogger(DateTimeUtils.class);
30     private static final Pattern HHMM_PATTERN = Pattern.compile("^([0-1][0-9]|2[0-3])(:[0-5][0-9])$");
31
32     private static final double J1970 = 2440588.0;
33     private static final double MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24;
34
35     /** Constructor */
36     private DateTimeUtils() {
37         throw new IllegalAccessError("Non-instantiable");
38     }
39
40     /**
41      * Truncates the time from the calendar object.
42      */
43     public static Calendar truncateToSecond(Calendar calendar) {
44         Calendar cal = (Calendar) calendar.clone();
45         cal.set(Calendar.MILLISECOND, 0);
46         return cal;
47     }
48
49     /**
50      * Truncates the time from the calendar object.
51      */
52     private static Calendar truncateToMinute(Calendar calendar) {
53         Calendar cal = truncateToSecond(calendar);
54         cal.set(Calendar.SECOND, 0);
55         return cal;
56     }
57
58     /**
59      * Truncates the time from the calendar object.
60      */
61     public static Calendar truncateToMidnight(Calendar calendar) {
62         Calendar cal = truncateToMinute(calendar);
63         cal.set(Calendar.HOUR_OF_DAY, 0);
64         cal.set(Calendar.MINUTE, 0);
65         return cal;
66     }
67
68     /**
69      * Creates a Range object within the specified months and days. The start
70      * time is midnight, the end time is end of the day.
71      */
72     public static Range getRange(int startYear, int startMonth, int startDay, int endYear, int endMonth, int endDay) {
73         Calendar start = Calendar.getInstance();
74         start.set(Calendar.YEAR, startYear);
75         start.set(Calendar.MONTH, startMonth);
76         start.set(Calendar.DAY_OF_MONTH, startDay);
77         start = truncateToMidnight(start);
78
79         Calendar end = Calendar.getInstance();
80         end.set(Calendar.YEAR, endYear);
81         end.set(Calendar.MONTH, endMonth);
82         end.set(Calendar.DAY_OF_MONTH, endDay);
83         end.set(Calendar.HOUR_OF_DAY, 23);
84         end.set(Calendar.MINUTE, 59);
85         end.set(Calendar.SECOND, 59);
86         end.set(Calendar.MILLISECOND, 999);
87
88         return new Range(start, end);
89     }
90
91     /**
92      * Returns a calendar object from a julian date.
93      */
94     public static Calendar toCalendar(double julianDate) {
95         if (Double.compare(julianDate, Double.NaN) == 0 || julianDate == 0) {
96             return null;
97         }
98         long millis = (long) ((julianDate + 0.5 - J1970) * MILLISECONDS_PER_DAY);
99         Calendar cal = Calendar.getInstance();
100         cal.setTimeInMillis(millis);
101         int second = cal.get(Calendar.SECOND);
102         if (second > 30) {
103             cal.add(Calendar.MINUTE, 1);
104         }
105         return truncateToMinute(cal);
106     }
107
108     /**
109      * Returns the julian date from the calendar object.
110      */
111     public static double dateToJulianDate(Calendar calendar) {
112         return calendar.getTimeInMillis() / MILLISECONDS_PER_DAY - 0.5 + J1970;
113     }
114
115     /**
116      * Returns the midnight julian date from the calendar object.
117      */
118     public static double midnightDateToJulianDate(Calendar calendar) {
119         return dateToJulianDate(truncateToMidnight(calendar));
120     }
121
122     /**
123      * Returns the end of day from the calendar object.
124      */
125     public static Calendar endOfDayDate(Calendar calendar) {
126         Calendar cal = truncateToMidnight(calendar);
127         cal.add(Calendar.DATE, 1);
128         cal.add(Calendar.MILLISECOND, -1);
129         return cal;
130     }
131
132     /**
133      * Returns the end of day julian date from the calendar object.
134      */
135     public static double endOfDayDateToJulianDate(Calendar calendar) {
136         return dateToJulianDate(endOfDayDate(calendar));
137     }
138
139     /**
140      * Returns the year of the calendar object as a decimal value.
141      */
142     public static double getDecimalYear(Calendar calendar) {
143         return calendar.get(Calendar.YEAR)
144                 + (double) calendar.get(Calendar.DAY_OF_YEAR) / calendar.getActualMaximum(Calendar.DAY_OF_YEAR);
145     }
146
147     /**
148      * Converts the time (hour.minute) to a calendar object.
149      */
150     public static Calendar timeToCalendar(Calendar calendar, double time) {
151         if (time < 0.0) {
152             return null;
153         }
154         Calendar cal = (Calendar) calendar.clone();
155         int hour = 0;
156         int minute = 0;
157         if (time == 24.0) {
158             cal.add(Calendar.DAY_OF_MONTH, 1);
159         } else {
160             hour = (int) time;
161             minute = (int) ((time * 100) - (hour * 100));
162         }
163         cal.set(Calendar.HOUR_OF_DAY, hour);
164         cal.set(Calendar.MINUTE, minute);
165         return truncateToMinute(cal);
166     }
167
168     /**
169      * Returns true, if two calendar objects are on the same day ignoring time.
170      */
171     public static boolean isSameDay(Calendar cal1, Calendar cal2) {
172         return cal1 != null && cal2 != null && cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA)
173                 && cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)
174                 && cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR);
175     }
176
177     /**
178      * Returns the next Calendar from today.
179      */
180     public static Calendar getNextFromToday(Calendar... calendars) {
181         return getNext(Calendar.getInstance(), calendars);
182     }
183
184     static Calendar getNext(Calendar now, Calendar... calendars) {
185         Calendar next = null;
186         Calendar firstSeasonOfYear = null;
187         for (Calendar calendar : calendars) {
188             if (firstSeasonOfYear == null || calendar.before(firstSeasonOfYear)) {
189                 firstSeasonOfYear = calendar;
190             }
191             if (calendar.after(now) && (next == null || calendar.before(next))) {
192                 next = calendar;
193             }
194         }
195         if (next == null) {
196             final Calendar nextYearSeason = (Calendar) firstSeasonOfYear.clone();
197
198             nextYearSeason.add(Calendar.YEAR, 1);
199             return nextYearSeason;
200         } else {
201             return next;
202         }
203     }
204
205     /**
206      * Returns true, if cal1 is greater or equal than cal2, ignoring seconds.
207      */
208     public static boolean isTimeGreaterEquals(Calendar cal1, Calendar cal2) {
209         Calendar truncCal1 = truncateToMinute(cal1);
210         Calendar truncCal2 = truncateToMinute(cal2);
211         return truncCal1.getTimeInMillis() >= truncCal2.getTimeInMillis();
212     }
213
214     /**
215      * Applies the config to the given calendar.
216      */
217     public static Calendar applyConfig(Calendar cal, AstroChannelConfig config) {
218         Calendar cCal = cal;
219         if (config.offset != 0) {
220             Calendar cOffset = Calendar.getInstance();
221             cOffset.setTime(cCal.getTime());
222             cOffset.add(Calendar.MINUTE, config.offset);
223             cCal = cOffset;
224         }
225
226         Calendar cEarliest = adjustTime(cCal, getMinutesFromTime(config.earliest));
227         if (cCal.before(cEarliest)) {
228             return cEarliest;
229         }
230         Calendar cLatest = adjustTime(cCal, getMinutesFromTime(config.latest));
231         if (cCal.after(cLatest)) {
232             return cLatest;
233         }
234
235         return cCal;
236     }
237
238     private static Calendar adjustTime(Calendar cal, int minutes) {
239         if (minutes > 0) {
240             Calendar cTime = truncateToMidnight(cal);
241             cTime.add(Calendar.MINUTE, minutes);
242             return cTime;
243         }
244         return cal;
245     }
246
247     /**
248      * Parses a HH:MM string and returns the minutes.
249      */
250     private static int getMinutesFromTime(String configTime) {
251         if (configTime != null) {
252             String time = configTime.trim();
253             if (!time.isEmpty()) {
254                 try {
255                     if (!HHMM_PATTERN.matcher(time).matches()) {
256                         throw new NumberFormatException();
257                     } else {
258                         String[] elements = time.split(":");
259                         int hour = Integer.parseInt(elements[0]);
260                         int minutes = Integer.parseInt(elements[1]);
261                         return (hour * 60) + minutes;
262                     }
263                 } catch (NumberFormatException ex) {
264                     LOGGER.warn(
265                             "Can not parse astro channel configuration '{}' to hour and minutes, use pattern hh:mm, ignoring!",
266                             time);
267                 }
268
269             }
270         }
271         return 0;
272     }
273 }