Java 如何在不使用外部库的情况下从毫秒获取月份
Java How to get Month from milliseconds WITHOUT using external Libraries
我正在使用 System.currentTimeMillis() 来获取自 1970 年以来的毫秒数,我能够获取当前的小时、分钟、秒和年份。例如,我使用以下公式获取年份,它 returns 2015:
((((currTimeInMilliSec / 1000) / 3600) / 24) / 365) + 1970
但是,考虑到闰年和不同月份(如 28、29、30、31)的不同天数,我如何从毫秒计算月份。
注意:由于某些原因,我只需要使用 currentTimeMillis 函数来计算,我不想使用其他函数或外部库。我也看过相关的帖子,但没有找到确切的答案。
使用公历。
GregorianCalendar c = new GregorianCalendar();
c.setTimeInMillis(1l);
int month = c.get(Calendar.MONTH);
这个returns0,就是一月。想象一个包含一年中的 12 个月的数组。
是的,这是可能的。
有一些天文算法可以实现儒略日数字和日期时间戳之间的数字转换。我熟悉的算法由 J. Meeus 在他的 天文算法,第 2 版中发表。 该算法将儒略日数转换为表示相应的整数向量:
- 年
- MONTH_IN_YEAR (1-12)
- DAY_IN_MONTH(1-28,29,30,31视情况而定)
- HOUR_IN_DAY (0-23)
- MINUTE_IN_HOUR (0-59)
- SECOND_IN_MINUTE (0-59)
- MILLISECOND_IN_SECOND (0-999)
因为 POSIX 时间和儒略日数都是日期序列(连续时间单位的计数),所以相互转换很容易。因此,使用此算法的第一步是将 POSIX 时间(自 1970 年 1 月 1 日午夜以来的毫秒数)转换为儒略日数(自公元前 4714 年 11 月 24 日以来的天数,在公历中) .这是微不足道的,因为您只需将毫秒转换为天数并调整纪元。
常量如下:
/** Accessor index for year field from a date/time vector of ints. */
public static final int YEAR = 0;
/** Accessor index for month-in-year field from a date/time vector of ints */
public static final int MONTH = 1;
/** Accessor index for day-in-month field from a date/time vector of ints */
public static final int DAY = 2;
/** Accessor index for hour-in-day field from a date/time vector of ints */
public static final int HOURS = 3;
/** Accessor index for minute-in-hour field from a date/time vector of ints */
public static final int MINUTES = 4;
/** Accessor index for second-in-minute field from a date/time vector of ints */
public static final int SECONDS = 5;
/** Accessor index for millis-in-second field from a date/time vector of ints */
public static final int MILLIS = 6;
/** The POSIX Epoch represented as a modified Julian Day number */
public static final double POSIX_EPOCH_AS_MJD = 40587.0d;
这里是将儒略日数(以 double
形式提供)转换为整数向量的算法的方法。在下面的代码中,您可以将 trunc()
函数替换为 Math.floor()
并保留正确的行为:
public static int[] toVectorFromDayNumber(double julianDay) {
int[] ymd_hmsm = {YEAR, MONTH, DAY, HOURS, MINUTES, SECONDS, MILLIS};
int a, b, c, d, e, z;
double f, x;
double jd = julianDay + 0.5;
z = (int) trunc(jd);
f = (jd - z) + (0.5 / (86400.0 * 1000.0));
if (z >= 2299161) {
int alpha = (int) trunc((z - 1867216.25) / 36524.25);
a = z + 1 + alpha - (alpha / 4);
} else {
a = z;
}
b = a + 1524;
c = (int) trunc((b - 122.1) / 365.25);
d = (int) trunc(365.25 * c);
e = (int) trunc((b - d) / 30.6001);
ymd_hmsm[DAY] = b - d - (int) trunc(30.6001 * e);
ymd_hmsm[MONTH] = (e < 14)
? (e - 1)
: (e - 13);
ymd_hmsm[YEAR] = (ymd_hmsm[MONTH] > 2)
? (c - 4716)
: (c - 4715);
for (int i = HOURS; i <= MILLIS; i++) {
switch (i) {
case HOURS:
f = f * 24.0;
break;
case MINUTES: case SECONDS:
f = f * 60.0;
break;
case MILLIS:
f = f * 1000.0;
break;
}
x = trunc(f);
ymd_hmsm[i] = (int) x;
f = f - x;
}
return ymd_hmsm;
}
例如,如果使用儒略日编号 2457272.5 调用该函数,它将 return 以下整数向量表示 UTC 2015 年 9 月 7 日(劳动节)午夜:
[ 2015, 9, 7, 0, 0, 0, 0 ]
编辑:Meeus 算法的一个显着之处在于它正确地考虑了闰年和世纪日(包括世纪日例外)。它仅使用整数和浮点运算,并且很可能比需要来自 Java 日历或日期时间 API 的对象实例化的解决方案更高效。
我的变体:
public class Main {
public static class MyDate {
int month;
int day;
public MyDate(int month, int day) {
this.month = month;
this.day = day;
}
}
public static final int[] daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
public static void main(String[] args) {
long millis = System.currentTimeMillis();
long days = millis / 86400000;
long millisToday = millis % 86400000;
int yearsPassedApprox = (int) days / 365;
int daysPassedThisYear = (int) (days - (yearsPassedApprox * 365 + leapYearsCount(yearsPassedApprox)));
int year = yearsPassedApprox + 1970;
MyDate myDate = getMonthAndDay(year, daysPassedThisYear);
int hours = (int) (millisToday / 3600000);
int minutes = (int) ((millisToday % 3600000) / 60000);
int seconds = (int) ((millisToday % 60000) / 1000);
System.out.println("Year: " + year);
System.out.println("Month: " + myDate.month);
System.out.println("Day: " + myDate.day);
System.out.println("Hour: " + hours);
System.out.println("Minutes: " + minutes);
System.out.println("Seconds: " + seconds);
}
public static MyDate getMonthAndDay(int year, int daysPassedThisYear) {
int i;
int daysLeft = daysPassedThisYear;
boolean leapYear = isLeapYear(year);
for (i = 0; i < daysInMonth.length; i++) {
int days = daysInMonth[i];
if (leapYear && i == 1) {
days++;
}
if (days <= daysLeft) {
daysLeft -= days;
} else {
break;
}
}
return new MyDate(i + 1, daysLeft + 1);
}
public static int leapYearsCount(long yearsPassed) {
int count = 0;
for (int i = 1970; i < 1970 + yearsPassed ; i++) {
if (isLeapYear(i)) {
count++;
}
}
return count;
}
public static boolean isLeapYear(int year) {
return (year % 4 == 0 && !(year % 100 == 0)) || year % 400 == 0;
}
}
我正在使用 System.currentTimeMillis() 来获取自 1970 年以来的毫秒数,我能够获取当前的小时、分钟、秒和年份。例如,我使用以下公式获取年份,它 returns 2015:
((((currTimeInMilliSec / 1000) / 3600) / 24) / 365) + 1970
但是,考虑到闰年和不同月份(如 28、29、30、31)的不同天数,我如何从毫秒计算月份。
注意:由于某些原因,我只需要使用 currentTimeMillis 函数来计算,我不想使用其他函数或外部库。我也看过相关的帖子,但没有找到确切的答案。
使用公历。
GregorianCalendar c = new GregorianCalendar();
c.setTimeInMillis(1l);
int month = c.get(Calendar.MONTH);
这个returns0,就是一月。想象一个包含一年中的 12 个月的数组。
是的,这是可能的。
有一些天文算法可以实现儒略日数字和日期时间戳之间的数字转换。我熟悉的算法由 J. Meeus 在他的 天文算法,第 2 版中发表。 该算法将儒略日数转换为表示相应的整数向量:
- 年
- MONTH_IN_YEAR (1-12)
- DAY_IN_MONTH(1-28,29,30,31视情况而定)
- HOUR_IN_DAY (0-23)
- MINUTE_IN_HOUR (0-59)
- SECOND_IN_MINUTE (0-59)
- MILLISECOND_IN_SECOND (0-999)
因为 POSIX 时间和儒略日数都是日期序列(连续时间单位的计数),所以相互转换很容易。因此,使用此算法的第一步是将 POSIX 时间(自 1970 年 1 月 1 日午夜以来的毫秒数)转换为儒略日数(自公元前 4714 年 11 月 24 日以来的天数,在公历中) .这是微不足道的,因为您只需将毫秒转换为天数并调整纪元。
常量如下:
/** Accessor index for year field from a date/time vector of ints. */
public static final int YEAR = 0;
/** Accessor index for month-in-year field from a date/time vector of ints */
public static final int MONTH = 1;
/** Accessor index for day-in-month field from a date/time vector of ints */
public static final int DAY = 2;
/** Accessor index for hour-in-day field from a date/time vector of ints */
public static final int HOURS = 3;
/** Accessor index for minute-in-hour field from a date/time vector of ints */
public static final int MINUTES = 4;
/** Accessor index for second-in-minute field from a date/time vector of ints */
public static final int SECONDS = 5;
/** Accessor index for millis-in-second field from a date/time vector of ints */
public static final int MILLIS = 6;
/** The POSIX Epoch represented as a modified Julian Day number */
public static final double POSIX_EPOCH_AS_MJD = 40587.0d;
这里是将儒略日数(以 double
形式提供)转换为整数向量的算法的方法。在下面的代码中,您可以将 trunc()
函数替换为 Math.floor()
并保留正确的行为:
public static int[] toVectorFromDayNumber(double julianDay) {
int[] ymd_hmsm = {YEAR, MONTH, DAY, HOURS, MINUTES, SECONDS, MILLIS};
int a, b, c, d, e, z;
double f, x;
double jd = julianDay + 0.5;
z = (int) trunc(jd);
f = (jd - z) + (0.5 / (86400.0 * 1000.0));
if (z >= 2299161) {
int alpha = (int) trunc((z - 1867216.25) / 36524.25);
a = z + 1 + alpha - (alpha / 4);
} else {
a = z;
}
b = a + 1524;
c = (int) trunc((b - 122.1) / 365.25);
d = (int) trunc(365.25 * c);
e = (int) trunc((b - d) / 30.6001);
ymd_hmsm[DAY] = b - d - (int) trunc(30.6001 * e);
ymd_hmsm[MONTH] = (e < 14)
? (e - 1)
: (e - 13);
ymd_hmsm[YEAR] = (ymd_hmsm[MONTH] > 2)
? (c - 4716)
: (c - 4715);
for (int i = HOURS; i <= MILLIS; i++) {
switch (i) {
case HOURS:
f = f * 24.0;
break;
case MINUTES: case SECONDS:
f = f * 60.0;
break;
case MILLIS:
f = f * 1000.0;
break;
}
x = trunc(f);
ymd_hmsm[i] = (int) x;
f = f - x;
}
return ymd_hmsm;
}
例如,如果使用儒略日编号 2457272.5 调用该函数,它将 return 以下整数向量表示 UTC 2015 年 9 月 7 日(劳动节)午夜:
[ 2015, 9, 7, 0, 0, 0, 0 ]
编辑:Meeus 算法的一个显着之处在于它正确地考虑了闰年和世纪日(包括世纪日例外)。它仅使用整数和浮点运算,并且很可能比需要来自 Java 日历或日期时间 API 的对象实例化的解决方案更高效。
我的变体:
public class Main {
public static class MyDate {
int month;
int day;
public MyDate(int month, int day) {
this.month = month;
this.day = day;
}
}
public static final int[] daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
public static void main(String[] args) {
long millis = System.currentTimeMillis();
long days = millis / 86400000;
long millisToday = millis % 86400000;
int yearsPassedApprox = (int) days / 365;
int daysPassedThisYear = (int) (days - (yearsPassedApprox * 365 + leapYearsCount(yearsPassedApprox)));
int year = yearsPassedApprox + 1970;
MyDate myDate = getMonthAndDay(year, daysPassedThisYear);
int hours = (int) (millisToday / 3600000);
int minutes = (int) ((millisToday % 3600000) / 60000);
int seconds = (int) ((millisToday % 60000) / 1000);
System.out.println("Year: " + year);
System.out.println("Month: " + myDate.month);
System.out.println("Day: " + myDate.day);
System.out.println("Hour: " + hours);
System.out.println("Minutes: " + minutes);
System.out.println("Seconds: " + seconds);
}
public static MyDate getMonthAndDay(int year, int daysPassedThisYear) {
int i;
int daysLeft = daysPassedThisYear;
boolean leapYear = isLeapYear(year);
for (i = 0; i < daysInMonth.length; i++) {
int days = daysInMonth[i];
if (leapYear && i == 1) {
days++;
}
if (days <= daysLeft) {
daysLeft -= days;
} else {
break;
}
}
return new MyDate(i + 1, daysLeft + 1);
}
public static int leapYearsCount(long yearsPassed) {
int count = 0;
for (int i = 1970; i < 1970 + yearsPassed ; i++) {
if (isLeapYear(i)) {
count++;
}
}
return count;
}
public static boolean isLeapYear(int year) {
return (year % 4 == 0 && !(year % 100 == 0)) || year % 400 == 0;
}
}