mirror of
https://github.com/cc65/cc65.git
synced 2024-12-28 06:30:16 +00:00
Merge pull request #2342 from colinleroy/rework-mktime
Rework time functions a bit
This commit is contained in:
commit
e1065d0c89
22
libsrc/common/_is_leap_year.h
Normal file
22
libsrc/common/_is_leap_year.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
** _is_leap_year.h
|
||||
**
|
||||
** (C) Copyright 2024, Colin Leroy-Mira <colin@colino.net>
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef __IS_LEAP_YEAR_H
|
||||
#define __IS_LEAP_YEAR_H
|
||||
|
||||
|
||||
|
||||
unsigned char __fastcall__ IsLeapYear (unsigned char Year);
|
||||
/* Returns 1 if the given year is a leap year. Expects a year from 0 to 206,
|
||||
* without 1900 added */
|
||||
|
||||
|
||||
|
||||
/* End of _is_leap_year.h */
|
||||
#endif
|
23
libsrc/common/_is_leap_year.s
Normal file
23
libsrc/common/_is_leap_year.s
Normal file
@ -0,0 +1,23 @@
|
||||
;
|
||||
; Colin Leroy-Mira, 2024
|
||||
;
|
||||
; unsigned char __fastcall__ IsLeapYear (unsigned char Year)
|
||||
; Returns 1 in A if the given year is a leap year. Expects a year from 0 to 206,
|
||||
; without 1900 added.
|
||||
;
|
||||
|
||||
.export _IsLeapYear
|
||||
|
||||
_IsLeapYear:
|
||||
ldx #$00 ; Prepare X for rts
|
||||
cmp #$00 ; Y 0 (1900) is not a leap year
|
||||
beq NotLeap
|
||||
cmp #$C8 ; Y 200 (2100) is not a leap year
|
||||
beq NotLeap
|
||||
and #$03 ; Year % 4 == 0 means leap year
|
||||
bne NotLeap
|
||||
lda #$01 ; Return 1
|
||||
rts
|
||||
NotLeap:
|
||||
lda #$00 ; Return 0
|
||||
rts
|
@ -42,18 +42,9 @@
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
struct tm* __fastcall__ gmtime (const time_t* timep)
|
||||
struct tm* __fastcall__ _time_t_to_tm (const time_t t)
|
||||
{
|
||||
static struct tm timebuf;
|
||||
time_t t;
|
||||
|
||||
/* Check the argument */
|
||||
if (timep == 0 || (long) (t = *timep) < 0) {
|
||||
/* Invalid arg */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Since our ints are just 16 bits, split the given time into seconds,
|
||||
** hours and days. Each of the values will fit in a 16 bit variable.
|
||||
** The mktime routine will then do the rest.
|
20
libsrc/common/gmtime.s
Normal file
20
libsrc/common/gmtime.s
Normal file
@ -0,0 +1,20 @@
|
||||
;
|
||||
; Colin Leroy-Mira, 2024
|
||||
;
|
||||
; struct tm* __fastcall__ gmtime (const time_t* timep);
|
||||
;
|
||||
|
||||
.export _gmtime
|
||||
.import __time_t_to_tm
|
||||
.import ldeaxi
|
||||
|
||||
_gmtime:
|
||||
cpx #$00 ; Check for null pointer
|
||||
bne :+
|
||||
cmp #$00
|
||||
beq no_pointer
|
||||
: jsr ldeaxi ; Load value from pointer
|
||||
jmp __time_t_to_tm ; Convert it
|
||||
|
||||
no_pointer:
|
||||
rts ; A/X already set
|
@ -1,60 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* localtime.c */
|
||||
/* */
|
||||
/* Convert calendar time into broken down local time */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* (C) 2002 Ullrich von Bassewitz */
|
||||
/* Wacholderweg 14 */
|
||||
/* D-70597 Stuttgart */
|
||||
/* EMail: uz@musoftware.de */
|
||||
/* */
|
||||
/* */
|
||||
/* This software is provided 'as-is', without any expressed or implied */
|
||||
/* warranty. In no event will the authors be held liable for any damages */
|
||||
/* arising from the use of this software. */
|
||||
/* */
|
||||
/* Permission is granted to anyone to use this software for any purpose, */
|
||||
/* including commercial applications, and to alter it and redistribute it */
|
||||
/* freely, subject to the following restrictions: */
|
||||
/* */
|
||||
/* 1. The origin of this software must not be misrepresented; you must not */
|
||||
/* claim that you wrote the original software. If you use this software */
|
||||
/* in a product, an acknowledgment in the product documentation would be */
|
||||
/* appreciated but is not required. */
|
||||
/* 2. Altered source versions must be plainly marked as such, and must not */
|
||||
/* be misrepresented as being the original software. */
|
||||
/* 3. This notice may not be removed or altered from any source */
|
||||
/* distribution. */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
#include <time.h>
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
struct tm* __fastcall__ localtime (const time_t* timep)
|
||||
{
|
||||
time_t t;
|
||||
|
||||
/* Check for a valid time spec */
|
||||
if (timep == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the time and correct for the time zone offset */
|
||||
t = *timep + _tz.timezone;
|
||||
|
||||
/* Use gmtime for conversion */
|
||||
return gmtime (&t);
|
||||
}
|
29
libsrc/common/localtime.s
Normal file
29
libsrc/common/localtime.s
Normal file
@ -0,0 +1,29 @@
|
||||
;
|
||||
; Colin Leroy-Mira, 2024
|
||||
;
|
||||
; struct tm* __fastcall__ localtime (const time_t* timep);
|
||||
;
|
||||
|
||||
.export _localtime
|
||||
.import __time_t_to_tm, __tz
|
||||
.import ldeaxi, tosaddeax, pusheax
|
||||
.importzp sreg
|
||||
|
||||
_localtime:
|
||||
cpx #$00 ; Check for null pointer
|
||||
bne :+
|
||||
cmp #$00
|
||||
beq no_pointer
|
||||
: jsr ldeaxi ; Load value
|
||||
jsr pusheax ; Push it
|
||||
lda __tz+1+3
|
||||
sta sreg+1
|
||||
lda __tz+1+2
|
||||
sta sreg
|
||||
ldx __tz+1+1
|
||||
lda __tz+1
|
||||
jsr tosaddeax ; Add _tz.timezone
|
||||
jmp __time_t_to_tm ; Convert to struct tm
|
||||
|
||||
no_pointer:
|
||||
rts ; A/X already set
|
@ -36,7 +36,7 @@
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "_is_leap_year.h"
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -67,14 +67,6 @@ static const unsigned MonthDays [] = {
|
||||
|
||||
|
||||
|
||||
static unsigned char __fastcall__ IsLeapYear (unsigned Year)
|
||||
/* Returns 1 if the given year is a leap year */
|
||||
{
|
||||
return (((Year % 4) == 0) && ((Year % 100) != 0 || (Year % 400) == 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
time_t __fastcall__ mktime (register struct tm* TM)
|
||||
/* Make a time in seconds since 1/1/1970 from the broken down time in TM.
|
||||
** A call to mktime does also correct the time in TM to contain correct
|
||||
@ -82,13 +74,13 @@ time_t __fastcall__ mktime (register struct tm* TM)
|
||||
*/
|
||||
{
|
||||
register div_t D;
|
||||
int Max;
|
||||
unsigned DayCount;
|
||||
static int Max;
|
||||
static unsigned DayCount;
|
||||
|
||||
/* Check if TM is valid */
|
||||
if (TM == 0) {
|
||||
/* Invalid data */
|
||||
goto Error;
|
||||
return (time_t) -1L;
|
||||
}
|
||||
|
||||
/* Adjust seconds. */
|
||||
@ -96,27 +88,29 @@ time_t __fastcall__ mktime (register struct tm* TM)
|
||||
TM->tm_sec = D.rem;
|
||||
|
||||
/* Adjust minutes */
|
||||
if (TM->tm_min + D.quot < 0) {
|
||||
goto Error;
|
||||
}
|
||||
TM->tm_min += D.quot;
|
||||
D = div (TM->tm_min, 60);
|
||||
TM->tm_min = D.rem;
|
||||
|
||||
/* Adjust hours */
|
||||
if (TM->tm_hour + D.quot < 0) {
|
||||
goto Error;
|
||||
}
|
||||
TM->tm_hour += D.quot;
|
||||
D = div (TM->tm_hour, 24);
|
||||
TM->tm_hour = D.rem;
|
||||
|
||||
/* Adjust days */
|
||||
if (TM->tm_mday + D.quot < 0) {
|
||||
goto Error;
|
||||
}
|
||||
TM->tm_mday += D.quot;
|
||||
|
||||
/* Adjust year */
|
||||
while (1) {
|
||||
Max = 365UL + IsLeapYear (TM->tm_year);
|
||||
if ((unsigned int)TM->tm_mday > Max) {
|
||||
++TM->tm_year;
|
||||
TM->tm_mday -= Max;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjust month and year. This is an iterative process, since changing
|
||||
** the month will change the allowed days for this month.
|
||||
*/
|
||||
@ -125,20 +119,17 @@ time_t __fastcall__ mktime (register struct tm* TM)
|
||||
/* Make sure, month is in the range 0..11 */
|
||||
D = div (TM->tm_mon, 12);
|
||||
TM->tm_mon = D.rem;
|
||||
if (TM->tm_year + D.quot < 0) {
|
||||
goto Error;
|
||||
}
|
||||
TM->tm_year += D.quot;
|
||||
|
||||
/* Now check if mday is in the correct range, if not, correct month
|
||||
** and eventually year and repeat the process.
|
||||
*/
|
||||
if (TM->tm_mon == FEBRUARY && IsLeapYear (TM->tm_year + 1900)) {
|
||||
if (TM->tm_mon == FEBRUARY && IsLeapYear (TM->tm_year)) {
|
||||
Max = 29;
|
||||
} else {
|
||||
Max = MonthLength[TM->tm_mon];
|
||||
}
|
||||
if (TM->tm_mday > Max) {
|
||||
if ((unsigned int)TM->tm_mday > Max) {
|
||||
/* Must correct month and eventually, year */
|
||||
if (TM->tm_mon == DECEMBER) {
|
||||
TM->tm_mon = JANUARY;
|
||||
@ -157,19 +148,27 @@ time_t __fastcall__ mktime (register struct tm* TM)
|
||||
** year.
|
||||
*/
|
||||
TM->tm_yday = MonthDays[TM->tm_mon] + TM->tm_mday - 1;
|
||||
if (TM->tm_mon > FEBRUARY && IsLeapYear (TM->tm_year + 1900)) {
|
||||
if (TM->tm_mon > FEBRUARY && IsLeapYear (TM->tm_year)) {
|
||||
++TM->tm_yday;
|
||||
}
|
||||
|
||||
/* Calculate days since 1/1/1970. In the complete epoch (1/1/1970 to
|
||||
** somewhere in 2038) all years dividable by 4 are leap years, so
|
||||
** dividing by 4 gives the days that must be added cause of leap years.
|
||||
** somewhere in 2106) all years dividable by 4 are leap years(1),
|
||||
** so dividing by 4 gives the days that must be added because of leap years.
|
||||
** (and the last leap year before 1970 was 1968)
|
||||
** (1): Exception on 2100, which is not leap, and handled just after.
|
||||
*/
|
||||
DayCount = ((unsigned) (TM->tm_year-70)) * 365U +
|
||||
(((unsigned) (TM->tm_year-(68+1))) / 4) +
|
||||
TM->tm_yday;
|
||||
|
||||
/* Handle the 2100 exception */
|
||||
if (TM->tm_year == 200 && TM->tm_mon > FEBRUARY) {
|
||||
DayCount--;
|
||||
} else if (TM->tm_year > 200) {
|
||||
DayCount--;
|
||||
}
|
||||
|
||||
/* Calculate the weekday */
|
||||
TM->tm_wday = (JAN_1_1970 + DayCount) % 7;
|
||||
|
||||
@ -182,11 +181,4 @@ time_t __fastcall__ mktime (register struct tm* TM)
|
||||
((unsigned) TM->tm_min) * 60U +
|
||||
((unsigned) TM->tm_sec) -
|
||||
_tz.timezone;
|
||||
|
||||
Error:
|
||||
/* Error exit */
|
||||
return (time_t) -1L;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
84
test/val/lib_common_gmtime_localtime.c
Normal file
84
test/val/lib_common_gmtime_localtime.c
Normal file
@ -0,0 +1,84 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
int fails = 0;
|
||||
|
||||
time_t timestamps[] = {
|
||||
0,
|
||||
0x2FFFFFFF,
|
||||
0x6FFFFFFF,
|
||||
0xF48656FF,
|
||||
0xF4865700,
|
||||
0xFC5A3EFF,
|
||||
0x6D6739FF,
|
||||
0x6D673A00,
|
||||
0xFFFFFFFF,
|
||||
};
|
||||
|
||||
/* Values checked against glibc 2.37's implementation of ctime() */
|
||||
const char *dates_gmt[] = {
|
||||
"Thu Jan 1 00:00:00 1970\n",
|
||||
"Sun Jul 9 16:12:47 1995\n",
|
||||
"Wed Jul 18 05:49:51 2029\n",
|
||||
"Thu Dec 31 23:59:59 2099\n",
|
||||
"Fri Jan 1 00:00:00 2100\n",
|
||||
"Fri Feb 29 23:59:59 2104\n",
|
||||
"Tue Feb 29 23:59:59 2028\n",
|
||||
"Wed Mar 1 00:00:00 2028\n",
|
||||
"Sun Feb 7 06:28:15 2106\n",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *dates_gmt_plus_one[] = {
|
||||
"Thu Jan 1 01:00:00 1970\n",
|
||||
"Sun Jul 9 17:12:47 1995\n",
|
||||
"Wed Jul 18 06:49:51 2029\n",
|
||||
"Fri Jan 1 00:59:59 2100\n",
|
||||
"Fri Jan 1 01:00:00 2100\n",
|
||||
"Sat Mar 1 00:59:59 2104\n",
|
||||
"Wed Mar 1 00:59:59 2028\n",
|
||||
"Wed Mar 1 01:00:00 2028\n",
|
||||
"Thu Jan 1 00:59:59 1970\n",
|
||||
NULL
|
||||
};
|
||||
|
||||
int main (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; dates_gmt[i] != NULL; i++) {
|
||||
struct tm *tm;
|
||||
char *str;
|
||||
|
||||
/* Check gmtime */
|
||||
tm = gmtime(×tamps[i]);
|
||||
str = asctime(tm);
|
||||
if (strcmp(str, dates_gmt[i])) {
|
||||
fails++;
|
||||
printf("gmtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n",
|
||||
timestamps[i], dates_gmt[i], str);
|
||||
}
|
||||
|
||||
/* Check localtime with UTC timezone */
|
||||
_tz.timezone = 0;
|
||||
tm = localtime(×tamps[i]);
|
||||
str = asctime(tm);
|
||||
if (strcmp(str, dates_gmt[i])) {
|
||||
fails++;
|
||||
printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n",
|
||||
timestamps[i], dates_gmt[i], str);
|
||||
}
|
||||
|
||||
/* Check localtime at UTC+1 */
|
||||
_tz.timezone = 3600;
|
||||
tm = localtime(×tamps[i]);
|
||||
str = asctime(tm);
|
||||
if (strcmp(str, dates_gmt_plus_one[i])) {
|
||||
fails++;
|
||||
printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n",
|
||||
timestamps[i], dates_gmt_plus_one[i], str);
|
||||
}
|
||||
}
|
||||
return fails;
|
||||
}
|
61
test/val/lib_common_mktime.c
Normal file
61
test/val/lib_common_mktime.c
Normal file
@ -0,0 +1,61 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
int fails = 0;
|
||||
|
||||
time_t timestamps[] = {
|
||||
0,
|
||||
0x2FFFFFFF,
|
||||
0x6FFFFFFF,
|
||||
0xF48656FF,
|
||||
0xF4865700,
|
||||
0xFC5A3EFF,
|
||||
0x6D6739FF,
|
||||
0x6D673A00,
|
||||
0xFFFFFFFF,
|
||||
};
|
||||
|
||||
/* Values checked against glibc 2.37's implementation of ctime() */
|
||||
const char *dates[] = {
|
||||
"Thu Jan 1 00:00:00 1970\n",
|
||||
"Sun Jul 9 16:12:47 1995\n",
|
||||
"Wed Jul 18 05:49:51 2029\n",
|
||||
"Thu Dec 31 23:59:59 2099\n",
|
||||
"Fri Jan 1 00:00:00 2100\n",
|
||||
"Fri Feb 29 23:59:59 2104\n",
|
||||
"Tue Feb 29 23:59:59 2028\n",
|
||||
"Wed Mar 1 00:00:00 2028\n",
|
||||
"Sun Feb 7 06:28:15 2106\n",
|
||||
NULL
|
||||
};
|
||||
|
||||
int main (void)
|
||||
{
|
||||
struct tm tm;
|
||||
time_t t;
|
||||
int i;
|
||||
|
||||
/* Verify conversion both ways */
|
||||
for (t = 0x0FFFFFFF; ; t += 0x10000000) {
|
||||
struct tm *tm = gmtime(&t);
|
||||
time_t r = mktime(tm);
|
||||
if (t != r) {
|
||||
fails++;
|
||||
printf("Unexpected result for t %lx: %lx\n", t, r);
|
||||
}
|
||||
if (t == 0xFFFFFFFF) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; dates[i] != NULL; i++) {
|
||||
char *str = ctime(×tamps[i]);
|
||||
if (strcmp(str, dates[i])) {
|
||||
fails++;
|
||||
printf("Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n",
|
||||
timestamps[i], dates[i], str);
|
||||
}
|
||||
}
|
||||
return fails;
|
||||
}
|
Loading…
Reference in New Issue
Block a user