java moment 日期转换_关于日期:如何使用Java 8 DateTime API转换修改后的儒略日数字...

我有一个数据库,用于存储日期和日期时间(分别为INTEGER和DOUBLE)作为已修改的儒略日数(MJD)。修改后的儒略日数是自1858年11月17日午夜UTC以来连续的天数。根据定义,它们始终以UTC计算,与格林尼治标准时间+0:00偏移,并且不针对夏令时进行调整。这些属性使用DateTimes简化了某些操作,例如优先级和日期算术。

不利之处在于,在使用前后,MJD必须从UTC重新定位并重新定位回UTC,特别是对于日间边界至关重要的应用程序(例如,Medicare将可计费的日期边界识别为-local-time中的午夜)。

请考虑以下静态工厂方法,其目的是将"区域日号"(基本上是已添加了适当偏移量的MJD,使其代表本地DateTime)迁移到MJD(在UTC中)。

public static MJD ofDayNumberInZone(double regDN, ZoneId zone) {

:

:

}

从直观上看,很明显,如果您具有本地日期和时间,并且知道本地时区,则应该具有将regDN偏移回UTC所需的所有信息(MJD要求)。

实际上,使用以前的Java Calendar API编写此函数相当简单。 regDN易于转换为Date,用于设置GregorianCalendar实例。知道"本地时区"后,日历会报告ZONE_OFFSET和DST_OFFSET值,这些值随后可用于将天数调整为MJD。

这是我尝试在Java 8 DateTime API中编写类似的算法的尝试:

public static MJD ofDayNumberInZone(double zonedMJD, ZoneId zone) {

double epochSec = ((zonedMJD - MJD.POSIX_EPOCH_AS_MJD) * 86400.0);

LocalDateTime dt = LocalDateTime

.ofEpochSecond(

(long) epochSec,

(int) (epochSec - Math.floor(epochSec) * 1000000000.0),

--->                zone.getRules().getOffset( )

);

}

该问题如箭头所示。使用ofEpochSecond方法构造LocalDateTime实例似乎要求您事先知道偏移量,这似乎违反直觉(我已经有了当地时间和时区,这就是我想要的偏移量)。

我没有成功找到使用Java 8 API从本地时间返回UTC的偏移量的简单方法。虽然我可以继续使用旧的Calendar API,但是新的DateTime库具有引人注目的优点...因此,我想尝试一下。我想念什么?

编辑:这是一个使用旧的Java Calendar API的示例,该示例如何将任意时区中的天数和小数天数"分解"为UTC。此方法采用一个双精度数字,即"区域化日期数"和一个时区对象。它使用GregorianCalendar将参数转换为从纪元到毫秒的UTC计数:

private static final Object             lockCal = new Object();

private static final SimpleDateFormat       SDF = new SimpleDateFormat();

private static final GregorianCalendar      CAL = new

GregorianCalendar(TimeZone.getTimeZone(HECTOR_ZONE));

:

:

public static MJD ofDayNumberInZone(double rdn, TimeZone tz) {

Date dat = new Date((long) ((rdn - MJD.POSIX_EPOCH_AS_MJD) *

(86400.0 * 1000.0)));

return MJD.ofDateInZone(dat, tz);

}

public static MJD ofDateInZone(Date dat, TimeZone tz) {

long utcMillisFromEpoch;

synchronized(lockCal) {

CAL.setTimeZone(tz);

CAL.setTime(dat);

utcMillisFromEpoch = CAL.getTimeInMillis();

}

return MJD.ofEpochMillisInUTC(utcMillisFromEpoch);

}

public static MJD ofEpochMillisInUTC(long millis)

{ return new MJD((millis / (86400.0 * 1000.0)) + POSIX_EPOCH_AS_MJD);          }

那么该方法的目的是将Zoned MJD转换为UTC MJD吗?

我不使用Java,但可以想象您成功了。我希望您能够在程序运行时获得补偿,或者考虑到日期并更改当年夏令时的补偿。我不希望Java在DST的开始/结束日期不同时能够提供历史偏移,当然,预测未来国会的行动是不可能的。那么,您是否为历史或将来的日期/时间预留了适当的配额?

@GerardAshton:这是与我的问题无关的单独问题,但是Java DateTime API完全支持IANA时区数据库,并为特定于区域的时间偏移量计算提供了历史上准确的偏移量。

我注意到getOffset方法需要一个瞬间,该瞬间是从历元和纳秒以来的几秒钟创建的。但是,如果您不知道偏移量并且不知道格林威治/ UTC时间,那么您将无法计算出自该纪元以来的秒数,因此无法创建时刻。

@GerardAshton:这是我遇到的悖论……如果-您-了解当地时间和当地时区,那么-您-有足够的信息可以正确到达UTC。但是,Java 8 DateTime API似乎无法构造任何Instant,除非它知道如何在UTC内部进行表示(显然,以先于UTC的偏移量为前提)。我被困在如何获取当地时间和时区并从中获取即时信息。在旧的API中这很简单,但是我发现新API难以使用。

@fdsa:是的,这是问题的简要说明:采取分区MJD(如果您可以同意有这样的事情;所有MJD都应该位于UTC中),然后从中到达UTC MJD。

我查看了Java 8日期文档。看来有两种非UTC /格林威治时间。 Java.time包未将LocalDateTime连接到任何特定时区,因此需要从上下文或与Java.time不相关的源代码中找出它。 ZonedDateTime是Java.time跟踪区域的本地时间。 Java知道规则是什么。如果您探索ZonedDateTime,则可能会找到所需的内容。在某些情况下,无法进行转换;本地-> UTC在秋季DST中更改。

根据您的评论,您的核心问题似乎是将不带时区的日期时间(a LocalDateTime)转换为带区矩(a ZonedDateTime)的歧义。您解释说诸如夏令时(DST)之类的异常可能导致无效值。

ZonedDateTime zdt = myLocalDateTime.atZone( myZoneId );

这是真的。在DST中"春季前移"或"后退"切换时,没有完美的解决方案。但是,java.time类确实通过采用某种策略来解决歧义。您可能同意也可能不同意该政策。但是,如果您同意,则可以依靠java.time来确定结果。

引用ZonedDateTime.ofLocal的文档:

In the case of an overlap, where clocks are set back, there are two valid offsets. If the preferred offset is one of the valid offsets then it is used. Otherwise the earlier valid offset is used, typically corresponding to"summer".

In the case of a gap, where clocks jump forward, there is no valid offset. Instead, the local date-time is adjusted to be later by the length of the gap. For a typical one hour daylight savings change, the local date-time will be moved one hour later into the offset typically corresponding to"summer".

LocalDate modifiedJulianEpoch = LocalDate.of( 1858 , 11 , 17 );

LocalDate today = LocalDate.now( ZoneOffset.UTC );

long days = ChronoUnit.DAYS.between (  modifiedJulianEpoch , today );

today: 2017-03-19

days: 57831

我不太了解您的问题。但是在我看来,MJD(修改后的朱利安天数)的目的是要有一种方法来跟踪"一个真实时间",以避免所有时区的混乱。在标准ISO 8601日历系统中,UTC扮演着"一次真实时间"的角色。因此,我建议您坚持使用UTC。

当您需要考虑某个地区的时钟时间(例如您在该地区结束时间的Medicare示例)时,请确定该地区的时钟时间,然后转换为UTC。根据定义,java.time中的Instant类始终使用UTC。

ZoneId z = ZoneId.of("America/Los_Angeles" );

LocalDate localDate = LocalDate.now( z );

ZonedDateTime firstMomentNextDay = localDate.plusDays( 1 ).atStartOfDay( z );

Instant medicareExpiration = firstMomentNextDay.toInstant(); // UTC

BigDecimal modJulDays = this.convertInstantToModifiedJulianDays( medicareExpiration ) ;

在精度很重要的地方使用小数位小数时,请使用BigDecimal。使用double,double,float或float意味着使用浮点技术,该技术会牺牲精度以提高性能。

这是一些代码的粗略用法,可以完成从BigDecimal(Modified Julian Days)到Instant的转换。我想有些聪明的人可能会找到此代码的精简版本,但我的代码似乎在起作用。使用风险自负。我几乎没有测试过此代码。

public Instant convertModifiedJulianDaysToInstant ( BigDecimal modJulDays ) {

Instant epoch = OffsetDateTime.of ( 1858, 11, 17, 0, 0, 0, 0, ZoneOffset.UTC ).toInstant ( ); // TODO: Make into a constant to optimize.

long days = modJulDays.toBigInteger ( ).longValue ( );

BigDecimal fractionOfADay = modJulDays.subtract ( new BigDecimal ( days ) ); // Extract the fractional number, separate from the integer number.

BigDecimal secondsFractional = new BigDecimal ( TimeUnit.DAYS.toSeconds ( 1 ) ).multiply ( fractionOfADay );

long secondsWhole = secondsFractional.longValue ( );

long nanos = secondsFractional.subtract ( new BigDecimal ( secondsWhole ) ).multiply ( new BigDecimal ( 1_000_000_000L ) ).longValue ( );

Duration duration = Duration.ofDays ( days ).plusSeconds ( secondsWhole ).plusNanos ( nanos );

Instant instant = epoch.plus ( duration );

return instant;

}

并朝另一个方向发展。

public BigDecimal convertInstantToModifiedJulianDays ( Instant instant ) {

Instant epoch = OffsetDateTime.of ( 1858, 11, 17, 0, 0, 0, 0, ZoneOffset.UTC ).toInstant ( ); // TODO: Make into a constant to optimize.

Duration duration = Duration.between ( epoch, instant );

long wholeDays = duration.toDays ( );

Duration durationRemainder = duration.minusDays ( wholeDays );

BigDecimal wholeDaysBd = new BigDecimal ( wholeDays );

BigDecimal partialDayInNanosBd = new BigDecimal ( durationRemainder.toNanos ( ) ); // Convert entire duration to a total number of nanoseconds.

BigDecimal nanosInADayBd = new BigDecimal ( TimeUnit.DAYS.toNanos ( 1 ) );  // How long is a standard day in nanoseconds?

int scale = 9; // Maximum number of digits to the right of the decimal point.

BigDecimal partialDayBd = partialDayInNanosBd.divide ( nanosInADayBd ); // Get a fraction by dividing a total number of nanos in a day by our partial day of nanos.

BigDecimal result = wholeDaysBd.add ( partialDayBd );

return result;

}

调用那些转换方法。

BigDecimal input = new BigDecimal ("57831.5" );

Instant instant = this.convertModifiedJulianDaysToInstant ( input );

BigDecimal output = this.convertInstantToModifiedJulianDays ( instant );

转储到控制台。

System.out.println ("input.toString():" + input );

System.out.println ("instant.toString():" + instant );

System.out.println ("output.toString():" + output );

input.toString(): 57831.5

instant.toString(): 2017-03-19T12:00:00Z

output.toString(): 57831.5

查看在IdeOne.com上实时运行的所有代码。

另外,我对类似问题的回答可能会有所帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值