【发布时间】:2019-05-09 19:15:33
【问题描述】:
我在 2018 年 12 月 7 日运行以下表达式。
我看到了这样的差异:
ZonedDateTime.now(ZoneId.of("America/New_York")).minusDays(30)
返回(正确):
2018-11-07T22:44:11.242576-05:00[America/New_York]
而转换为瞬间:
ZonedDateTime.now(ZoneId.of("America/New_York")).minusDays(30).toInstant()
似乎把结果弄乱了增加了一天:
2018-11-08T03:58:01.724728Z
我需要一个即时转换以在以下代码中使用其结果作为日期:
... = Date.from(t.toInstant())
等效的 Python 代码 (Django) 可以正常工作:
datetime.datetime.now('America/New_York')+datetime.timedelta(days=-30)
评估至:datetime: 2018-11-07 20:13:55.063888-05:00
造成这种差异的原因是什么?
我应该使用什么来将 Java 转换为 Date 导致返回 November 7th,就像在 Python 的情况下一样?基本上,我正在寻找将 Python 代码等效翻译成 Java 或伪代码:
`datetime.X = datetime.now(deployment_zone) - (N_days)`,
where `deployment_zone` is configurable (i.e. `America/New_York`)
`N_days` is configurable (i.e. 30)
@Basil Bourque 更新:
当我提出最初的问题时,我(根据 SO 规则)试图将其简化为易于理解的形式,这可能破坏了大部分必要的上下文,使其变得模糊。让我再试一次。
正如我在 cmets 中解释的那样,我正在将现有的 Python 代码(更积极地维护并且哪个客户端希望保持完整)转换为现有的 Java 代码(没有正确维护并偏离 Python 的遗留代码)一段时间后的逻辑)。两个代码库都需要在功能上彼此相提并论。 Java 需要做 Python 已经在做的事情。
Python 代码如下(为了简洁起见,我将所有代码集中到一个地方,实际上它分布在几个文件中):
analytics.time_zone=America/New_York
TIME_ZONE = props.getProperty('analytics.time_zone', 'UTC')
TZ = pytz.timezone(TIME_ZONE)
def days_back(num_days=0):
adjusted_datetime = datetime.datetime.now(TZ)+datetime.timedelta(days=-num_days)
return DateRangeUtil.get_start_of_day(adjusted_datetime)
class DateRangeUtil():
@staticmethod
def get_start_of_day(date):
return date.astimezone(TZ).replace(hour=0, minute=0, second=0, microsecond=0)
它基本上采用配置的时区,在该时区中获取当前时刻,从中减去指定的天数,将其转换为该日期的开始,从而在查询时接收要使用的范围的下限DB,类似于开始时间:datetime: 2018-11-07 20:13:55.063888-05:00
当我开始从事 Java 方面 时,它有:
public final static DateRange parse(String dateRange) {
//.....
int days = ...
return new DateRange(toBeginningOfDay(daysBack(days)), toEndOfDay(daysBack(0)));
private final static Date daysBack(int days) {
return toDate(LocalDateTime.now().minusDays(days));
}
private final static Date toBeginningOfDay(Date d)
{
Calendar c=Calendar.getInstance();
c.setTime(d);
c.set(HOUR_OF_DAY,0);
c.set(MINUTE,0);
c.set(SECOND,0);
c.set(MILLISECOND, 0);
return c.getTime();
}
private final static Date toDate(LocalDateTime t) {
return Date.from(t.atZone(ZoneId.systemDefault()).toInstant());
}
该代码不起作用并引入了我在原始问题中描述的差异。我开始试验并将 ZonedDateTime 引入图片中。在调查过程中,我发现对 .toInstant() 的调用似乎是罪魁祸首,我想更深入地了解其背后的原因。
In his answer, @ernest_k 提出了一个似乎有效的解决方案,但我仍然不太明白,从 cmets 中的问题到他的回答中可以清楚地看出这一点。
我根据@ernest_k 的回复做出的修改如下:
private final static Date daysBack(int days) {
return toDate(ZonedDateTime.now(ZoneId.of("America/New_York")).minusDays(days).toLocalDateTime());
private final static Date toDate(LocalDateTime t) {
return Date.from(t.toInstant(ZoneOffset.UTC));
}
这似乎产生了预期的结果:但是从本地到分区然后再返回似乎太多了,所以我进行了更多实验,发现简单的 LocalDateTime 也可以解决问题:
private final static Date toDate(LocalDateTime t) {
return Date.from(t.toInstant(ZoneOffset.UTC));
}
private final static Date daysBack(int days) {
return toDate(LocalDateTime.now().minusDays(days));
}
我可以看到LocalDate(可能还有LocalDateTime)有一个方便的atStartOfDay(),这似乎是消除Dates 的合适人选,同时替换上面的旧toBeginningOfDay(Date d) 方法.不确定它是否在做同样的事情 - 我还没有尝试过这个想法,所以非常欢迎提出建议。
因此,由于上述所有磨难,我的问题是围绕toInstant() 行为开始的,当它传递一个区域 id 时,它是在该区域中瞬间转换为 TO 还是 来自它,还是什么?
我想对于我所描述的情况,我们只关心 DB 查询中的下限时间是通过将当前时间的一些 一致 标记(其上限)与其原来的时间进行比较而形成的在过去 N 天内在同一个地方(时区?),因此将其与 UTC 进行比较应该达到目的。
这样就不需要通过该区域了吗?
现在,似乎已经找到了一个解决方案,问题围绕着上述方法的合理性和偶然发现的解决方案 - 它是最佳方案,还是围绕 Java 计时库的最佳实践等。代码需要适用于最终部署代码库的任何时区,这就是该区域通过配置传入的原因。
另外,我想知道当/如果数据库本身从代码库的其余部分部署到外部部署并配置为在其他时区持久保存数据时,情况是否会发生变化。但这可能是另一个问题。
【问题讨论】:
-
您只需要 11 月 7 日,还是还需要与现在相同的时间?
-
逻辑是从当天减去某些(可配置的)天数。区域 id ('America/New_York') 也是参数化和配置的,为了简单起见,我只是在这里对这些值进行硬编码。因此,如果在 12 月 8 日运行,我预计 11 月 8 日将返回,12 月 7 日至 11 月 7 日将返回,依此类推。
-
例如,如果在
Fri, Dec-7, 8:14pm上运行,Python 会返回:datetime: 2018-11-07 20:13:55.063888-05:00,但 Java 不会。我希望在 Java 方面也能得到类似的结果。 -
对于我们这些不了解(足够)Python 的人,也许如果你解释一下你需要什么日期,我们可以更好地建议。
-
我已经用我所追求的伪代码更新了这个问题。这是对现有代码库的重构,以使其与 Python 相提并论,因此我试图以最少的更改来做到这一点,因此删除它们对
Date的(大量)使用是不可取的(至少在这一点上) .
标签: java python django java-time pytz