对于像1989Dec31和31Dec1989这样的日期,pyspark无法识别spark.read.load()中的MMM dateFormat模式

pie*_*rop 6 python java date-formatting apache-spark pyspark

pyspark 在macOS Sierra上遇到了一个非常奇怪的问题.我的目标是以ddMMMyyyy格式解析日期(例如:),31Dec1989但会收到错误.我运行Spark 2.0.1,Python 2.7.10和Java 1.8.0_101.我也试过使用Anaconda 4.2.0(它附带Python 2.7.12),但也得到了错误.

Ubuntu Server 15.04上具有相同Java版本和Python 2.7.9的相同代码可以正常运行而不会出现任何错误.

关于州的官方文件spark.read.load():

dateFormat - 设置指示日期格式的字符串.自定义日期格式遵循以下格式java.text.SimpleDateFormat.这适用于日期类型.如果设置为None,则使用默认值value yyyy-MM-dd.

官方Java文档有关谈判MMM的正确的格式解析如月的名字Jan,Dec等等,但它抛出了很多开始的错误java.lang.IllegalArgumentException.文档说明LLL也可以使用,但pyspark不识别它并抛出pyspark.sql.utils.IllegalArgumentException: u'Illegal pattern component: LLL'.

我知道另一个解决方案dateFormat,但这是解析数据的最快方法和最简单的代码.我在这里错过了什么?

为了运行下面的例子你只需要放置test.csvtest.py在同一目录下,然后运行<spark-bin-directory>/spark-submit <working-directory>/test.py.

我的测试用例使用ddMMMyyyy格式

我有一个名为test.csv包含以下两行的纯文本文件:

col1
31Dec1989
Run Code Online (Sandbox Code Playgroud)

代码如下:

from pyspark.sql import SparkSession
from pyspark.sql.types import *

spark = SparkSession \
    .builder \
    .appName("My app") \
    .config("spark.some.config.option", "some-value") \
    .getOrCreate()

struct = StructType([StructField("column", DateType())])
df = spark.read.load(   "test.csv", \
                            schema=struct, \
                            format="csv", \
                            sep=",", \
                            header="true", \
                            dateFormat="ddMMMyyyy", \
                            mode="FAILFAST")
df.show()
Run Code Online (Sandbox Code Playgroud)

我收到错误.我还尝试在日期和年份之前或之后移动月份名称(例如:1989Dec31yyyyMMMdd),但没有成功.

使用ddMMyyyy格式的工作示例

此示例与前一个示例相同,但日期格式除外.test.csv现在包含:

col1
31121989
Run Code Online (Sandbox Code Playgroud)

以下代码打印以下内容test.csv:

from pyspark.sql import SparkSession
from pyspark.sql.types import *

spark = SparkSession \
    .builder \
    .appName("My app") \
    .config("spark.some.config.option", "some-value") \
    .getOrCreate()

struct = StructType([StructField("column", DateType())])
df = spark.read.load(   "test.csv", \
                            schema=struct, \
                            format="csv", \
                            sep=",", \
                            header="true", \
                            dateFormat="ddMMyyyy", \
                            mode="FAILFAST")
df.show()
Run Code Online (Sandbox Code Playgroud)

输出如下(我省略了各种冗长的行):

+----------+
|    column|
+----------+
|1989-12-31|
+----------+
Run Code Online (Sandbox Code Playgroud)

UPDATE1

我创建了一个简单的Java类,它使用java.text.SimpleDateFormat:

import java.text.*;
import java.util.Date;

class testSimpleDateFormat 
{
    public static void main(String[] args) 
    {
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMMdd");
        String dateString = "1989Dec31";

        try {
            Date parsed = format.parse(dateString);
            System.out.println(parsed.toString());
        }
        catch(ParseException pe) {
            System.out.println("ERROR: Cannot parse \"" + dateString + "\"");
        }       
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码不适用于我的环境并抛出此错误:

java.text.ParseException: Unparseable date: "1989Dec31"
Run Code Online (Sandbox Code Playgroud)

但在另一个系统(Ubuntu 15.04)上完美运行.这似乎是一个Java问题,但我不知道如何解决它.我安装了最新的Java版本,并且我的所有软件都已更新.

有任何想法吗?


UPDATE2

我已经找到了如何通过指定Locale.US以下内容使其在纯Java下工作:

import java.text.*;
import java.util.Date;
import java.util.*;

class HelloWorldApp 
{
    public static void main(String[] args) 
    {
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMMdd", Locale.US);
        String dateString = "1989Dec31";

        try {
            Date parsed = format.parse(dateString);
            System.out.println(parsed.toString());
        }
        catch(ParseException pe) {
            System.out.println(pe);
            System.out.println("ERROR: Cannot parse \"" + dateString + "\"");
        }       
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,问题变成:如何指定Java的Locale pyspark

edd*_*ies 4

可能值得注意的是,这个问题已于 2016 年 10 月 24 日在Spark 邮件列表中得到解决。根据原始发布者:

这无需设置其他选项即可工作:spark/bin/spark-submit --conf "spark.driver.extraJavaOptions=-Duser.language=en" test.py

并在 Spark 2.0.1 中被报告为SPARK-18076(将 DateFormat、NumberFormat 中使用的默认区域设置修复为 Locale.US),并在 Spark 2.1.0 中得到解决。

--conf "spark.driver.extraJavaOptions=-Duser.language=en"此外,虽然如果使用 Spark 2.1.0,则不再需要针对提交者提出的特定问题使用上述解决方法(传入),但一个显着的副作用是,对于 Spark 2.1.0 用户,您不能再传入类似的内容--conf "spark.driver.extraJavaOptions=-Duser.language=fr"如果您想解析非英语日期,例如“31mai1989”。

事实上,从 Spark 2.1.0 开始,当使用spark.read()加载 csv 时,我认为不再可能使用该dateFormat选项来解析诸如“31mai1989”之类的日期,即使您的默认区域设置是法语。我甚至将操作系统中的默认区域和语言更改为法语,并传递了我能想到的几乎所有区域设置排列,即

JAVA_OPTS="-Duser.language=fr -Duser.country=FR -Duser.region=FR" \
JAVA_ARGS="-Duser.language=fr -Duser.country=FR -Duser.region=FR" \
LC_ALL=fr_FR.UTF-8 \
spark-submit \
--conf "spark.driver.extraJavaOptions=-Duser.country=FR -Duser.language=fr -Duser.region=FR" \
--conf "spark.executor.extraJavaOptions=-Duser.country=FR -Duser.language=fr -Duser.region=FR" \
test.py
Run Code Online (Sandbox Code Playgroud)

却无济于事,导致

java.lang.IllegalArgumentException
    at java.sql.Date.valueOf(Date.java:143)
    at org.apache.spark.sql.catalyst.util.DateTimeUtils$.stringToTime(DateTimeUtils.scala:137)
Run Code Online (Sandbox Code Playgroud)

但同样,这仅影响 Spark 2.1.0 中解析非英语日期。