Быстрый ввод даты и времени без разделителей
Если Вам часто приходится вводить даты и время в ячейки, то Вам должна понравиться идея писать их сокращенно, без точек-дробей-двоеточий - просто как число. Чтобы в заданном диапазоне ячеек листа, например, число 250699 автоматически превращалось в 25.06.1999, а 1125 в 11:25.
Для этого щелкните по ярлычку листа, куда будут вводиться даты и время и выберите команду Исходный текст (Source Code). В открывшееся окно редактора Visual Basic скопируйте и вставьте следующий код:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim vVal
Dim StrVal As String
Dim dDate As Date
If Target.Cells.Count > 1 Then Exit Sub
If Not Intersect(Target, Range("A2:A10")) Is Nothing Then
With Target
StrVal = Format(.Text, "000000")
If IsNumeric(StrVal) And Len(StrVal) = 6 Then
Application.EnableEvents = False
dDate = DateValue(Left(StrVal, 2) & "/" & Mid(StrVal, 3, 2) & "/" & Right(StrVal, 2))
.NumberFormat = "dd/mm/yyyy"
.Value = CDate(DateSerial(Year(dDate), Month(dDate), Day(dDate)))
End If
End With
End If
If Not Intersect(Target, Range("B2:B10")) Is Nothing Then
With Target
vVal = Format(.Value, "0000")
If IsNumeric(vVal) And Len(vVal) = 4 Then
Application.EnableEvents = False
.Value = Left(vVal, 2) & ":" & Right(vVal, 2)
.NumberFormat = "[h]:mm"
End If
End With
End If
Application.EnableEvents = True
End Sub
Диапазоны A2:A10 и B2:B10 замените на свои области листа, куда будут подобным образом вводиться даты и время, соответственно.
Ссылки по теме
- Всплывающий календарь DatePicker для быстрого ввода дат в ячейки
- Всплывающий календарь в надстройке PLEX
- Автоматический ввод даты при занесении данных в ячейку
- Что такое макросы, куда вставлять код макроса на VBA, как их использовать.
И что поразительно у меня это работает!!!
На работе этот макрос используем на нескольких компьтерах ошибка «runtimeerror ‘13’ typemismatch» появляется при ошибочном внесении например «1»или «131312».
После этого макрос перестает работать. Макрос включается только после перезапускаExcel.
Я не могу разобраться почему так происходит.
Подскажите,что нужно сделать что бы:
1. -макрос снова начинал работать без перезапуска Excel
2. - не появлялась на экране ошибка «runtimeerror ‘13’ typemismatch» которая приводит в панику некоторых пользователей.
3. - и посмотрите пожалуйста макрос предложенный Вами чуть чуть мной переделанный. Какие в нем недостатки. В VBA профан за ранее извеняюсь за назойливость.
Private Sub Worksheet_Change(ByVal Target As Range) Dim vVal Dim StrVal As String Dim dDate As Date If Target.Cells.Count > 1 Then Exit Sub If Not Intersect(Target, Range("A2:A10")) Is Nothing Then With Target StrVal = Format(.Text, "000000") If IsNumeric(StrVal) And Len(StrVal) = 6 Then Application.EnableEvents = False End If On Error GoTo ErrorHandlerDivision dDate = DateValue(Left(StrVal, 2) & "/" & Mid(StrVal, 3, 2) & "/" & Right(StrVal, 2)) .NumberFormat = "dd/mm/yyyy" .Value = CDate(DateSerial(Year(dDate), Month(dDate), Day(dDate))) ErrorHandlerDivision: End With End If If Not Intersect(Target, Range("B2:B10")) Is Nothing Then With Target vVal = Format(.Value, "0000") If IsNumeric(vVal) And Len(vVal) = 4 Then Application.EnableEvents = False .Value = Left(vVal, 2) & ":" & Right(vVal, 2) .NumberFormat = "[h]:mm" End If End With End If Application.EnableEvents = True End Sub(файл в котором у меня ошибка прикрепить не могу не нахожу кнопки "прикрепить файл"
А зависание макроса происходит, скорее всего, из за того, что вы в коде отключаете обработку событий Application.EnableEvents=False, а после возникновения ошибки она у вас обратно не включается.
а как переделать макрос для времени, если вводится формат 21.15 (ну или любой другой знак, всякое бывает), ну а нужен обычный 21:15
12 строка подсвечивается для отладки, когда макрос перестаёт работать.
Это происходит, как я заметил, в случае:
1. Сортировки таблицы, несколько столбцов которой заполнены при помощи данного макроса датой и временем.
2. После ошибочного ввода в рабочем диапазоне макроса даты с разделителями, удаления ошибчной записи и последующей попытки заполнить ячейку не применяя разделители, при помощи макроса.
для даты:
в формате ячейки -> все форматы 00\.00\.0000
пример:
12122012 -> 12.12.2012
для времени -> все форматы 00\:00
пример:
1212 -> 12:12
после слеша влево можно поставить любой знак, который предпочтителен для Вас.
Надеюсь Вам понравится такая идея.
если у кого будут вопросы или захотите сказать спасибо, я в facebook Parviz Ruziev
удачи всем!!!
Пример
В формате ячейки (у меня это D2) выставляем формат типа:
00"."00"."0000
На проверку вводимых данных задаем условия проверки:
тип данных – другой; и на соответствие формуле в соответствующей ячейке (у меня это R2).
Формула такого вида:
=ЕСЛИ(ИЛИ(И(ДЛСТР(D2)=7;ДАТАЗНАЧ(ТЕКСТ(ДАТА(ПРАВСИМВ(D2;4);ПСТР(D2;2;2);СЦЕПИТЬ("0";ЛЕВСИМВ(D2)));"ДД.ММ.ГГГГ";))>=25569;ДАТАЗНАЧ(ТЕКСТ(ДАТА(ПРАВСИМВ(D2;4);ПСТР(D2;2;2);СЦЕПИТЬ("0";ЛЕВСИМВ(D2)));"ДД.ММ.ГГГГ";))<=44196;ЗНАЧЕН(ТЕКСТ(ПСТР(D2;2;2);"00";))<=12);И(ДЛСТР(D2)=8;ДАТАЗНАЧ(ТЕКСТ(ДАТА(ПРАВСИМВ(D2;4);ПСТР(D2;3;2);ЛЕВСИМВ(D2;2));"ДД.ММ.ГГГГ";))>=25569;ДАТАЗНАЧ(ТЕКСТ(ДАТА(ПРАВСИМВ(D2;4);ПСТР(D2;3;2);ЛЕВСИМВ(D2;2));"ДД.ММ.ГГГГ";))<=44196;ЗНАЧЕН(ТЕКСТ(ПСТР(D2;3;2);"00";))<=12;ИЛИ(ЕСЛИ(И(ИЛИ(ЗНАЧЕН(ТЕКСТ(ПСТР(D2;3;2);"00";))=1;ЗНАЧЕН(ТЕКСТ(ПСТР(D2;3;2);"00";))=3;ЗНАЧЕН(ТЕКСТ(ПСТР(D2;3;2);"00";))=5;ЗНАЧЕН(ТЕКСТ(ПСТР(D2;3;2);"00";))=7;ЗНАЧЕН(ТЕКСТ(ПСТР(D2;3;2);"00";))=8;ЗНАЧЕН(ТЕКСТ(ПСТР(D2;3;2);"00";))=10;ЗНАЧЕН(ТЕКСТ(ПСТР(D2;3;2);"00";))=12;);ЗНАЧЕН(ТЕКСТ(ЛЕВСИМВ(D2;2);"00";))<=31);ИСТИНА;ЛОЖЬ);ЕСЛИ(И(ИЛИ(ЗНАЧЕН(ТЕКСТ(ПСТР(D2;3;2);"00";))=4;ЗНАЧЕН(ТЕКСТ(ПСТР(D2;3;2);"00";))=6;ЗНАЧЕН(ТЕКСТ(ПСТР(D2;3;2);"00";))=9;ЗНАЧЕН(ТЕКСТ(ПСТР(D2;3;2);"00";))=11);ЗНАЧЕН(ТЕКСТ(ЛЕВСИМВ(D2;2);"00";))<=30);ИСТИНА;ЛОЖЬ);ЕСЛИ(И(ЗНАЧЕН(ТЕКСТ(ПСТР(D2;3;2);"00";))=2;ЗНАЧЕН(ТЕКСТ(ЛЕВСИМВ(D2;2);"00";))<=29);ИСТИНА;ЛОЖЬ)))
Не пойму почему так отображает, в выше приведенной формуле смайлики заменить на ")" без кавычек естественно.
Соответственно дата вводится без каких-либо посторонних знаков, только цифры. Формула проверяет что бы введенный диапазон был между 01.01.1970 и 31.12.2020, при вводе большей или меньшей даты – выдает ошибку, при попытке ввести 13 и т.д. месяц – выдает ошибку, при попытке ввести более 31 дня (в январе, марте, мае, июле, августе, октябре, декабре) – выдает ошибку, при попытке ввести более 30 дней (в апреле, июне, сентябре, ноябре) – выдает ошибку, при попытке ввести более 29 дней в феврале – выдает ошибку. Осталось добить проверку високосных лет. )))))
Ну и перевести это все безобразие в дату тоже не проблема, у меня в ячейке I2 идет подсчет даты от введенной в ячейку D2 плюс 2 месяца вот такой формулой:
=ЕСЛИ(ЕПУСТО(D2);""; ЕСЛИ(ДЛСТР(D2)=7; ДАТАМЕС(ДАТА(ПРАВСИМВ(D2;4);ПСТР(D2;2;2);(СЦЕПИТЬ("0";ЛЕВСИМВ(D2))));2); ДАТАМЕС(ДАТА(ПРАВСИМВ(D2;4);ПСТР(D2;3;2);ЛЕВСИМВ(D2;2));2)))
Сразу отвечу на вопрос зачем так все усложнять – просто задача стояла сделать без макросов.
P.S. и год как убрать?
все сделал как у Вас написано, работает все прекрасно, вот только при повторном вводе даты в ячейки где уже были введены описанным Вами способом даты, выводится как ответ совершенно другая дата, т.к. из-за присвоенного автоматически формата "Дата", введенное значение воспринимается как код даты. Подскажите пожалуйста как это исправить.
Заранее благодарю
Подскажите пожалуйста, если мне нужно даты во многих столбцах (B; N;O; S) начиная с 3 строки указывать, как это прописать в макросе?
Прошу прощения за вопрос, в VBA новичок. К сожалению, нет времени чтобы изучить возникшую проблему, необходимо срочно сделать форму отчета кассира КМ-6 (форма типовая, скачана из альбома унифицированных форм в Консультанте). Проблема в том, что в этой форме графы "дата составления" и "время работы" представляют собой объединение нескольких ячеек и изменение диапазона указывает на неправильное событие, если я правильно поняла. Какой должен быть макрос, чтобы эта проблема была устранена?
К сожалению, не удается вставить скрин-шот для наглядности.
Заранее благодарна.
For Each cell In Target 'проходим по всем измененным ячейкам
If Not Intersect(cell, Range("F3:F50200") Is Nothing Then 'если изменененная ячейка попадает в диапазон A2:A100
With cell.Offset(0, 1) 'вводим в соседнюю справа ячейку дату
.Value = Now
.EntireColumn.AutoFit 'выполняем автоподбор ширины для столбца B, чтобы дата умещалась в ячейке
End With
End If
Next cell
End Sub
Если ввожу 10янв14, преобразует в дату 10.01.2014 , формат ячейки становится Custom (все ОК)
Если ввожу 10Jan14, остается в формате General
Пробовал заранее ставить формат ячейки "Date", все равно остается текст 10Jan14
Private Sub Worksheet_Change(ByVal Target As Range) Dim vVal Dim StrVal As String Dim dDate As Date If Target.Cells.Count > 1 Then Exit Sub If Not Intersect(Target, Range("A2:A10")) Is Nothing Then With Target StrVal = Format(.Text, "000000") If IsNumeric(StrVal) And Len(StrVal) = 6 Then Application.EnableEvents = False End If On Error GoTo ErrorHandlerDivision dDate = DateValue(Left(StrVal, 2) & "/" & Mid(StrVal, 3, 2) & "/" & Right(StrVal, 2)) .NumberFormat = "dd/mm/yyyy" .Value = CDate(DateSerial(Year(dDate), Month(dDate), Day(dDate)))ErrorHandlerDivision: End With End If If Not Intersect(Target, Range("K16:AO41")) Is Nothing Then With Target vVal = Format(.Value, "0000")2 If IsNumeric(vVal) And Len(vVal) = 4 Then Application.EnableEvents = False .Value = Left(vVal, 2) & ":" & Right(vVal, 2) .NumberFormat = "[h]:mm" End If End With End If Application.EnableEvents = TrueEnd SubВыскакивать окно Visual Basic при любом редактировании значений.
Дико неудобно, при введении каждого значения приходится закрывать окно VB, дважды нажимая на кнопки.
Один пользователь даже пробовал менять дистрибутивы винды и офиса, переустанавливать – везде одно и то же.
Судя по ошибке у стоит библиотека или скрипт, который позволяет выполнять эту процедуру.
Пожалуйста, помогите исправить.
Второй момент, если вводить дату в формате 15022016, то в ячейке отображаются ###### - бесконечное количество решеток.
Спасибо. Код золотой и очень полезен в работе, но хочется чтобы не было таких ошибок т.к будет активно использоваться в работе. Буду очень благодарен.
Замечательный код, но вот вопрос:
Если я хочу внедрить этот код для подобного способа ввода даты в колонку где у меня уже был изначально какой-то формат ячеек к сожалению, и на какой бы другой формат я их не менял, результата нет положительного, тогда как я могу вернуть "никакой формат ячеек" или может мне что то в коде вашем поменять можно?
ЗЫ более того, я ввожу эти 6 цифр не в саму ячейку, у меня для этого есть код, с помощью которого вылазит диалоговое окно с просьбой ввести данные, этот код ссылается на нужную ячейку где я собственно и хотел бы увидеть дату. Но опять же эти 6 цифр я ввожу в диалоговое окно, для которого написал код.
Если не понятно что я имею в виду говоря про код и диалоговое окно, то вот немного подробнее:
Я настроил свою таблицу для работы максимально удобно, чтобы не возить мышкой и кликать триста раз по ячейкам, я уже в исходном коде листа прописал несколько полезных штук, таким образом, что мне надо только в начале ввести в определенную ячейку данные, после чего запускается код и цепь разных действий в виде диалоговых окон типа:
Диалоговое окно "Введите дату" - это как раз первый запрос - и мне она нужна не текущая, а та что я сам впишу, ибо это дэдлайн проекта. Текущая дата вводится автоматом в другом месте, это я уже благодаря кстати Николаю реализовал пару-тройку лет назад, за что большое спасибо и не только за это)) В общем мне пока приходится вводить дату полностью или писать типа "9 мая" чтобы быстрее было, но мне гораздо удобнее было бы все вводить на нампаде, раз 6 цифр ввел нажал Enter, вылазит следующее диалоговое окно "введите бюджет" ну и так далее, нампад - энтер, нампад-энтер — быстро и удобно, и не надо бегать по строке по разным ячейкам глазами выискивать, тем более риск ввести не туда, а колонок много и перепутать легко.
Это я для полной картины просто написал, чтобы понимали)) а то мало ли, может если способ ввода реализуется с помощью вот таких диалоговых окон, то этот ваш код для ввода даты не годится.
Заранее благодарю всех и Николая в особенности.
Например у меня есть текущая дата 6 мая в ячейке A22, вылазит диалоговое окно с просьбой ввести число на которое будет увеличена дата ячейки A22 и введена в ячейку K22 с учетом прибавления того числа что я ввел в диалоговое окно, например я ввел цифру 3 и в ячейке K22 появилась дата 9 мая, то есть код поссчитал 6+3=9 и ввел нужные данные в ячейку K22 в формате даты "9 мая" или "09.05.2017" - без разницы
Как считаете можно такое провернуть??
If Not Intersect(Target, Range("A2:A10")) Is Nothing ThenDim Num1 As Range Num1 = InputBox("Текст в окошке", "Название окошка") If Not Intersect(Target, Range(Num1)) Is Nothing Thenпробовал менять код, но не особо получается (
Но, я всё же решил немного его доработать, так как при вводе некоторых значений, которые не нравятся скрипту, он вылетал с ошибкой. А именно, например, при попытке ввести двухзначное число в ячейку, он сообщал об ошибке и предлагал либо нажать End, либо Debug. И после этого не важно, что нажать, скрипт переставал работать до перезапуска Excel, так как не успевала отработать функция Application.EnableEvents = True
С новыми поправками таких проблем не возникает. Делюсь:
Private Sub Worksheet_Change(ByVal Target As Range) Dim vVal Dim StrVal As String Dim dDate As Date On Error GoTo ErrorHandler ' Обработка ошибок If Target.Cells.Count > 1 Then Exit Sub If Not Intersect(Target, Range("A2:A10")) Is Nothing Then With Target StrVal = Format(.Text, "000000") If IsNumeric(StrVal) And Len(StrVal) = 6 Then Application.EnableEvents = False dDate = DateValue(Left(StrVal, 2) & "/" & Mid(StrVal, 3, 2) & "/" & Right(StrVal, 2)) .NumberFormat = "dd/mm/yyyy" .Value = CDate(DateSerial(Year(dDate), Month(dDate), Day(dDate))) End If End With End If If Not Intersect(Target, Range("B2:B10")) Is Nothing Then With Target vVal = Format(.Value, "0000") If IsNumeric(vVal) And Len(vVal) = 4 Then Application.EnableEvents = False .Value = Left(vVal, 2) & ":" & Right(vVal, 2) .NumberFormat = "[h]:mm" End If End With End If ExitHandler: ' Возвращение функции Application.EnableEvents Application.EnableEvents = True Exit Sub ErrorHandler: ' Обработка ошибок Resume ExitHandler End Subfunction onEdit(e) { var range = e.range; var sheet = range.getSheet(); if(sheet.getName() === 'Лист1' && ((range.getColumn() === 3 && range.getRow() >= 2 && range.getRow() <= 9999) || (range.getColumn() === 5 && range.getRow() >= 2 && range.getRow() <= 9999))) { var value = e.value; var regex = /^(\d{1}|\d{2})(\d{2})(\d{2}|\d{4})$/; if(regex.test(value)) { var match = regex.exec(value); var day = match[1]; var month = match[2]; if (month[0] === '0') { month = month[1]; } month = parseInt(month) - 1; // уменьшаем значение месяца на единицу, иначе он будет больше чем нужно var year = match[3]; if(year.length === 2) { var current_year = parseInt(new Date().getFullYear().toString().substr(-2)); var input_year = parseInt(year); year = input_year <= current_year + 29 ? '20' + year : '19' + year; // если год введён 2 цифрами, правим на 4 } var date = new Date(year, month, day); date.setDate(date.getDate()+1); // добавляем один день, иначе он будет уменьшен на единицу var dateString = Utilities.formatDate(date, 'GMT', 'dd.MM.yyyy'); sheet.getRange(range.getRow(), range.getColumn()).setValue(dateString); } } }1. название листа,
2. столбцы (в моём случае это 2 столбца С - под номером 3, и E - под номером 5)
3. диапазон (в моём случае со строки 2 по строку 9999)
Если Вам нужен только один столбец, и не нужно указывать конечное значение строк, то код в этой строке можно упростить:
if(sheet.getName() === 'Лист1' && range.getColumn() === 3 && range.getRow() > 1 ) {'Лист1' - название листа
3 - номер столбца - 'С'
>1 - начинать со второй строки (исключаем шапку таблицы)
В данном коде осталась одна недоработка:
Если ввести в ячейку дату как положено с разделителями, а не просто голые цифры, то скрипт изменит введённую дату совершенно на другую. Пока борюсь с этим моментом. Как будет решение, изменю данный пост или напишу дополнение.
function onEdit(e) { var range = e.range; var idRow = e.range.getRow(); // получаем номер текущей строки var idCol = e.range.getColumn(); // получаем номер текущего столбца var sheet = range.getSheet(); if(sheet.getName() === 'Лист1' && ((idCol === 3 && idRow > 1 ) || (idCol === 4 && idRow > 1 ) || (idCol === 5 && idRow > 1 ))) { var sheet = e.source.getActiveSheet(); var value = sheet.getRange(idRow, idCol).getDisplayValue(); // получаем введённое значение в ячейку не в том виде, в каком оно хранится, а в том как оно отображается на экране var regex = /^(\d{1}|\d{2})(\d{2})(\d{2}|\d{4})$/; if (value.includes(".")) return; // проверяем на наличие точек в введённой строке, и если находим прерываем выполнение скрипта if(regex.test(value)) { var match = regex.exec(value); var day = match[1]; var month = match[2]; if (month[0] === '0') { month = month[1]; } month = parseInt(month) - 1; var year = match[3]; if(year.length === 2) { var current_year = parseInt(new Date().getFullYear().toString().substr(-2)); var input_year = parseInt(year); year = input_year <= current_year + 29 ? '20' + year : '19' + year; } var date = new Date(year, month, day); date.setDate(date.getDate()+1); // добавляем один день, иначе он будет уменьшен на единицу var dateString = Utilities.formatDate(date, 'GMT', 'dd.MM.yyyy'); sheet.getRange(range.getRow(), range.getColumn()).setValue(dateString); } } }Что сделал я, ещё помимо встраивания данного скрипта в Google таблицы:
1. Нужным столбцам присвоил формат ячеек - Дата. Это позволило вводить даты с любым разделителем. В отличие от Excel таблицы понимают следующие варианты написания дат: 18,02,2021 18/02/2021 18 02 21 и на полном автомате, без всякого скрипта преобразуют указанное написание в нормальное с разделением через точки: 18.02.2021.
2. Настроил проверку данных по типу "Допустимая дата". Это позволило вводить даты просто двойным кликом мышки в нужную ячейку. При таком клике открывается календарик, где можно выбрать желаемую дату.
P.S. Изменения настроек названия листа, и желаемых столбцов и строк, переместились в новом скрипте в строку 6.
Но в вашем коде вроде нет быстрого ввода времени. Только быстрый ввод даты.
Мы перешли с Excel на Google Sheets и для табеля учёта времени это очень пригодилось бы.
Спасибо за решение, отлично работает. В моем случае стало удобнее вводить время без двуеточия.
НО есть один нюанс: если пользователь введет время по привычке с двуеточием, то код меняет время.
Подскажите пожалуйста, как "допилить" код, чтобы в случае, если пользователь внес время по привычке с двуеточием, то ничего не происходило.
Долгих лет автору сего макроса!!!!!
Очень классно. У меня всё работает как нужно.
Пока не смог переделать еще и на секунды))))), но всё равно "победю".
Еще раз огромное спасибо за Ваш труд.
Подскажите по поводу данного макроса - можно как то ставить год по умолчанию 2025?) Типа вводим 1501, а дата 15.01.2025 ?
function onEdit(e) { var range = e.range; var idRow = range.getRow(); var idCol = range.getColumn(); var sheet = range.getSheet(); if (sheet.getName() === 'Лист1' && (idCol === 3 || idCol === 4 || idCol === 5) && idRow > 1) { var value = sheet.getRange(idRow, idCol).getDisplayValue(); if (value.includes(".")) return; // если есть точки — пропускаем var currentYearFull = new Date().getFullYear().toString(); // например, "2025" var currentYearShort = currentYearFull.substr(2, 2); // например, "25" value = value.trim(); var timeZone = SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone(); if (/^\d{2}$/.test(value)) { // 2-значное число, например "83" -> день 8, месяц 3 var day = value.substr(0, 1); var month = value.substr(1, 1); day = day.length === 1 ? '0' + day : day; var year = parseInt(currentYearFull, 10); var date = new Date(year, parseInt(month, 10) - 1, parseInt(day, 10)); if (isValidDate(date, day, month, year)) { var dateString = Utilities.formatDate(date, timeZone, 'dd.MM.yyyy'); sheet.getRange(idRow, idCol).setValue(dateString); } } else if (/^\d{3}$/.test(value)) { // 3-значное число // Проверяем, если первые две цифры >= 10 и последняя цифра от 1 до 9 var dayPart = value.substr(0, 2); var monthPart = value.substr(2, 1); var dayNum = parseInt(dayPart, 10); var monthNum = parseInt(monthPart, 10); var year = parseInt(currentYearFull, 10); if (dayNum >= 10 && monthNum >= 1 && monthNum <= 9) { // Случай: день 28, месяц 1 (например 281) var day = dayPart; var month = monthPart; } else { // Иначе считаем, что день — первая цифра, месяц — две следующие (как раньше) day = value.substr(0, 1); month = value.substr(1, 2); day = day.length === 1 ? '0' + day : day; } var date = new Date(year, parseInt(month, 10) - 1, parseInt(day, 10)); if (isValidDate(date, day, month, year)) { var dateString = Utilities.formatDate(date, timeZone, 'dd.MM.yyyy'); sheet.getRange(idRow, idCol).setValue(dateString); } } else if (/^\d{4}$/.test(value)) { // Четырёхзначное число, например "1210" -> день 12, месяц 10, год текущий var day = value.substr(0, 2); var month = value.substr(2, 2); var year = parseInt(currentYearFull, 10); var date = new Date(year, parseInt(month, 10) - 1, parseInt(day, 10)); if (isValidDate(date, day, month, year)) { var dateString = Utilities.formatDate(date, timeZone, 'dd.MM.yyyy'); sheet.getRange(idRow, idCol).setValue(dateString); } } else { // Прежний функционал для 6 или 8 цифр var regex = /^(\d{1,2})(\d{2})(\d{2}|\d{4})$/; if (regex.test(value)) { var match = regex.exec(value); var day = match[1]; var month = match[2]; if (month[0] === '0') { month = month[1]; } month = parseInt(month, 10) - 1; var year = match[3]; if (year.length === 2) { var current_year_short = parseInt(currentYearShort, 10); var input_year = parseInt(year, 10); year = input_year <= current_year_short + 29 ? '20' + year : '19' + year; } var date = new Date(year, month, parseInt(day, 10)); if (isValidDate(date, day, month + 1, year)) { var dateString = Utilities.formatDate(date, timeZone, 'dd.MM.yyyy'); sheet.getRange(idRow, idCol).setValue(dateString); } } } } } // Проверка корректности даты function isValidDate(d, day, month, year) { return d && d.getFullYear() == year && d.getMonth() + 1 == parseInt(month, 10) && d.getDate() == parseInt(day, 10); }Поэтому, я пока остановился на двух вариантах решения:
1. Перед тем, как использовать скрипт, столбец с датами переводим в текстовый или числовой формат. В этом случае, введённое число Excel ни как не преобразует, и скрипт отрабатывает хорошо. По идее, в скрипт можно добавить функцию, которая сразу сменит формат ячейки на "Дата", но тогда, если пользователь решит изменить дату, то скрипт уже будет отрабатывать неверно. Поэтому я этого делать не стал. По факту многим пользователям, неважен формат ячейки, им главное, чтобы правильно отображалось на экране. Тогда ячейки в формат даты можно и не преобразовывать.
Ниже следует скрипт на VBA для MS Excel, который меняет числа (поддерживает с 2-х по 8-мизначные числа). Главное, не забудьте перед вводом чисел, преобразовать ячейки в нужном диапазоне в текстовый или числовой формат:
Private Sub Worksheet_Change(ByVal Target As Range) If Not Intersect(Target, Range("A2:A19")) Is Nothing And Target.Cells.Count = 1 Then On Error GoTo ErrorHandler Application.EnableEvents = False Dim inputVal As String Dim d As Integer, m As Integer, y As Integer Dim resultDate As Date Dim currentYear As Integer currentYear = Year(Date) inputVal = CStr(Target.Value) inputVal = Replace(inputVal, ".", "") inputVal = Replace(inputVal, "/", "") inputVal = Trim(inputVal) If Not IsNumeric(inputVal) Then GoTo ExitHandler Select Case Len(inputVal) Case 2 d = CInt(Left(inputVal, 1)) m = CInt(Right(inputVal, 1)) y = currentYear Case 3 d = CInt(Left(inputVal, 1)) m = CInt(Right(inputVal, 2)) If m < 1 Or m > 12 Then d = CInt(Left(inputVal, 2)) m = CInt(Right(inputVal, 1)) If m = 0 Then m = 10 End If y = currentYear Case 4 d = CInt(Left(inputVal, 2)) m = CInt(Right(inputVal, 2)) y = currentYear Case 5 d = CInt(Left(inputVal, 1)) m = CInt(Mid(inputVal, 2, 2)) y = 2000 + CInt(Right(inputVal, 2)) If m < 1 Or m > 12 Then d = CInt(Left(inputVal, 2)) m = CInt(Mid(inputVal, 3, 1)) y = 2000 + CInt(Right(inputVal, 2)) End If Case 6 d = CInt(Left(inputVal, 2)) m = CInt(Mid(inputVal, 3, 2)) y = 2000 + CInt(Right(inputVal, 2)) Case 8 d = CInt(Left(inputVal, 2)) m = CInt(Mid(inputVal, 3, 2)) y = CInt(Right(inputVal, 4)) Case Else GoTo ExitHandler End Select If d >= 1 And d <= 31 And m >= 1 And m <= 12 Then resultDate = DateSerial(y, m, d) Target.NumberFormat = "dd.mm.yyyy" Target.Value = resultDate End If ExitHandler: Application.EnableEvents = True Exit Sub ErrorHandler: Resume ExitHandler End If End SubВ данном примере скрипта в строке 2 указан диапазон для преобразования A2:A19. Измените его на свой.
2. Вторая версия скрипта не требует предварительного перевода ячеек в текстовый формат. Работает полностью аналогично первой версии скрипта. За одной лишь разницей, что при клике в ячейку в нужном диапазоне, выскакивает окно ввода InputBox, в которое и нужно ввести число. В этом варианте число, введённое через форму, напрямую попадает для обработки скрипту, и поэтому скрипт отрабатывает число правильно. Но, само окно InputBox, не совсем удобный вариант:
Private Sub Worksheet_SelectionChange(ByVal Target As Range) If Not Intersect(Target, Range("A2:A19")) Is Nothing And Target.Cells.Count = 1 Then Dim inputVal As String Dim d As Integer, m As Integer, y As Integer Dim currentYear As Integer Dim resultDate As Date currentYear = Year(Date) inputVal = InputBox("Введите дату цифрами (например 130525 или 83):", "Ввод даты") If inputVal = "" Then Exit Sub If Not IsNumeric(inputVal) Then MsgBox "Ввод должен быть числом", vbExclamation Exit Sub End If inputVal = Trim(inputVal) On Error GoTo InvalidInput Select Case Len(inputVal) Case 2 d = CInt(Left(inputVal, 1)) m = CInt(Right(inputVal, 1)) y = currentYear Case 3 d = CInt(Left(inputVal, 1)) m = CInt(Right(inputVal, 2)) If m < 1 Or m > 12 Then d = CInt(Left(inputVal, 2)) m = CInt(Right(inputVal, 1)) If m = 0 Then m = 10 End If y = currentYear Case 4 d = CInt(Left(inputVal, 2)) m = CInt(Right(inputVal, 2)) y = currentYear Case 5 d = CInt(Left(inputVal, 2)) m = CInt(Mid(inputVal, 3, 1)) y = 2000 + CInt(Right(inputVal, 2)) If m < 1 Or m > 12 Then d = CInt(Left(inputVal, 1)) m = CInt(Mid(inputVal, 2, 2)) End If Case 6 d = CInt(Left(inputVal, 2)) m = CInt(Mid(inputVal, 3, 2)) y = 2000 + CInt(Right(inputVal, 2)) Case 8 d = CInt(Left(inputVal, 2)) m = CInt(Mid(inputVal, 3, 2)) y = CInt(Right(inputVal, 4)) Case Else GoTo InvalidInput End Select If d >= 1 And d <= 31 And m >= 1 And m <= 12 Then resultDate = DateSerial(y, m, d) Target.NumberFormat = "dd.mm.yyyy" Target.Value = resultDate Else GoTo InvalidInput End If End If Exit Sub InvalidInput: MsgBox "Неверный формат даты!", vbCritical End Sub