Jawa
Wszyscy wiemy, że Java Date / Time API jest bezużyteczny i uszkodzony. Oto poprawka, która nie używa (przynajmniej bezpośrednio) żadnego z istniejących API. Obsługuje nawet sekundy przestępne! :) Dane wyjściowe są w UTC.
import java.lang.reflect.Field;
import java.net.HttpCookie;
import java.util.*;
public class FixedTimeAPI4Java {
private static final List<Integer> MONTHS_WITH_30_DAYS = Arrays.asList(4, 6, 9, 11);
private static final List<Integer> YEARS_WITH_LEAP_SECOND_IN_DECEMBER = Arrays.asList(1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1987, 1989, 1990, 1995, 1998, 2005, 2008);
private static final List<Integer> YEARS_WITH_LEAP_SECOND_IN_JUNE = Arrays.asList(1972, 1981, 1982, 1983, 1985, 1992, 1993, 1994, 1997, 2012);
/**
* Returns the UTC time, at the time of method invocation, with millisecond
* precision, in format <code>yyyy-MM-dd HH:mm:ss.SSS</code>.
*/
public String getTime() throws Exception {
// The cookie is only used for accessing current system time
HttpCookie cookie = new HttpCookie("Offline", "Cookie");
Field created = HttpCookie.class.getDeclaredField("whenCreated");
created.setAccessible(true);
long millisecondsSinceEpoch = created.getLong(cookie);
long fullSecondsSinceEpoch = millisecondsSinceEpoch / 1000L;
int year = 1970, month = 1, dayOfMonth = 1, hour = 0, minute = 0, second = 0,
millisecond = (int)(millisecondsSinceEpoch - (fullSecondsSinceEpoch * 1000L));
ticks:
for (;; year++) {
for (month = 1; month <= 12; month++) {
for (dayOfMonth = 1; dayOfMonth <= daysInMonth(month, year); dayOfMonth++) {
for (hour = 0; hour < 24; hour++) {
for (minute = 0; minute < 60; minute++) {
for (second = 0; second < secondsInMinute(minute, hour, dayOfMonth, month, year); second++, fullSecondsSinceEpoch--) {
if (fullSecondsSinceEpoch == 0) {
break ticks;
}
}
}
}
}
}
}
return String.format("%04d-%02d-%02d %02d:%02d:%02d.%03d", year, month,
dayOfMonth, hour, minute, second, millisecond);
}
/**
* Returns the seconds in the given minute of the given hour/day/month/year,
* taking into account leap seconds that can be added to the last minute of
* June or December.
*/
private static int secondsInMinute(int minute, int hour, int day, int month, int year) {
return (minute == 59 && hour == 23 && ((day == 30 && month == 6) || (day == 31 && month == 12)))
? 60 + leapSecondsInMonth( month, year)
: 60;
}
/**
* Returns the number of days in the given month of the given year.
*/
private static int daysInMonth(int month, int year) {
return month == 2 ? isLeapYear(year) ? 29 : 28
: MONTHS_WITH_30_DAYS.contains(month) ? 30
: 31;
}
/**
* Returns whether the given year is a leap year or not.
* A leap year is every 4th year, but not if the year is divisible by 100, unless if it's divisible by 400.
*/
private static boolean isLeapYear(int year) {
return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? true : false;
}
/**
* Returns the number of leap seconds that were added to UTC time at the end of the given month and year.
* Leap seconds are added (by the decison of International Earth Rotation Service / Paris Observatory)
* in order to keep UTC within 0.9 seconds of international atomic time (TAI).
* <p>TODO: implement parser for updated list at http://www.ietf.org/timezones/data/leap-seconds.list :)
*/
private static int leapSecondsInMonth(int month, int year) {
return (year < 1972 || year > 2012) ? 0
: (month == 6 && YEARS_WITH_LEAP_SECOND_IN_JUNE.contains(year)) ? 1
: (month == 12 && YEARS_WITH_LEAP_SECOND_IN_DECEMBER.contains(year)) ? 1
: 0;
}
public final static void main(String[] args) throws Exception {
System.out.println(new FixedTimeAPI4Java().getTime());
}
}