Skip to content

Commit

Permalink
CHANGE: Convert decimal values to dates using Unix time with decimal …
Browse files Browse the repository at this point in the history
…precision

resolves: Oldes/Rebol-issues#2631
resolves: Oldes/Rebol-issues#2632
  • Loading branch information
Oldes committed Dec 25, 2024
1 parent 8171ed7 commit 381bd5c
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 55 deletions.
55 changes: 50 additions & 5 deletions src/core/t-date.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
** REBOL [R3] Language Interpreter and Run-time Environment
**
** Copyright 2012 REBOL Technologies
** Copyright 2012-2024 Rebol Open Source Contributors
** REBOL is a trademark of REBOL Technologies
**
** Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -313,7 +314,7 @@ static const REBI64 DAYS_OF_JAN_1ST_1970 = 719468; // number of days for 1st Jan

/***********************************************************************
**
*/ REBCNT Date_To_Timestamp(REBVAL *date)
*/ REBI64 Date_To_Timestamp(REBVAL *date)
/*
** Return the unix time stamp for a specific date value.
**
Expand All @@ -326,6 +327,22 @@ static const REBI64 DAYS_OF_JAN_1ST_1970 = 719468; // number of days for 1st Jan
return epoch + ((time + 500000000) / SEC_SEC);
}


/***********************************************************************
**
*/ REBDEC Date_To_Timestamp_Decimal(REBVAL *date)
/*
** Return the unix time stamp as a decimal for a specific date value.
**
***********************************************************************/
{
REBDAT d = VAL_DATE(date);
REBI64 epoch = (Days_Of_Date(d.date.day, d.date.month, d.date.year) - DAYS_OF_JAN_1ST_1970) * SECS_IN_DAY;
REBDEC time = (REBDEC)VAL_TIME(date);
if (time == NO_TIME) time = 0;
return (REBDEC)epoch + (time/ SEC_SEC);
}

/***********************************************************************
**
*/ void Timestamp_To_Date(REBVAL *date, REBI64 epoch)
Expand All @@ -335,10 +352,39 @@ static const REBI64 DAYS_OF_JAN_1ST_1970 = 719468; // number of days for 1st Jan
***********************************************************************/
{
REBI64 days = (epoch / SECS_IN_DAY) + DAYS_OF_JAN_1ST_1970;
REBI64 secs = epoch % SECS_IN_DAY;
// Adjust for negative seconds
if (secs < 0) {
days--;
secs = SECS_IN_DAY + secs;
}
VAL_SET(date, REB_DATE);
Date_Of_Days(days, &VAL_DATE(date));
VAL_TIME(date) = TIME_SEC(secs);
VAL_ZONE(date) = 0;
}

/***********************************************************************
**
*/ void Timestamp_Decimal_To_Date(REBVAL *date, REBDEC epoch)
/*
** Set Rebol date from the unix time stamp epoch with a decimal precision.
** NOTE: Using microseconds for time precision instead of nanoseconds
** to avoid rounding errors.
**
***********************************************************************/
{
REBI64 usecs = (REBI64)(epoch * 1000000);
REBI64 days = (usecs / MICROSECONDS_IN_DAY) + DAYS_OF_JAN_1ST_1970;
usecs = usecs % MICROSECONDS_IN_DAY;
// Adjust for negative seconds
if (usecs < 0) {
days--;
usecs = MICROSECONDS_IN_DAY + usecs;
}
VAL_SET(date, REB_DATE);
Date_Of_Days(days, &VAL_DATE(date));
VAL_TIME(date) = TIME_SEC((epoch % 86400));
VAL_TIME(date) = 1000 * usecs;
VAL_ZONE(date) = 0;
}

Expand Down Expand Up @@ -1035,9 +1081,8 @@ static const REBI64 DAYS_OF_JAN_1ST_1970 = 719468; // number of days for 1st Jan
return R_RET;
}
else if (IS_DECIMAL(arg)) {
Julian_To_Gregorian_Date(VAL_DECIMAL(arg) + 2400000.5, &day, &month, &year, &secs);
day--; month--; // The date/time normalization expects 0-based day and month
goto fixTime;
Timestamp_Decimal_To_Date(D_RET, VAL_DECIMAL(arg));
return R_RET;
}
// else if (IS_NONE(arg)) {
// secs = nsec = day = month = year = tz = 0;
Expand Down
10 changes: 2 additions & 8 deletions src/core/t-decimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
** REBOL [R3] Language Interpreter and Run-time Environment
**
** Copyright 2012 REBOL Technologies
** Copyright 2012-2021 Rebol Open Source Contributors
** Copyright 2012-2024 Rebol Open Source Contributors
** REBOL is a trademark of REBOL Technologies
**
** Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -411,14 +411,8 @@ REBOOL almost_equal(REBDEC a, REBDEC b, REBCNT max_diff) {
break;

case REB_DATE:
{
REB_TIMEF time = {12,0,0,0};
if (VAL_TIME(val) != NO_TIME) {
Split_Time(VAL_TIME(val), &time);
}
d1 = Gregorian_To_Julian_Date(VAL_DATE(val), time) - 2400000.5;
d1 = Date_To_Timestamp_Decimal(val);
break;
}

#ifdef removed
// case REB_ISSUE:
Expand Down
2 changes: 1 addition & 1 deletion src/core/t-integer.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
** REBOL [R3] Language Interpreter and Run-time Environment
**
** Copyright 2012 REBOL Technologies
** Copyright 2012-2021 Rebol Open Source Contributors
** Copyright 2012-2024 Rebol Open Source Contributors
** REBOL is a trademark of REBOL Technologies
**
** Licensed under the Apache License, Version 2.0 (the "License");
Expand Down
1 change: 1 addition & 0 deletions src/include/sys-value.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ typedef struct Reb_Time {
#define DEC_TO_SECS(n) (i64)(((n) + 5.0e-10) * SEC_SEC)

#define SECS_IN_DAY 86400
#define MICROSECONDS_IN_DAY 86400000000
#define TIME_IN_DAY (SEC_TIME((i64)SECS_IN_DAY))

#define NO_TIME MIN_I64
Expand Down
24 changes: 23 additions & 1 deletion src/tests/units/date-test.r3
Original file line number Diff line number Diff line change
Expand Up @@ -339,9 +339,31 @@ Rebol [
;@@ https://github.com/Oldes/Rebol-issues/issues/2551
--test-- "Julian accessor"
date: 10-Jun-2023/20:47:53+2:00
--assert date/julian = (2400000.5 + to decimal! date) ;; conversion using TO counts with Modified Julian Date
;; `to decimal! date` now returns unix timestamp with a precision instead of MJD
;; --assert date/julian = (2400000.5 + to decimal! date) ;; conversion using TO counts with Modified Julian Date
--assert 2460106.28325231 = date/julian
--assert 2460106.28325231 = pick date 'julian

--assert (pick 17-Nov-1858/00:00:00 'julian) == 2400000.5
--assert (pick 17-Nov-1858/00:00:00 'julian) == 2400000.5
--assert (pick 01-Jan-1900/00:00:00 'julian) == 2415020.5
--assert (pick 01-Jan-1900/00:00:00 'julian) == 2415020.5
--assert (pick 02-May-2003/12:00:00 'julian) == 2452762.0
--assert (pick 02-May-2003/12:00:00 'julian) == 2452762.0
--assert (pick 10-Jun-2023/01:30:00 'julian) == 2460105.5625
--assert (pick 10-Jun-2023/01:30:00 'julian) == 2460105.5625
--assert (pick 10-Jun-2023/19:30:00 'julian) == 2460106.3125
--assert (pick 10-Jun-2023/19:30:00 'julian) == 2460106.3125
--assert (pick 01-Jan-2023/12:00:00 'julian) == 2459946.0
--assert (pick 01-Jan-2023/12:00:00 'julian) == 2459946.0
--assert (pick 01-Jan-2023/19:30:00 'julian) == 2459946.3125
--assert (pick 01-Jan-2023/19:30:00 'julian) == 2459946.3125
--assert (pick 01-Jan-2023/01:30:00 'julian) == 2459945.5625
--assert (pick 01-Jan-2023/01:30:00 'julian) == 2459945.5625
--assert (pick 31-Aug-2132/00:00:00 'julian) == 2499999.5
--assert (pick 31-Aug-2132/00:00:00 'julian) == 2499999.5
--assert (pick 01-Sep-2132/00:00:00 'julian) == 2500000.5
--assert (pick 01-Sep-2132/00:00:00 'julian) == 2500000.5
--test-- "Julian date setter"
--assert 2415020.5 = date/julian: 2415020.5
--assert date = 1-Jan-1900/0:00
Expand Down
132 changes: 92 additions & 40 deletions src/tests/units/make-test.r3
Original file line number Diff line number Diff line change
Expand Up @@ -61,28 +61,56 @@ Rebol [
--assert 20-Sep-2021/12:46:42 = to date! 1632142002
--assert 20-Sep-2021/10:58:32 = make date! 1632135512
--assert 20-Sep-2021/10:58:32 = to date! 1632135512
;@@ https://github.com/Oldes/Rebol-issues/issues/2632
--assert 1-Jan-1970/00:00:00 = to date! 0
--assert 31-Dec-1969/23:59:59 = to date! -1
--test-- "make/to date! decimal!"
;@@ https://github.com/Oldes/Rebol-issues/issues/2551
--assert 17-Nov-1858/00:00:00 = make date! 0.0
--assert 17-Nov-1858/00:00:00 = to date! 0.0
--assert 01-Jan-1900/00:00:00 = make date! 15020.0
--assert 01-Jan-1900/00:00:00 = to date! 15020.0
--assert 02-May-2003/12:00:00 = make date! 52761.5
--assert 02-May-2003/12:00:00 = to date! 52761.5
--assert 10-Jun-2023/01:30:00 = make date! 60105.0625
--assert 10-Jun-2023/01:30:00 = to date! 60105.0625
--assert 10-Jun-2023/19:30:00 = make date! 60105.8125
--assert 10-Jun-2023/19:30:00 = to date! 60105.8125
--assert 01-Jan-2023/12:00:00 = make date! 59945.5
--assert 01-Jan-2023/12:00:00 = to date! 59945.5
--assert 01-Jan-2023/19:30:00 = make date! 59945.8125
--assert 01-Jan-2023/19:30:00 = to date! 59945.8125
--assert 01-Jan-2023/01:30:00 = make date! 59945.0625
--assert 01-Jan-2023/01:30:00 = to date! 59945.0625
--assert 31-Aug-2132/00:00:00 = make date! 99999.0
--assert 31-Aug-2132/00:00:00 = to date! 99999.0
--assert 01-Sep-2132/00:00:00 = make date! 100000.0
--assert 01-Sep-2132/00:00:00 = to date! 100000.0
;@@ https://github.com/Oldes/Rebol-issues/issues/2631
--assert 17-Nov-1858/00:00:00 == make date! -3506716800.0
--assert 17-Nov-1858/00:00:00 == to date! -3506716800.0
--assert 01-Jan-1900/00:00:00 == make date! -2208988800.0
--assert 01-Jan-1900/00:00:00 == to date! -2208988800.0
--assert 31-Dec-1969/23:59:59.9 == to date! -0.1
--assert 01-Jan-1970/00:00:00 == to date! 0.0
--assert 01-Jan-1970/00:00:00.1 == to date! 0.1
--assert 02-May-2003/12:00:00 == make date! 1051876800.0
--assert 02-May-2003/12:00:00 == to date! 1051876800.0
--assert 10-Jun-2023/01:30:00 == make date! 1686360600.0
--assert 10-Jun-2023/01:30:00 == to date! 1686360600.0
--assert 10-Jun-2023/19:30:00 == make date! 1686425400.0
--assert 10-Jun-2023/19:30:00 == to date! 1686425400.0
--assert 01-Jan-2023/12:00:00 == make date! 1672574400.0
--assert 01-Jan-2023/12:00:00 == to date! 1672574400.0
--assert 01-Jan-2023/19:30:00 == make date! 1672601400.0
--assert 01-Jan-2023/19:30:00 == to date! 1672601400.0
--assert 01-Jan-2023/01:30:00 == make date! 1672536600.0
--assert 01-Jan-2023/01:30:00 == to date! 1672536600.0
--assert 31-Aug-2132/00:00:00 == make date! 5133196800.0
--assert 31-Aug-2132/00:00:00 == to date! 5133196800.0
--assert 01-Sep-2132/00:00:00 == make date! 5133283200.0
--assert 01-Sep-2132/00:00:00 == to date! 5133283200.0

--assert 16-Nov-1858/23:59:59.75 == make date! -3506716800.25
--assert 16-Nov-1858/23:59:59.75 == to date! -3506716800.25
--assert 31-Dec-1899/23:59:59.75 == make date! -2208988800.25
--assert 31-Dec-1899/23:59:59.75 == to date! -2208988800.25
--assert 02-May-2003/12:00:00.25 == make date! 1051876800.25
--assert 02-May-2003/12:00:00.25 == to date! 1051876800.25
--assert 10-Jun-2023/01:30:00.25 == make date! 1686360600.25
--assert 10-Jun-2023/01:30:00.25 == to date! 1686360600.25
--assert 10-Jun-2023/19:30:00.25 == make date! 1686425400.25
--assert 10-Jun-2023/19:30:00.25 == to date! 1686425400.25
--assert 01-Jan-2023/12:00:00.25 == make date! 1672574400.25
--assert 01-Jan-2023/12:00:00.25 == to date! 1672574400.25
--assert 01-Jan-2023/19:30:00.25 == make date! 1672601400.25
--assert 01-Jan-2023/19:30:00.25 == to date! 1672601400.25
--assert 01-Jan-2023/01:30:00.25 == make date! 1672536600.25
--assert 01-Jan-2023/01:30:00.25 == to date! 1672536600.25
--assert 31-Aug-2132/00:00:00.25 == make date! 5133196800.25
--assert 31-Aug-2132/00:00:00.25 == to date! 5133196800.25
--assert 01-Sep-2132/00:00:00.25 == make date! 5133283200.25
--assert 01-Sep-2132/00:00:00.25 == to date! 5133283200.25
--test-- "make/to date! block!"
--assert 1-Feb-2000 == make date! [2000 2 1]
;@@ https://github.com/Oldes/Rebol-issues/issues/2619
Expand Down Expand Up @@ -135,6 +163,8 @@ Rebol [
--assert 1685750400 == to integer! 3-Jun-2023/0:00
--assert 1685750400 == make integer! 3-Jun-2023
--assert 1685750400 == make integer! 3-Jun-2023/0:00
;@@ https://github.com/Oldes/Rebol-issues/issues/2632
--assert -1 == to integer! 31-Dec-1969/23:59:59

--test-- "make/to integer! string!"
;@@ https://github.com/Oldes/Rebol-issues/issues/2164
Expand Down Expand Up @@ -191,26 +221,48 @@ Rebol [

--test-- "make/to decimal! date!"
;@@ https://github.com/Oldes/Rebol-issues/issues/2551
--assert 0.0 = make decimal! 17-Nov-1858/00:00:00
--assert 0.0 = to decimal! 17-Nov-1858/00:00:00
--assert 15020.0 = make decimal! 01-Jan-1900/00:00:00
--assert 15020.0 = to decimal! 01-Jan-1900/00:00:00
--assert 52761.5 = make decimal! 02-May-2003/12:00:00
--assert 52761.5 = to decimal! 02-May-2003/12:00:00
--assert 60105.0625 = make decimal! 10-Jun-2023/01:30:00
--assert 60105.0625 = to decimal! 10-Jun-2023/01:30:00
--assert 60105.8125 = make decimal! 10-Jun-2023/19:30:00
--assert 60105.8125 = to decimal! 10-Jun-2023/19:30:00
--assert 59945.5 = make decimal! 01-Jan-2023/12:00:00
--assert 59945.5 = to decimal! 01-Jan-2023/12:00:00
--assert 59945.8125 = make decimal! 01-Jan-2023/19:30:00
--assert 59945.8125 = to decimal! 01-Jan-2023/19:30:00
--assert 59945.0625 = make decimal! 01-Jan-2023/01:30:00
--assert 59945.0625 = to decimal! 01-Jan-2023/01:30:00
--assert 99999.0 = make decimal! 31-Aug-2132/00:00:00
--assert 99999.0 = to decimal! 31-Aug-2132/00:00:00
--assert 100000.0 = make decimal! 01-Sep-2132/00:00:00
--assert 100000.0 = to decimal! 01-Sep-2132/00:00:00
;@@ https://github.com/Oldes/Rebol-issues/issues/2631
--assert -3506716800.0 == make decimal! 17-Nov-1858/00:00:00
--assert -3506716800.0 == to decimal! 17-Nov-1858/00:00:00
--assert -2208988800.0 == make decimal! 01-Jan-1900/00:00:00
--assert -2208988800.0 == to decimal! 01-Jan-1900/00:00:00
--assert 1051876800.0 == make decimal! 02-May-2003/12:00:00
--assert 1051876800.0 == to decimal! 02-May-2003/12:00:00
--assert 1686360600.0 == make decimal! 10-Jun-2023/01:30:00
--assert 1686360600.0 == to decimal! 10-Jun-2023/01:30:00
--assert 1686425400.0 == make decimal! 10-Jun-2023/19:30:00
--assert 1686425400.0 == to decimal! 10-Jun-2023/19:30:00
--assert 1672574400.0 == make decimal! 01-Jan-2023/12:00:00
--assert 1672574400.0 == to decimal! 01-Jan-2023/12:00:00
--assert 1672601400.0 == make decimal! 01-Jan-2023/19:30:00
--assert 1672601400.0 == to decimal! 01-Jan-2023/19:30:00
--assert 1672536600.0 == make decimal! 01-Jan-2023/01:30:00
--assert 1672536600.0 == to decimal! 01-Jan-2023/01:30:00
--assert 5133196800.0 == make decimal! 31-Aug-2132/00:00:00
--assert 5133196800.0 == to decimal! 31-Aug-2132/00:00:00
--assert 5133283200.0 == make decimal! 01-Sep-2132/00:00:00
--assert 5133283200.0 == to decimal! 01-Sep-2132/00:00:00

--assert -3506716800.25 == make decimal! 16-Nov-1858/23:59:59.75
--assert -3506716800.25 == to decimal! 16-Nov-1858/23:59:59.75
--assert -2208988800.25 == make decimal! 31-Dec-1899/23:59:59.75
--assert -2208988800.25 == to decimal! 31-Dec-1899/23:59:59.75
--assert 1051876800.25 == make decimal! 02-May-2003/12:00:00.25
--assert 1051876800.25 == to decimal! 02-May-2003/12:00:00.25
--assert 1686360600.25 == make decimal! 10-Jun-2023/01:30:00.25
--assert 1686360600.25 == to decimal! 10-Jun-2023/01:30:00.25
--assert 1686425400.25 == make decimal! 10-Jun-2023/19:30:00.25
--assert 1686425400.25 == to decimal! 10-Jun-2023/19:30:00.25
--assert 1672574400.25 == make decimal! 01-Jan-2023/12:00:00.25
--assert 1672574400.25 == to decimal! 01-Jan-2023/12:00:00.25
--assert 1672601400.25 == make decimal! 01-Jan-2023/19:30:00.25
--assert 1672601400.25 == to decimal! 01-Jan-2023/19:30:00.25
--assert 1672536600.25 == make decimal! 01-Jan-2023/01:30:00.25
--assert 1672536600.25 == to decimal! 01-Jan-2023/01:30:00.25
--assert 5133196800.25 == make decimal! 31-Aug-2132/00:00:00.25
--assert 5133196800.25 == to decimal! 31-Aug-2132/00:00:00.25
--assert 5133283200.25 == make decimal! 01-Sep-2132/00:00:00.25
--assert 5133283200.25 == to decimal! 01-Sep-2132/00:00:00.25
===end-group===


Expand Down

0 comments on commit 381bd5c

Please sign in to comment.