【ISO 8601】時間也會自己跨年!Swift、Python、C# 年份格式比較

有個朋友跟我說遇到一個12/31好玩的東西,就是Swift中「YYYY」和「yyyy」產生的年份不同,一個是2020,一個是2019。

到底為何會變成這樣呢?是大Y們自己太開心了,已經先喝醉就自己先跨年嗎?

C#Pythondatetime的相關功能是否也會有相同問題? 

先說結論 。

根據 ISO8601的week calendar:(補充:格里曆-Gregorian Calendar

3.1.1.23

week calendar

calendar (3.1.1.18) based on an unbounded series of contiguous calendar weeks (3.1.2.16) that uses the time scale unit (3.1.1.7) of calendar week as its basic unit to represent a calendar year (3.1.2.21), according to the rule that the first calendar week of a calendar year is the week including the first Thursday of that year, and that the last one is the week immediately preceding the first calendar week of the next calendar year

Note 1 to entry: This rule is based on the principle that a week belongs to the calendar year to which the majority of its calendar days (3.1.2.11) belong.

Note 2 to entry: In the week calendar, calendar days of the first and last calendar week of a calendar year may belong to the previous and the next calendar year respectively in the Gregorian calendar (3.1.1.19).

Note 3 to entry: The week calendar is described in 4.2.2.

可以知道他們有定義一個「週曆」(week calendar),這個就是以週為單位去計算的日曆。根據定義「calendar (3.1.1.18) based on an unbounded series of contiguous calendar weeks (3.1.2.16) that uses the time scale unit (3.1.1.7) of calendar week as its basic unit to represent a calendar year (3.1.2.21), according to the rule that the first calendar week of a calendar year is the week including the first Thursday of that year, and that the last one is the week immediately preceding the first calendar week of the next calendar year」,也就是說,後一年的第一天,只要落在週四之前,就開始計算,而在此的前一週,為當年的最後一週;同理在週四之後,不會列入新年的第一週。

而這個可以有其他判斷方式,像是在WIKI有說:

There are several mutually equivalent and compatible descriptions of week 01:

  • the week with the year's first Thursday in it (the formal ISO definition),
  • the week with 4 January in it,
  • the first week with the majority (four or more) of its days in the starting year, and
  • the week starting with the Monday in the period 29 December – 4 January.

簡單來說只要,1/1是在星期一、星期二、星期三、星期四,就試算是新年的第一週。

所以在今天(2019/12/31)為週二,而2020/1/1是在週三,它會計算成第一週。


Swift

Swift中「YYYY」是以「week」為基準的日曆,所以會是2020;而「yyyy」是一般的日曆,也就會是2019。

let currentdate = Date()
let dateformatter = DateFormatter()
        dateformatter.locale = Locale(identifier: "zh_Hant_TW")
        dateformatter.dateFormat = "YYYYMMddHHmm"
        guard let customDate = Int(dateformatter.string(from: currentdate)) else{
            assertionFailure("[AssertionFailure] Date: customDate dateformatter is invaild!! (View02WeatherObservationViewController)")
            return
        }

而在開發文件中,蘋果說明這兩者的差異:

A common mistake is to use YYYY. yyyy specifies the calendar year whereas YYYY specifies the year (of “Week of Year”), used in the ISO year-week calendar. 

 

蘋果也知道YYYY常被誤用。YYYY是週日曆,因此要使用yyyy才能正確的取得年份,才能取到真正的一般日曆年,也就是2019。

 


C#

這時候就想看看,在.Net是否有這樣的情況?

就以.Net CoreC#datetimeformat做範例試看看:

DateTime dateTime = DateTime.Now;
Console.WriteLine($"formate[DateTime.Now],time=[{DateTime.Now}]");
Console.WriteLine($"formate[DateTime.Now.Year],time=[{DateTime.Now.Year}]");

Console.WriteLine($"formate[yy],time=[{DateTime.Now.ToString("yy")}]");
Console.WriteLine($"formate[YY],time=[{DateTime.Now.ToString("YY")}]");

Console.WriteLine($"formate[yyyy],time=[{DateTime.Now.ToString("yyyy")}]");
Console.WriteLine($"formate[YYYY],time=[{DateTime.Now.ToString("YYYY")}]");

Console.WriteLine($"formate[yyyyy],time=[{DateTime.Now.ToString("yyyyy")}]");

發現format功能沒有「Y」這個東西,因此沒有這樣的狀況:

formate[DateTime.Now],time=[12/31/2019 4:14:31 PM]
formate[DateTime.Now.Year],time=[2019]
formate[yy],time=[19]
formate[YY],time=[YY]
formate[yyyy],time=[2019]
formate[YYYY],time=[YYYY]
formate[yyyyy],time=[02019]

來查查文件,發現文件也沒寫道這一塊,它只有「y」的數量,也就是年份顯示的長度。


Python

直接使用strftime

import datetime


print_str = 'format[%s],time=[%s]'
time_now = datetime.datetime.now()

print((print_str % ('datetime.now', time_now)))
print((print_str % ('datetime.now.year', time_now.year)))
print((print_str % ('datetime.now.ctime', time_now.ctime())))
print((print_str % ('%Y', time_now.strftime('%Y'))))
print((print_str % ('%y', time_now.strftime('%y'))))

結果也是沒有這樣的問題:

format[datetime.now],time=[2019-12-31 14:32:13.077664]
format[datetime.now.year],time=[2019]
format[datetime.now.ctime],time=[Tue Dec 31 14:32:13 2019]
format[%Y],time=[2019]
format[%y],time=[19]

在Python中,大小寫「Y」的差異只有年份的長度。「%Y」是四碼年份,「%y」是兩碼年份。

 


總結:

使用Swift要注意,有分週日曆和一般日曆的區分,因此「YYYY」別亂用,應該用「yyyy

C#Python則沒有這個問題。

 

另外...

新年快樂~~


 

參考資料:

~Copyright by Eyelash500~

IT技術文章EY*研究院
iT邦幫忙eyelash*睫毛
Blog睫毛*Relax
Facebook睫毛*Relax