在Excel中混淆date和时间

我有Java时间(ms自1970年1月1日以来的ms),并且希望将该时间写入csv文件,以便Excel可以正确解释和格式化它。 据我所知,excel使用“序列date时间”作为一种格式 – 这是一个浮点数,其中整数部分给出从1/1/1900天以来的天数,小数部分给出了一天的分数。

我不明白这个时区和夏令时处理。

这个页面说Excel时代(1/1/1900)是基于本地(=创buildExcel文件的计算机?)时区。 这意味着如果没有计算机时区创build的信息,序列date并不表示一个唯一的即时信息。 不是我会select的,但可以。

现在接受这一点,我相信我可以通过以下Java代码将Java时间转换为Excel序列date(nb:我在苏黎世,CET时区):

private static final long ONE_HOUR= 60L * 60 * 1000; private static final long ONE_DAY = 24 * ONE_HOUR; private static final long excelEpoch; static{ Calendar cal; cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Zurich")); cal.set(Calendar.YEAR, 1900); cal.set(Calendar.DAY_OF_YEAR, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); excelEpoch = cal.getTimeInMillis(); } private static String formatForExcel(long time){ return ""+(time-excelEpoch)/(double)ONE_DAY; } 

使用这个我可以打印几次:

 public static void main(String[] args) { String sep = "\t"; // csv field separator SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss d/M/yyyy"); fmt.setTimeZone(TimeZone.getTimeZone("Europe/Zurich")); System.out.println("Time in ms since 1/1/1970 UTC"+ sep + "Time as string" + sep + "Excel serial" + sep + "Excel serial formatted by excel"); long startTime = 1332630000000L; // 25/3/2012 00:00 CET , shortly before change from winter time to DST for (long t = startTime; t < startTime + 4*ONE_HOUR; t+=ONE_HOUR) { System.out.println(t + sep + fmt.format(new Date(t)) + sep + formatForExcel(t) + sep + formatForExcel(t)); } } 

哪个返回

 Time in ms since 1/1/1970 UTC Time as string Excel serial Excel serial formatted by excel 1332630000000 00:00:00 25/3/2012 40991.0 40991.0 1332633600000 01:00:00 25/3/2012 40991.041666666664 40991.041666666664 1332637200000 03:00:00 25/3/2012 40991.083333333336 40991.083333333336 1332640800000 04:00:00 25/3/2012 40991.125 40991.125 

请注意,从冬季到夏令时的变化发生在那几个小时(检查第二列,小时2不见了)。

现在是混乱。 如果我把它粘贴在excel中,最后一列select“Format cells …”,然后select“Time”(任何格式)

 Time in ms since 1/1/1970 UTC Time as string Excel serial Excel serial formatted by excel 1332630000000 25.03.2012 00:00 40991 0:00:00 1332633600000 25.03.2012 01:00 40991.04167 1:00:00 1332637200000 25.03.2012 03:00 40991.08333 2:00:00 1332640800000 25.03.2012 04:00 40991.125 3:00:00 

请注意,格式化序列date,不会更改为DST。 所以这不是壁钟的时间。

长话短说:

我应该如何将Java时间转换为Excel,使其正常工作?

怀疑 Excel并没有真正把时区考虑在内。 我怀疑这只是把它当作一个“当地时间”,每一个可以想象的date/时间都是有效的。 (乔达时代的说法是“本地的即时”,我相信 – 虽然我不知道这个用法有多广泛)。

我怀疑没有办法代表一个特定的时间,而是你应该:

  • 采取任何你想代表当地时间的date/时间(例如“2012年3月25日凌晨3点”)
  • 把它放到设置为使用UTC的Calendar
  • calendar.getTime().getTime()
  • 减去1900-01-01T00:00:00Z的“Excel epoch”值(再次,通过设置为UTC的日历获取)
  • 除以“每天毫秒”

对于Excel在处理1900年3月1日之前的date方面,现在也有一个奇怪的地方 ,但希望这不会让你受到影响 。

将浮点“序列date/时间”转换为“自1970/1/1以来的工厂”

注意:daysFrom1900to1970适用于谷歌电子表格date,但可能需要略微调整为Excel

  int daysFrom1900to1970 =365*70 + 19; // Or maybe =365*70 + 17 But 19 worked. int millisPerDay = 1000 * 60 * 60 * 24; long millisSince1970 = (long) ((timeToSend.doubleValue() - daysFrom1900to1970) * millisPerDay ); Calendar dateTimeToSend = Calendar.getInstance(); dateTimeToSend.setTimeZone(TimeZone.getTimeZone("GMT")); dateTimeToSend.setTimeInMillis(millisSince1970); System.out.println("timeToSend:"+ new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z").format(dateTimeToSend.getTime()));