PySpark中yyyyww格式周年份提取周日日期失败问题咨询
这个问题本质是周历标准不匹配导致的,咱们一步步拆解原因,再给出解决办法:
一、为什么第一周返回null?
Spark中yyyyww格式对应的是美国周历标准,这个标准有两个关键规则:
- 每周以周日作为起始日
- 每年的第一周是包含1月1日的那一周
拿2020年举例:2020年1月1日是周三,属于美国周历的2020年第1周——这一周的时间范围是2019年12月29日(周日)到2020年1月4日(周六)。你拼接的字符串是202001-Sunday,相当于告诉解析器“找2020年第1周的周日”,但这个周日其实是2019年的12月29日,年份不匹配,所以解析失败返回null。
同理2019年1月1日是周二,美国周历2019年第1周的周日是2018年12月30日,同样因为年份不匹配导致201901-Sunday解析失败。
二、为什么第二周的日期看起来“不对”?
还是看美国周历的规则:2020年第2周的起始日是2020年1月5日(周日),所以你提取的202002-Sunday就是这一周的第一天(周日),这和你预期的“第二周的最后一天周日”完全不是一回事——本质是你搞混了周的起始日,以及周数对应的时间范围。
三、解决方案:匹配你需要的周历标准
如果你想要的是ISO周历标准(每周周一到周日,第一周是包含当年第一个周四的周,这是国际通用的周定义),可以用以下两种方法:
方法1:用ISO年份和周数格式解析(Spark 3.0+支持)
ISO标准里用YYYY表示ISO年份,ww表示ISO周数,直接拼接成YYYY-ww-E格式解析:
from pyspark.sql.functions import to_timestamp, concat, lit, col # 把yyyyww拆成YYYY和ww,拼接后解析 df = df.withColumn( "iso_week_str", concat(col("week_year").substr(1,4), lit("-"), col("week_year").substr(5,2), lit("-Sunday")) ).withColumn( "day", to_timestamp(col("iso_week_str"), 'YYYY-ww-E') )
这个方法会正确返回ISO周对应的周日:比如2020年ISO第1周的周日是2020年1月5日,2019年ISO第1周的周日是2019年1月6日。
方法2:手动计算周日日期(兼容所有Spark版本)
如果你的Spark版本低于3.0,可以通过计算当年第一天,再推导对应周的周日:
from pyspark.sql.functions import to_date, expr, col # 拆分年份和周数字段 df = df.withColumn( "year", col("week_year").cast("string").substr(1,4).cast("int") ).withColumn( "week_num", col("week_year").cast("string").substr(5,2).cast("int") ) # 计算对应周的周日:先找到当年第1天,再推导到目标周的周日 df = df.withColumn( "day", expr(""" date_add( to_date(concat(year, '-01-01'), 'yyyy-MM-dd'), (week_num - 1)*7 + 6 - dayofweek(to_date(concat(year, '-01-01'), 'yyyy-MM-dd')) ) """) )
这里的逻辑是:先找到当年1月1日,计算它是周几,再推导到目标周的最后一天(周日)。
总结
核心矛盾在于你使用的yyyyww是美国周历标准,而你预期的是ISO周历标准,两者的周数计算、起始日规则完全不同。选择上面任一方法,都能解决你的问题。
内容的提问来源于stack exchange,提问作者dorotamaja

