【问题标题】:Converting a ldap date转换 ldap 日期
【发布时间】:2019-12-13 17:33:35
【问题描述】:

我正在以编程方式从 ldap 导出用户。因此,我正在从 ldap 检索用户。其中一个属性是whenCreated

我必须转换的值之一是:20090813145607.0Z 直接拆分我得到以下格式:yyyyMMddHHmmss+.0Z。问题是应用程序在 CET 时区运行,并且存储的时间是 UTC,这可能由 .0Z 指示。它是14:56 UTC,本地表示是16:56。夏季时间似乎为 2 小时,冬季时间为 1 小时。

我检查了SimpleDateFormat 并且有一个时区占位符,但是它的格式不同。

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
sdf.parse("20090813145607.0Z");

将显示错误的日期,因为它忽略了日期时区。

有没有办法直接转换?

【问题讨论】:

    标签: java ldap


    【解决方案1】:

    ISO 8601

    正如提到的其他几个答案,有问题的日期时间格式由RFC 4517 Lightweight Directory Access Protocol (LDAP): Syntaxes and Matching Rules 定义。请参阅第 3.3.13 节,广义时间

    该部分说明此 LDAP 格式是 ISO 8601 定义的日期时间格式之一的受限版本。这种使用最少分隔符的样式在 ISO 8601 中称为“基本”样式。

    在这些格式中,末尾的ZZulu 的缩写,表示UTC(与GMT 基本相同)。

    最后的小数点和数字代表秒的一小部分。请注意,在 RFC 4517 和 ISO 8601 中都可以使用逗号代替点(句点)。实际上,在 ISO 8601 中建议使用逗号而不是点。RFC 4517 规范仅允许单个数字小数(小数的十分之一) ) 或根本没有点/逗号和数字。请注意,相比之下:(a) ISO 8601 允许任意数量的小数位,并且 (b) java.time 对象具有纳秒分辨率,最多可显示九位小数秒。

    java.time

    java.time 框架内置于 Java 8 及更高版本中。这些类取代了旧的麻烦的日期时间类,例如java.util.Date.Calendarjava.text.SimpleDateFormat

    现在在maintenance modeJoda-Time 项目也建议迁移到 java.time。

    要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。

    大部分 java.time 功能在ThreeTen-Backport 中向后移植到 Java 6 和 7,并在 ThreeTenABP 中进一步适应 Android。

    ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。

    解析

    定义一个格式模式以适应 RFC 4517。研究DateTimeFormatter 类的模式编码。这应该有效:uuuuMMddHHmmss[,S][.S]X。方括号表示可选。我们可以容纳点或逗号。注意秒的小数部分的单数。末尾的 X 允许使用 Zoffset-from-UTC,例如 -08 或 -0830 或 -08:30 或 -083015 或 -08:30:15。

    String input = "20090813145607.0Z";
    DateTimeFormatter f = DateTimeFormatter.ofPattern ( "uuuuMMddHHmmss[,S][.S]X" );
    OffsetDateTime odt = OffsetDateTime.parse ( input , f );
    Instant instant = odt.toInstant ();
    

    转储到控制台。

    System.out.println ( "input: " + input + " | odt: " + odt + " | instant: " + instant );
    

    输入:20090813145607.0Z | odt: 2009-08-13T14:56:07Z |瞬间:2009-08-13T14:56:07Z

    当然,您还应该编写一个检查 java.time.format.DateTimeParseException 的代码,以防出现意外输入。

    【讨论】:

      【解决方案2】:

      检查上面提到的 RFC,似乎使用 UTC 是 ldap 日期的推荐默认行为。所以我直接转换了:

      public Date parseLdapDate(String ldapDate){
          SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
          sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
      
          try {
              return sdf.parse(ldapDate);
          } catch (ParseException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
          return null;
      }
      

      【讨论】:

        【解决方案3】:

        如何使用上述拆分,然后将0Z 时区重新格式化为标准格式,然后使用sdf.parse(...)?也许是这样的(当然,添加了适当的错误检查):

        String[] parts = inputDateTime.split("[.]");
        String dateTimePart = parts[0];
        String timeZonePart = "+0" + parts[1].substring(0, parts[1].length() - 1) + "00";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssZ");
        Date theDate = sdf.parse(dateTimePart + timeZonePart);
        

        【讨论】:

        • 我不确定.0Z 的确切含义是什么,并且由于日期来自ldap,我不知道它将来是否会改变。我不知道还有什么可以期待的,但是它应该是一个 ldap 标准并符合其中一个 rfcs。 msdn.microsoft.com/en-us/library/windows/desktop/…
        • NOPE -- ".0" 与时区无关。 “Z”表示祖鲁 (UTC) 时间——故事结束。秒后的“.0”表示小数秒。所以中午之后的五十九秒半可能看起来像“yyyymmdd120059.5Z”。有关更多详细信息,请参阅@Basil Borrque 下面的“ISO 8601”答案。
        【解决方案4】:

        可以使用org.apache.directory.shared.ldap.util.DateUtils的方法:

        字符串 ldapDate="20090813145607.0Z";

        日期日期 = DateUtils.parse(ldapDate);

        String generalizedTime = DateUtils.getGeneralizedTime(date);

        【讨论】:

          【解决方案5】:

          属性的语法在目录模式中描述。应用程序在转换、比较和排序从目录中检索或存储的数据时必须使用该模式。如果whenCreated 属性的语法是generalizedTime,则应用程序在转换时必须使用库来获得通用时间。 generalizedTime 的语法在RFC4517 中描述。

          【讨论】:

          • 感谢您的信息。你碰巧知道任何这样的库或一些内置方法吗?
          【解决方案6】:

          这是唯一对我有用的代码:

          static String parseLdapDate(String ldapDate) {
          
                      long nanoseconds = Long.parseLong(ldapDate);   // nanoseconds since target time that you want to convert to java.util.Date
          
                      long mills = (nanoseconds / 10000000);
          
                      long unix = (((1970 - 1601) * 365) - 3 + Math.round((1970 - 1601) / 4)) * 86400L;
          
                      long timeStamp = mills - unix;
          
                      Date date = new Date(timeStamp * 1000L); // *1000 is to convert seconds to milliseconds
                      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); // the format of your date
              //sdf.setTimeZone(TimeZone.getTimeZone("GMT")); // give a timezone reference for formating (see comment at the bottom
                      String formattedDate = sdf.format(date);
          
                      return  formattedDate;
                  }
          

          【讨论】:

            【解决方案7】:

            我尝试使用 apache util GeneralizedTime 类 http://directory.apache.org/api/gen-docs/1.0.0-M11/apidocs/org/apache/directory/shared/util/GeneralizedTime.html,结果好坏参半

            从当前时间转换为 Active Direcotry 格式:

            GeneralizedTime gt = new GeneralizedTime(Calendar.getInstance());
            String gtADString = gt.toGeneralizedTime(GeneralizedTime.Format.YEAR_MONTH_DAY_HOUR_MIN_SEC_FRACTION, GeneralizedTime.FractionDelimiter.DOT, 1, GeneralizedTime.TimeZoneFormat.Z).replaceFirst("Z", "\\.0Z");
            

            唯一的问题是它不像宣传的那样工作。根据这个调用,点之后的小数部分的长度应该是“1”,但结果仍然是 3。我得到的是“20120410011958.687Z”而不是“20120410011958.687Z”,所以我仍然需要花时间秒并在 Z 之前插入“.0”。所以这就是你必须做的(在我的情况下,我不关心分数,所以我输入零。AD 关心)

            GeneralizedTime gt = new GeneralizedTime(Calendar.getInstance());
            String gtADString = gt.toGeneralizedTime(GeneralizedTime.Format.YEAR_MONTH_DAY_HOUR_MIN_SEC, GeneralizedTime.FractionDelimiter.DOT, 1, GeneralizedTime.TimeZoneFormat.Z).replaceFirst("Z", "\\.0Z");
            

            顺便说一句,这段代码将 AD GeneralizedTime 字符串格式转换为 Java 日期

             GeneralizedTime gt = new GeneralizedTime(str);
             Date d = gt.getCalendar().getTime();
            

            【讨论】:

              猜你喜欢
              • 2015-04-21
              • 1970-01-01
              • 2023-03-14
              • 2016-07-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多