一个历史错误以及如何解决它
TL;DR:大多数语言无法找到闰年计算的正确行为。
免责声明:虽然我已尽力提供跨各种编程语言的准确见解,但我承认我可能不是每个人的专家。如果您发现错误或不同意任何观点,请留下尊重的评论,我会立即解决。
确定一年是否为闰年是一个简单的数学问题。
每个学生都可以将其作为他们的第一个编程作业来解决。
为了简化问题,我们假设一个年份可以被 4整除,但它也可以被 100 整除,那么它就是闰年,但如果它可以被 400 整除,那么它就是闰年。
现实世界和宇宙力学要复杂一些,但这超出了本文的范围。
让我们探讨一下几种编程语言是如何解决这个问题的:
PHP:
<?php $yearNumber = 2024; $isLeap = date('L', mktime(0, 0, 0, 1, 1, $yearNumber));
SQL(PostgreSQL):
SELECT (EXTRACT(year FROM TIMESTAMP '2024-02-29') IS NOT NULL) AS is_leap_year;
这些语言尝试创建有效(或无效)的闰日并利用真实值。
这次黑客攻击违反了快速失败原则,并滥用了数十亿美元的错误。
尝试创建无效日期应该在严肃的语言中引发异常,因为这种情况发生在现实世界中。
执行其他操作,例如将错误隐藏在表面之下,违反了最小惊讶原则。
艾达:
function Is_Leap_Year (Year : Integer) return Boolean is begin return (Year mod 4 = 0 and then Year mod 100 /= 0) or else (Year mod 400 = 0); end Is_Leap_Year;
C/C++:
bool isLeapYear(int year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); }
去:
package main import ( "fmt" "time" ) func isLeapYear(year int) bool { return year%4 == 0 && (year%100 != 0 || year%400 == 0) }
哈斯克尔:
import Data.Time.Calendar (isLeapYear) let year = 2024 let isLeap = isLeapYear year
JavaScript/TypeScript:
function isLeapYear(year) { return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0); }
朱莉娅:
using Dates year = 2024 isleap(year)
路亚:
local year = 2024 local isLeap = (year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0)
MATLAB:
year = 2024; isLeap = mod(year, 4) == 0 && (mod(year, 100) ~= 0 || mod(year, 400) == 0);
Objective-C:
int yearNumber = 2024; BOOL isLeap = (yearNumber % 4 == 0 && yearNumber % 100 != 0) || (yearNumber % 400 == 0);
电源外壳:
$yearNumber = 2024 $isLeap = ($yearNumber % 4 -eq 0 -and $yearNumber % 100 -ne 0) -or ($yearNumber % 400 -eq 0)
锈:
fn is_leap_year(year: i32) -> bool { (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) }
短暂聊天:
| yearNumber | yearNumber := 2024. (yearNumber \\ 4 = 0) and: [(yearNumber \\ 100 ~= 0) or: [ yearNumber \\ 400 = 0 ]]
上述语言不提供本机支持。
您需要定义全局函数或使用helpers 。
PHP(再次):
<?php $yearNumber = 2024; $isLeap = checkdate(2, 29, $yearNumber);
回复:
leap_year(2024)
红宝石:
year = 2024 is_leap = Date.leap?(year)
迅速:
let yearNumber = 2024 let isLeap = Calendar.current.isDateInLeapYear( Date(timeIntervalSince1970: TimeInterval(yearNumber)))
这些语言使用全局函数来检查年份是否为闰年。
这些实用程序全局方法错误地将责任置于错误的位置(全局访问点)。
C#:
int yearNumber = 2024; bool isLeap = System.DateTime.IsLeapYear(yearNumber);
镖:
import 'package:intl/intl.dart'; var year = 2024; var isLeap = DateTime(year).isLeapYear;
珀尔:
use Time::Piece; my $yearNumber = 2024; my $isLeap = Time::Piece ->strptime("$yearNumber-01-01", "%Y-%m-%d")->leapyear;
Python:
import calendar leap = calendar.isleap(2024)
Visual Basic .NET:
Dim year As Integer = 2024 Dim isLeap As Boolean = DateTime.IsLeapYear(year)
这些语言使用助手作为库来检查一年是否为闰年。
错误的地方并不存在于真实的对象中,而是存在于与DateTime 相关的函数中。
爪哇:
int yearNumber = 2024; boolean isLeap = java.time.Year.of(yearNumber).isLeap();
科特林:
val yearNumber = 2024 val isLeap = java.time.Year.of(yearNumber).isLeap
斯卡拉:
val year = 2024 val isLeap = java.time.Year.of(year).isLeap
这些语言依靠年份来检查是否是跳跃。
该协议在双射方面更接近现实世界
请注意,它们创建Year对象而不是Integer对象,因为这也会破坏双射。
Year与整数有不同的协议,将 Year 建模为整数也会是一种过早的优化味道,也是混合“what”和“how”的症状。
Year可以判断它是否是一个闰年(整数不应该这样做),并且可以告诉您它的月份(是Months ,而不是基于 0 的整数、基于 1 的整数或字符串)。
相反, Integer的功能扩展到算术运算,例如乘法和求幂。
将时间点表示为float 、 integer或任何其他数据类型都会产生后果。
您可以将现实世界中的时间点分解为微小的分数(但不能太小)
使用浮点数不是一个有效的选项。
0.01 + 0.02不是 0.03,这对于处理浮点时间点会产生可怕的后果。
我们一直在谈论闰年。
需要什么才能知道一年是否为闰年?
您建模的日期和时间机制需要知道 2024 年 2 月 28 日的后继日期。
但这不是你的问题。
遵循信息隐藏原则,您应该将责任保留为私有协议。
没有银弹。
明智地使用你的语言。
今天是 2 月 29 日,这是一个停下来反思一下您每天使用的工具的闰日。
4年后见。