【问题标题】:Inconsistent Timezones with GMT set in parsing LDAP date and Timestamps解析 LDAP 日期和时间戳时设置的 GMT 时区不一致
【发布时间】:2016-10-08 18:41:35
【问题描述】:

我正在解析来自 AD 的时间。有两种格式,即whenCreated,whenChanged的YMD LDAP时间戳,lastLogonTimestamp,pwdLastSet等的18位LDAP / FILETIME时间戳。因为我需要按时间分析数据。获取当地时间是有意义的。这是我为解析两种不同格式而编写的两个函数。我从Convert 18-digit LDAP Timestamps To Human Teadable Date Using Java引用的第二个函数中的计算

public static String parseLdapDate(String ldapDate) {
    String[] parts = ldapDate.split("[.]");
    String dateTimePart = parts[0];  //take the date string before .0Z
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmss");
    sdf.setTimeZone(TimeZone.getTimeZone("GMT"));  //Z means UTC time, to the local timezone
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 

    try {
        Date tempDate = sdf.parse(dateTimePart); //parse the string to a date
        return formatter.format(tempDate);  //format it as what we want
    } catch (ParseException ex) {
        System.out.println("Parsing LDAP Date exception \n");
        ex.printStackTrace();
    }
    return null;
}

public static String parseLdapTimestamp(String ldapTimestamp) {
    long nanoseconds = Long.parseLong(ldapTimestamp);  // 100 nanoseconds 
    long mills = (nanoseconds/10000000);  // To seconds
    long unix = (((1970-1601)*365)-3+Math.round((1970-1601)/4))*86400L;
    long timeStamp = mills - unix;

    Date date = new Date(timeStamp*1000L); // To milliseconds
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    sdf.setTimeZone(TimeZone.getTimeZone("GMT")); 

    return sdf.format(date);
}

我有一个示例 20150520143936.0Z,它被转换为“2015-05-20 16:39:36”。 例如131097986571852097,它被转换为“2016-06-07 18:44:17”,而http://www.epochconverter.com/ldap告诉我这是格林威治标准时间,当地时间是“2016-06-07 20:44:17” .如果我评论设置时区的代码,我会得到当地时间。

所以现在我很困惑,sdf.setTimeZone(TimeZone.getTimeZone("GMT")); 给了我本地时区或世界时。我在想AD是否在通用时间存储whenCreated,在本地时间存储lastLogonTimestamp。但在函数中,我将它们解析为字符串。时区没有符号。如果我在第二个函数中评论这句话,当我在另一个地方访问 LDAP 目录时,我会得到这两个属性的本地时间吗?

【问题讨论】:

  • 在第二个函数中注释了setTimeZone。当解析参数为0时,即1601-01-01 00:00:00 GMT,返回值为1601-01-01 01:00:00。根据epochconverter.com/ldap,在我的时区中,真实值为 1601-01-01, 00:53:28。大致正确。

标签: java datetime active-directory ldap gmt


【解决方案1】:

在第二种情况下,您正在构建一个 Date,然后告诉它以 UTC 格式格式化该日期 - 而在第一种情况下,您正在解析 以 UTC 格式显示,但以当地时间格式化。您已经假设时间戳存储为自 Unix 纪元以来的刻度数,这是一种时区中性格式。

如果目标是在本地时间生成字符串表示,那么您应该删除sdf.setTimeZone 调用。我认为parse 方法无论如何都应该返回Date,而不是String。或者更好的是,返回 java.time.Instant...

【讨论】:

  • 谢谢。这很清楚。所以这里的重点是当我从毫秒构造一个Date 时,它会在本地机器上返回结果吗?我不明白的是,当我将时区设置为“GMT”时,它又将我指向“GMT+00”。
  • @MonikaDiao:不,从毫秒构造Date 始终是“自 Unix 纪元以来的毫秒”。系统默认时区无关紧要。如果您的评论基于 Date.toString() 的结果,请忽略它 - 这会将 Date 值(毕竟这只是一个瞬间)转换为系统默认时区。但这并不意味着Date 对象本身有任何时区信息。
  • 那么当我将其格式化为字符串时,它就会转换为系统的默认时区。格林威治标准时间设置怎么样?在第一个函数中,我将字符串格式化为我想要的日期字符串。不关心时区。当我设置 GMT 时,它似乎在 GMT+00 时间处理日期字符串并返回我的本地时间,即 GMT+02。在第二个函数中,日期字符串已经是本地时间,它返回给我的时间是 GMT+00。
  • @MonikaDiao:我不知道您所说的“GMT 设置”是什么意思。在第一种方法中, 涉及一个时区——只是SimpleDateFormat 默认为系统默认时区。我认为你真的需要一次只关注其中的一部分,并确保你彻底理解这一点。当每篇文章都针对一个非常具体的问题时,Stack Overflow 的效果最好——而且不需要像这样冗长的评论线程。如果您只想将值作为本地时区的字符串,只需从第二种方法中删除 sdf.setTimeZone 调用。
  • @MonikaDiao:我不知道你所说的“它不再是默认的本地时间”是什么意思......但是当我建议删除 parseLong 电话时,我的意思是 minimal reproducible example。在这种情况下,将字符串解析为long 的行为不是问题的一部分,因此它不应该成为示例的一部分。这类事情对于能够诊断问题以及在 Stack Overflow 上呈现问题至关重要。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-03
  • 1970-01-01
  • 2016-11-12
  • 1970-01-01
  • 2011-06-01
  • 1970-01-01
相关资源
最近更新 更多