[英]Formatting Dates & regional setting Excel VBA
这非常令人沮丧,对我来说没有意义。
这是在 Excel 2010 中。
我录制了一个宏来格式化一些数据,包括日期(DD/MM/YYYY)。 我导入它,Excel 将其视为文本。 所以我使用我记录的“文本到日期”宏,存储到 VBA 子中。
这是录制的宏:
Sub DateFormatting()
Sheets("Donnees").Select
Columns("H:H").Select
Selection.TextToColumns Destination:=Range("H1"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, _
Semicolon:=False, Comma:=False, Space:=False, Other:=False, OtherChar _
:="/", FieldInfo:=Array(1, 4), TrailingMinusNumbers:=True
End Sub
当我导入我的数据时,起初我有这个。 您可以看到它被 Excel 视为 TEXT,因为它是左对齐的(H 列,我确认它确实是):
然后我运行宏,得到这个(H 列):
您可以看到 Excel 将其视为日期,因为它是右对齐的。 如果我将它转换为“数字”,我会看到底层序列,正如预期的那样。 所以有人会认为这很好。 但实际上并非如此:
如果我再次运行宏(我会,因为稍后会添加更多数据,所以我需要确保这些底部新导入的数据也将正确格式化),我得到这个:
所以基本上它将格式从 DD/MM/YYYY(它应该是)更改为 MM/DD/YYYY(这是错误的)。 如果我在该数据集上再次运行该宏,它会切换回 DD/MM/YYYY。
但最糟糕的是,如果我手动执行完全相同的操作(例如,我没有运行宏,而是手动将 go 设置为“数据”、“文本到列”和 select 完全相同的选项),那么它不会改变。 如果日期格式为 DD/MM/YYYY,它会保持这种状态,如果它的格式是 MM/DD/YYYY(因为这个愚蠢的怪癖),那么它也会保持这种状态。 我重复了很多次(甚至重新录制了几次宏)以确保我做完全相同的事情。
我知道这是因为区域设置,但该文件不会一直在我的计算机上使用,而且我无法确保最终用户将拥有任何特定的区域设置。 基本上,我需要这个文件独立于区域设置。
我的问题是:我怎样才能确保这些日期是:
我知道我可以进行中间导入步骤(并在那里格式化数据,而不是在主文件中),或者然后调整代码,以便只有新导入的数据才应用宏......但后来我觉得不是可靠,因为我怎么知道 Excel 不会弄乱格式?
哦,因为看 VBA 的宏有点神秘:
我 go 到数据,文本到列,选择“分隔”(没关系,因为我实际上并没有将其拆分为列),然后将“分隔符”作为默认值(再次无关紧要,我实际上并没有拆分文本到列中),然后是“日期”,然后在 DD/MM/YYYY 的下拉列表“DYM”中
编辑:有关详细信息,请参阅 Ron Rosenfield 的回答。 为了完整起见,我将运行以下代码来导入数据并在导入时对其进行格式化,而不是先导入然后格式化:
Sub importData()
Dim myFile As String
myFile = Sheet5.Cells(2, 5).Value 'My metadata sheet, containing name of file to import
Sheet1.Select 'Setting target sheet as active worksheet
With ActiveSheet.QueryTables.Add(Connection:="TEXT;C:\ChadTest\" & myFile, _
Destination:=Sheet1.Cells(Sheet5.Cells(2, 1).Value, 1))
.Name = "ExternalData_1"
.FieldNames = True
.RowNumbers = False
.FillAdjacentFormulas = False
.PreserveFormatting = True
.RefreshOnFileOpen = False
.RefreshStyle = xlOverwriteCells
.SavePassword = False
.SaveData = True
.AdjustColumnWidth = True
.RefreshPeriod = 0
.TextFilePromptOnRefresh = False
.TextFilePlatform = 1252
If Sheet1.Cells(1, 1).Value = "" Then
.TextFileStartRow = 1
Else
.TextFileStartRow = 2
End If
.TextFileParseType = xlDelimited
.TextFileTextQualifier = xlTextQualifierDoubleQuote
.TextFileConsecutiveDelimiter = False
.TextFileTabDelimiter = True
.TextFileSemicolonDelimiter = False
.TextFileCommaDelimiter = True
.TextFileSpaceDelimiter = False
.TextFileColumnDataTypes = Array(1, 1, 1, 4, 1, 1, 1, 4, 1, 1, 1, 1)
.TextFileTrailingMinusNumbers = True
.Refresh BackgroundQuery:=False
End With
Sheet1.Select
Application.WindowState = xlMinimized
Application.WindowState = xlNormal
End Sub
从您写的内容看来,您似乎是从在Excel中打开的TEXT或CSV文件中获取数据,然后尝试处理日期。 常见问题。
相反,您可以做的是导入文件。 如果这样做,文本导入向导(与文本到列向导相同)将在数据写入工作表之前打开,并为您提供指定导入日期格式的机会。
该选项应该在数据功能区的“获取外部数据”选项卡上:
对于从源头将数据获取到Excel工作表的确切过程,我有些困惑,但是整个过程肯定可以使用VBA自动化。 并且不需要在已导入的数据上运行宏。
这就是我在上面的评论中提到的内容:
Sub DateFormatting()
On Error Resume Next
Application.ScreenUpdating = False
Dim wbCurrent As Workbook
Dim wsCurrent As Worksheet
Dim nLastRow, i, nDay, nMonth, nYear As Integer
Dim lNewDate As Long
Set wbCurrent = ActiveWorkbook
Set wsCurrent = wbCurrent.ActiveSheet
nLastRow = wsCurrent.Cells.Find("*", LookIn:=xlValues, SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
For i = nLastRow To 2 Step -1
If InStr(1, wsCurrent.Cells(i, 8), "/", vbBinaryCompare) > 0 Then
nYear = Right(wsCurrent.Cells(i, 8), 4)
nMonth = Mid(wsCurrent.Cells(i, 8), InStr(1, wsCurrent.Cells(i, 8), "/", vbBinaryCompare) + 1, 2)
nDay = Left(wsCurrent.Cells(i, 8), 2)
lNewDate = CLng(DateSerial(nYear, nMonth, nDay))
wsCurrent.Cells(i, 8).Value = lNewDate
End If
Next i
wsCurrent.Range("H:H").NumberFormat = "dd/mm/yyyy"
Application.ScreenUpdating = True
End Sub
此代码假定原始原始数据将始终以dd / mm / yyyy格式作为文本导入,包括前导零。
除了格式化日期外,它不会触及任何实际日期(即单元格的实际值是一个序列号)。
在这里,我假设您的日期将始终以dd/mm/yyyy
格式书写。
如果我只有一个单元格要检查,则可以这样做:
Dim s() As String
With Range("A1")
If .NumberFormat = "@" Then
'It's formatted as text.
.NumberFormat = "dd/mm/yyyy"
s = Split(.Value, "/")
.Value = DateSerial(CInt(s(2)), CInt(s(1)), CInt(s(0)))
Else
'It isn't formatted as text. Do nothing.
End If
End With
但是,如果要检查的单元格很多,这将无法很好地扩展。 通过细胞循环很慢。 这样遍历数组要快得多,如下所示:
Dim r As Range
Dim v As Variant
Dim i As Long
Dim s() As String
Set r = Range("H:H").Resize(GetLastNonblankRowNumber(Range("H:H"))) 'or wherever
v = r.Value ' read all values into an array (could be strings, could be real dates)
For i = 1 To UBound(v, 1)
If VarType(v(i, 1)) = vbString Then
s = Split(v(i, 1), "/")
v(i, 1) = DateSerial(CInt(s(2)), CInt(s(1)), CInt(s(0)))
End If
Next i
With r
.NumberFormat = "dd/mm/yyyy"
.Value = v 'write dates back to sheet
End With
我在这里利用:
Function GetLastNonblankRowNumber(r As Range) As Long
GetLastNonblankRowNumber = r.Find("*", r.Cells(1, 1), xlFormulas, xlPart, _
xlByRows, xlPrevious).Row
End Function
实际上,我们在工作中遇到了很多问题。 我们发现解决此问题的最佳方法是确保基本数据中的日期采用“ YYYY / MM / DD”格式,这样就可以将其识别为美国或普通日期格式中的同一日期,并且实际上会正确遵循您的区域设置以在Excel中放置正确的日期...
如果您正在打开文本文件(例如 csv),则使用 vba 强制使用本地日期格式打开它 Workbooks.Open Filename:=strPath & strFile, Local:=True
避免所有变通方法
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.