Unix epoch to date String – Slightly wrong month day - datetime

I am trying to format SystemTime as a String in Rust without using external crates, but I am facing a bizarre issue regarding the "day" field.
Can anyone explain me why the line preceded by the "FIXME" comment yields slightly wrong month days as the time distance from Thu, 01 Jan 1970 00:00:00 GMT increases?
fn time_to_http_date_string(time: &SystemTime) -> String {
const MINUTE: u64 = 60;
const HOUR: u64 = 3600;
const DAY: u64 = 86400;
const WEEKDAYS: [&str; 7] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const MONTH: u64 = 2629743;
const MONTHS: [&str; 12] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const YEAR: u64 = 31556926;
let time = time.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
let year = 1970 + (time / YEAR);
let month = MONTHS[(time % YEAR / MONTH) as usize];
let weekday = WEEKDAYS[((time / DAY + 4) % 7) as usize];
// FIXME: Slightly wrong as time distance from the UNIX epoch increases.
let day = (time % MONTH / DAY) + 1;
let hour = time % DAY / HOUR;
let minute = time % HOUR / MINUTE;
let second = time % MINUTE;
format!(
"{weekday}, {day:02} {month} {year} {hour:02}:{minute:02}:{second:02} GMT",
weekday = weekday,
day = day,
month = month,
year = year,
hour = hour,
minute = minute,
second = second,
)
}
Let me give you more details about this issue; here are some tests I ran this function through:
Unix Timestamp
Expected
Mine
OK
0
Thu, 01 Jan 1970 00:00:00 GMT
Thu, 01 Jan 1970 00:00:00 GMT
👍
68169600
Tue, 29 Feb 1972 00:00:00 GMT
Tue, 29 Feb 1972 00:00:00 GMT
👍
874540800
Thu, 18 Sep 1997 00:00:00 GMT
Thu, 17 Sep 1997 00:00:00 GMT
❌
1052790840
Tue, 13 May 2003 01:54:00 GMT
Tue, 11 May 2003 01:54:00 GMT
❌
As you may have already noticed from my test cases, the day is the only wrong field in any of the failing cases. Any ideas about the culprit?
PS: I have already tried to use f64 instead of u64 and to round the results... but it didn't fix all of the test cases.

It turns out I was looking for an impossible solution. It is indeed impossible to perform such a computation without taking into account leap years and the simple fact that months do not share the same duration in days. Even though now I feel a bit dumb about posting this question, I'd like to thank the people who first commented this thread. They pushed me in the right direction.

Related

how get this specific format of date in pre-request of postman

I have this in c#:
var date = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
and the result is like this:
date = "Tue, 27 Dec 2022 13:30:35 GMT";
I want to have this result in pre-request of postman to pass this variable as date.
But this command doesn't give me the exact result:
var date = new Date();
//result: Tue Dec 27 2022 16:26:00 GMT+0100 (Central European Standard Time)
As I'm using this date variable for encryption, it's important to have it in the special format I have in c#.
Do you have any idea how can I have this result in postman?
To display time, you can use momentjs, that's already included in postman. The cons is it doesn't support timezone, so the code would be:
const moment = require('moment')
let datetime = moment().format("ddd, DD MMM YYYY HH:mm:ss ") + "GMT"
//Wed, 28 Dec 2022 08:08:36 GMT
Using reg expression in pre-request section
var date = new Date();
// Tue Dec 27 2022 12:10:39 GMT-0500 (Eastern Standard Time)
console.log(date);
let match = /(Sun|Mon|Tue|Wed|Thu|Fri|Sat)\s+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+(\d{1,2})\s+(\d{4})\s+(\d{2}|\d{1})\:(\d{2})\:(\d{2})\s([a-zA-Z]{3})/.exec(date);
// 0: "Tue Dec 27 2022 12:10:39 GMT"
// 1: "Tue"
// 2: "Dec"
// 3: "27"
// 4: "2022"
// 5: "12"
// 6: "10"
// 7: "39"
// 8: "GMT"
// newDate = "Tue, 27 Dec 2022 13:30:39 GMT";
newDate = `${match[1]}, ${match[3]} ${match[2]} ${match[4]} ${match[5]}:${match[6]}:${match[7]} ${match[8]}`
console.log(newDate);
Result in console
Tue Dec 27 2022 12:22:39 GMT-0500 (Eastern Standard Time)
Tue, 27 Dec 2022 12:22:39 GMT
Test string set in https://regex101.com/
Regular Expression
(Sun|Mon|Tue|Wed|Thu|Fri|Sat)\s+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+(\d{1,2})\s+(\d{4})\s+(\d{2}|\d{1})\:(\d{2})\:(\d{2})\s([a-zA-Z]{3})
In Reg Expression Visualization https://regexper.com/

How to parse CCYY-MM-DDThh:mm:ss[.sss...] date format

As we all know, date parsing in Go has it's quirks*.
However, I have now come up against needing to parse a datetime string in CCYY-MM-DDThh:mm:ss[.sss...] to a valid date in Go.
This CCYY format is a format that seems to be ubiquitous in astronomy, essentially the CC is the current century, so although we're in 2022, the century is the 21st century, meaning the date in CCYY format would be 2122.
How do I parse a date string in this format, when we can't specify a coded layout?
Should I just parse in that format, and subtract one "century" e.g., 2106 becomes 2006 in the parsed datetime...?
Has anyone come up against this niche problem before?
*(I for one would never have been able to remember January 2nd, 3:04:05 PM of 2006, UTC-0700 if it wasn't the exact time of my birth! I got lucky)
The time package does not support parsing centuries. You have to handle it yourself.
Also note that a simple subtraction is not enough, as e.g. the 21st century takes place between January 1, 2001 and December 31, 2100 (the year may start with 20 or 21). If the year ends with 00, you do not have to subtract 100 years.
I would write a helper function to parse such dates:
func parse(s string) (t time.Time, err error) {
t, err = time.Parse("2006-01-02T15:04:05[.000]", s)
if err == nil && t.Year()%100 != 0 {
t = t.AddDate(-100, 0, 0)
}
return
}
Testing it:
fmt.Println(parse("2101-12-31T12:13:14[.123]"))
fmt.Println(parse("2122-10-29T12:13:14[.123]"))
fmt.Println(parse("2100-12-31T12:13:14[.123]"))
fmt.Println(parse("2201-12-31T12:13:14[.123]"))
Which outputs (try it on the Go Playground):
2001-12-31 12:13:14.123 +0000 UTC <nil>
2022-10-29 12:13:14.123 +0000 UTC <nil>
2100-12-31 12:13:14.123 +0000 UTC <nil>
2101-12-31 12:13:14.123 +0000 UTC <nil>
As for remembering the layout's time:
January 2, 15:04:05, 2006 (zone: -0700) is a common order in the US, and in this representation parts are in increasing numerical order: January is month 1, 15 hour is 3PM, year 2006 is 6. So the ordinals are 1, 2, 3, 4, 5, 6, 7.
I for one would never have been able to remember January 2nd, 3:04:05 PM of 2006, UTC-0700 if it wasn't the exact time of my birth! I got lucky.
The reason for the Go time package layout is that it is derived from the Unix (and Unix-like) date command format. For example, on Linux,
$ date
Fri Apr 15 08:20:43 AM EDT 2022
$
Now, count from left to right,
Month = 1
Day = 2
Hour = 3 (or 15 = 12 + 3)
Minute = 4
Second = 5
Year = 6
Note: Rob Pike is an author of The Unix Programming Environment

How to handle leap years in Ada

I'm supposed to do a program that handles the dates of a year. I'm having a problem with handling the leap year in a good way. I'm not going to post the entire code because it'll be too much. My program runs but I don't like how my last if statement looks.
Information about months and leap years:
Month: Jan Feb Mar Apr May Jun Jul Aug Sep Okt Nov Dec
Days : 31 28/29 31 30 31 30 31 31 30 31 30 30
A year is a leap year if it's evenly dividable by 4, but not even when divided by 100. Years are also evenly dividable by 400 leap years. If it's a leap year, February has 29 days instead of 28.
This is my code (only the part I need help with):
Get(Days);
if Days > 31 or Day < 1 then
raise Day_Error;
elsif Day = 31 and (Month = 2 or Month = 4 or Month = 6 or Month = 9 or Month = 11) then
raise Day_Error;
elsif Day = 30 and (Month = 1 or Month = 3 or Month = 5 or Month = 7 or
Month = 8 or Month = 10 or Month = 12) then
raise Day_Error;
end if;
if Day = 29 and Month = 2 then
if Year mod 400 = 0 or (Year mod 4 = 0 and Year mod 100 /= 0) then
null;
else
raise Day_Error;
end if;
end if;
exit;
end;
end loop;
end Day_Procedure;
My concern is the if statement where I put Day = 29 and Month = 2, can this be solved without having the need to put two if statements?
I think a lot can be gained by putting certain tests in subroutines, even if they are only called once. In the code below I have a routine GetMaxDayInMonth(Month, Year) that gives the maximum day for the given month in the given year. The year is necessary only in the case of February, where the maximum allowed day of the month depends on whether it is in a leap year.
For all other months the return value is constant, which I have recorded in a const array MaxDaysInMonth. Only days in February must be treated specially. That treatment has again been stowed away in its own little subroutine GetFebMaxDay(Day, Month), making everything neat and intelligible.
I have used the new Ada equivalent to the C ternary operator cond?a:b, (if cond then a else b):
function GetFebMaxDay(Year: YearT) return DayInMonthT is
begin
return (if IsLeapYear(Year) then 29 else 28);
end GetFebMaxDay;
The test for valid day then is thus reduced to a single function call
Date.Day <= GetMaxDayInMonth(Date.Month, Date.Year);
I have also created distinct types for year, month and day, imposing constraints where possible (max day in month is 31, after all). Many mistakes would have been avoided if programmers had put feet and meters in their own subtypes that need explicit conversion.
Here is the entire program that checks a few borderline dates:
with Ada.Text_IO; use Ada.Text_IO;
procedure DateCheck is
subtype DayInMonthT is Integer range 1..31;
type MonthT is (JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC);
subtype YearT is Integer;
type DateT is record Year: YearT; Month: MonthT; Day: DayInMonthT; end record;
function IsLeapYear(Y: YearT) return Boolean is
begin
return (Y mod 4 = 0 and Y mod 100 /= 0)
or Y mod 400 = 0;
end IsLeapYear;
function GetFebMaxDay(Year: YearT) return DayInMonthT is
begin
return (if IsLeapYear(Year) then 29 else 28);
end GetFebMaxDay;
function GetMaxDayInMonth(Month: MonthT; Year: YearT) return DayInMonthT is
MaxDaysInMonth: constant array(MonthT) of DayInMonthT :=
( JAN => 31,
FEB => 29,
MAR => 31,
APR => 30,
MAY => 31,
JUN => 30,
JUL => 31,
AUG => 31,
SEP => 30,
OCT => 31,
NOV => 30,
DEC => 31 );
begin return (if Month = FEB then GetFebMaxDay(Year) else MaxDaysInMonth(Month)); end;
function IsValidateDate(Date: DateT) return Boolean is
begin
return Date.Day <= GetMaxDayInMonth(Date.Month, Date.Year);
end;
Dates: array (1..6) of DateT := (others => (0, JAN, 1));
begin
Dates(1) := (2021, FEB, 28);
Dates(2) := (2021, FEB, 29);
Dates(3) := (2021, FEB, 30);
Dates(4) := (2000, FEB, 31);
-- Dates(4) := (2000, FEB, 32); -- raises warning, fails with constraint error
Dates(5) := (2000, FEB, 29);
Dates(6) := (1900, FEB, 29);
for i in Dates'Range loop
Put("Date ");
Put(Dates(i).Year'Image);
Put("-");
Put(Dates(i).Month'Image);
Put("-");
Put(Dates(i).Day'Image);
Put(": Valid date? ");
Put_Line(IsValidateDate(Dates(i))'Image);
end loop;
end DateCheck;
Sample session:
$ gnatmake -gnatwa datecheck.adb && ./datecheck.exe
gcc -c -gnatwa datecheck.adb
gnatbind -x datecheck.ali
gnatlink datecheck.ali
Date 2021-FEB- 28: Valid date? TRUE
Date 2021-FEB- 29: Valid date? FALSE
Date 2021-FEB- 30: Valid date? FALSE
Date 2000-FEB- 31: Valid date? FALSE
Date 2000-FEB- 29: Valid date? TRUE
Date 1900-FEB- 29: Valid date? FALSE

Moment.js - Parse Date Time Ago

I am saving my date like this in my mongo database:
Thu Oct 25 2018 17:30:03 GMT+0300 (EAT)
I would like to use moment.js to have in the front end like 1 hour ago or 3 hours ago. How would I go about this?
If you're using single locale try
moment('Thu Oct 25 2018 17:30:03 GMT+0300').fromNow(); //eg. 1 day ago, 2 hours ago etc
or
moment('Thu Oct 25 2018 17:30:03 GMT+0300').fromNow(true); //eg. 1 day, 2 hours
for more see docs
and:
you can add your date and then compare with the current time:
const timestamp = moment(dateFromDatabase, 'ddd MMM DD YYYY HH:mm:ss GMT Z').fromNow();
or you can also use diff()
const timestamp = moment(dateFromDatabase, 'ddd MMM DD YYYY HH:mm:ss GMT Z').diff(Date.now(), 'hours');
you can change the measurements using years, months, weeks, days, hours, minutes, and seconds.
For more information, you can take a look on here.

Find date for same day of the week last year?

OK so for example, today is Tuesday, Feb 02. Well the equivalent "Tuesday" from last year was on Feb 03.
How can I find this out programmatically?
Thanks!!
According to Google, there are 604,800,000 milliseconds in a week. That times 52 should give you the same day of the week a year later (right?).
For example:
var date:Date = new Date(2010, 1, 2);
trace(date);
date.setMilliseconds(date.milliseconds - 604800000 * 52);
trace(date);
Output:
Tue Feb 2 00:00:00 GMT-0800 2010
Tue Feb 3 00:00:00 GMT-0800 2009
Just my two cents. I don't like the idea, that the second answer assumes 52 weeks in a year, it will work for a single year, but is a solution to only this exact problem - eg. if you want to check the same thing moving back 10 years it won't work. I'd do it like this:
var today:Date = new Date();
// Here we store the day of the week
var currentDay:int = today.day;
trace (today);
const milisecondsInADay:uint = 1000*60*60*24;
// Here we move back a year, but we can just as well move back 10 years
// or 2 months
today.fullYear -= 1;
// Find the closest date that is the same day of the week as current day
today.time -= milisecondsInADay*(today.day-currentDay);
trace (today);
returns:
Tue Feb 2 21:13:18 GMT+0100 2010
Tue Feb 3 21:13:18 GMT+0100 2009

Resources