Blog. Just Blog

Полезные функции для работы с датами на Ассемблере

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Полезные функции для работы с датами на Ассемблере
Полезные функции для работы с датами на Ассемблере

Из разных источников насобирал различные полезные функции для работы с датами на Ассемблере. Преимущества их в том, что они работают очень быстро, хорошо оптимизированы и позволяют обходиться вообще без вызова системных функций. Все функции самодостаточные и не требуют для работы каких-либо дополнительных данных.

Функция для определения является ли год високосным. Как известно, високосным годом является такой год, продолжительность которого равна 366 дням - на одни сутки больше продолжительности обычного. Год является високосным в двух случаях: либо он кратен 4, но при этом не кратен 100, либо кратен 400. Год не является високосным, если он не кратен 4, либо он кратен 100, но при этом не кратен 400.
  1. ;-------------------------------------------------------------
  2. ; Функция определения является ли год високосным
  3. ;-------------------------------------------------------------
  4. ; Параметры: год
  5. ; На выходе:
  6. ;       EAX = 1 - год високосный
  7. ;       EAX = 0 - год невисокосный
  8. ;-------------------------------------------------------------
  9. proc    IsLeapYear year:DWORD
  10.         push    edx ebx
  11.         xor     edx,edx
  12.         mov     eax,[year]
  13.         mov     ebx,100
  14.         div     ebx
  15.  
  16.         or      edx,edx
  17.         jz      @f
  18.         mov     eax,edx
  19. @@:
  20.         shr     eax,1
  21.         jc      @f
  22.         shr     eax,1
  23. @@:
  24.         setnc   al
  25.         movzx   eax,al
  26.         pop     ebx edx
  27.         ret
  28. endp
Функция получения дня недели для любой даты. В природе есть несколько вариантов алгоритмов для решения этой задачи, здесь используется алгоритм Tomohiko Sakamoto. После выполнения в регистре EAX будет записан номер день недели от 0 до 6, нумерация начинается с воскресенья.
  1. ;-------------------------------------------------------------
  2. ; Функция вычисления дня недели для любой даты
  3. ; Автор: Lingo
  4. ;-------------------------------------------------------------
  5. ; На основе алгоритма Tomohiko Sakamoto
  6. ; int dayofweek (int d, int m, int y) {
  7. ;     static int t[] = {0,3,2,5,0,3,5,1,4,6,2,4};
  8. ;     y -= m < 3;
  9. ;     return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
  10. ; }
  11. ;-------------------------------------------------------------
  12. ; Параметры: год, месяц, день
  13. ; На выходе:
  14. ;       EAX = день недели (0-6, 0 = воскресенье)
  15. ;-------------------------------------------------------------
  16. proc    DayOfWeek year:DWORD, month:DWORD, day:DWORD
  17.         push    ebx ecx edx
  18.  
  19.         mov     ecx,[month]
  20.         mov     edx,[year]
  21.         cmp     ecx,3
  22.         sbb     edx,0
  23.         mov     ecx,[.monthdata+ecx*4-4]
  24.         mov     eax,edx
  25.         imul    edx,0A3D8h
  26.         shr     edx,22
  27.         add     ecx,[day]
  28.         add     ecx,eax
  29.         shr     eax,2
  30.         add     eax,ecx
  31.         sub     eax,edx
  32.         shr     edx,2
  33.         add     eax,edx
  34.         mov     ecx,eax
  35.         mov     edx,24924925h
  36.         mul     edx
  37.         mov     eax,ecx
  38.         lea     ecx,[edx+edx*2]
  39.         lea     edx,[ecx+edx*4]
  40.         sub     eax,edx
  41.  
  42.         pop     edx ecx ebx
  43.         ret
  44.  
  45.         .monthdata dd 0,3,2,5,0,3,5,1,4,6,2,4
  46. endp
Функция вычисления порядкового номера любого дня в году с учетом високосности года. Очень удобно для нахождения разницы во времени между двумя событиями одного года или определения количества дней, оставшихся до определенной даты.
  1. ;-------------------------------------------------------------
  2. ; Функция вычисления порядкового номера любого дня в году
  3. ; Автор: micmic
  4. ;-------------------------------------------------------------
  5. ; Параметры: год, месяц, день
  6. ; На выходе:
  7. ;       EAX = порядковый номер дня (1-366)
  8. ;-------------------------------------------------------------
  9. proc    DayOfYear year:DWORD, month:DWORD, day:DWORD
  10.         push    ebx ecx edx
  11.  
  12.         mov     eax,[year]
  13.         mov     ecx,[month]
  14.         mov     edx,[day]
  15.  
  16.         dec     ecx
  17.         mov     ebx,ecx
  18.         imul    ecx,30
  19.         test    ebx,9
  20.         jnz     @f
  21.         dec     ecx
  22. @@:
  23.         shr     ebx,1
  24.         jnz     @f
  25.         add     ecx,2
  26. @@:
  27.         add     ecx,ebx
  28.         cmp     ecx,32
  29.         lea     ecx,[ecx+edx-1]
  30.         jna     .loc_done
  31.         test    eax,3
  32.         jnz     .loc_done
  33.         xor     edx,edx
  34.         mov     ebx,100
  35.         div     ebx
  36.         cmp     edx,0
  37.         jnz     @f
  38.         and     eax,3
  39.         jnz     .loc_done
  40. @@:
  41.         inc     ecx
  42. .loc_done:
  43.         mov     eax, ecx
  44.  
  45.         pop     edx ecx ebx
  46.         ret
  47. endp
Штатными средствами Windows невозможно одной операцией пересчитать четырехбайтный TIMESTAMP в человекопонятную дату. Для этого я написал вот такую функцию. По умолчанию в ней подразумевается, что отсчет TIMESTAMP начинается с полуночи 1 января 1900 года.
  1. ;-----------------------------------------------------------
  2. ; Функция перевода TIMESTAMP в дату
  3. ; by ManHunter / PCL
  4. ; http://www.manhunter.ru
  5. ;-----------------------------------------------------------
  6. ; Параметры:
  7. ;   dTimestamp - значение TIMESTAMP в секундах от 01.01.1900
  8. ;   lpSTime - указатель на структуру SYSTEMTIME
  9. ; На выходе:
  10. ;   заполненная структура SYSTEMTIME
  11. ;-----------------------------------------------------------
  12. proc    timestamp_to_date dTimestamp:DWORD, lpSTime:DWORD
  13.         locals
  14.             ftime FILETIME
  15.         endl
  16.  
  17.         pusha
  18.  
  19.         mov     eax,[lpSTime]
  20.         mov     [eax+SYSTEMTIME.wYear],1900
  21.         mov     [eax+SYSTEMTIME.wMonth],1
  22.         mov     [eax+SYSTEMTIME.wDay],1
  23.         mov     [eax+SYSTEMTIME.wHour],0
  24.         mov     [eax+SYSTEMTIME.wMinute],0
  25.         mov     [eax+SYSTEMTIME.wSecond],0
  26.         lea     ebx,[ftime]
  27.         invoke  SystemTimeToFileTime,eax,ebx
  28.  
  29.         mov     eax,[dTimestamp]
  30.         xor     edx,edx
  31.         mov     ecx,989680h
  32.         mul     ecx
  33.  
  34.         add     [ftime.dwLowDateTime],eax
  35.         adc     [ftime.dwHighDateTime],edx
  36.  
  37.         invoke  FileTimeToSystemTime,ebx,[lpSTime]
  38.  
  39.         popa
  40.         ret
  41. endp
Обратная функция - преобразование даты и времени в TIMESTAMP. Требуется заполненная структура SYSTEMTIME, на выходе - количество секунд, прошедшее с полуночи 1 января 1900 года до заданной даты.
  1. ;-----------------------------------------------------------
  2. ; Функция перевода даты в TIMESTAMP
  3. ; by ManHunter / PCL
  4. ; http://www.manhunter.ru
  5. ;-----------------------------------------------------------
  6. ; Параметры:
  7. ;   lpSTime - указатель на заполненную структуру SYSTEMTIME
  8. ; На выходе:
  9. ;   EAX = значение TIMESTAMP в секундах от 01.01.1900
  10. ;-----------------------------------------------------------
  11. proc    date_to_timestamp lpSTime:DWORD
  12.         locals
  13.                 ftime FILETIME
  14.                 ztime SYSTEMTIME
  15.                 dTimestamp dd ?
  16.         endl
  17.  
  18.         pusha
  19.  
  20.         lea     ebx,[ftime]
  21.         invoke  SystemTimeToFileTime,[lpSTime],ebx
  22.         push    [ftime.dwHighDateTime]
  23.         push    [ftime.dwLowDateTime]
  24.  
  25.         lea     eax,[ztime]
  26.         mov     [eax+SYSTEMTIME.wYear],1900
  27.         mov     [eax+SYSTEMTIME.wMonth],1
  28.         mov     [eax+SYSTEMTIME.wDay],1
  29.         mov     [eax+SYSTEMTIME.wHour],0
  30.         mov     [eax+SYSTEMTIME.wMinute],0
  31.         mov     [eax+SYSTEMTIME.wSecond],0
  32.         invoke  SystemTimeToFileTime,eax,ebx
  33.  
  34.         pop     eax
  35.         sub     eax,[ftime.dwLowDateTime]
  36.         pop     edx
  37.         sbb     edx,[ftime.dwHighDateTime]
  38.  
  39.         mov     ecx,989680h
  40.         div     ecx
  41.         mov     [dTimestamp],eax
  42.  
  43.         popa
  44.  
  45.         mov     eax,[dTimestamp]
  46.         ret
  47. endp
Для проверки корректности даты можно воспользоваться следующим трюком с функцией SystemTimeToFileTime. В структуру SYSTEMTIME заносим проверяемую дату и затем пытаемся ее конвертировать. При несуществующей дате функция вернет ошибку.
  1. stime   SYSTEMTIME
  2. ftime   FILETIME
  3.         ...
  4.         ; Проверяемая дата
  5.         mov     [stime.wYear],2023
  6.         mov     [stime.wMonth],2
  7.         mov     [stime.wDay],30   ; <--- ошибочное значение
  8.         invoke  SystemTimeToFileTime,stime,ftime
  9.         or      eax,eax
  10.         ; EAX=1 - дата корректная
  11.         ; EAX=0 - дата неправильная
  12.         jz      incorrect_date
На этом же принципе можно сделать функцию для определения количества дней в любом месяце любого года. Проверяем корректность даты, начиная с 31-го числа по убыванию до момента, пока дата не станет валидной.
  1. ;-----------------------------------------------------------
  2. ; Функция получения количества дней в меняце
  3. ; by ManHunter / PCL
  4. ; https://www.manhunter.ru
  5. ;-----------------------------------------------------------
  6. ; Параметры:
  7. ;   dYear  - год
  8. ;   dMonth - месяц
  9. ; На выходе:
  10. ;   EAX = количество дней в месяце
  11. ;-----------------------------------------------------------
  12. proc days_in_month dYear:DWORD,dMonth:DWORD
  13.         locals
  14.             stime   SYSTEMTIME
  15.             ftime   FILETIME
  16.         endl
  17.  
  18.         pusha
  19.  
  20.         lea     esi,[stime]
  21.         lea     edi,[ftime]
  22.  
  23.         mov     eax,[dYear]
  24.         mov     [esi+SYSTEMTIME.wYear],ax
  25.         mov     eax,[dMonth]
  26.         mov     [esi+SYSTEMTIME.wMonth],ax
  27.  
  28.         mov     ebx,32
  29. @@:
  30.         dec     ebx
  31.         or      ebx,ebx
  32.         jz      @f
  33.  
  34.         mov     [esi+SYSTEMTIME.wDay],bx
  35.         invoke  SystemTimeToFileTime,esi,edi
  36.         or      eax,eax
  37.         jz      @b
  38. @@:
  39.         mov     [esp+28],ebx
  40.  
  41.         popa
  42.         ret
  43. endp
В приложении исходники и примеры программ с использованием всех описанных в статье функций.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

Usefull.Date.Functions.Demo.zip (7,879 bytes)

Уже известный вам программист Николай Гайдук разработал замечательную библиотеку календарных функций для юлианского и григорианского календарей. Она написана на FASM, отлично документирована, и дает программисту многие возможности: обслуживает даты с периода длиной сверх 11 миллионов лет (с 1 января 5843880 года до н.э. по 3 августа 5915100 года для юлианского календаря, с 30 декабря 5844001 года до н.э. по 17 января 5915222 года для григорианского календаря), свободная конверсия дат между календарями юлианском и григорианском в упомянутом периоде, определение дня недели для данной даты, вычисление номера дня в году для данных номеров месяца и дня в месяце, определение високосности года в данном календаре, вычисление "абсолютного" номера дня соответствующего данной дате, благодаря чему можно легко рассчитывать количество дней между двумя датами. Скачать библиотеку с исходником и документацией можно с офсайта или по ссылке ниже.

Библиотека календарных функций (автор Николай Гайдук)Библиотека календарных функций (автор Николай Гайдук)

Calendar.zip (132,787 bytes)

Если у вас есть еще какие-нибудь интересные функции для работы с календарем или датами, то оставляйте в комментариях, я с удовольствием дополню статью.

Поделиться ссылкой ВКонтакте
Просмотров: 8816 | Комментариев: 4

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (24.11.2019 в 16:41):
Ну так подключи, в чем проблема.
Олег (28.09.2019 в 19:23):
Не работает.
Не подключены библиотеки
ManHunter (21.01.2016 в 00:19):
Написал и добавил функцию перевода TIMESTAMP в дату и обратную ей функцию перевода даты в TIMESTAMP. Архив с исходниками обновлен.
Grey (04.06.2014 в 11:38):
Спасибо.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2025
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0075 сек.) / Память: 4.5 Mb
Наверх