001/* 002 * Shredzone Commons - suncalc 003 * 004 * Copyright (C) 2017 Richard "Shred" Körber 005 * http://commons.shredzone.org 006 * 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 013 */ 014package org.shredzone.commons.suncalc.util; 015 016import static java.lang.Math.*; 017import static org.shredzone.commons.suncalc.util.ExtendedMath.*; 018 019import java.util.Calendar; 020import java.util.Date; 021 022import org.shredzone.commons.suncalc.param.TimeResultParameter.Unit; 023 024/** 025 * This class contains a Julian Date representation of a date. 026 * <p> 027 * Objects are immutable and threadsafe. 028 */ 029public class JulianDate { 030 031 private final Calendar cal; 032 private final double mjd; 033 034 /** 035 * Creates a new {@link JulianDate}. 036 * 037 * @param cal 038 * {@link Calendar} to use. Do not modify this object after invocation. 039 */ 040 public JulianDate(Calendar cal) { 041 this.cal = cal; 042 mjd = cal.getTimeInMillis() / 86400000.0 + 40587.0; 043 } 044 045 /** 046 * Returns a {@link JulianDate} of the current date and the given hour. 047 * 048 * @param hour 049 * Hour of this date. This is a floating point value. Fractions are used 050 * for minutes and seconds. 051 * @return {@link JulianDate} instance. 052 */ 053 public JulianDate atHour(double hour) { 054 Calendar clone = getCalendar(); 055 clone.add(Calendar.SECOND, (int) round(hour * 60.0 * 60.0)); 056 return new JulianDate(clone); 057 } 058 059 /** 060 * Returns a {@link JulianDate} of the given modified Julian date. 061 * 062 * @param mjd 063 * Modified Julian Date 064 * @return {@link JulianDate} instance. 065 * @since 2.3 066 */ 067 public JulianDate atModifiedJulianDate(double mjd) { 068 Calendar clone = getCalendar(); 069 clone.setTimeInMillis(Math.round((mjd - 40587.0) * 86400000.0)); 070 clone.clear(Calendar.MILLISECOND); 071 return new JulianDate(clone); 072 } 073 074 /** 075 * Returns a {@link JulianDate} of the given Julian century. 076 * 077 * @param jc 078 * Julian Century 079 * @return {@link JulianDate} instance. 080 * @since 2.3 081 */ 082 public JulianDate atJulianCentury(double jc) { 083 return atModifiedJulianDate(jc * 36525.0 + 51544.5); 084 } 085 086 /** 087 * Returns this {@link JulianDate} as {@link Date} object. 088 * 089 * @return {@link Date} of this {@link JulianDate}. 090 */ 091 public Date getDate() { 092 return cal.getTime(); 093 } 094 095 /** 096 * Returns this {@link JulianDate} as truncated {@link Date} object. 097 * 098 * @param unit 099 * {@link Unit} to truncate to 100 * @return Rounded {@link Date} of this {@link JulianDate}. 101 * @since 2.3 102 */ 103 public Date getDateTruncated(Unit unit) { 104 if (unit == null) { //NOSONAR: safety null check 105 throw new NullPointerException(); 106 } 107 108 Calendar clone = getCalendar(); 109 clone.set(Calendar.MILLISECOND, 0); 110 111 if (unit == Unit.MINUTES || unit == Unit.HOURS || unit == Unit.DAYS) { 112 clone.add(Calendar.SECOND, 30); 113 clone.set(Calendar.SECOND, 0); 114 } 115 116 if (unit == Unit.HOURS || unit == Unit.DAYS) { 117 clone.add(Calendar.MINUTE, 30); 118 clone.set(Calendar.MINUTE, 0); 119 } 120 121 if (unit == Unit.DAYS) { 122 clone.set(Calendar.HOUR_OF_DAY, 0); 123 } 124 125 return clone.getTime(); 126 } 127 128 /** 129 * Returns this {@link JulianDate} as {@link Calendar} object. 130 * 131 * @return New {@link Calendar} instance of this {@link JulianDate}. 132 */ 133 public Calendar getCalendar() { 134 return (Calendar) cal.clone(); 135 } 136 137 /** 138 * Returns the Modified Julian Date. 139 * 140 * @return Modified Julian Date, UTC. 141 */ 142 public double getModifiedJulianDate() { 143 return mjd; 144 } 145 146 /** 147 * Returns the Julian Centuries. 148 * 149 * @return Julian Centuries, based on J2000 epoch, UTC. 150 */ 151 public double getJulianCentury() { 152 return (mjd - 51544.5) / 36525.0; 153 } 154 155 /** 156 * Returns the Greenwich Mean Sidereal Time of this Julian Date. 157 * 158 * @return GMST 159 */ 160 public double getGreenwichMeanSiderealTime() { 161 final double secs = 86400.0; 162 163 double mjd0 = floor(mjd); 164 double ut = (mjd - mjd0) * secs; 165 double t0 = (mjd0 - 51544.5) / 36525.0; 166 double t = (mjd - 51544.5) / 36525.0; 167 168 double gmst = 24110.54841 169 + 8640184.812866 * t0 170 + 1.0027379093 * ut 171 + (0.093104 - 6.2e-6 * t) * t * t; 172 173 return (PI2 / secs) * (gmst % secs); 174 } 175 176 /** 177 * Returns the earth's true anomaly of the current date. 178 * <p> 179 * A simple approximation is used here. 180 * 181 * @return True anomaly, in radians 182 */ 183 public double getTrueAnomaly() { 184 int dayOfYear = cal.get(Calendar.DAY_OF_YEAR) - 1; 185 return PI2 * frac((dayOfYear - 4.0) / 365.256363); 186 } 187 188 @Override 189 public String toString() { 190 return String.format("%dd %02dh %02dm %02ds", 191 (long) mjd, 192 (long) (mjd * 24 % 24), 193 (long) (mjd * 24 * 60 % 60), 194 (long) (mjd * 24 * 60 * 60 % 60)); 195 } 196 197}