【问题标题】:Unable to parse Oracle timestamp in C#无法在 C# 中解析 Oracle 时间戳
【发布时间】:2014-09-19 08:10:54
【问题描述】:

我有 Oracle 的时间戳:

string timestamp = "23-JUN-14 09.39.04.000000000 AM";

我无法将其解析为系统日期时间对象。我用过:

CultureInfo provider = CultureInfo.InvariantCulture;
                String format = "yy-MMM-dd hh:mm:ss:fffffff";
                string timestamp = "10-DEC-07 10.32.47.797201123 AM";
                {
                    var date = DateTime.ParseExact(timestamp, format, provider);
                    DateTime dateTime = DateTime.ParseExact(timestamp.ToString(), "dd-MMM-y HH:mm:ss", CultureInfo.InvariantCulture);
}

它仍然传递错误。它在 m 后 7 f 处工作,但仅此而已。我用了试试Parse,试试ParseExact——有什么办法吗?

【问题讨论】:

  • DateTime.ParseExact 需要一个完全匹配输入的格式字符串:那个不匹配。考虑使用正确的格式字符串重新尝试。 dd'-'MMM'-'yy hh'.'mm'.'ss'.'fffffff tt 之类的东西会更接近(日期格式匹配,分隔符也是如此,但 .NET 不支持那么多小数时间数字,因此可能需要一些处理截断分数(快速测试表明不超过 7 个十进制数字有效)。
  • 更具体一点:只考虑日期部分:yyyy-MM-dd 不会匹配 10-DEC-07

标签: c# asp.net regex oracle odp.net


【解决方案1】:

.NET DateTime 结构的精度为刻度 - 100 纳秒 - 0.0000001 秒 - 小数点后 7 位。

Oracle TimeStamp 的精度高达纳秒 - 0.000000001 - 小数点后 9 位。


这就是为什么标准的 DateTime 不能存储所有可能的 oracle 时间戳的原因。而且它的解析功能在更精确的时间戳字符串表示上失败了。


那么,可以尝试什么:

【讨论】:

    【解决方案2】:

    根据https://stackoverflow.com/a/23198962/328864,没有办法跳过精确模式的一部分,所以我想你可以这样做:

    CultureInfo provider = CultureInfo.InvariantCulture;
    string timestamp = "10-DEC-07 10.32.47.797201123 AM";
    String format = String.Format("yy-MMM-dd hh.mm.ss.fffffff{0} tt", timestamp.Substring(26,2));
    
    DateTime date = DateTime.ParseExact(timestamp, format, provider);
    Console.WriteLine(date);
    

    虽然不是很漂亮。

    【讨论】:

    • 我们可以在不使用子字符串 (26,2) 的情况下做同样的事情吗
    • 好吧,您需要以某种方式去掉秒分数中的最后两位数字,因为 .NET 无法处理它们。像这样使用子字符串是一种方法,但您也可以以某种方式从时间戳中删除最后两位数字,然后您可以使用更简单的格式说明符。
    【解决方案3】:

    一旦我们开始使用 ODP.NET,我们就必须实现如下扩展:

    public static T ConvertOracleValue<T>(this object value)
    {
         if (value != null)
         {
              Type typeOfValue = value.GetType();
    
              if (typeOfValue.Namespace.Contains("Oracle.DataAccess"))
              {
                  if (typeOfValue.Name.Equals("OracleTimeStamp"))
                  {
                       int tempInt = 0;
                       Oracle.DataAccess.Types.OracleTimeStamp ots = (Oracle.DataAccess.Types.OracleTimeStamp)value;
                       tempInt = Int32.TryParse(ots.Millisecond.ToString("000").Substring(0, 3), out tempInt) ? tempInt : 0;
                       DateTime ret = new DateTime(ots.Year, ots.Month, ots.Day, ots.Hour, ots.Minute, ots.Second, tempInt);
                        return ConvertHelper.ConvertValue<T>(ret);
                  }
                  if (typeOfValue.Name.Equals("OracleTimeStampLTZ"))
                  {
                        int tempInt = 0;
                        Oracle.DataAccess.Types.OracleTimeStampLTZ ots = (Oracle.DataAccess.Types.OracleTimeStampLTZ)value;
                        tempInt = Int32.TryParse(ots.Millisecond.ToString("000").Substring(0, 3), out tempInt) ? tempInt : 0;
                        DateTime ret = new DateTime(ots.Year, ots.Month, ots.Day, ots.Hour, ots.Minute, ots.Second, tempInt);
                        return ConvertHelper.ConvertValue<T>(ret);
                  }
                  if (typeOfValue.Name.Equals("OracleTimeStampTZ"))
                  {
                        int tempInt = 0;
                        Oracle.DataAccess.Types.OracleTimeStampTZ ots = (Oracle.DataAccess.Types.OracleTimeStampTZ)value;
                        tempInt = Int32.TryParse(ots.Millisecond.ToString("000").Substring(0, 3), out tempInt) ? tempInt : 0;
                        DateTime ret = new DateTime(ots.Year, ots.Month, ots.Day, ots.Hour, ots.Minute, ots.Second, tempInt);
                        return ConvertHelper.ConvertValue<T>(ret);
                  }
    
                  string temp = value.ToString();
                  return ConvertHelper.ConvertValue<T>(temp);
              }
          }
          else
          {
               return default(T);
          }
    
          return ConvertHelper.ConvertValue<T>(value);
    }
    

    ConvertHelper.ConvertValue 是另一个扩展:

    public static class ConvertHelper
        {
            public static T ConvertValue<T>(object value)
            {
                Type typeOfT = typeof(T);
                if (typeOfT.BaseType != null && typeOfT.BaseType.ToString() == "System.Enum")
                {
                    return (T)Enum.Parse(typeOfT, Convert.ToString(value));
                }
                if ((value == null || value == Convert.DBNull) && (typeOfT.IsValueType))
                {
                    return default(T);
                }
                if (value is IConvertible)
                {
                    return (T)Convert.ChangeType(value, typeOfT, new CultureInfo("en-GB"));
                }
    
                return (T)Convert.ChangeType(value, typeOfT);
            }
    }
    

    这在我们的测试、集成和生产环境中非常有用。

    【讨论】:

    • 小心使用此代码,因为它会进行毫秒舍入。如果 OracleTimeStamps 的精度高于毫秒,就会发生这种情况。如果 ots.Milliseconds 的值为 130.909,调用 toString("000") 会将其四舍五入为“131”,这是非常糟糕的。只需更改为 toString() 即可将其保留为 130,这很好。
    • 在进一步测试中,我认为最好的方法是使用 toString("000.000")。如果 ots.Milliseconds 为 5,我的最后一个建议将在子字符串上崩溃,因为它没有被填充。通过在 toString 格式中添加“000.000”,我们避免了舍入。
    • @ogrim 我看不出投反对票的原因。我在这个答案上给出的信息没有错,也不错。如果需要,您应该根据自己的需要对其进行改进。
    • 您的解决方案将导致许多 Oracle 安装上的数据损坏,因此使用起来很危险。
    • OracleTimeStampLTZ(DateTime) 构造函数中存在/曾经存在错误,您需要调用完全重载并将毫秒转换为双精度。 Dim ots As New OracleTimeStampLTZ(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second, CType(value.Millisecond, Double))
    猜你喜欢
    • 1970-01-01
    • 2020-05-27
    • 1970-01-01
    • 2020-02-14
    • 1970-01-01
    • 2018-05-19
    • 2017-10-29
    • 2012-10-14
    • 2018-07-02
    相关资源
    最近更新 更多